Compare commits
138 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6de0914788 | |||
| e4f702d1e2 | |||
| 2f864c5e15 | |||
| 7139590798 | |||
| 0870655455 | |||
| ba24a7bb8c | |||
| f32d7465a2 | |||
| e202698bf1 | |||
| 11c2bf7dae | |||
| 53cf0e1148 | |||
| 2d1e49e3f2 | |||
| 199161f611 | |||
| ece5c775f1 | |||
| 3121c43b71 | |||
| 51e8caf369 | |||
| e7e7937678 | |||
| a0f5a60ea0 | |||
| af29c6e441 | |||
| 00dfecb1c7 | |||
| cc249bef15 | |||
| 9a2231804d | |||
| 1edcf15bcd | |||
| 0190e08763 | |||
| d3cc5a69c4 | |||
| 07ca53a778 | |||
| 6910b6d024 | |||
| f02c934ad7 | |||
| 236680aab9 | |||
| 6316f97c06 | |||
| 14830af306 | |||
| b620decde7 | |||
| b08ca620a0 | |||
| 66a2bb0393 | |||
| 0675844d97 | |||
| 148eda2990 | |||
| 330f7b4002 | |||
| def0fc8866 | |||
| fa1bcf9608 | |||
| 5a5e56a47b | |||
| e64f40eaa3 | |||
| 3f3d86ad4c | |||
| 91833dd72e | |||
| c049f7c11f | |||
| 6bd2f3bff5 | |||
| 8c3c519679 | |||
| 4e5002a31e | |||
| 5a53c58b04 | |||
| a61d1e3d10 | |||
| 1a50f99189 | |||
| 6caacbf388 | |||
| f876aa7d0f | |||
| 31e1281600 | |||
| 90072a9332 | |||
| bacd2893b9 | |||
| 7362fd859b | |||
| 3811fb46b3 | |||
| ab42c0c0df | |||
| de5aed2f6a | |||
| ce68a88293 | |||
| 288f75929d | |||
| 8528dd0c7f | |||
| e0f35eceab | |||
| 7e36517a79 | |||
| 69327766bb | |||
| 9ecf8a61ba | |||
| 2323506c82 | |||
| caa1a55e7a | |||
| 2ce44c157a | |||
| e17778190d | |||
| d1d81a0043 | |||
| ddb5ced3d5 | |||
| 9cebe655fd | |||
| 51b5a11a54 | |||
| f3405851a7 | |||
| 5b6870d610 | |||
| c3dceaff5c | |||
| 0a9b35c039 | |||
| dc32997f45 | |||
| 67c64c17d5 | |||
| 9cc02982be | |||
| b4c70a62a9 | |||
| c9d5921f4e | |||
| 0f47a4de5d | |||
| 0ba5e4d3c7 | |||
| 73e61dbcff | |||
| ba142e3b19 | |||
| f651a401e8 | |||
| 3301252fb2 | |||
| fe77e2b0d3 | |||
| d586567c3b | |||
| 211178d39e | |||
| d8354faa49 | |||
| f9e5c05046 | |||
| 596afe9451 | |||
| f7b5de986a | |||
| ee9262472b | |||
| 8a6a8db730 | |||
| 3642370f11 | |||
| 2db2cfb81c | |||
| 279b028c5e | |||
| f268827a2e | |||
| d8bc065057 | |||
| 6301bbbb66 | |||
| 3ad65eaef8 | |||
| 3d1bc7949d | |||
| 26e092fd20 | |||
| be7ddec1b0 | |||
| 5781ac6bbd | |||
| a51bc4427a | |||
| 80854584bb | |||
| 93af289d3b | |||
| a27b882493 | |||
| ba6f957692 | |||
| ec0e1bf26f | |||
| e4df298bb4 | |||
| 804d21e31d | |||
| 6d33687866 | |||
| 21bb1efb46 | |||
| 9d0986f1ca | |||
| fd483f397f | |||
| 09364850ee | |||
| 4d3a8bad4c | |||
| cc39b0aa41 | |||
| 1cceb7d4b4 | |||
| 6fc3b4be75 | |||
| af6d690fc8 | |||
| eea5152444 | |||
| 1ca38b625b | |||
| f964407c47 | |||
| 3be2e76ec0 | |||
| f9a9b90ed7 | |||
| 1f2917ac22 | |||
| fe1a8569f6 | |||
| 33e8d4f328 | |||
| e183260d0a | |||
| b4023942b4 | |||
| c10c24d90d | |||
| 915f63cac4 |
+6
-2
@@ -1,8 +1,12 @@
|
||||
rvm:
|
||||
- 1.9.3
|
||||
- 2.0.0
|
||||
- 2.1.0
|
||||
- 2.1.1
|
||||
- 2.2.2
|
||||
- 2.3.0
|
||||
- 2.4.0
|
||||
- jruby-9.1.8.0
|
||||
jdk:
|
||||
- oraclejdk8
|
||||
before_install:
|
||||
- sudo apt-get update
|
||||
- sudo apt-get install libicu-dev
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
# Contributing to Gollum
|
||||
|
||||
Thanks for your interest in the gollum project!
|
||||
|
||||
## Submitting an Issue
|
||||
|
||||
Please note that the issue tracker is meant for:
|
||||
|
||||
1. Bug reports.
|
||||
2. Feature requests.
|
||||
|
||||
If your have problems using or installing the software which stem from bugs in the software or a lack of documentation, we are always happy to help out! However, **for ordinary usage questions, please consider asking elsewhere**, for instance on [StackOverflow](http://stackoverflow.com/questions/tagged/gollum-wiki).
|
||||
|
||||
Gollum supports [custom macros](https://github.com/gollum/gollum/wiki#macros) for the creation of additional wiki markup tags. Please **do not** use this tracker to request macros specific to your situation. However, if you have or are working on a macro that you think may be useful to more users, you can share it as a GitHub [gist](https://gist.github.com) and link to it in the [wiki](https://github.com/gollum/gollum/wiki/Custom-macros).
|
||||
|
||||
Before submitting an issue, **please carefully look through the following places** to make sure your problem is not already addressed:
|
||||
|
||||
1. The issue tracker.
|
||||
1. The [README](https://github.com/gollum/gollum/blob/master/README.md).
|
||||
1. The project's [wiki](https://github.com/gollum/gollum/wiki).
|
||||
|
||||
Lastly, please **consider helping out** by opening a Pull Request!
|
||||
|
||||
## Opening a Pull Request
|
||||
|
||||
Pull Requests fixing bugs, implementing new features, or updating documentation and dependencies are all very welcome! If you would like to help out with the project, you can pick an open issue from the issue tracker. We're more than happy to help you get started! Here's how you can proceed:
|
||||
|
||||
1. Fork and clone Gollum.
|
||||
2. Create a thoughtfully named topic branch to contain your changes.
|
||||
3. If you haven't installed dependencies yet, navigate to your clone and execute:
|
||||
```
|
||||
[sudo] bundle install
|
||||
```
|
||||
4. Hack away.
|
||||
5. Add your own tests and make sure they're all still passing.
|
||||
6. If some of your changes deserve a mention on Gollum's home page, edit the README accordingly.
|
||||
7. If necessary, rebase your commits into logical chunks, without errors.
|
||||
8. Push the branch to your fork on GitHub.
|
||||
9. Create a pull request for Gollum.
|
||||
|
||||
**Notes:**
|
||||
* Do not change Gollum's version numbers, we will do that on our own.
|
||||
|
||||
### Running tests
|
||||
|
||||
1. Install [Bundler](http://bundler.io/).
|
||||
2. Navigate to the cloned source of Gollum.
|
||||
3. Install dependencies:
|
||||
```
|
||||
[sudo] bundle install
|
||||
```
|
||||
4. Run the tests:
|
||||
```
|
||||
bundle exec rake test
|
||||
```
|
||||
|
||||
### Working with test repositories
|
||||
|
||||
An example of how to add a test file to the bare repository lotr.git.
|
||||
|
||||
```
|
||||
mkdir tmp
|
||||
cd tmp
|
||||
git clone ../lotr.git/
|
||||
git log
|
||||
echo "test" > test.md
|
||||
git add .
|
||||
git commit -am "Add test"
|
||||
git push ../lotr.git/ master
|
||||
```
|
||||
|
||||
## Releasing the gem
|
||||
|
||||
Gollum uses [Semantic Versioning](http://semver.org/).
|
||||
|
||||
x.y.z
|
||||
|
||||
For z releases:
|
||||
|
||||
```
|
||||
rake bump
|
||||
rake release
|
||||
```
|
||||
|
||||
For x.y releases:
|
||||
|
||||
```
|
||||
# First update VERSION in lib/gollum.rb and then:
|
||||
rake gemspec
|
||||
rake release
|
||||
```
|
||||
+14
@@ -1,3 +1,17 @@
|
||||
# 4.1.2 /2017-08-07
|
||||
|
||||
* Lock to a newer version of gollum-lib to avoid installing an outdated and vulnerable dependency (nokogiri) on ruby 2.0. See https://github.com/gollum/gollum-lib/pull/279. Note: this breaks semantic versioning so those using outdated rubies will discover the problem on update.
|
||||
|
||||
# 4.1.0 /2017-03-09
|
||||
|
||||
* Added file deletion functionality to file view
|
||||
* Various performance improvements
|
||||
* Emoji support
|
||||
|
||||
# 4.0.0 /2015-04-11
|
||||
|
||||
* Now compatible with JRuby (via the [rjgit](https://github.com/repotag/rjgit) [adapter](https://github.com/repotag/gollum-lib_rjgit_adapter))
|
||||
|
||||
# 3.1.1 /2014-12-04
|
||||
|
||||
* Security fix for [remote code execution issue](https://github.com/gollum/gollum/issues/913). Please update!
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vulputate tincidunt sollicitudin. Quisque sit amet leo sed nunc eleifend rhoncus in consectetur leo. Vivamus posuere semper convallis. Duis malesuada lacus sed erat lobortis tincidunt mattis ligula consectetur. Sed aliquam vulputate eros at euismod. Aenean egestas lorem ligula, quis faucibus turpis. Curabitur a eros in ipsum gravida ornare. Sed elementum enim vel mi scelerisque dapibus. Nulla id libero ligula, quis tempus sem. Morbi nec felis tortor, ac cursus risus. Mauris elementum tortor id lacus eleifend non lobortis tellus pharetra. Etiam posuere cursus vestibulum. $x \lt y$
|
||||
|
||||
Morbi tincidunt dolor vel massa dictum volutpat. Vestibulum quis nibh metus, id tincidunt nisl. Vivamus eget sem ac risus eleifend rhoncus at eu nisl. Nunc elit massa, vulputate ac gravida eget, condimentum quis justo. Integer scelerisque, libero vel consequat ultricies, eros libero sagittis libero, pellentesque bibendum quam massa ut orci. Maecenas sit amet urna eget quam aliquam posuere. Nulla facilisi. Duis imperdiet augue sit amet metus ornare et hendrerit dui feugiat. Aenean a lacus neque. Sed eu enim tincidunt dolor pharetra porttitor. Vestibulum ut felis dui, rutrum iaculis orci. Duis eu bibendum tortor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse mollis sagittis purus sit amet sollicitudin. Phasellus vel interdum erat.
|
||||
@@ -1,4 +1,4 @@
|
||||
gollum -- A wiki built on top of Git
|
||||
gollum -- A git-based Wiki
|
||||
====================================
|
||||
|
||||
[](http://badge.fury.io/rb/gollum)
|
||||
@@ -7,229 +7,171 @@ gollum -- A wiki built on top of Git
|
||||
|
||||
## DESCRIPTION
|
||||
|
||||
Gollum is a simple wiki system built on top of Git.
|
||||
Gollum is a simple wiki system built on top of Git. A Gollum Wiki is simply a git repository (either bare or regular) of a specific nature:
|
||||
* A Gollum repository's contents are human-editable, unless the repository is bare. Pages are unique text files which may be organized into directories any way you choose. Other content can also be included, for example images, PDFs and headers/footers for your pages.
|
||||
* Gollum pages:
|
||||
* May be written in a variety of [markups](#markups).
|
||||
* Can be edited with your favourite system editor or IDE (changes will be visible after committing) or with the built-in web interface.
|
||||
* Can be displayed in all versions (commits).
|
||||
|
||||
Gollum wikis are simply Git repositories that adhere to a specific format.
|
||||
Gollum pages may be written in a variety of formats and can be edited in a
|
||||
number of ways depending on your needs. You can edit your wiki locally:
|
||||
Gollum can be launched either as a webserver (with the web interface) or in "console mode", where you can use a predefined API to query and manipulate the repository. For more information, see the [Running](#running) and [Configuration](#configuration) sections.
|
||||
|
||||
* With your favorite text editor or IDE (changes will be visible after committing).
|
||||
* With the built-in web interface.
|
||||
* With the Gollum Ruby API.
|
||||
For more information on Gollum's capabilities and pitfalls:
|
||||
|
||||
Gollum follows the rules of [Semantic Versioning](http://semver.org/) and uses
|
||||
[TomDoc](http://tomdoc.org/) for inline documentation.
|
||||
1. [Syntax/capability overview for pages](https://github.com/gollum/gollum/wiki).
|
||||
2. [Known limitations](https://github.com/gollum/gollum/wiki/Known-limitations).
|
||||
3. [Troubleshoot guide](https://github.com/gollum/gollum/wiki/Troubleshoot-guide).
|
||||
4. [Security overview](https://github.com/gollum/gollum/wiki/Security).
|
||||
|
||||
### Videos
|
||||
|
||||
* [Quick impression of gollum](https://www.youtube.com/watch?v=gj1qqK3Oku8)
|
||||
* [Gollum overview and simple markdown tutorial (german with english subtitles)](https://www.youtube.com/watch?v=wfWgDRmcbU4)
|
||||
* [Advanced features in action](https://www.youtube.com/watch?v=EauxgxsLDC4)
|
||||
|
||||
## SYSTEM REQUIREMENTS
|
||||
|
||||
- Python 2.5+ (2.7.3 recommended)
|
||||
- Ruby 1.9.3+ (1.9.3 recommended)
|
||||
- Unix like operating system (OS X, Ubuntu, Debian, and more)
|
||||
- Will not work on Windows (because of [grit](https://github.com/github/grit))
|
||||
| Operating System | Ruby | Adapters | Supported |
|
||||
| ---------------- | -------------- | ------------------ | --------- |
|
||||
| Unix/Linux-like | Ruby 1.9.3+ | all except [RJGit](https://github.com/repotag/rjgit) | yes |
|
||||
| Unix/Linux-like | [JRuby](https://github.com/jruby/jruby) (1.9.3+ compatible) | [RJGit](https://github.com/repotag/rjgit) | yes |
|
||||
| Windows | Ruby 1.9.3+ | all except [RJGit](https://github.com/repotag/rjgit) | no |
|
||||
| Windows | [JRuby](https://github.com/jruby/jruby) (1.9.3+ compatible) | [RJGit](https://github.com/repotag/rjgit) | almost<sup>1</sup> |
|
||||
|
||||
## SECURITY
|
||||
**Notes:**
|
||||
|
||||
Don't enable `--custom-css`, `--custom-js` or `--mathjax-config` unless you trust every user who has the ability to edit the wiki.
|
||||
A better solution with more security is being tracked in [#665](https://github.com/gollum/gollum/issues/665).
|
||||
1. There are still some bugs and this setup is not ready for production yet. You can track the progress at [Support Windows via JRuby - Meta Issue](https://github.com/gollum/gollum/issues/1044).
|
||||
|
||||
## INSTALLATION
|
||||
|
||||
The best way to install Gollum is with RubyGems:
|
||||
Varies depending on operating system, package manager and Ruby installation. Generally, you should first install Ruby and then Gollum.
|
||||
|
||||
```bash
|
||||
$ [sudo] gem install gollum
|
||||
```
|
||||
You may first need to install some additional [development packages](https://github.com/gollum/gollum/wiki/Installation) for your OS.
|
||||
1. Ruby is best installed either via [RVM](https://rvm.io/) or a package manager of choice.
|
||||
2. Gollum is best installed via RubyGems:
|
||||
```
|
||||
[sudo] gem install gollum
|
||||
```
|
||||
|
||||
If you're installing from source, you can use [Bundler][bundler] to pick up all the
|
||||
gems:
|
||||
Installation examples for individual systems can be seen [here](https://github.com/gollum/gollum/wiki/Installation).
|
||||
|
||||
```bash
|
||||
$ bundle install
|
||||
```
|
||||
**Notes:**
|
||||
* Whichever Ruby implementation you're using, Gollum ships with the appropriate default git adapter. So the above installation procedure is common for both MRI and JRuby.
|
||||
* If you're installing from source:
|
||||
* Optionally uninstall any previous versions of Gollum:
|
||||
```
|
||||
[sudo] gem uninstall -aIx gollum
|
||||
```
|
||||
* Install [Bundler](http://bundler.io/).
|
||||
* Navigate to the cloned source of Gollum.
|
||||
* Install dependencies:
|
||||
```
|
||||
[sudo] bundle install
|
||||
```
|
||||
* Build:
|
||||
```
|
||||
rake build
|
||||
```
|
||||
* And install:
|
||||
```
|
||||
[sudo] gem install --no-document pkg/gollum*.gem
|
||||
```
|
||||
|
||||
In order to use the various formats that Gollum supports, you will need to
|
||||
separately install the necessary dependencies for each format. You only need
|
||||
to install the dependencies for the formats that you plan to use.
|
||||
### Markups
|
||||
|
||||
* [AsciiDoc](http://asciidoctor.org) -- `gem install asciidoctor`
|
||||
* [Creole](http://wikicreole.org/) -- `gem install creole`
|
||||
* [Markdown](http://daringfireball.net/projects/markdown/) -- `gem install redcarpet`
|
||||
* [GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown) -- `gem install github-markdown`
|
||||
* [Org](http://orgmode.org/) -- `gem install org-ruby`
|
||||
* [Pod](http://search.cpan.org/dist/perl/pod/perlpod.pod) -- `Pod::Simple::HTML` comes with Perl >= 5.10. Lower versions should install Pod::Simple from CPAN.
|
||||
Gollum presently ships with support for the following markups:
|
||||
* [Markdown](http://daringfireball.net/projects/markdown/syntax)
|
||||
* [RDoc](http://rdoc.sourceforge.net/)
|
||||
* [ReStructuredText](http://docutils.sourceforge.net/rst.html) -- `easy_install docutils`
|
||||
* [Textile](http://www.textism.com/tools/textile/) -- `gem install RedCloth`
|
||||
* [MediaWiki](http://www.mediawiki.org/wiki/Help:Formatting) -- `gem install wikicloth`
|
||||
|
||||
[bundler]: http://gembundler.com/
|
||||
|
||||
|
||||
## SYNTAX
|
||||
|
||||
Gollum supports a variety of formats and extensions (Markdown, MediaWiki, Textile, …).
|
||||
On top of these formats Gollum lets you insert headers, footers, links, image, math and more.
|
||||
|
||||
Check out the [Gollum Wiki](https://github.com/gollum/gollum/wiki) for all of Gollum's formats and syntactic options.
|
||||
Since all markups are rendered by the [github-markup](https://github.com/github/markup) gem, you can easily add support for other markups by additional installation:
|
||||
* [AsciiDoc](http://asciidoctor.org/docs/asciidoc-syntax-quick-reference/) -- `[sudo] gem install asciidoctor`
|
||||
* [Creole](http://www.wikicreole.org/wiki/CheatSheet) -- `[sudo] gem install creole`
|
||||
* [MediaWiki](http://www.mediawiki.org/wiki/Help:Formatting) -- `[sudo] gem install wikicloth`
|
||||
* [Org](http://orgmode.org/worg/dev/org-syntax.html) -- `[sudo] gem install org-ruby`
|
||||
* [Pod](http://perldoc.perl.org/perlpod.html) -- requires Perl >= 5.10 (the `perl` command must be available on your command line)
|
||||
* Lower versions should install `Pod::Simple` from CPAN.
|
||||
* [ReStructuredText](http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html) -- requires python >= 2 (the `python2` command must be available on your command line)
|
||||
* Note that Gollum will also need you to install `docutils` for your Python 2. Installation procedure can, again, vary depending on operating system and package manager.
|
||||
* [Textile](http://redcloth.org/hobix.com/textile/quick.html) -- `[sudo] gem install RedCloth`
|
||||
|
||||
By default, Gollum ships with the `kramdown` gem to render Markdown. However, you can use any [Markdown renderer supported by github-markup](https://github.com/github/markup/blob/master/lib/github/markup/markdown.rb). The thing to remember is that the first installed renderer from the list will be used. So, for example, `redcarpet` will NOT be used if `github/markdown` is installed.
|
||||
|
||||
## RUNNING
|
||||
|
||||
To view and edit your Gollum repository locally via the built in web
|
||||
interface, simply install the Gollum gem, navigate to your repository via the
|
||||
command line, and run the executable:
|
||||
Simply:
|
||||
|
||||
```bash
|
||||
$ gollum
|
||||
```
|
||||
1. Navigate to your git repository (wiki) via the command line.
|
||||
2. Run: `gollum`.
|
||||
3. Open `http://localhost:4567` in your browser.
|
||||
|
||||
This will start up a web server running the Gollum frontend and you can view
|
||||
and edit your wiki at http://localhost:4567. To get help on the command line
|
||||
utility, you can run it like so:
|
||||
This will start up a web server (WEBrick) running Gollum with a web interface, where you can view and edit your wiki.
|
||||
|
||||
```bash
|
||||
$ gollum --help
|
||||
```
|
||||
### Running from source
|
||||
|
||||
This will show you the options you can pass as arguments to the `gollum` command:
|
||||
1. `git clone https://github.com/gollum/gollum`
|
||||
2. `cd gollum`
|
||||
3. `[sudo] bundle install` (may not always be necessary).
|
||||
4. `bundle exec bin/gollum`
|
||||
* Like that, gollum assumes the target wiki (git repository) is the project repository itself. If it's not, execute `bundle exec bin/gollum <path-to-wiki>` instead.
|
||||
5. Open `http://localhost:4567` in your browser.
|
||||
|
||||
```bash
|
||||
Options:
|
||||
--port [PORT] Bind port (default 4567).
|
||||
--host [HOST] Hostname or IP address to listen on (default 0.0.0.0).
|
||||
--version Display current version.
|
||||
--config [CONFIG] Path to additional configuration file.
|
||||
--adapter [ADAPTER] Git adapter to use in the backend. Defaults to grit.
|
||||
--irb Start an irb process with gollum loaded for the current wiki.
|
||||
--css Inject custom css. Uses custom.css from root repository.
|
||||
--js Inject custom js. Uses custom.js from root repository.
|
||||
--template-dir [PATH] Specify custom template directory.
|
||||
--page-file-dir [PATH] Specify the sub directory for all page files (default: repository root).
|
||||
--base-path [PATH] Specify the base path for the served pages (default: /) Example: --base-path wiki yields the home page accessible at http://localhost:4567/wiki/.
|
||||
--gollum-path [PATH] Specify the path to the git repository to be served.
|
||||
--ref [REF] Specify the repository ref to use (default: master).
|
||||
--bare Specify that the repository is bare (only necessary when using the grit adapter).
|
||||
--no-edit Restricts editing capability through frontend.
|
||||
--no-live-preview Disables livepreview.
|
||||
--live-preview Enables livepreview.
|
||||
--allow-uploads [MODE] Allows file uploads. Modes: dir (default, store all uploads in the same directory), page (store each upload at the same location as the page).
|
||||
--mathjax Enables mathjax for rendering mathematical equations. Uses the TeX-AMS-MML_HTMLorMML config with the autoload-all extension by default.
|
||||
--mathjax-config [SOURCE] Inject custom mathjax config file. Uses mathjax.config.js from root repository by default.
|
||||
--user-icons [SOURCE] Set the history user icons. Valid values: gravatar, identicon, none. Default: none.
|
||||
--show-all Shows all files in file view. By default only valid pages are shown.
|
||||
--collapse-tree Collapse file view tree. By default, expanded tree is shown.
|
||||
--h1-title Sets page title to value of first h1.
|
||||
```
|
||||
### Rack
|
||||
|
||||
Note that the gollum server will not run on Windows because of [an issue](https://github.com/rtomayko/posix-spawn/issues/9) with posix-spawn (which is used by Grit).
|
||||
Gollum can also be ran with any [rack-compatible web server](https://github.com/rack/rack#supported-web-servers). More on that [over here](https://github.com/gollum/gollum/wiki/Gollum-via-Rack).
|
||||
|
||||
### RACK
|
||||
### Rack, with an authentication server
|
||||
|
||||
You can also run gollum with any rack-compatible server by placing this config.ru
|
||||
file inside your wiki repository. This allows you to utilize any Rack middleware
|
||||
like Rack::Auth, OmniAuth, etc.
|
||||
Gollum can also be ran alongside a CAS (Central Authentication Service) SSO (single sign-on) server. With a bit of tweaking, this adds basic user-support to Gollum. To see an example and an explanation, navigate [over here](https://github.com/gollum/gollum/wiki/Gollum-via-Rack-and-CAS-SSO).
|
||||
|
||||
```ruby
|
||||
#!/usr/bin/env ruby
|
||||
require 'rubygems'
|
||||
require 'gollum/app'
|
||||
### Docker
|
||||
|
||||
gollum_path = File.expand_path(File.dirname(__FILE__)) # CHANGE THIS TO POINT TO YOUR OWN WIKI REPO
|
||||
Precious::App.set(:gollum_path, gollum_path)
|
||||
Precious::App.set(:default_markup, :markdown) # set your favorite markup language
|
||||
Precious::App.set(:wiki_options, {:universal_toc => false})
|
||||
run Precious::App
|
||||
```
|
||||
Gollum can also be ran via [Docker](https://www.docker.com/). More on that [over here](https://github.com/gollum/gollum/wiki/Gollum-via-Docker).
|
||||
|
||||
Your Rack middleware can pass author details to Gollum in a Hash in the session under the 'gollum.author' key.
|
||||
### Service
|
||||
|
||||
## CONFIG FILE
|
||||
Gollum can also be ran as a service. More on that [over here](https://github.com/gollum/gollum/wiki/Gollum-as-a-service).
|
||||
|
||||
Gollum optionally takes a `--config file`. See [config.rb](https://github.com/gollum/gollum/blob/master/config.rb) for an example.
|
||||
## CONFIGURATION
|
||||
|
||||
## CUSTOM CSS/JS
|
||||
Gollum comes with the following command line options:
|
||||
|
||||
The `--css` flag will inject `custom.css` from the root of your git repository into each page. `custom.css` must be commited to git or you will get a 302 redirect to the create page.
|
||||
| Option | Arguments | Description |
|
||||
| ----------------- | --------- | ----------- |
|
||||
| --host | [HOST] | Specify the hostname or IP address to listen on. Default: `0.0.0.0`.<sup>1</sup> |
|
||||
| --port | [PORT] | Specify the port to bind Gollum with. Default: `4567`. |
|
||||
| --config | [FILE] | Specify path to Gollum's configuration file. |
|
||||
| --ref | [REF] | Specify the git branch to serve. Default: `master`. |
|
||||
| --adapter | [ADAPTER] | Launch Gollum using a specific git adapter. Default: `grit`.<sup>2</sup> |
|
||||
| --bare | none | Tell Gollum that the git repository should be treated as bare. This is only necessary when using the default grit adapter. |
|
||||
| --base-path | [PATH] | Specify the leading portion of all Gollum URLs (path info). Setting this to `/wiki` will make the wiki accessible under `http://localhost:4567/wiki/`. Default: `/`. |
|
||||
| --page-file-dir | [PATH] | Specify the subdirectory for all pages. If set, Gollum will only serve pages from this directory and its subdirectories. Default: repository root. |
|
||||
| --css | none | Tell Gollum to inject custom CSS into each page. Uses `custom.css` from repository root.<sup>3,5</sup> |
|
||||
| --js | none | Tell Gollum to inject custom JS into each page. Uses `custom.js` from repository root.<sup>3,5</sup> |
|
||||
| --emoji | none | Parse and interpret emoji tags (e.g. :heart:). |
|
||||
| --no-edit | none | Disable the feature of editing pages. |
|
||||
| --live-preview | none | Enable the live preview feature in page editor. |
|
||||
| --no-live-preview | none | Disable the live preview feature in page editor. |
|
||||
| --allow-uploads | [MODE] | Enable file uploads. If set to `dir`, Gollum will store all uploads in the `/uploads/` directory in repository root. If set to `page`, Gollum will store each upload at the currently edited page.<sup>4</sup> |
|
||||
| --mathjax | none | Enables MathJax (renders mathematical equations). By default, uses the `TeX-AMS-MML_HTMLorMML` config with the `autoload-all` extension.<sup>5</sup> |
|
||||
| --irb | none | Launch Gollum in "console mode", with a [predefined API](https://github.com/gollum/gollum-lib/). |
|
||||
| --h1-title | none | Tell Gollum to use the first `<h1>` as page title. |
|
||||
| --show-all | none | Tell Gollum to also show files in the file view. By default, only valid pages are shown. |
|
||||
| --collapse-tree | none | Tell Gollum to collapse the file tree, when the file view is opened. By default, the tree is expanded. |
|
||||
| --user-icons | [MODE] | Tell Gollum to use specific user icons for history view. Can be set to `gravatar`, `identicon` or `none`. Default: `none`. |
|
||||
| --mathjax-config | [FILE] | Specify path to a custom MathJax configuration. If not specified, uses the `mathjax.config.js` file from repository root. |
|
||||
| --template-dir | [PATH] | Specify custom mustache template directory. |
|
||||
| --help | none | Display the list of options on the command line. |
|
||||
| --version | none | Display the current version of Gollum. |
|
||||
|
||||
The `--js` flag will inject `custom.js` from the root of your git repository into each page. `custom.js` must be commited to git or you will get a 302 redirect to the create page.
|
||||
**Notes:**
|
||||
|
||||
1. The `0.0.0.0` IP address allows remote access. Should you wish for Gollum to turn into a personal Wiki, use `127.0.0.1`.
|
||||
2. Before using `--adapter`, you should probably read [this](https://github.com/gollum/gollum/wiki/Git-adapters) first.
|
||||
3. When `--css` or `--js` is used, respective files must be committed to your git repository or you will get a 302 redirect to the create a page.
|
||||
4. Files can be uploaded simply by dragging and dropping them onto the editor's text area (this is, however exclusive to the default editor, not the live preview editor).
|
||||
5. Read the relevant [Security note](https://github.com/gollum/gollum/wiki/Security#custom-cssjs--mathjax-config) before using these.
|
||||
|
||||
## API DOCUMENTATION
|
||||
### Config file
|
||||
|
||||
The [Gollum API](https://github.com/gollum/gollum-lib/) allows you to retrieve
|
||||
raw or formatted wiki content from a Git repository, write new content to the
|
||||
repository, and collect various meta data about the wiki as a whole.
|
||||
When `--config` option is used, certain inner parts of Gollum can be customized. This is used throughout our wiki for certain user-level alterations, among which [customizing supported markups](https://github.com/gollum/gollum/wiki/Formats-and-extensions) will probably stand out.
|
||||
|
||||
|
||||
## CONTRIBUTE
|
||||
|
||||
If you'd like to hack on Gollum, start by forking the repo on GitHub:
|
||||
|
||||
http://github.com/gollum/gollum
|
||||
|
||||
To get all of the dependencies, install the gem first. The best way to get
|
||||
your changes merged back into core is as follows:
|
||||
|
||||
1. Clone down your fork
|
||||
1. Create a thoughtfully named topic branch to contain your change
|
||||
1. Hack away
|
||||
1. Add tests and make sure everything still passes by running `rake`
|
||||
1. If you are adding new functionality, document it in the README
|
||||
1. Do not change the version number, I will do that on my end
|
||||
1. If necessary, rebase your commits into logical chunks, without errors
|
||||
1. Push the branch up to GitHub
|
||||
1. Send a pull request to the gollum/gollum project.
|
||||
|
||||
## RELEASING
|
||||
|
||||
Gollum uses [Semantic Versioning](http://semver.org/).
|
||||
|
||||
x.y.z
|
||||
|
||||
For z releases:
|
||||
|
||||
```bash
|
||||
$ rake bump
|
||||
$ rake release
|
||||
```
|
||||
|
||||
For x.y releases:
|
||||
|
||||
```bash
|
||||
Update VERSION in lib/gollum.rb
|
||||
$ rake gemspec
|
||||
$ rake release
|
||||
```
|
||||
|
||||
## BUILDING THE GEM FROM MASTER
|
||||
|
||||
```bash
|
||||
$ gem uninstall -aIx gollum
|
||||
$ git clone https://github.com/gollum/gollum.git
|
||||
$ cd gollum
|
||||
gollum$ rake build
|
||||
gollum$ gem install --no-ri --no-rdoc pkg/gollum*.gem
|
||||
```
|
||||
|
||||
## RUN THE TESTS
|
||||
|
||||
```bash
|
||||
$ bundle install
|
||||
$ bundle exec rake test
|
||||
```
|
||||
|
||||
## WORK WITH TEST REPOS
|
||||
|
||||
An example of how to add a test file to the bare repository lotr.git.
|
||||
|
||||
```bash
|
||||
$ mkdir tmp; cd tmp
|
||||
$ git clone ../lotr.git/ .
|
||||
Cloning into '.'...
|
||||
done.
|
||||
$ git log
|
||||
$ echo "test" > test.md
|
||||
$ git add . ; git commit -am "Add test"
|
||||
$ git push ../lotr.git/ master
|
||||
```
|
||||
**All of the mentioned alterations work both for Gollum's config file (`config.rb`) and Rack's config file (`config.ru`).**
|
||||
|
||||
+169
-107
@@ -2,128 +2,169 @@
|
||||
|
||||
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
|
||||
|
||||
help = <<HELP
|
||||
Gollum is a multi-format Wiki Engine/API/Frontend.
|
||||
|
||||
Basic Command Line Usage:
|
||||
gollum [OPTIONS] [PATH]
|
||||
|
||||
PATH The path to the Gollum repository (default .).
|
||||
|
||||
Options:
|
||||
HELP
|
||||
|
||||
require 'optparse'
|
||||
require 'rubygems'
|
||||
require 'gollum'
|
||||
require 'cgi'
|
||||
|
||||
exec = {}
|
||||
options = { 'port' => 4567, 'bind' => '0.0.0.0' }
|
||||
options = {
|
||||
:port => 4567,
|
||||
:bind => '0.0.0.0',
|
||||
}
|
||||
wiki_options = {
|
||||
:live_preview => false,
|
||||
:allow_uploads => false,
|
||||
:allow_editing => true,
|
||||
:live_preview => false,
|
||||
:allow_uploads => false,
|
||||
:allow_editing => true,
|
||||
}
|
||||
|
||||
opts = OptionParser.new do |opts|
|
||||
opts.banner = help
|
||||
|
||||
opts.on("--port [PORT]", "Bind port (default 4567).") do |port|
|
||||
options['port'] = port.to_i
|
||||
# define program name (although this defaults to the name of the file, just in case...)
|
||||
opts.program_name = "gollum"
|
||||
|
||||
# set basic info for the "--help" command (options will be appended automatically from the below definitions)
|
||||
opts.banner = '
|
||||
Gollum is a multi-format Wiki Engine/API/Frontend.
|
||||
|
||||
Usage:
|
||||
gollum [options] [git-repo]
|
||||
|
||||
Arguments:
|
||||
[git-repo] Path to the git repository being served. If not specified, current working directory is used.
|
||||
|
||||
Notes:
|
||||
Paths for all options are relative to <git-repo> unless absolute.
|
||||
This message is only a basic description. For more information, please visit:
|
||||
https://github.com/gollum/gollum
|
||||
|
||||
OPTIONS'
|
||||
|
||||
# define gollum options
|
||||
opts.separator ""
|
||||
opts.separator " Major:"
|
||||
|
||||
opts.on("-h", "--host [HOST]", "Specify the hostname or IP address to listen on. Default: '0.0.0.0'.") do |host|
|
||||
options[:bind] = host
|
||||
end
|
||||
|
||||
opts.on("--host [HOST]", "Hostname or IP address to listen on (default 0.0.0.0).") do |host|
|
||||
options['bind'] = host
|
||||
opts.on("-p", "--port [PORT]", "Specify the port to bind Gollum with. Default: '4567'.") do |port|
|
||||
begin
|
||||
# don't use "port.to_i" here... it doesn't raise errors which might result in a nice confusion later on
|
||||
options[:port] = Integer(port)
|
||||
rescue ArgumentError
|
||||
puts "Error: '#{port}' is not a valid port number."
|
||||
exit 1
|
||||
end
|
||||
end
|
||||
|
||||
opts.on("--version", "Display current version.") do
|
||||
puts "Gollum " + Gollum::VERSION
|
||||
exit 0
|
||||
opts.on("-c", "--config [FILE]", "Specify path to the Gollum's configuration file.") do |file|
|
||||
options[:config] = file
|
||||
end
|
||||
|
||||
opts.on("--config [CONFIG]", "Path to additional configuration file") do |config|
|
||||
options['config'] = config
|
||||
end
|
||||
|
||||
opts.on("--adapter [ADAPTER]", "Git adapter to use in the backend. Defaults to grit.") do |adapter|
|
||||
Gollum::GIT_ADAPTER = adapter
|
||||
end
|
||||
|
||||
opts.on("--irb", "Start an irb process with gollum loaded for the current wiki.") do
|
||||
options['irb'] = true
|
||||
end
|
||||
|
||||
opts.on("--css", "Inject custom css. Uses custom.css from root repository") do
|
||||
wiki_options[:css] = true
|
||||
end
|
||||
|
||||
opts.on("--js", "Inject custom js. Uses custom.js from root repository") do
|
||||
wiki_options[:js] = true
|
||||
end
|
||||
|
||||
opts.on("--template-dir [PATH]", "Specify custom template directory") do |path|
|
||||
wiki_options[:template_dir] = path
|
||||
end
|
||||
|
||||
opts.on("--page-file-dir [PATH]", "Specify the sub directory for all page files (default: repository root).") do |path|
|
||||
wiki_options[:page_file_dir] = path
|
||||
end
|
||||
|
||||
opts.on("--base-path [PATH]", "Specify the base path for the served pages (default: /) Example: --base-path wiki yields the home page accessible at http://localhost:4567/wiki/.") do |path|
|
||||
wiki_options[:base_path] = path
|
||||
end
|
||||
|
||||
opts.on("--gollum-path [PATH]", "Specify the path to the git repository to be served.") do |path|
|
||||
wiki_options[:gollum_path] = path
|
||||
end
|
||||
|
||||
opts.on("--ref [REF]", "Specify the repository ref to use (default: master).") do |ref|
|
||||
opts.on("-r", "--ref [REF]", "Specify the branch to serve. Default: 'master'.") do |ref|
|
||||
wiki_options[:ref] = ref
|
||||
end
|
||||
|
||||
opts.on("--bare", "Specify that the repository is bare (only necessary when using the grit adapter).") do
|
||||
opts.on("-a", "--adapter [ADAPTER]", "Launch Gollum using a specific git adapter. Default: 'grit'.") do |adapter|
|
||||
Gollum::GIT_ADAPTER = adapter
|
||||
end
|
||||
opts.on("--bare", "Declare '<git-repo>' to be bare. This is only necessary when using the grit adapter.") do
|
||||
wiki_options[:repo_is_bare] = true
|
||||
end
|
||||
opts.on("-b", "--base-path [PATH]", "Specify the leading portion of all Gollum URLs (path info). Default: '/'.",
|
||||
"Example: setting this to '/wiki' will make the wiki accessible under 'http://localhost:4567/wiki/'.") do |base_path|
|
||||
|
||||
# first trim a leading slash, if any
|
||||
base_path.sub!(/^\/+/, '')
|
||||
|
||||
opts.on("--no-edit", "Restricts editing capability through frontend.") do
|
||||
# make a backup of the option and sanitize it
|
||||
base_path_original = base_path.dup
|
||||
base_path = CGI.escape(base_path)
|
||||
|
||||
# then let the user know if we changed the URL
|
||||
unless base_path_original == base_path
|
||||
puts <<MSG
|
||||
Warning: your base-path has been sanitized:
|
||||
- original: '#{base_path_original}'
|
||||
- sanitized: '#{base_path}'
|
||||
MSG
|
||||
end
|
||||
|
||||
# and finally, let others enjoy our hard work:
|
||||
wiki_options[:base_path] = base_path
|
||||
end
|
||||
opts.on("--page-file-dir [PATH]", "Specify the subdirectory for all pages. Default: repository root.",
|
||||
"Example: setting this to 'pages' will make Gollum serve only pages at '<git-repo>/pages/*'.") do |path|
|
||||
wiki_options[:page_file_dir] = path
|
||||
end
|
||||
opts.on("--css", "Inject custom CSS into each page. The '<git-repo>/custom.css' file is used (must be committed).") do
|
||||
wiki_options[:css] = true
|
||||
end
|
||||
opts.on("--js", "Inject custom JavaScript into each page. The '<git-repo>/custom.js' file is used (must be committed).") do
|
||||
wiki_options[:js] = true
|
||||
end
|
||||
opts.on("--emoji", "Parse and interpret emoji tags (e.g. :heart:).") do
|
||||
wiki_options[:emoji] = true
|
||||
end
|
||||
opts.on("--no-edit", "Disable the feature of editing pages.") do
|
||||
wiki_options[:allow_editing] = false
|
||||
end
|
||||
|
||||
opts.on("--no-live-preview", "Disables livepreview.") do
|
||||
wiki_options[:live_preview] = false
|
||||
end
|
||||
|
||||
opts.on("--live-preview", "Enables livepreview.") do
|
||||
opts.on("--live-preview", "Enable the live preview feature in page editor.") do
|
||||
wiki_options[:live_preview] = true
|
||||
end
|
||||
|
||||
opts.on("--allow-uploads [MODE]", [:dir, :page], "Allows file uploads. Modes: dir (default, store all uploads in the same directory), page (store each upload at the same location as the page).") do |mode|
|
||||
opts.on("--no-live-preview", "Disable the live preview feature in page editor.") do
|
||||
wiki_options[:live_preview] = false
|
||||
end
|
||||
opts.on("--allow-uploads [MODE]", [:dir, :page], "Enable file uploads.",
|
||||
"If set to 'dir', Gollum will store all uploads in the '<git-repo>/uploads/' directory.",
|
||||
"If set to 'page', Gollum will store each upload at the currently edited page.") do |mode|
|
||||
wiki_options[:allow_uploads] = true
|
||||
wiki_options[:per_page_uploads] = true if mode == :page
|
||||
end
|
||||
|
||||
opts.on("--mathjax", "Enables mathjax for rendering mathematical equations. Uses the TeX-AMS-MML_HTMLorMML config with the autoload-all extension by default.") do
|
||||
opts.on("--mathjax", "Enable MathJax (renders mathematical equations).",
|
||||
"By default, uses the 'TeX-AMS-MML_HTMLorMML' config with the 'autoload-all' extension.") do
|
||||
wiki_options[:mathjax] = true
|
||||
end
|
||||
|
||||
opts.on("--mathjax-config [SOURCE]", "Inject custom mathjax config file. Uses mathjax.config.js from root repository by default") do |source|
|
||||
wiki_options[:mathjax_config] = source || 'mathjax.config.js'
|
||||
opts.on("--irb", "Launch Gollum in 'console mode', with a predefined API.") do
|
||||
options[:irb] = true
|
||||
end
|
||||
|
||||
opts.on("--user-icons [SOURCE]", "Set the history user icons. Valid values: gravatar, identicon, none. Default: none.") do |source|
|
||||
wiki_options[:user_icons] = source
|
||||
end
|
||||
|
||||
opts.on("--show-all", "Shows all files in file view. By default only valid pages are shown.") do
|
||||
wiki_options[:show_all] = true
|
||||
end
|
||||
|
||||
opts.on("--collapse-tree", "Collapse file view tree. By default, expanded tree is shown.") do
|
||||
wiki_options[:collapse_tree] = true
|
||||
end
|
||||
opts.on("--h1-title", "Sets page title to value of first h1") do
|
||||
|
||||
opts.separator ""
|
||||
opts.separator " Minor:"
|
||||
|
||||
opts.on("--h1-title", "Use the first '<h1>' as page title.") do
|
||||
wiki_options[:h1_title] = true
|
||||
end
|
||||
opts.on("--show-all", "Also show files in the file view. By default, only valid pages are shown.") do
|
||||
wiki_options[:show_all] = true
|
||||
end
|
||||
opts.on("--collapse-tree", "Collapse the tree, when file view is opened. By default, the tree is expanded.") do
|
||||
wiki_options[:collapse_tree] = true
|
||||
end
|
||||
opts.on("--user-icons [MODE]", [:gravatar, :identicon, :none], "Use specific user-icons for history view.",
|
||||
"Can be set to 'gravatar', 'identicon' or 'none'. Default: 'none'.") do |mode|
|
||||
wiki_options[:user_icons] = mode
|
||||
end
|
||||
opts.on("--mathjax-config [FILE]", "Specify path to a custom MathJax configuration.",
|
||||
"If not specified, uses the '<git-repo>/mathjax.config.js' file.") do |file|
|
||||
wiki_options[:mathjax_config] = file || 'mathjax.config.js'
|
||||
end
|
||||
opts.on("--plantuml-url [URL]", "Sets the PlantUML server endpoint.") do |url|
|
||||
wiki_options[:plantuml_url] = url
|
||||
end
|
||||
opts.on("--template-dir [PATH]", "Specify custom mustache template directory.") do |path|
|
||||
wiki_options[:template_dir] = path
|
||||
end
|
||||
|
||||
opts.separator ""
|
||||
opts.separator " Common:"
|
||||
|
||||
opts.on("--help", "Display this message.") do
|
||||
puts opts
|
||||
exit 0
|
||||
end
|
||||
opts.on("--version", "Display the current version of Gollum.") do
|
||||
puts "Gollum " + Gollum::VERSION
|
||||
exit 0
|
||||
end
|
||||
|
||||
opts.separator ""
|
||||
end
|
||||
|
||||
# Read command line options into `options` hash
|
||||
@@ -136,11 +177,9 @@ rescue OptionParser::InvalidOption
|
||||
end
|
||||
|
||||
# --gollum-path wins over ARGV[0]
|
||||
gollum_path = wiki_options[:gollum_path] ?
|
||||
wiki_options[:gollum_path] :
|
||||
ARGV[0] || Dir.pwd
|
||||
gollum_path = ARGV[0] || Dir.pwd
|
||||
|
||||
if options['irb']
|
||||
if options[:irb]
|
||||
require 'irb'
|
||||
# http://jameskilton.com/2009/04/02/embedding-irb-into-your-ruby-application/
|
||||
module IRB # :nodoc:
|
||||
@@ -171,8 +210,17 @@ if options['irb']
|
||||
if !wiki.exist? then
|
||||
raise Gollum::InvalidGitRepositoryError
|
||||
end
|
||||
puts "Loaded Gollum wiki at #{File.expand_path(gollum_path).inspect}."
|
||||
if wiki_options[:plantuml_url]
|
||||
Gollum::Filter::PlantUML.configure do |config|
|
||||
puts "Using #{wiki_options[:plantuml_url]} as PlantUML endpoint"
|
||||
config.url = wiki_options[:plantuml_url]
|
||||
end
|
||||
end
|
||||
puts
|
||||
puts "Loaded Gollum wiki at:"
|
||||
puts "#{File.expand_path(gollum_path).inspect}"
|
||||
puts
|
||||
puts "Example API calls:"
|
||||
puts %( page = wiki.page('page-name'))
|
||||
puts %( # => <Gollum::Page>)
|
||||
puts
|
||||
@@ -182,7 +230,9 @@ if options['irb']
|
||||
puts %( page.formatted_data)
|
||||
puts %( # => "<h1>My wiki page</h1>")
|
||||
puts
|
||||
puts "Check out the Gollum README for more."
|
||||
puts "Full API documentation at:"
|
||||
puts "https://github.com/gollum/gollum-lib"
|
||||
puts
|
||||
IRB.start_session(binding)
|
||||
rescue Gollum::InvalidGitRepositoryError, Gollum::NoSuchPathError
|
||||
puts "Invalid Gollum wiki at #{File.expand_path(gollum_path).inspect}"
|
||||
@@ -194,30 +244,41 @@ else
|
||||
Precious::App.set(:wiki_options, wiki_options)
|
||||
Precious::App.settings.mustache[:templates] = wiki_options[:template_dir] if wiki_options[:template_dir]
|
||||
|
||||
if cfg = options['config']
|
||||
if cfg = options[:config]
|
||||
# If the path begins with a '/' it will be considered an absolute path,
|
||||
# otherwise it will be relative to the CWD
|
||||
cfg = File.join(Dir.getwd, cfg) unless cfg.slice(0) == File::SEPARATOR
|
||||
require cfg
|
||||
end
|
||||
|
||||
if wiki_options[:plantuml_url]
|
||||
Gollum::Filter::PlantUML.configure do |config|
|
||||
puts "Using #{wiki_options[:plantuml_url]} as PlantUML endpoint"
|
||||
config.url = wiki_options[:plantuml_url]
|
||||
end
|
||||
end
|
||||
|
||||
base_path = wiki_options[:base_path]
|
||||
|
||||
if wiki_options[:base_path].nil?
|
||||
if base_path.nil?
|
||||
Precious::App.run!(options)
|
||||
else
|
||||
require 'rack'
|
||||
|
||||
class MapGollum
|
||||
def initialize base_path
|
||||
def initialize(base_path)
|
||||
@mg = Rack::Builder.new do
|
||||
map '/' do
|
||||
run Proc.new { [302, { 'Location' => "/#{base_path}" }, []] }
|
||||
end
|
||||
|
||||
|
||||
map "/#{base_path}" do
|
||||
run Precious::App
|
||||
end
|
||||
map '/' do
|
||||
run Proc.new { [302, { 'Location' => "/#{base_path}" }, []] }
|
||||
end
|
||||
map '/*' do
|
||||
run Proc.new { [302, { 'Location' => "/#{base_path}" }, []] }
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -225,7 +286,8 @@ else
|
||||
@mg.call(env)
|
||||
end
|
||||
end
|
||||
|
||||
# Rack::Handler does not work with Ctrl + C. Use Rack::Server instead.
|
||||
Rack::Server.new(:app => MapGollum.new(base_path), :Port => options['port'], :Host => options['bind']).start
|
||||
Rack::Server.new(:app => MapGollum.new(base_path), :Port => options[:port], :Host => options[:bind]).start
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,28 +1,11 @@
|
||||
# Example gollum config
|
||||
# gollum ../wiki --config config.rb
|
||||
#
|
||||
# or run from source with
|
||||
#
|
||||
# bundle exec bin/gollum ../wiki/ --config config.rb
|
||||
|
||||
# Remove const to avoid
|
||||
# warning: already initialized constant FORMAT_NAMES
|
||||
#
|
||||
# only remove if it's defined.
|
||||
# constant Gollum::Page::FORMAT_NAMES not defined (NameError)
|
||||
Gollum::Page.send :remove_const, :FORMAT_NAMES if defined? Gollum::Page::FORMAT_NAMES
|
||||
# limit to one format
|
||||
Gollum::Page::FORMAT_NAMES = { :markdown => "Markdown" }
|
||||
|
||||
=begin
|
||||
Valid formats are:
|
||||
{ :markdown => "Markdown",
|
||||
:textile => "Textile",
|
||||
:rdoc => "RDoc",
|
||||
:org => "Org-mode",
|
||||
:creole => "Creole",
|
||||
:rest => "reStructuredText",
|
||||
:asciidoc => "AsciiDoc",
|
||||
:mediawiki => "MediaWiki",
|
||||
:pod => "Pod" }
|
||||
This file can be used to (e.g.):
|
||||
- alter certain inner parts of Gollum,
|
||||
- extend it with your stuff.
|
||||
|
||||
It is especially useful for customizing supported formats/markups. For more information and examples:
|
||||
- https://github.com/gollum/gollum#config-file
|
||||
|
||||
=end
|
||||
|
||||
# enter your Ruby code here ...
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
=begin
|
||||
You should use this file, if you wish to:
|
||||
- launch Gollum as a Rack app,
|
||||
- alter certain startup behaviour of Gollum.
|
||||
|
||||
For more information and examples:
|
||||
- https://github.com/gollum/gollum/wiki/Gollum-via-Rack
|
||||
- https://github.com/gollum/gollum#config-file
|
||||
|
||||
=end
|
||||
|
||||
# enter your Ruby code here ...
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
#!/bin/sh
|
||||
### BEGIN INIT INFO
|
||||
# Provides: gollum
|
||||
# Required-Start: $local_fs $remote_fs $network $syslog
|
||||
# Required-Stop: $local_fs $remote_fs $network $syslog
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# X-Interactive: true
|
||||
# Short-Description: Start/stop gollum wiki
|
||||
### END INIT INFO
|
||||
|
||||
# Distributed under the terms of the MIT License
|
||||
|
||||
set -e
|
||||
|
||||
# Edit these settings to your liking:
|
||||
GOLLUM_USER=gollum
|
||||
GOLLUM_BASE=/var/lib/gollum/.git
|
||||
GOLLUM_OPTS="--bare"
|
||||
|
||||
NAME=gollum
|
||||
PID=/var/run/${NAME}.pid
|
||||
EXEC=/usr/local/bin/gollum
|
||||
LOG=/var/log/gollum.log
|
||||
|
||||
. /lib/lsb/init-functions
|
||||
|
||||
start ()
|
||||
{
|
||||
# Change log file to be owned by GOLLUM_USER
|
||||
touch "${LOG}"
|
||||
chown "${GOLLUM_USER}" "${LOG}"
|
||||
|
||||
log_daemon_msg "Starting Gollum"
|
||||
start-stop-daemon --start \
|
||||
--name "${NAME}" \
|
||||
--user "${GOLLUM_USER}" \
|
||||
--chuid "${GOLLUM_USER}" \
|
||||
--pidfile "${PID}" \
|
||||
--make-pidfile --background \
|
||||
--startas /bin/sh -- -c "exec ${EXEC} $GOLLUM_OPTS \"$GOLLUM_BASE\" > \"${LOG}\" 2>&1"
|
||||
log_end_msg $?
|
||||
}
|
||||
|
||||
stop ()
|
||||
{
|
||||
log_daemon_msg "Stopping Gollum"
|
||||
start-stop-daemon --stop \
|
||||
--user "${GOLLUM_USER}" \
|
||||
--signal INT \
|
||||
--pidfile "${PID}" \
|
||||
--retry 10
|
||||
log_end_msg $?
|
||||
}
|
||||
|
||||
status ()
|
||||
{
|
||||
status_of_proc -p $PID $EXEC $NAME
|
||||
}
|
||||
|
||||
case $1 in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
restart)
|
||||
stop
|
||||
start
|
||||
;;
|
||||
status)
|
||||
status
|
||||
;;
|
||||
*)
|
||||
log_success_msg "Usage: $0 {start|stop|restart|status}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
@@ -1,33 +0,0 @@
|
||||
Sanitization Rules
|
||||
==================
|
||||
|
||||
Gollum uses the [Sanitize](http://wonko.com/post/sanitize) gem for HTML
|
||||
sanitization. Below you find the default allowed tags, attributes, and protocols, as well as directions to customize these settings.
|
||||
|
||||
# Default Settings
|
||||
|
||||
## ALLOWED TAGS
|
||||
|
||||
a, abbr, acronym, address, area, b, big, blockquote, br, button, caption, center, cite, code, col, colgroup, dd, del, dfn, dir, div, dl, dt, em, fieldset, font, form, h1, h2, h3, h4, h5, h6, hr, i, img, input, ins, kbd, label, legend, li, map, menu, ol, optgroup, option, p, pre, q, s, samp, select, small, span, strike, strong, sub, sup, table, tbody, td, textarea, tfoot, th, thead, tr, tt, u, ul, var
|
||||
|
||||
## ALLOWED ATTRIBUTES
|
||||
|
||||
a href, abbr, accept, accept-charset, accesskey, action, align, alt, axis, border, cellpadding, cellspacing, char, charoff, class, charset, checked, cite, clear, cols, colspan, color, compact, coords, datetime, dir, disabled, enctype, for, frame, headers, height, hreflang, hspace, id, img src, ismap, label, lang, longdesc, maxlength, media, method, multiple, name, nohref, noshade, nowrap, prompt, readonly, rel, rev, rows, rowspan, rules, scope, selected, shape, size, span, start, summary, tabindex, target, title, type, usemap, valign, value, vspace, width
|
||||
|
||||
## ALLOWED PROTOCOLS
|
||||
|
||||
* a href: http, https, mailto, ftp, irc, apt, :relative
|
||||
* img src: http, https, :relative
|
||||
* form action: http, https, :relative
|
||||
|
||||
# Customizing
|
||||
|
||||
To customize these settings, edit your `config.rb` file along the following lines (be sure to run gollum with the `--config` option):
|
||||
|
||||
```ruby
|
||||
sanitizer = Gollum::Sanitization.new
|
||||
sanitizer.protocols['a']['href'].concat ['ssh', 'vnc'] # Protocols
|
||||
sanitizer.elements.concat ['customtag1', 'customtag2'] # Tags
|
||||
sanitizer.attributes['a'].push 'target' # Attributes
|
||||
Precious::App.set(:wiki_options, {:sanitization => sanitizer})
|
||||
```
|
||||
+572
-34
@@ -5,8 +5,8 @@ Gem::Specification.new do |s|
|
||||
s.required_ruby_version = '>= 1.9'
|
||||
|
||||
s.name = 'gollum'
|
||||
s.version = '3.1.2'
|
||||
s.date = '2015-01-23'
|
||||
s.version = '4.1.2'
|
||||
s.date = '2017-08-07'
|
||||
s.rubyforge_project = 'gollum'
|
||||
s.license = 'MIT'
|
||||
|
||||
@@ -24,20 +24,24 @@ Gem::Specification.new do |s|
|
||||
s.rdoc_options = ['--charset=UTF-8']
|
||||
s.extra_rdoc_files = %w[README.md LICENSE]
|
||||
|
||||
s.add_dependency 'gollum-lib', '~> 4.0', '>= 4.0.1'
|
||||
s.add_dependency 'github-markdown', '~> 0.6.5'
|
||||
s.add_dependency 'gollum-lib', '>= 4.2.7'
|
||||
s.add_dependency 'kramdown', '~> 1.9.0'
|
||||
s.add_dependency 'sinatra', '~> 1.4', '>= 1.4.4'
|
||||
s.add_dependency 'mustache', ['>= 0.99.5', '< 1.0.0']
|
||||
s.add_dependency 'useragent', '~> 0.10.0'
|
||||
s.add_dependency 'useragent', '~> 0.16.2'
|
||||
s.add_dependency 'gemojione', '~> 3.2'
|
||||
|
||||
s.add_development_dependency 'rack-test', '~> 0.6.2'
|
||||
s.add_development_dependency 'shoulda', '~> 3.5.0'
|
||||
s.add_development_dependency 'minitest-reporters', '~> 0.14.16'
|
||||
s.add_development_dependency 'twitter_cldr', '~> 2.4.2'
|
||||
s.add_development_dependency 'mocha', '~> 1.0.0'
|
||||
s.add_development_dependency 'twitter_cldr', '~> 3.2.0'
|
||||
s.add_development_dependency 'mocha', '~> 1.1.0'
|
||||
s.add_development_dependency 'test-unit', '~> 3.1.0'
|
||||
s.add_development_dependency 'webrick', '~> 1.3.1'
|
||||
|
||||
# = MANIFEST =
|
||||
s.files = %w[
|
||||
CONTRIBUTING.md
|
||||
Gemfile
|
||||
HISTORY.md
|
||||
LICENSE
|
||||
@@ -45,14 +49,14 @@ Gem::Specification.new do |s|
|
||||
Rakefile
|
||||
bin/gollum
|
||||
config.rb
|
||||
config.ru
|
||||
contrib/openrc/conf.d/gollum
|
||||
contrib/openrc/init.d/gollum
|
||||
contrib/systemd/gollum@.service
|
||||
docs/sanitization.md
|
||||
contrib/sysv-debian/init.d/gollum
|
||||
gollum.gemspec
|
||||
lib/gollum.rb
|
||||
lib/gollum/app.rb
|
||||
lib/gollum/editing_auth.rb
|
||||
lib/gollum/helpers.rb
|
||||
lib/gollum/public/gollum/css/_styles.css
|
||||
lib/gollum/public/gollum/css/dialog.css
|
||||
@@ -71,6 +75,7 @@ Gem::Specification.new do |s|
|
||||
lib/gollum/public/gollum/images/fileview/folder-horizontal.png
|
||||
lib/gollum/public/gollum/images/fileview/toggle-small-expand.png
|
||||
lib/gollum/public/gollum/images/fileview/toggle-small.png
|
||||
lib/gollum/public/gollum/images/fileview/trashcan.png
|
||||
lib/gollum/public/gollum/images/icon-sprite.png
|
||||
lib/gollum/public/gollum/images/man_24.png
|
||||
lib/gollum/public/gollum/images/para.png
|
||||
@@ -99,16 +104,21 @@ Gem::Specification.new do |s|
|
||||
lib/gollum/public/gollum/livepreview/images/lr_24.png
|
||||
lib/gollum/public/gollum/livepreview/images/save_24.png
|
||||
lib/gollum/public/gollum/livepreview/images/savecomment_24.png
|
||||
lib/gollum/public/gollum/livepreview/index.html
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ace.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/anchor.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/anchor_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/autocomplete.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/autocomplete/popup.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/autocomplete/text_completer.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/autocomplete/util.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/background_tokenizer.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/background_tokenizer_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/commands/command_manager.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/commands/command_manager_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/commands/default_commands.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/commands/incremental_search_commands.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/commands/multi_select_commands.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/commands/occur_commands.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/config.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/config_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/css/codefolding-fold-button-states.png
|
||||
@@ -127,28 +137,57 @@ Gem::Specification.new do |s|
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/editor_highlight_selected_word_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/editor_navigation_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/editor_text_edit_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/beautify.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/beautify/php_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/chromevox.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/elastic_tabstops_lite.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/emmet.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/error_marker.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/keybinding_menu.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/language_tools.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/linking.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/menu_tools/add_editor_menu_options.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/menu_tools/element_generator.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/menu_tools/generate_settings_menu.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/menu_tools/get_editor_keyboard_shortcuts.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/menu_tools/get_set_functions.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/menu_tools/overlay_page.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/menu_tools/settings_menu.css
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/modelist.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/old_ie.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/old_ie_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/searchbox.css
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/searchbox.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/settings_menu.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/spellcheck.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/split.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/static.css
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/static_highlight.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/static_highlight_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/statusbar.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/textarea.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/themelist.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/whitespace.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/whitespace_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/incremental_search.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/incremental_search_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/emacs.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/emacs_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/hash_handler.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/keybinding.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/keybinding_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/state_handler.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/textarea.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/textinput.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/vim.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/vim/commands.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/vim/maps/aliases.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/vim/maps/motions.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/vim/maps/operators.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/vim/maps/util.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/vim/registers.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/vim_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/layer/cursor.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/layer/font_metrics.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/layer/gutter.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/layer/marker.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/layer/text.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/layer/text_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/lib/browser_focus.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/lib/app_config.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/lib/dom.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/lib/es5-shim.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/lib/event.js
|
||||
@@ -161,56 +200,158 @@ Gem::Specification.new do |s|
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/lib/oop.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/lib/regexp.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/lib/useragent.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/line_widgets.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/Readme.md
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/highlight_rules_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/package.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/test_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_asciidoc.txt
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_coffee.txt
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_curly.txt
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_html.txt
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_javascript.txt
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_livescript.txt
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_lucene.txt
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_markdown.txt
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_php.txt
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_ruby.txt
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_xml.txt
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_abap.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_abc.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_actionscript.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_ada.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_asciidoc.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_assembly_x86.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_autohotkey.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_batchfile.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_c9search.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_c_cpp.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_cirru.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_clojure.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_cobol.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_coffee.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_coldfusion.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_csharp.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_css.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_curly.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_d.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_dart.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_diff.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_dot.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_eiffel.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_ejs.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_elixir.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_elm.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_erlang.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_forth.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_ftl.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_gcode.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_gherkin.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_gitignore.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_glsl.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_golang.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_groovy.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_haml.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_handlebars.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_haskell.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_haxe.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_html.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_html_ruby.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_ini.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_io.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_jack.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_jade.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_java.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_javascript.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_json.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_jsoniq.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_jsp.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_jsx.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_julia.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_latex.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_less.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_liquid.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_lisp.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_livescript.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_logiql.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_lsl.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_lua.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_luapage.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_lucene.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_markdown.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_mask.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_matlab.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_mel.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_mushcode.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_mysql.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_nix.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_objectivec.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_ocaml.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_pascal.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_perl.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_pgsql.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_php.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_powershell.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_prolog.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_properties.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_protobuf.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_python.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_r.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_rdoc.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_rhtml.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_ruby.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_rust.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_sass.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_scad.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_scala.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_scheme.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_scss.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_sh.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_sjs.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_smarty.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_snippets.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_soy_template.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_space.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_sql.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_stylus.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_svg.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_tcl.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_tex.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_text.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_textile.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_toml.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_twig.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_typescript.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_vala.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_vbscript.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_velocity.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_verilog.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_vhdl.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_xml.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_xquery.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_yaml.json
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/abap.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/abap_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/abc.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/abc_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/actionscript.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/actionscript_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ada.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ada_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/apache_conf.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/apache_conf_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/applescript.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/applescript_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/asciidoc.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/asciidoc_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/assembly_x86.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/assembly_x86_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/autohotkey.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/autohotkey_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/batchfile.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/batchfile_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/behaviour.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/behaviour/behaviour_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/behaviour/css.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/behaviour/cstyle.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/behaviour/html.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/behaviour/xml.js
|
||||
@@ -219,8 +360,12 @@ Gem::Specification.new do |s|
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/c9search_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/c_cpp.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/c_cpp_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/cirru.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/cirru_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/clojure.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/clojure_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/cobol.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/cobol_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/coffee.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/coffee/coffee-script.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/coffee/helpers.js
|
||||
@@ -231,7 +376,6 @@ Gem::Specification.new do |s|
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/coffee/rewriter.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/coffee/scope.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/coffee_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/coffee_highlight_rules_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/coffee_worker.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/coldfusion.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/coldfusion_highlight_rules.js
|
||||
@@ -241,33 +385,63 @@ Gem::Specification.new do |s|
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/css.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/css/csslint.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/css_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/css_highlight_rules_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/css_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/css_worker.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/css_worker_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/curly.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/curly_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/d.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/d_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/dart.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/dart_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/diff.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/diff_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/django.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/doc_comment_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/dockerfile.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/dockerfile_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/dot.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/dot_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/eiffel.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/eiffel_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ejs.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/elixir.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/elixir_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/elm.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/elm_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/erlang.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/erlang_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/asciidoc.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/c9search.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/coffee.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/coffee_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/csharp.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/cstyle.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/cstyle_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/diff.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/fold_mode.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/html.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/html_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/ini.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/latex.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/lua.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/markdown.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/mixed.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/pythonic.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/pythonic_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/velocity.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/xml.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/xml_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/forth.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/forth_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ftl.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ftl_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/gcode.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/gcode_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/gherkin.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/gherkin_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/gitignore.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/gitignore_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/glsl.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/glsl_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/golang.js
|
||||
@@ -276,12 +450,26 @@ Gem::Specification.new do |s|
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/groovy_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/haml.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/haml_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/handlebars.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/handlebars_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/haskell.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/haskell_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/haxe.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/haxe_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/html.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/html/saxparser.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/html_completions.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/html_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/html_highlight_rules_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/html_ruby.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/html_ruby_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/html_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/html_worker.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ini.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ini_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/io.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/io_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/jack.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/jack_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/jade.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/jade_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/java.js
|
||||
@@ -289,54 +477,89 @@ Gem::Specification.new do |s|
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/javascript.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/javascript/jshint.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/javascript_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/javascript_highlight_rules_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/javascript_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/javascript_worker.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/javascript_worker_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/js_regex_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/json.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/json/json_parse.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/json_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/json_worker.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/json_worker_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/jsoniq.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/jsp.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/jsp_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/jsx.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/jsx_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/julia.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/julia_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/latex.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/latex_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/less.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/less_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/liquid.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/liquid_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/liquid_highlight_rules_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lisp.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lisp_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/livescript.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/logiql.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/logiql_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/logiql_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lsl.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lsl_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lua.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lua/luaparse.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lua_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lua_worker.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/luapage.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/luapage_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lucene.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lucene_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lucene_highlight_rules_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/makefile.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/makefile_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/markdown.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/markdown_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/markdown_highlight_rules_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/mask.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/mask_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/matching_brace_outdent.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/matching_parens_outdent.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/matlab.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/matlab_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/mel.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/mel_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/mushcode.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/mushcode_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/mysql.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/mysql_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/nix.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/nix_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/objectivec.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/objectivec_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ocaml.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ocaml_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/pascal.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/pascal_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/perl.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/perl_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/pgsql.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/pgsql_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/php.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/php/php.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/php_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/php_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/php_worker.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/plain_text.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/plain_text_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/powershell.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/powershell_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/praat.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/praat_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/prolog.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/prolog_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/properties.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/properties_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/protobuf.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/protobuf_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/python.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/python_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/python_test.js
|
||||
@@ -348,15 +571,30 @@ Gem::Specification.new do |s|
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/rhtml_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ruby.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ruby_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ruby_highlight_rules_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ruby_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/rust.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/rust_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/sass.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/sass_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/scad.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/scad_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/scala.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/scala_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/scheme.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/scheme_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/scss.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/scss_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/sh.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/sh_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/sjs.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/sjs_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/smarty.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/smarty_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/snippets.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/soy_template.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/soy_template_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/space.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/space_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/sql.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/sql_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/stylus.js
|
||||
@@ -372,32 +610,50 @@ Gem::Specification.new do |s|
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/text_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/textile.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/textile_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/toml.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/toml_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/twig.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/twig_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/typescript.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/typescript_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/vala.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/vala_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/vbscript.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/vbscript_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/velocity.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/velocity_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/verilog.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/verilog_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/vhdl.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/vhdl_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xml.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xml/dom-parser.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xml/dom.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xml/sax.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xml_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xml_highlight_rules_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xml_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xml_util.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xml_worker.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xquery.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xquery/JSONParseTreeHandler.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xquery/Readme.md
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xquery/XQueryParser.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xquery/visitors/SyntaxHighlighter.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xquery_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xquery/jsoniq_lexer.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xquery/xqlint.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xquery/xquery_lexer.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xquery_worker.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/yaml.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/yaml_highlight_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/model/editor.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mouse/default_gutter_handler.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mouse/default_handlers.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mouse/dragdrop.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mouse/dragdrop_handler.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mouse/fold_handler.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mouse/mouse_event.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mouse/mouse_handler.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mouse/mouse_handler_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mouse/multi_select_handler.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/multi_select.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/multi_select_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/occur.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/occur_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/placeholder.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/placeholder_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/range.js
|
||||
@@ -406,12 +662,282 @@ Gem::Specification.new do |s|
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/range_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/renderloop.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/requirejs/text.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/requirejs/text_build.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/scrollbar.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/search.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/search_highlight.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/search_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/selection.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/selection_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/_.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/_all_modes.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/_all_modes.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/abap.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/abap.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/abc.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/abc.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/actionscript.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/actionscript.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ada.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ada.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/all_modes.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/all_modes.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/apache.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/apache_conf.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/apache_conf.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/applescript.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/applescript.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/asciidoc.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/asciidoc.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/assembly_x86.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/assembly_x86.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/autohotkey.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/autohotkey.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/autoit.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/batchfile.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/batchfile.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/c.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/c9search.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/c9search.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/c_cpp.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/c_cpp.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/chef.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/cirru.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/cirru.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/clojure.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/clojure.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/cmake.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/cobol.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/cobol.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/coffee.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/coffee.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/coldfusion.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/coldfusion.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/cs.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/csharp.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/csharp.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/css.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/css.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/curly.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/curly.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/d.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/d.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/dart.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/dart.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/diff.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/diff.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/django.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/django.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/dockerfile.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/dockerfile.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/dot.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/dot.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/dummy.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/dummy_syntax.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/eiffel.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/eiffel.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ejs.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ejs.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/elixir.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/elixir.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/elm.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/elm.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/erlang.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/erlang.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/eruby.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/falcon.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/forth.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/forth.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ftl.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ftl.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/gcode.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/gcode.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/gherkin.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/gherkin.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/gitignore.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/gitignore.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/glsl.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/glsl.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/go.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/golang.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/golang.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/groovy.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/groovy.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/haml.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/haml.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/handlebars.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/handlebars.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/haskell.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/haskell.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/haxe.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/haxe.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/html.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/html.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/html_ruby.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/html_ruby.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/htmldjango.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/htmltornado.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ini.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ini.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/io.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/io.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/jack.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/jack.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/jade.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/jade.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/java.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/java.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/javascript-jquery.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/javascript.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/javascript.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/json.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/json.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/jsoniq.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/jsoniq.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/jsp.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/jsp.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/jsx.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/jsx.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/julia.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/julia.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/latex.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/latex.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ledger.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/less.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/less.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/liquid.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/liquid.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/lisp.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/lisp.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/livescript.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/livescript.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/logiql.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/logiql.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/lsl.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/lsl.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/lua.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/lua.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/luapage.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/luapage.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/lucene.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/lucene.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/makefile.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/makefile.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/mako.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/markdown.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/markdown.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/matlab.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/matlab.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/mel.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/mel.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/mushcode.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/mushcode.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/mushcode_high_rules.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/mushcode_high_rules.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/mysql.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/mysql.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/nix.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/nix.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/objc.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/objectivec.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/objectivec.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ocaml.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ocaml.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/pascal.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/pascal.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/perl.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/perl.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/pgsql.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/pgsql.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/php.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/php.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/plain_text.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/plain_text.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/powershell.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/powershell.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/praat.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/praat.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/prolog.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/prolog.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/properties.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/properties.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/protobuf.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/protobuf.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/python.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/python.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/r.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/r.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/rdoc.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/rdoc.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/rhtml.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/rhtml.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/rst.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ruby.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ruby.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/rust.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/rust.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/sass.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/sass.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/scad.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/scad.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/scala.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/scala.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/scheme.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/scheme.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/scss.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/scss.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/sh.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/sh.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/sjs.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/sjs.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/smarty.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/smarty.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/snippets.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/snippets.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/soy_template.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/soy_template.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/space.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/space.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/sql.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/sql.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/stylus.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/stylus.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/svg.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/svg.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/tcl.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/tcl.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/tex.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/tex.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/text.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/text.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/textile.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/textile.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/tmsnippet.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/toml.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/toml.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/twig.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/twig.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/typescript.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/typescript.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/vala.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/vala.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/vbscript.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/vbscript.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/velocity.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/velocity.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/verilog.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/verilog.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/vhdl.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/vhdl.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/xml.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/xml.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/xquery.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/xquery.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/xslt.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/yaml.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/yaml.snippets
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/split.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/test/all.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/test/all_browser.js
|
||||
@@ -427,6 +953,8 @@ Gem::Specification.new do |s|
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/test/tests.html
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/ambiance.css
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/ambiance.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/chaos.css
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/chaos.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/chrome.css
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/chrome.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/clouds.css
|
||||
@@ -447,8 +975,12 @@ Gem::Specification.new do |s|
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/github.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/idle_fingers.css
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/idle_fingers.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/katzenmilch.css
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/katzenmilch.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/kr_theme.css
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/kr_theme.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/kuroir.css
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/kuroir.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/merbivore.css
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/merbivore.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/merbivore_soft.css
|
||||
@@ -463,6 +995,8 @@ Gem::Specification.new do |s|
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/solarized_dark.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/solarized_light.css
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/solarized_light.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/terminal.css
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/terminal.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/textmate.css
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/textmate.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/tomorrow.css
|
||||
@@ -485,6 +1019,8 @@ Gem::Specification.new do |s|
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/token_iterator_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/tokenizer.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/tokenizer_dev.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/tokenizer_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/tooltip.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/undomanager.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/unicode.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/virtual_renderer.js
|
||||
@@ -492,7 +1028,7 @@ Gem::Specification.new do |s|
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/worker/mirror.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/worker/worker.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/worker/worker_client.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/worker/worker_sourcemint.js
|
||||
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/worker/worker_test.js
|
||||
lib/gollum/public/gollum/livepreview/js/jquery.ba-throttle-debounce.min.js
|
||||
lib/gollum/public/gollum/livepreview/js/livepreview.js
|
||||
lib/gollum/public/gollum/livepreview/js/md_sundown.js
|
||||
@@ -523,6 +1059,7 @@ Gem::Specification.new do |s|
|
||||
lib/gollum/templates/history_authors/none.mustache
|
||||
lib/gollum/templates/latest_changes.mustache
|
||||
lib/gollum/templates/layout.mustache
|
||||
lib/gollum/templates/livepreview.mustache
|
||||
lib/gollum/templates/page.mustache
|
||||
lib/gollum/templates/pages.mustache
|
||||
lib/gollum/templates/search.mustache
|
||||
@@ -538,6 +1075,7 @@ Gem::Specification.new do |s|
|
||||
lib/gollum/views/history.rb
|
||||
lib/gollum/views/latest_changes.rb
|
||||
lib/gollum/views/layout.rb
|
||||
lib/gollum/views/livepreview.rb
|
||||
lib/gollum/views/page.rb
|
||||
lib/gollum/views/pages.rb
|
||||
lib/gollum/views/search.rb
|
||||
|
||||
+1
-1
@@ -16,7 +16,7 @@ require File.expand_path('../gollum/uri_encode_component', __FILE__)
|
||||
$KCODE = 'U' if RUBY_VERSION[0, 3] == '1.8'
|
||||
|
||||
module Gollum
|
||||
VERSION = '3.1.2'
|
||||
VERSION = '4.1.2'
|
||||
|
||||
def self.assets_path
|
||||
::File.expand_path('gollum/public', ::File.dirname(__FILE__))
|
||||
|
||||
+56
-14
@@ -13,8 +13,6 @@ require 'gollum/views/has_page'
|
||||
|
||||
require File.expand_path '../helpers', __FILE__
|
||||
|
||||
require 'gollum/editing_auth'
|
||||
|
||||
#required to upload bigger binary files
|
||||
Gollum::set_git_timeout(120)
|
||||
Gollum::set_git_max_filesize(190 * 10**6)
|
||||
@@ -28,7 +26,7 @@ class String
|
||||
self # Do not transliterate utf-8 url's unless using Grit
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# _Header => header which causes errors
|
||||
def to_url
|
||||
return nil if self.nil?
|
||||
@@ -51,7 +49,6 @@ module Precious
|
||||
class App < Sinatra::Base
|
||||
register Mustache::Sinatra
|
||||
include Precious::Helpers
|
||||
use Precious::EditingAuth
|
||||
|
||||
dir = File.dirname(File.expand_path(__FILE__))
|
||||
|
||||
@@ -62,6 +59,7 @@ module Precious
|
||||
Browser.new('Internet Explorer', '10.0'),
|
||||
Browser.new('Chrome', '7.0'),
|
||||
Browser.new('Firefox', '4.0'),
|
||||
Browser.new('Safari', '9.0')
|
||||
]
|
||||
|
||||
def supported_useragent?(user_agent)
|
||||
@@ -96,19 +94,20 @@ module Precious
|
||||
end
|
||||
|
||||
before do
|
||||
settings.wiki_options[:allow_editing] = settings.wiki_options.fetch(:allow_editing, true)
|
||||
@allow_editing = settings.wiki_options[:allow_editing]
|
||||
Precious::App.set(:mustache, {:templates => settings.wiki_options[:template_dir]}) if settings.wiki_options[:template_dir]
|
||||
@base_url = url('/', false).chomp('/')
|
||||
@page_dir = settings.wiki_options[:page_file_dir].to_s
|
||||
# above will detect base_path when it's used with map in a config.ru
|
||||
settings.wiki_options.merge!({ :base_path => @base_url })
|
||||
@css = settings.wiki_options[:css]
|
||||
@js = settings.wiki_options[:js]
|
||||
@mathjax_config = settings.wiki_options[:mathjax_config]
|
||||
settings.wiki_options[:allow_editing] = settings.wiki_options.fetch(:allow_editing, true)
|
||||
@allow_editing = settings.wiki_options[:allow_editing]
|
||||
end
|
||||
|
||||
get '/' do
|
||||
page_dir = settings.wiki_options[:page_file_dir].to_s
|
||||
redirect clean_url(::File.join(@base_url, page_dir, wiki_new.index_page))
|
||||
redirect clean_url(::File.join(@base_url, @page_dir, wiki_new.index_page))
|
||||
end
|
||||
|
||||
# path is set to name if path is nil.
|
||||
@@ -131,6 +130,14 @@ module Precious
|
||||
Gollum::Wiki.new(settings.gollum_path, settings.wiki_options)
|
||||
end
|
||||
|
||||
get '/emoji/:name' do
|
||||
begin
|
||||
[200, {'Content-Type' => 'image/png'}, emoji(params['name'])]
|
||||
rescue ArgumentError
|
||||
not_found
|
||||
end
|
||||
end
|
||||
|
||||
get '/data/*' do
|
||||
if page = wiki_page(params[:splat].first).page
|
||||
page.raw_data
|
||||
@@ -148,7 +155,7 @@ module Precious
|
||||
@allow_uploads = wiki.allow_uploads
|
||||
if page = wikip.page
|
||||
if wiki.live_preview && page.format.to_s.include?('markdown') && supported_useragent?(request.user_agent)
|
||||
live_preview_url = '/livepreview/index.html?page=' + encodeURIComponent(@name)
|
||||
live_preview_url = '/livepreview/?page=' + encodeURIComponent(@name)
|
||||
if @path
|
||||
live_preview_url << '&path=' + encodeURIComponent(@path)
|
||||
end
|
||||
@@ -165,6 +172,8 @@ module Precious
|
||||
end
|
||||
|
||||
post '/uploadFile' do
|
||||
forbid unless @allow_editing
|
||||
|
||||
wiki = wiki_new
|
||||
|
||||
unless wiki.allow_uploads
|
||||
@@ -177,8 +186,10 @@ module Precious
|
||||
fullname = params[:file][:filename]
|
||||
tempfile = params[:file][:tempfile]
|
||||
end
|
||||
halt 500 unless tempfile.is_a? Tempfile
|
||||
|
||||
dir = wiki.per_page_uploads ? params[:upload_dest] : 'uploads'
|
||||
# Remove page file dir prefix from upload path if necessary -- committer handles this itself
|
||||
dir = wiki.per_page_uploads ? params[:upload_dest] : ::File.join([wiki.page_file_dir, 'uploads'].compact)
|
||||
ext = ::File.extname(fullname)
|
||||
format = ext.split('.').last || 'txt'
|
||||
filename = ::File.basename(fullname, ext)
|
||||
@@ -211,7 +222,22 @@ module Precious
|
||||
end
|
||||
end
|
||||
|
||||
post '/deleteFile/*' do
|
||||
forbid unless @allow_editing
|
||||
wiki = wiki_new
|
||||
filepath = params[:splat].first
|
||||
unless filepath.nil?
|
||||
commit = commit_message
|
||||
commit[:message] = "Deleted #{filepath}"
|
||||
wiki.delete_file(filepath, commit)
|
||||
end
|
||||
|
||||
redirect to('/fileview')
|
||||
end
|
||||
|
||||
post '/rename/*' do
|
||||
forbid unless @allow_editing
|
||||
|
||||
wikip = wiki_page(params[:splat].first)
|
||||
halt 500 if wikip.nil?
|
||||
wiki = wikip.wiki
|
||||
@@ -248,6 +274,8 @@ module Precious
|
||||
end
|
||||
|
||||
post '/edit/*' do
|
||||
forbid unless @allow_editing
|
||||
|
||||
path = '/' + clean_url(sanitize_empty_params(params[:path])).to_s
|
||||
page_name = CGI.unescape(params[:page])
|
||||
wiki = wiki_new
|
||||
@@ -307,6 +335,8 @@ module Precious
|
||||
end
|
||||
|
||||
post '/create' do
|
||||
forbid unless @allow_editing
|
||||
|
||||
name = params[:page].to_url
|
||||
path = sanitize_empty_params(params[:path]) || ''
|
||||
format = params[:format].intern
|
||||
@@ -318,7 +348,7 @@ module Precious
|
||||
wiki.write_page(name, format, params[:content], commit_message, path)
|
||||
|
||||
page_dir = settings.wiki_options[:page_file_dir].to_s
|
||||
redirect to("/#{clean_url(::File.join(page_dir, path, encodeURIComponent(name)))}")
|
||||
redirect to("/#{clean_url(::File.join(encodeURIComponent(page_dir), encodeURIComponent(path), encodeURIComponent(name)))}")
|
||||
rescue Gollum::DuplicatePageError => e
|
||||
@message = "Duplicate page: #{e.message}"
|
||||
mustache :error
|
||||
@@ -326,6 +356,8 @@ module Precious
|
||||
end
|
||||
|
||||
post '/revert/*/:sha1/:sha2' do
|
||||
forbid unless @allow_editing
|
||||
|
||||
wikip = wiki_page(params[:splat].first)
|
||||
@path = wikip.path
|
||||
@name = wikip.name
|
||||
@@ -349,6 +381,8 @@ module Precious
|
||||
end
|
||||
|
||||
post '/preview' do
|
||||
forbid unless @allow_editing
|
||||
|
||||
wiki = wiki_new
|
||||
@name = params[:page] || "Preview"
|
||||
@page = wiki.preview_page(@name, params[:content], params[:format])
|
||||
@@ -357,10 +391,17 @@ module Precious
|
||||
@mathjax = wiki.mathjax
|
||||
@h1_title = wiki.h1_title
|
||||
@editable = false
|
||||
@bar_side = wiki.bar_side
|
||||
@allow_uploads = wiki.allow_uploads
|
||||
mustache :page
|
||||
end
|
||||
|
||||
get '/livepreview/' do
|
||||
wiki = wiki_new
|
||||
@mathjax = wiki.mathjax
|
||||
mustache :livepreview, { :layout => false }
|
||||
end
|
||||
|
||||
get '/history/*' do
|
||||
@page = wiki_page(params[:splat].first).page
|
||||
@page_num = [params[:page].to_i, 1].max
|
||||
@@ -378,7 +419,7 @@ module Precious
|
||||
@versions = @wiki.latest_changes({:max_count => max_count})
|
||||
mustache :latest_changes
|
||||
end
|
||||
|
||||
|
||||
post '/compare/*' do
|
||||
@file = encodeURIComponent(params[:splat].first)
|
||||
@versions = params[:versions] || []
|
||||
@@ -423,6 +464,7 @@ module Precious
|
||||
@name = name
|
||||
@content = page.formatted_data
|
||||
@version = version
|
||||
@bar_side = wikip.wiki.bar_side
|
||||
mustache :page
|
||||
elsif file = wikip.wiki.file("#{file_path}", version, true)
|
||||
show_file(file)
|
||||
@@ -432,7 +474,7 @@ module Precious
|
||||
end
|
||||
|
||||
get '/search' do
|
||||
@query = params[:q]
|
||||
@query = params[:q] || ''
|
||||
wiki = wiki_new
|
||||
# Sort wiki search results by count (desc) and then by name (asc)
|
||||
@results = wiki.search(@query).sort { |a, b| (a[:count] <=> b[:count]).nonzero? || b[:name] <=> a[:name] }.reverse
|
||||
@@ -488,7 +530,7 @@ module Precious
|
||||
|
||||
# Extensions and layout data
|
||||
@editable = true
|
||||
@page_exists = !page.versions.empty?
|
||||
@page_exists = !page.last_version.nil?
|
||||
@toc_content = wiki.universal_toc ? @page.toc_data : nil
|
||||
@mathjax = wiki.mathjax
|
||||
@h1_title = wiki.h1_title
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
module Precious
|
||||
class EditingAuth < Sinatra::Base
|
||||
def initialize(app)
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
@env = env
|
||||
# Blocks all potentially editable pages. Use EditingAuth::whitelist_pages to unblock pages.
|
||||
unless (env["REQUEST_METHOD"] == "GET") || @app.settings.wiki_options[:allow_editing]
|
||||
return block unless excluded_page?
|
||||
end
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
def block
|
||||
[403, {'Content-Type' => 'text/html', 'Content-Length' => '9'}, ['Forbidden']]
|
||||
end
|
||||
|
||||
def excluded_page?
|
||||
return false if env["REQUEST_PATH"].nil?
|
||||
whitelist_pages.any? do |whitelisted_page|
|
||||
env["REQUEST_PATH"].include? whitelisted_page
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
# List pages paths as str that you want to whitelist.
|
||||
# Pages will be compared with env["REQUEST_PATH"] using String::include? method.
|
||||
def whitelist_pages
|
||||
return ["/compare/"]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,11 @@
|
||||
# ~*~ encoding: utf-8 ~*~
|
||||
require 'gemojione'
|
||||
|
||||
module Precious
|
||||
module Helpers
|
||||
|
||||
EMOJI_PATHNAME = Pathname.new(Gemojione.images_path).freeze
|
||||
|
||||
# Extract the path string that Gollum::Wiki expects
|
||||
def extract_path(file_path)
|
||||
return nil if file_path.nil?
|
||||
@@ -51,5 +56,13 @@ module Precious
|
||||
return mustache :error
|
||||
end
|
||||
|
||||
def emoji(name)
|
||||
if emoji = Gemojione.index.find_by_name(name)
|
||||
IO.read(EMOJI_PATHNAME.join("#{emoji['unicode']}.png"))
|
||||
else
|
||||
fail ArgumentError, "emoji `#{name}' not found"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -85,6 +85,22 @@ ol.tree
|
||||
margin-right: 7px;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
li.file form
|
||||
{
|
||||
vertical-align:middle;
|
||||
display: inline-block;
|
||||
}
|
||||
li.file form button
|
||||
{
|
||||
vertical-align:middle;
|
||||
height: 20px;
|
||||
padding-left: 36px;
|
||||
padding-right: 10px;
|
||||
border: 0px solid #000000;
|
||||
background: url("../images/fileview/trashcan.png") no-repeat 16px center;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
li.file a[href *= '.pdf'] span.icon { background: url(../images/fileview/document.png) 0 0 no-repeat; }
|
||||
li.file a[href *= '.html'] span.icon { background: url(../images/fileview/document.png) 0 0 no-repeat; }
|
||||
li.file a[href $= '.css'] span.icon { background: url(../images/fileview/document.png) 0 0 no-repeat; }
|
||||
@@ -125,4 +141,3 @@ ol.tree
|
||||
}
|
||||
li input:checked + ol > li { display: block; margin: 0 0 0.125em; /* 2px */}
|
||||
li input:checked + ol > li:last-child { margin: 0 0 0.063em; /* 1px */ }
|
||||
|
||||
|
||||
@@ -849,3 +849,9 @@ ul.actions {
|
||||
.clearfloats {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.emoji {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
vertical-align: -18%;
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
@@ -121,7 +121,7 @@
|
||||
formData.append('file', file);
|
||||
|
||||
$.ajax({
|
||||
url: '/uploadFile',
|
||||
url: baseUrl + '/uploadFile',
|
||||
data: formData,
|
||||
cache: false,
|
||||
contentType: false,
|
||||
@@ -129,7 +129,7 @@
|
||||
type: 'POST',
|
||||
success: function(){
|
||||
$editorBody.removeClass('uploading');
|
||||
var text = '[[' + uploadDest + '/' + file.name + ']]';
|
||||
var text = '[[/' + uploadDest + '/' + file.name + ']]';
|
||||
var pos = editorBody.selectionStart || 0;
|
||||
editorBody.value = editorBody.value.substring(0, pos) + text + editorBody.value.substring(pos);
|
||||
editorBody.selectionStart = pos + text.length;
|
||||
|
||||
@@ -211,6 +211,11 @@ var MarkDownHelp = [
|
||||
{
|
||||
menuName: 'Escaping',
|
||||
data: '<p>If you want to use a special Markdown character in your document (such as displaying literal asterisks), you can escape the character with the backslash (<code>\\</code>). Markdown will ignore the character directly after a backslash.'
|
||||
},
|
||||
|
||||
{
|
||||
menuName: 'Emoji',
|
||||
data: '<p>See the <a href="http://emojione.com/demo/" target="_blank">EmojiOne demo</a> for all available emoji. To include one, wrap the emoji name in colons and use underscores instead of spaces (e.g. :heart: or :point_up:).'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -46,13 +46,16 @@ var Editor = require("./editor").Editor;
|
||||
var EditSession = require("./edit_session").EditSession;
|
||||
var UndoManager = require("./undomanager").UndoManager;
|
||||
var Renderer = require("./virtual_renderer").VirtualRenderer;
|
||||
var MultiSelect = require("./multi_select").MultiSelect;
|
||||
|
||||
// The following require()s are for inclusion in the built ace file
|
||||
require("./worker/worker_client");
|
||||
require("./keyboard/hash_handler");
|
||||
require("./placeholder");
|
||||
require("./multi_select");
|
||||
require("./mode/folding/fold_mode");
|
||||
require("./theme/textmate");
|
||||
require("./ext/error_marker");
|
||||
|
||||
exports.config = require("./config");
|
||||
|
||||
/**
|
||||
@@ -71,34 +74,53 @@ exports.require = require;
|
||||
exports.edit = function(el) {
|
||||
if (typeof(el) == "string") {
|
||||
var _id = el;
|
||||
var el = document.getElementById(_id);
|
||||
el = document.getElementById(_id);
|
||||
if (!el)
|
||||
throw "ace.edit can't find div #" + _id;
|
||||
throw new Error("ace.edit can't find div #" + _id);
|
||||
}
|
||||
|
||||
if (el.env && el.env.editor instanceof Editor)
|
||||
if (el && el.env && el.env.editor instanceof Editor)
|
||||
return el.env.editor;
|
||||
|
||||
var doc = exports.createEditSession(dom.getInnerText(el));
|
||||
el.innerHTML = '';
|
||||
var value = "";
|
||||
if (el && /input|textarea/i.test(el.tagName)) {
|
||||
var oldNode = el;
|
||||
value = oldNode.value;
|
||||
el = dom.createElement("pre");
|
||||
oldNode.parentNode.replaceChild(el, oldNode);
|
||||
} else {
|
||||
value = dom.getInnerText(el);
|
||||
el.innerHTML = '';
|
||||
}
|
||||
|
||||
var doc = exports.createEditSession(value);
|
||||
|
||||
var editor = new Editor(new Renderer(el));
|
||||
new MultiSelect(editor);
|
||||
editor.setSession(doc);
|
||||
|
||||
var env = {
|
||||
document: doc,
|
||||
editor: editor,
|
||||
onResize: editor.resize.bind(editor)
|
||||
onResize: editor.resize.bind(editor, null)
|
||||
};
|
||||
if (oldNode) env.textarea = oldNode;
|
||||
event.addListener(window, "resize", env.onResize);
|
||||
el.env = editor.env = env;
|
||||
editor.on("destroy", function() {
|
||||
event.removeListener(window, "resize", env.onResize);
|
||||
env.editor.container.env = null; // prevent memory leak on old ie
|
||||
});
|
||||
editor.container.env = editor.env = env;
|
||||
return editor;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new [[EditSession]], and returns the associated [[Document]].
|
||||
* @param {Document | String} text {:textParam}
|
||||
* @param {TextMode} mode {:modeParam}
|
||||
*
|
||||
**/
|
||||
exports.createEditSession = function(text, mode) {
|
||||
var doc = new EditSession(text, doc);
|
||||
var doc = new EditSession(text, mode);
|
||||
doc.setUndoManager(new UndoManager());
|
||||
return doc;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
@@ -14,7 +14,7 @@
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
@@ -35,8 +35,8 @@ var oop = require("./lib/oop");
|
||||
var EventEmitter = require("./lib/event_emitter").EventEmitter;
|
||||
|
||||
/**
|
||||
*
|
||||
* Defines the floating pointer in the document. Whenever text is inserted or deleted before the cursor, the position of the cursor is updated.
|
||||
*
|
||||
* Defines a floating pointer in the document. Whenever text is inserted or deleted before the cursor, the position of the anchor is updated.
|
||||
*
|
||||
* @class Anchor
|
||||
**/
|
||||
@@ -52,112 +52,112 @@ var EventEmitter = require("./lib/event_emitter").EventEmitter;
|
||||
**/
|
||||
|
||||
var Anchor = exports.Anchor = function(doc, row, column) {
|
||||
this.document = doc;
|
||||
this.$onChange = this.onChange.bind(this);
|
||||
this.attach(doc);
|
||||
|
||||
if (typeof column == "undefined")
|
||||
this.setPosition(row.row, row.column);
|
||||
else
|
||||
this.setPosition(row, column);
|
||||
|
||||
this.$onChange = this.onChange.bind(this);
|
||||
doc.on("change", this.$onChange);
|
||||
};
|
||||
|
||||
(function() {
|
||||
|
||||
oop.implement(this, EventEmitter);
|
||||
|
||||
|
||||
/**
|
||||
* Returns an object identifying the `row` and `column` position of the current anchor.
|
||||
* @returns {Object}
|
||||
**/
|
||||
|
||||
this.getPosition = function() {
|
||||
return this.$clipPositionToDocument(this.row, this.column);
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
/**
|
||||
*
|
||||
* Returns the current document.
|
||||
* @returns {Document}
|
||||
**/
|
||||
|
||||
this.getDocument = function() {
|
||||
return this.document;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
/**
|
||||
* experimental: allows anchor to stick to the next on the left
|
||||
*/
|
||||
this.$insertRight = false;
|
||||
/**
|
||||
* Fires whenever the anchor position changes.
|
||||
*
|
||||
* Both of these objects have a `row` and `column` property corresponding to the position.
|
||||
*
|
||||
* Events that can trigger this function include [[Anchor.setPosition `setPosition()`]].
|
||||
*
|
||||
* @event change
|
||||
* @param {Object} e An object containing information about the anchor position. It has two properties:
|
||||
* - `old`: An object describing the old Anchor position
|
||||
* - `value`: An object describing the new Anchor position
|
||||
*
|
||||
* @event change
|
||||
* @param {Object} e An object containing information about the anchor position. It has two properties:
|
||||
* - `old`: An object describing the old Anchor position
|
||||
* - `value`: An object describing the new Anchor position
|
||||
*
|
||||
**/
|
||||
|
||||
this.onChange = function(e) {
|
||||
var delta = e.data;
|
||||
var range = delta.range;
|
||||
|
||||
|
||||
if (range.start.row == range.end.row && range.start.row != this.row)
|
||||
return;
|
||||
|
||||
|
||||
if (range.start.row > this.row)
|
||||
return;
|
||||
|
||||
|
||||
if (range.start.row == this.row && range.start.column > this.column)
|
||||
return;
|
||||
|
||||
|
||||
var row = this.row;
|
||||
var column = this.column;
|
||||
|
||||
var start = range.start;
|
||||
var end = range.end;
|
||||
|
||||
if (delta.action === "insertText") {
|
||||
if (range.start.row === row && range.start.column <= column) {
|
||||
if (range.start.row === range.end.row) {
|
||||
column += range.end.column - range.start.column;
|
||||
if (start.row === row && start.column <= column) {
|
||||
if (start.column === column && this.$insertRight) {
|
||||
// do nothing
|
||||
} else if (start.row === end.row) {
|
||||
column += end.column - start.column;
|
||||
} else {
|
||||
column -= start.column;
|
||||
row += end.row - start.row;
|
||||
}
|
||||
else {
|
||||
column -= range.start.column;
|
||||
row += range.end.row - range.start.row;
|
||||
}
|
||||
}
|
||||
else if (range.start.row !== range.end.row && range.start.row < row) {
|
||||
row += range.end.row - range.start.row;
|
||||
} else if (start.row !== end.row && start.row < row) {
|
||||
row += end.row - start.row;
|
||||
}
|
||||
} else if (delta.action === "insertLines") {
|
||||
if (range.start.row <= row) {
|
||||
row += range.end.row - range.start.row;
|
||||
if (start.row === row && column === 0 && this.$insertRight) {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
else if (delta.action == "removeText") {
|
||||
if (range.start.row == row && range.start.column < column) {
|
||||
if (range.end.column >= column)
|
||||
column = range.start.column;
|
||||
else if (start.row <= row) {
|
||||
row += end.row - start.row;
|
||||
}
|
||||
} else if (delta.action === "removeText") {
|
||||
if (start.row === row && start.column < column) {
|
||||
if (end.column >= column)
|
||||
column = start.column;
|
||||
else
|
||||
column = Math.max(0, column - (range.end.column - range.start.column));
|
||||
|
||||
} else if (range.start.row !== range.end.row && range.start.row < row) {
|
||||
if (range.end.row == row) {
|
||||
column = Math.max(0, column - range.end.column) + range.start.column;
|
||||
}
|
||||
row -= (range.end.row - range.start.row);
|
||||
}
|
||||
else if (range.end.row == row) {
|
||||
row -= range.end.row - range.start.row;
|
||||
column = Math.max(0, column - range.end.column) + range.start.column;
|
||||
column = Math.max(0, column - (end.column - start.column));
|
||||
|
||||
} else if (start.row !== end.row && start.row < row) {
|
||||
if (end.row === row)
|
||||
column = Math.max(0, column - end.column) + start.column;
|
||||
row -= (end.row - start.row);
|
||||
} else if (end.row === row) {
|
||||
row -= end.row - start.row;
|
||||
column = Math.max(0, column - end.column) + start.column;
|
||||
}
|
||||
} else if (delta.action == "removeLines") {
|
||||
if (range.start.row <= row) {
|
||||
if (range.end.row <= row)
|
||||
row -= range.end.row - range.start.row;
|
||||
if (start.row <= row) {
|
||||
if (end.row <= row)
|
||||
row -= end.row - start.row;
|
||||
else {
|
||||
row = range.start.row;
|
||||
row = start.row;
|
||||
column = 0;
|
||||
}
|
||||
}
|
||||
@@ -166,16 +166,13 @@ var Anchor = exports.Anchor = function(doc, row, column) {
|
||||
this.setPosition(row, column, true);
|
||||
};
|
||||
|
||||
/**
|
||||
/**
|
||||
* Sets the anchor position to the specified row and column. If `noClip` is `true`, the position is not clipped.
|
||||
* @param {Number} row The row index to move the anchor to
|
||||
* @param {Number} column The column index to move the anchor to
|
||||
* @param {Boolean} noClip Identifies if you want the position to be clipped
|
||||
*
|
||||
*
|
||||
*
|
||||
**/
|
||||
|
||||
this.setPosition = function(row, column, noClip) {
|
||||
var pos;
|
||||
if (noClip) {
|
||||
@@ -183,47 +180,47 @@ var Anchor = exports.Anchor = function(doc, row, column) {
|
||||
row: row,
|
||||
column: column
|
||||
};
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
pos = this.$clipPositionToDocument(row, column);
|
||||
}
|
||||
|
||||
|
||||
if (this.row == pos.row && this.column == pos.column)
|
||||
return;
|
||||
|
||||
|
||||
var old = {
|
||||
row: this.row,
|
||||
column: this.column
|
||||
};
|
||||
|
||||
|
||||
this.row = pos.row;
|
||||
this.column = pos.column;
|
||||
this._emit("change", {
|
||||
this._signal("change", {
|
||||
old: old,
|
||||
value: pos
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* When called, the `'change'` event listener is removed.
|
||||
* When called, the `"change"` event listener is removed.
|
||||
*
|
||||
**/
|
||||
|
||||
this.detach = function() {
|
||||
this.document.removeEventListener("change", this.$onChange);
|
||||
};
|
||||
|
||||
/**
|
||||
this.attach = function(doc) {
|
||||
this.document = doc || this.document;
|
||||
this.document.on("change", this.$onChange);
|
||||
};
|
||||
|
||||
/**
|
||||
* Clips the anchor position to the specified row and column.
|
||||
* @param {Number} row The row index to clip the anchor to
|
||||
* @param {Number} column The column index to clip the anchor to
|
||||
*
|
||||
*
|
||||
*
|
||||
**/
|
||||
this.$clipPositionToDocument = function(row, column) {
|
||||
var pos = {};
|
||||
|
||||
|
||||
if (row >= this.document.getLength()) {
|
||||
pos.row = Math.max(0, this.document.getLength() - 1);
|
||||
pos.column = this.document.getLine(pos.row).length;
|
||||
@@ -236,13 +233,13 @@ var Anchor = exports.Anchor = function(doc, row, column) {
|
||||
pos.row = row;
|
||||
pos.column = Math.min(this.document.getLine(pos.row).length, Math.max(0, column));
|
||||
}
|
||||
|
||||
|
||||
if (column < 0)
|
||||
pos.column = 0;
|
||||
|
||||
|
||||
return pos;
|
||||
};
|
||||
|
||||
|
||||
}).call(Anchor.prototype);
|
||||
|
||||
});
|
||||
|
||||
@@ -57,6 +57,15 @@ module.exports = {
|
||||
doc.insert({row: 1, column: 1}, "123");
|
||||
assert.position(anchor.getPosition(), 1, 7);
|
||||
},
|
||||
|
||||
"test insert text at anchor should not move anchor when insertRight is true": function() {
|
||||
var doc = new Document("juhu\nkinners");
|
||||
var anchor = new Anchor(doc, 1, 4);
|
||||
anchor.$insertRight = true;
|
||||
|
||||
doc.insert({row: 1, column: 4}, "123");
|
||||
assert.position(anchor.getPosition(), 1, 4);
|
||||
},
|
||||
|
||||
"test insert lines before cursor should move anchor row": function() {
|
||||
var doc = new Document("juhu\nkinners");
|
||||
@@ -65,6 +74,32 @@ module.exports = {
|
||||
doc.insertLines(1, ["123", "456"]);
|
||||
assert.position(anchor.getPosition(), 3, 4);
|
||||
},
|
||||
|
||||
"test insert lines at anchor position should move anchor down": function() {
|
||||
var doc = new Document("juhu\nkinners");
|
||||
var anchor = new Anchor(doc, 1, 0);
|
||||
|
||||
doc.insertLines(1, ["line"]);
|
||||
assert.position(anchor.getPosition(), 2, 0);
|
||||
},
|
||||
|
||||
"test insert lines at anchor position should not move anchor down when insertRight is true and column is 0": function() {
|
||||
var doc = new Document("juhu\nkinners");
|
||||
var anchor = new Anchor(doc, 1, 0);
|
||||
anchor.$insertRight = true;
|
||||
|
||||
doc.insertLines(1, ["line"]);
|
||||
assert.position(anchor.getPosition(), 1, 0);
|
||||
},
|
||||
|
||||
"test insert lines at anchor row should move anchor down when column > 0": function() {
|
||||
var doc = new Document("juhu\nkinners");
|
||||
var anchor = new Anchor(doc, 1, 2);
|
||||
anchor.$insertRight = true;
|
||||
|
||||
doc.insertLines(1, ["line"]);
|
||||
assert.position(anchor.getPosition(), 2, 2);
|
||||
},
|
||||
|
||||
"test insert new line before cursor should move anchor column": function() {
|
||||
var doc = new Document("juhu\nkinners");
|
||||
@@ -167,6 +202,17 @@ module.exports = {
|
||||
});
|
||||
|
||||
doc.remove(new Range(2, 0, 2, 1));
|
||||
},
|
||||
|
||||
"test insert/remove lines at the end of the document": function() {
|
||||
var doc = new Document("juhu\nkinners\n123");
|
||||
var anchor = new Anchor(doc, 2, 4);
|
||||
|
||||
doc.removeLines(0, 3);
|
||||
assert.position(anchor.getPosition(), 0, 0);
|
||||
doc.insertLines(0, ["a", "b", "c"]);
|
||||
assert.position(anchor.getPosition(), 3, 0);
|
||||
assert.equal(doc.getValue(), "a\nb\nc\n");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,497 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2012, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var HashHandler = require("./keyboard/hash_handler").HashHandler;
|
||||
var AcePopup = require("./autocomplete/popup").AcePopup;
|
||||
var util = require("./autocomplete/util");
|
||||
var event = require("./lib/event");
|
||||
var lang = require("./lib/lang");
|
||||
var dom = require("./lib/dom");
|
||||
var snippetManager = require("./snippets").snippetManager;
|
||||
|
||||
var Autocomplete = function() {
|
||||
this.autoInsert = false;
|
||||
this.autoSelect = true;
|
||||
this.exactMatch = false;
|
||||
this.gatherCompletionsId = 0;
|
||||
this.keyboardHandler = new HashHandler();
|
||||
this.keyboardHandler.bindKeys(this.commands);
|
||||
|
||||
this.blurListener = this.blurListener.bind(this);
|
||||
this.changeListener = this.changeListener.bind(this);
|
||||
this.mousedownListener = this.mousedownListener.bind(this);
|
||||
this.mousewheelListener = this.mousewheelListener.bind(this);
|
||||
|
||||
this.changeTimer = lang.delayedCall(function() {
|
||||
this.updateCompletions(true);
|
||||
}.bind(this));
|
||||
|
||||
this.tooltipTimer = lang.delayedCall(this.updateDocTooltip.bind(this), 50);
|
||||
};
|
||||
|
||||
(function() {
|
||||
|
||||
this.$init = function() {
|
||||
this.popup = new AcePopup(document.body || document.documentElement);
|
||||
this.popup.on("click", function(e) {
|
||||
this.insertMatch();
|
||||
e.stop();
|
||||
}.bind(this));
|
||||
this.popup.focus = this.editor.focus.bind(this.editor);
|
||||
this.popup.on("show", this.tooltipTimer.bind(null, null));
|
||||
this.popup.on("select", this.tooltipTimer.bind(null, null));
|
||||
this.popup.on("changeHoverMarker", this.tooltipTimer.bind(null, null));
|
||||
return this.popup;
|
||||
};
|
||||
|
||||
this.getPopup = function() {
|
||||
return this.popup || this.$init();
|
||||
};
|
||||
|
||||
this.openPopup = function(editor, prefix, keepPopupPosition) {
|
||||
if (!this.popup)
|
||||
this.$init();
|
||||
|
||||
this.popup.setData(this.completions.filtered);
|
||||
|
||||
editor.keyBinding.addKeyboardHandler(this.keyboardHandler);
|
||||
|
||||
var renderer = editor.renderer;
|
||||
this.popup.setRow(this.autoSelect ? 0 : -1);
|
||||
if (!keepPopupPosition) {
|
||||
this.popup.setTheme(editor.getTheme());
|
||||
this.popup.setFontSize(editor.getFontSize());
|
||||
|
||||
var lineHeight = renderer.layerConfig.lineHeight;
|
||||
|
||||
var pos = renderer.$cursorLayer.getPixelPosition(this.base, true);
|
||||
pos.left -= this.popup.getTextLeftOffset();
|
||||
|
||||
var rect = editor.container.getBoundingClientRect();
|
||||
pos.top += rect.top - renderer.layerConfig.offset;
|
||||
pos.left += rect.left - editor.renderer.scrollLeft;
|
||||
pos.left += renderer.$gutterLayer.gutterWidth;
|
||||
|
||||
this.popup.show(pos, lineHeight);
|
||||
} else if (keepPopupPosition && !prefix) {
|
||||
this.detach();
|
||||
}
|
||||
};
|
||||
|
||||
this.detach = function() {
|
||||
this.editor.keyBinding.removeKeyboardHandler(this.keyboardHandler);
|
||||
this.editor.off("changeSelection", this.changeListener);
|
||||
this.editor.off("blur", this.blurListener);
|
||||
this.editor.off("mousedown", this.mousedownListener);
|
||||
this.editor.off("mousewheel", this.mousewheelListener);
|
||||
this.changeTimer.cancel();
|
||||
this.hideDocTooltip();
|
||||
|
||||
this.gatherCompletionsId += 1;
|
||||
if (this.popup && this.popup.isOpen)
|
||||
this.popup.hide();
|
||||
|
||||
if (this.base)
|
||||
this.base.detach();
|
||||
this.activated = false;
|
||||
this.completions = this.base = null;
|
||||
};
|
||||
|
||||
this.changeListener = function(e) {
|
||||
var cursor = this.editor.selection.lead;
|
||||
if (cursor.row != this.base.row || cursor.column < this.base.column) {
|
||||
this.detach();
|
||||
}
|
||||
if (this.activated)
|
||||
this.changeTimer.schedule();
|
||||
else
|
||||
this.detach();
|
||||
};
|
||||
|
||||
this.blurListener = function(e) {
|
||||
// we have to check if activeElement is a child of popup because
|
||||
// on IE preventDefault doesn't stop scrollbar from being focussed
|
||||
var el = document.activeElement;
|
||||
var text = this.editor.textInput.getElement()
|
||||
if (el != text && el.parentNode != this.popup.container
|
||||
&& el != this.tooltipNode && e.relatedTarget != this.tooltipNode
|
||||
&& e.relatedTarget != text
|
||||
) {
|
||||
this.detach();
|
||||
}
|
||||
};
|
||||
|
||||
this.mousedownListener = function(e) {
|
||||
this.detach();
|
||||
};
|
||||
|
||||
this.mousewheelListener = function(e) {
|
||||
this.detach();
|
||||
};
|
||||
|
||||
this.goTo = function(where) {
|
||||
var row = this.popup.getRow();
|
||||
var max = this.popup.session.getLength() - 1;
|
||||
|
||||
switch(where) {
|
||||
case "up": row = row <= 0 ? max : row - 1; break;
|
||||
case "down": row = row >= max ? -1 : row + 1; break;
|
||||
case "start": row = 0; break;
|
||||
case "end": row = max; break;
|
||||
}
|
||||
|
||||
this.popup.setRow(row);
|
||||
};
|
||||
|
||||
this.insertMatch = function(data) {
|
||||
if (!data)
|
||||
data = this.popup.getData(this.popup.getRow());
|
||||
if (!data)
|
||||
return false;
|
||||
|
||||
if (data.completer && data.completer.insertMatch) {
|
||||
data.completer.insertMatch(this.editor, data);
|
||||
} else {
|
||||
if (this.completions.filterText) {
|
||||
var ranges = this.editor.selection.getAllRanges();
|
||||
for (var i = 0, range; range = ranges[i]; i++) {
|
||||
range.start.column -= this.completions.filterText.length;
|
||||
this.editor.session.remove(range);
|
||||
}
|
||||
}
|
||||
if (data.snippet)
|
||||
snippetManager.insertSnippet(this.editor, data.snippet);
|
||||
else
|
||||
this.editor.execCommand("insertstring", data.value || data);
|
||||
}
|
||||
this.detach();
|
||||
};
|
||||
|
||||
|
||||
this.commands = {
|
||||
"Up": function(editor) { editor.completer.goTo("up"); },
|
||||
"Down": function(editor) { editor.completer.goTo("down"); },
|
||||
"Ctrl-Up|Ctrl-Home": function(editor) { editor.completer.goTo("start"); },
|
||||
"Ctrl-Down|Ctrl-End": function(editor) { editor.completer.goTo("end"); },
|
||||
|
||||
"Esc": function(editor) { editor.completer.detach(); },
|
||||
"Space": function(editor) { editor.completer.detach(); editor.insert(" ");},
|
||||
"Return": function(editor) { return editor.completer.insertMatch(); },
|
||||
"Shift-Return": function(editor) { editor.completer.insertMatch(true); },
|
||||
"Tab": function(editor) {
|
||||
var result = editor.completer.insertMatch();
|
||||
if (!result && !editor.tabstopManager)
|
||||
editor.completer.goTo("down");
|
||||
else
|
||||
return result;
|
||||
},
|
||||
|
||||
"PageUp": function(editor) { editor.completer.popup.gotoPageUp(); },
|
||||
"PageDown": function(editor) { editor.completer.popup.gotoPageDown(); }
|
||||
};
|
||||
|
||||
this.gatherCompletions = function(editor, callback) {
|
||||
var session = editor.getSession();
|
||||
var pos = editor.getCursorPosition();
|
||||
|
||||
var line = session.getLine(pos.row);
|
||||
var prefix = util.retrievePrecedingIdentifier(line, pos.column);
|
||||
|
||||
this.base = session.doc.createAnchor(pos.row, pos.column - prefix.length);
|
||||
this.base.$insertRight = true;
|
||||
|
||||
var matches = [];
|
||||
var total = editor.completers.length;
|
||||
editor.completers.forEach(function(completer, i) {
|
||||
completer.getCompletions(editor, session, pos, prefix, function(err, results) {
|
||||
if (!err)
|
||||
matches = matches.concat(results);
|
||||
// Fetch prefix again, because they may have changed by now
|
||||
var pos = editor.getCursorPosition();
|
||||
var line = session.getLine(pos.row);
|
||||
callback(null, {
|
||||
prefix: util.retrievePrecedingIdentifier(line, pos.column, results[0] && results[0].identifierRegex),
|
||||
matches: matches,
|
||||
finished: (--total === 0)
|
||||
});
|
||||
});
|
||||
});
|
||||
return true;
|
||||
};
|
||||
|
||||
this.showPopup = function(editor) {
|
||||
if (this.editor)
|
||||
this.detach();
|
||||
|
||||
this.activated = true;
|
||||
|
||||
this.editor = editor;
|
||||
if (editor.completer != this) {
|
||||
if (editor.completer)
|
||||
editor.completer.detach();
|
||||
editor.completer = this;
|
||||
}
|
||||
|
||||
editor.on("changeSelection", this.changeListener);
|
||||
editor.on("blur", this.blurListener);
|
||||
editor.on("mousedown", this.mousedownListener);
|
||||
editor.on("mousewheel", this.mousewheelListener);
|
||||
|
||||
this.updateCompletions();
|
||||
};
|
||||
|
||||
this.updateCompletions = function(keepPopupPosition) {
|
||||
if (keepPopupPosition && this.base && this.completions) {
|
||||
var pos = this.editor.getCursorPosition();
|
||||
var prefix = this.editor.session.getTextRange({start: this.base, end: pos});
|
||||
if (prefix == this.completions.filterText)
|
||||
return;
|
||||
this.completions.setFilter(prefix);
|
||||
if (!this.completions.filtered.length)
|
||||
return this.detach();
|
||||
if (this.completions.filtered.length == 1
|
||||
&& this.completions.filtered[0].value == prefix
|
||||
&& !this.completions.filtered[0].snippet)
|
||||
return this.detach();
|
||||
this.openPopup(this.editor, prefix, keepPopupPosition);
|
||||
return;
|
||||
}
|
||||
|
||||
// Save current gatherCompletions session, session is close when a match is insert
|
||||
var _id = this.gatherCompletionsId;
|
||||
this.gatherCompletions(this.editor, function(err, results) {
|
||||
// Only detach if result gathering is finished
|
||||
var detachIfFinished = function() {
|
||||
if (!results.finished) return;
|
||||
return this.detach();
|
||||
}.bind(this);
|
||||
|
||||
var prefix = results.prefix;
|
||||
var matches = results && results.matches;
|
||||
|
||||
if (!matches || !matches.length)
|
||||
return detachIfFinished();
|
||||
|
||||
// Wrong prefix or wrong session -> ignore
|
||||
if (prefix.indexOf(results.prefix) !== 0 || _id != this.gatherCompletionsId)
|
||||
return;
|
||||
|
||||
this.completions = new FilteredList(matches);
|
||||
|
||||
if (this.exactMatch)
|
||||
this.completions.exactMatch = true;
|
||||
|
||||
this.completions.setFilter(prefix);
|
||||
var filtered = this.completions.filtered;
|
||||
|
||||
// No results
|
||||
if (!filtered.length)
|
||||
return detachIfFinished();
|
||||
|
||||
// One result equals to the prefix
|
||||
if (filtered.length == 1 && filtered[0].value == prefix && !filtered[0].snippet)
|
||||
return detachIfFinished();
|
||||
|
||||
// Autoinsert if one result
|
||||
if (this.autoInsert && filtered.length == 1 && results.finished)
|
||||
return this.insertMatch(filtered[0]);
|
||||
|
||||
this.openPopup(this.editor, prefix, keepPopupPosition);
|
||||
}.bind(this));
|
||||
};
|
||||
|
||||
this.cancelContextMenu = function() {
|
||||
this.editor.$mouseHandler.cancelContextMenu();
|
||||
};
|
||||
|
||||
this.updateDocTooltip = function() {
|
||||
var popup = this.popup;
|
||||
var all = popup.data;
|
||||
var selected = all && (all[popup.getHoveredRow()] || all[popup.getRow()]);
|
||||
var doc = null;
|
||||
if (!selected || !this.editor || !this.popup.isOpen)
|
||||
return this.hideDocTooltip();
|
||||
this.editor.completers.some(function(completer) {
|
||||
if (completer.getDocTooltip)
|
||||
doc = completer.getDocTooltip(selected);
|
||||
return doc;
|
||||
});
|
||||
if (!doc)
|
||||
doc = selected;
|
||||
|
||||
if (typeof doc == "string")
|
||||
doc = {docText: doc}
|
||||
if (!doc || !(doc.docHTML || doc.docText))
|
||||
return this.hideDocTooltip();
|
||||
this.showDocTooltip(doc);
|
||||
};
|
||||
|
||||
this.showDocTooltip = function(item) {
|
||||
if (!this.tooltipNode) {
|
||||
this.tooltipNode = dom.createElement("div");
|
||||
this.tooltipNode.className = "ace_tooltip ace_doc-tooltip";
|
||||
this.tooltipNode.style.margin = 0;
|
||||
this.tooltipNode.style.pointerEvents = "auto";
|
||||
this.tooltipNode.tabIndex = -1;
|
||||
this.tooltipNode.onblur = this.blurListener.bind(this);
|
||||
}
|
||||
|
||||
var tooltipNode = this.tooltipNode;
|
||||
if (item.docHTML) {
|
||||
tooltipNode.innerHTML = item.docHTML;
|
||||
} else if (item.docText) {
|
||||
tooltipNode.textContent = item.docText;
|
||||
}
|
||||
|
||||
if (!tooltipNode.parentNode)
|
||||
document.body.appendChild(tooltipNode);
|
||||
var popup = this.popup;
|
||||
var rect = popup.container.getBoundingClientRect();
|
||||
tooltipNode.style.top = popup.container.style.top;
|
||||
tooltipNode.style.bottom = popup.container.style.bottom;
|
||||
|
||||
if (window.innerWidth - rect.right < 320) {
|
||||
tooltipNode.style.right = window.innerWidth - rect.left + "px";
|
||||
tooltipNode.style.left = "";
|
||||
} else {
|
||||
tooltipNode.style.left = (rect.right + 1) + "px";
|
||||
tooltipNode.style.right = "";
|
||||
}
|
||||
tooltipNode.style.display = "block";
|
||||
};
|
||||
|
||||
this.hideDocTooltip = function() {
|
||||
this.tooltipTimer.cancel();
|
||||
if (!this.tooltipNode) return;
|
||||
var el = this.tooltipNode;
|
||||
if (!this.editor.isFocused() && document.activeElement == el)
|
||||
this.editor.focus();
|
||||
this.tooltipNode = null;
|
||||
if (el.parentNode)
|
||||
el.parentNode.removeChild(el);
|
||||
};
|
||||
|
||||
}).call(Autocomplete.prototype);
|
||||
|
||||
Autocomplete.startCommand = {
|
||||
name: "startAutocomplete",
|
||||
exec: function(editor) {
|
||||
if (!editor.completer)
|
||||
editor.completer = new Autocomplete();
|
||||
editor.completer.autoInsert = false;
|
||||
editor.completer.autoSelect = true;
|
||||
editor.completer.showPopup(editor);
|
||||
// prevent ctrl-space opening context menu on firefox on mac
|
||||
editor.completer.cancelContextMenu();
|
||||
},
|
||||
bindKey: "Ctrl-Space|Ctrl-Shift-Space|Alt-Space"
|
||||
};
|
||||
|
||||
var FilteredList = function(array, filterText, mutateData) {
|
||||
this.all = array;
|
||||
this.filtered = array;
|
||||
this.filterText = filterText || "";
|
||||
this.exactMatch = false;
|
||||
};
|
||||
(function(){
|
||||
this.setFilter = function(str) {
|
||||
if (str.length > this.filterText && str.lastIndexOf(this.filterText, 0) === 0)
|
||||
var matches = this.filtered;
|
||||
else
|
||||
var matches = this.all;
|
||||
|
||||
this.filterText = str;
|
||||
matches = this.filterCompletions(matches, this.filterText);
|
||||
matches = matches.sort(function(a, b) {
|
||||
return b.exactMatch - a.exactMatch || b.score - a.score;
|
||||
});
|
||||
|
||||
// make unique
|
||||
var prev = null;
|
||||
matches = matches.filter(function(item){
|
||||
var caption = item.snippet || item.caption || item.value;
|
||||
if (caption === prev) return false;
|
||||
prev = caption;
|
||||
return true;
|
||||
});
|
||||
|
||||
this.filtered = matches;
|
||||
};
|
||||
this.filterCompletions = function(items, needle) {
|
||||
var results = [];
|
||||
var upper = needle.toUpperCase();
|
||||
var lower = needle.toLowerCase();
|
||||
loop: for (var i = 0, item; item = items[i]; i++) {
|
||||
var caption = item.value || item.caption || item.snippet;
|
||||
if (!caption) continue;
|
||||
var lastIndex = -1;
|
||||
var matchMask = 0;
|
||||
var penalty = 0;
|
||||
var index, distance;
|
||||
|
||||
if (this.exactMatch) {
|
||||
if (needle !== caption.substr(0, needle.length))
|
||||
continue loop;
|
||||
}else{
|
||||
// caption char iteration is faster in Chrome but slower in Firefox, so lets use indexOf
|
||||
for (var j = 0; j < needle.length; j++) {
|
||||
// TODO add penalty on case mismatch
|
||||
var i1 = caption.indexOf(lower[j], lastIndex + 1);
|
||||
var i2 = caption.indexOf(upper[j], lastIndex + 1);
|
||||
index = (i1 >= 0) ? ((i2 < 0 || i1 < i2) ? i1 : i2) : i2;
|
||||
if (index < 0)
|
||||
continue loop;
|
||||
distance = index - lastIndex - 1;
|
||||
if (distance > 0) {
|
||||
// first char mismatch should be more sensitive
|
||||
if (lastIndex === -1)
|
||||
penalty += 10;
|
||||
penalty += distance;
|
||||
}
|
||||
matchMask = matchMask | (1 << index);
|
||||
lastIndex = index;
|
||||
}
|
||||
}
|
||||
item.matchMask = matchMask;
|
||||
item.exactMatch = penalty ? 0 : 1;
|
||||
item.score = (item.score || 0) - penalty;
|
||||
results.push(item);
|
||||
}
|
||||
return results;
|
||||
};
|
||||
}).call(FilteredList.prototype);
|
||||
|
||||
exports.Autocomplete = Autocomplete;
|
||||
exports.FilteredList = FilteredList;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,341 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2012, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var EditSession = require("../edit_session").EditSession;
|
||||
var Renderer = require("../virtual_renderer").VirtualRenderer;
|
||||
var Editor = require("../editor").Editor;
|
||||
var Range = require("../range").Range;
|
||||
var event = require("../lib/event");
|
||||
var lang = require("../lib/lang");
|
||||
var dom = require("../lib/dom");
|
||||
|
||||
var $singleLineEditor = function(el) {
|
||||
var renderer = new Renderer(el);
|
||||
|
||||
renderer.$maxLines = 4;
|
||||
|
||||
var editor = new Editor(renderer);
|
||||
|
||||
editor.setHighlightActiveLine(false);
|
||||
editor.setShowPrintMargin(false);
|
||||
editor.renderer.setShowGutter(false);
|
||||
editor.renderer.setHighlightGutterLine(false);
|
||||
|
||||
editor.$mouseHandler.$focusWaitTimout = 0;
|
||||
editor.$highlightTagPending = true;
|
||||
|
||||
return editor;
|
||||
};
|
||||
|
||||
var AcePopup = function(parentNode) {
|
||||
var el = dom.createElement("div");
|
||||
var popup = new $singleLineEditor(el);
|
||||
|
||||
if (parentNode)
|
||||
parentNode.appendChild(el);
|
||||
el.style.display = "none";
|
||||
popup.renderer.content.style.cursor = "default";
|
||||
popup.renderer.setStyle("ace_autocomplete");
|
||||
|
||||
popup.setOption("displayIndentGuides", false);
|
||||
popup.setOption("dragDelay", 150);
|
||||
|
||||
var noop = function(){};
|
||||
|
||||
popup.focus = noop;
|
||||
popup.$isFocused = true;
|
||||
|
||||
popup.renderer.$cursorLayer.restartTimer = noop;
|
||||
popup.renderer.$cursorLayer.element.style.opacity = 0;
|
||||
|
||||
popup.renderer.$maxLines = 8;
|
||||
popup.renderer.$keepTextAreaAtCursor = false;
|
||||
|
||||
popup.setHighlightActiveLine(false);
|
||||
// set default highlight color
|
||||
popup.session.highlight("");
|
||||
popup.session.$searchHighlight.clazz = "ace_highlight-marker";
|
||||
|
||||
popup.on("mousedown", function(e) {
|
||||
var pos = e.getDocumentPosition();
|
||||
popup.selection.moveToPosition(pos);
|
||||
selectionMarker.start.row = selectionMarker.end.row = pos.row;
|
||||
e.stop();
|
||||
});
|
||||
|
||||
var lastMouseEvent;
|
||||
var hoverMarker = new Range(-1,0,-1,Infinity);
|
||||
var selectionMarker = new Range(-1,0,-1,Infinity);
|
||||
selectionMarker.id = popup.session.addMarker(selectionMarker, "ace_active-line", "fullLine");
|
||||
popup.setSelectOnHover = function(val) {
|
||||
if (!val) {
|
||||
hoverMarker.id = popup.session.addMarker(hoverMarker, "ace_line-hover", "fullLine");
|
||||
} else if (hoverMarker.id) {
|
||||
popup.session.removeMarker(hoverMarker.id);
|
||||
hoverMarker.id = null;
|
||||
}
|
||||
};
|
||||
popup.setSelectOnHover(false);
|
||||
popup.on("mousemove", function(e) {
|
||||
if (!lastMouseEvent) {
|
||||
lastMouseEvent = e;
|
||||
return;
|
||||
}
|
||||
if (lastMouseEvent.x == e.x && lastMouseEvent.y == e.y) {
|
||||
return;
|
||||
}
|
||||
lastMouseEvent = e;
|
||||
lastMouseEvent.scrollTop = popup.renderer.scrollTop;
|
||||
var row = lastMouseEvent.getDocumentPosition().row;
|
||||
if (hoverMarker.start.row != row) {
|
||||
if (!hoverMarker.id)
|
||||
popup.setRow(row);
|
||||
setHoverMarker(row);
|
||||
}
|
||||
});
|
||||
popup.renderer.on("beforeRender", function() {
|
||||
if (lastMouseEvent && hoverMarker.start.row != -1) {
|
||||
lastMouseEvent.$pos = null;
|
||||
var row = lastMouseEvent.getDocumentPosition().row;
|
||||
if (!hoverMarker.id)
|
||||
popup.setRow(row);
|
||||
setHoverMarker(row, true);
|
||||
}
|
||||
});
|
||||
popup.renderer.on("afterRender", function() {
|
||||
var row = popup.getRow();
|
||||
var t = popup.renderer.$textLayer;
|
||||
var selected = t.element.childNodes[row - t.config.firstRow];
|
||||
if (selected == t.selectedNode)
|
||||
return;
|
||||
if (t.selectedNode)
|
||||
dom.removeCssClass(t.selectedNode, "ace_selected");
|
||||
t.selectedNode = selected;
|
||||
if (selected)
|
||||
dom.addCssClass(selected, "ace_selected");
|
||||
});
|
||||
var hideHoverMarker = function() { setHoverMarker(-1) };
|
||||
var setHoverMarker = function(row, suppressRedraw) {
|
||||
if (row !== hoverMarker.start.row) {
|
||||
hoverMarker.start.row = hoverMarker.end.row = row;
|
||||
if (!suppressRedraw)
|
||||
popup.session._emit("changeBackMarker");
|
||||
popup._emit("changeHoverMarker");
|
||||
}
|
||||
};
|
||||
popup.getHoveredRow = function() {
|
||||
return hoverMarker.start.row;
|
||||
};
|
||||
|
||||
event.addListener(popup.container, "mouseout", hideHoverMarker);
|
||||
popup.on("hide", hideHoverMarker);
|
||||
popup.on("changeSelection", hideHoverMarker);
|
||||
|
||||
popup.session.doc.getLength = function() {
|
||||
return popup.data.length;
|
||||
};
|
||||
popup.session.doc.getLine = function(i) {
|
||||
var data = popup.data[i];
|
||||
if (typeof data == "string")
|
||||
return data;
|
||||
return (data && data.value) || "";
|
||||
};
|
||||
|
||||
var bgTokenizer = popup.session.bgTokenizer;
|
||||
bgTokenizer.$tokenizeRow = function(row) {
|
||||
var data = popup.data[row];
|
||||
var tokens = [];
|
||||
if (!data)
|
||||
return tokens;
|
||||
if (typeof data == "string")
|
||||
data = {value: data};
|
||||
if (!data.caption)
|
||||
data.caption = data.value || data.name;
|
||||
|
||||
var last = -1;
|
||||
var flag, c;
|
||||
for (var i = 0; i < data.caption.length; i++) {
|
||||
c = data.caption[i];
|
||||
flag = data.matchMask & (1 << i) ? 1 : 0;
|
||||
if (last !== flag) {
|
||||
tokens.push({type: data.className || "" + ( flag ? "completion-highlight" : ""), value: c});
|
||||
last = flag;
|
||||
} else {
|
||||
tokens[tokens.length - 1].value += c;
|
||||
}
|
||||
}
|
||||
|
||||
if (data.meta) {
|
||||
var maxW = popup.renderer.$size.scrollerWidth / popup.renderer.layerConfig.characterWidth;
|
||||
if (data.meta.length + data.caption.length < maxW - 2)
|
||||
tokens.push({type: "rightAlignedText", value: data.meta});
|
||||
}
|
||||
return tokens;
|
||||
};
|
||||
bgTokenizer.$updateOnChange = noop;
|
||||
bgTokenizer.start = noop;
|
||||
|
||||
popup.session.$computeWidth = function() {
|
||||
return this.screenWidth = 0;
|
||||
};
|
||||
|
||||
popup.$blockScrolling = Infinity;
|
||||
|
||||
// public
|
||||
popup.isOpen = false;
|
||||
popup.isTopdown = false;
|
||||
|
||||
popup.data = [];
|
||||
popup.setData = function(list) {
|
||||
popup.data = list || [];
|
||||
popup.setValue(lang.stringRepeat("\n", list.length), -1);
|
||||
popup.setRow(0);
|
||||
};
|
||||
popup.getData = function(row) {
|
||||
return popup.data[row];
|
||||
};
|
||||
|
||||
popup.getRow = function() {
|
||||
return selectionMarker.start.row;
|
||||
};
|
||||
popup.setRow = function(line) {
|
||||
line = Math.max(-1, Math.min(this.data.length, line));
|
||||
if (selectionMarker.start.row != line) {
|
||||
popup.selection.clearSelection();
|
||||
selectionMarker.start.row = selectionMarker.end.row = line || 0;
|
||||
popup.session._emit("changeBackMarker");
|
||||
popup.moveCursorTo(line || 0, 0);
|
||||
if (popup.isOpen)
|
||||
popup._signal("select");
|
||||
}
|
||||
};
|
||||
|
||||
popup.on("changeSelection", function() {
|
||||
if (popup.isOpen)
|
||||
popup.setRow(popup.selection.lead.row);
|
||||
popup.renderer.scrollCursorIntoView();
|
||||
});
|
||||
|
||||
popup.hide = function() {
|
||||
this.container.style.display = "none";
|
||||
this._signal("hide");
|
||||
popup.isOpen = false;
|
||||
};
|
||||
popup.show = function(pos, lineHeight, topdownOnly) {
|
||||
var el = this.container;
|
||||
var screenHeight = window.innerHeight;
|
||||
var screenWidth = window.innerWidth;
|
||||
var renderer = this.renderer;
|
||||
// var maxLines = Math.min(renderer.$maxLines, this.session.getLength());
|
||||
var maxH = renderer.$maxLines * lineHeight * 1.4;
|
||||
var top = pos.top + this.$borderSize;
|
||||
if (top + maxH > screenHeight - lineHeight && !topdownOnly) {
|
||||
el.style.top = "";
|
||||
el.style.bottom = screenHeight - top + "px";
|
||||
popup.isTopdown = false;
|
||||
} else {
|
||||
top += lineHeight;
|
||||
el.style.top = top + "px";
|
||||
el.style.bottom = "";
|
||||
popup.isTopdown = true;
|
||||
}
|
||||
|
||||
el.style.display = "";
|
||||
this.renderer.$textLayer.checkForSizeChanges();
|
||||
|
||||
var left = pos.left;
|
||||
if (left + el.offsetWidth > screenWidth)
|
||||
left = screenWidth - el.offsetWidth;
|
||||
|
||||
el.style.left = left + "px";
|
||||
|
||||
this._signal("show");
|
||||
lastMouseEvent = null;
|
||||
popup.isOpen = true;
|
||||
};
|
||||
|
||||
popup.getTextLeftOffset = function() {
|
||||
return this.$borderSize + this.renderer.$padding + this.$imageSize;
|
||||
};
|
||||
|
||||
popup.$imageSize = 0;
|
||||
popup.$borderSize = 1;
|
||||
|
||||
return popup;
|
||||
};
|
||||
|
||||
dom.importCssString("\
|
||||
.ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line {\
|
||||
background-color: #CAD6FA;\
|
||||
z-index: 1;\
|
||||
}\
|
||||
.ace_editor.ace_autocomplete .ace_line-hover {\
|
||||
border: 1px solid #abbffe;\
|
||||
margin-top: -1px;\
|
||||
background: rgba(233,233,253,0.4);\
|
||||
}\
|
||||
.ace_editor.ace_autocomplete .ace_line-hover {\
|
||||
position: absolute;\
|
||||
z-index: 2;\
|
||||
}\
|
||||
.ace_editor.ace_autocomplete .ace_scroller {\
|
||||
background: none;\
|
||||
border: none;\
|
||||
box-shadow: none;\
|
||||
}\
|
||||
.ace_rightAlignedText {\
|
||||
color: gray;\
|
||||
display: inline-block;\
|
||||
position: absolute;\
|
||||
right: 4px;\
|
||||
text-align: right;\
|
||||
z-index: -1;\
|
||||
}\
|
||||
.ace_editor.ace_autocomplete .ace_completion-highlight{\
|
||||
color: #000;\
|
||||
text-shadow: 0 0 0.01em;\
|
||||
}\
|
||||
.ace_editor.ace_autocomplete {\
|
||||
width: 280px;\
|
||||
z-index: 200000;\
|
||||
background: #fbfbfb;\
|
||||
color: #444;\
|
||||
border: 1px lightgray solid;\
|
||||
position: fixed;\
|
||||
box-shadow: 2px 3px 5px rgba(0,0,0,.2);\
|
||||
line-height: 1.4;\
|
||||
}");
|
||||
|
||||
exports.AcePopup = AcePopup;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,78 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2012, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
var Range = require("../range").Range;
|
||||
|
||||
var splitRegex = /[^a-zA-Z_0-9\$\-\u00C0-\u1FFF\u2C00-\uD7FF\w]+/;
|
||||
|
||||
function getWordIndex(doc, pos) {
|
||||
var textBefore = doc.getTextRange(Range.fromPoints({row: 0, column:0}, pos));
|
||||
return textBefore.split(splitRegex).length - 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does a distance analysis of the word `prefix` at position `pos` in `doc`.
|
||||
* @return Map
|
||||
*/
|
||||
function wordDistance(doc, pos) {
|
||||
var prefixPos = getWordIndex(doc, pos);
|
||||
var words = doc.getValue().split(splitRegex);
|
||||
var wordScores = Object.create(null);
|
||||
|
||||
var currentWord = words[prefixPos];
|
||||
|
||||
words.forEach(function(word, idx) {
|
||||
if (!word || word === currentWord) return;
|
||||
|
||||
var distance = Math.abs(prefixPos - idx);
|
||||
var score = words.length - distance;
|
||||
if (wordScores[word]) {
|
||||
wordScores[word] = Math.max(score, wordScores[word]);
|
||||
} else {
|
||||
wordScores[word] = score;
|
||||
}
|
||||
});
|
||||
return wordScores;
|
||||
}
|
||||
|
||||
exports.getCompletions = function(editor, session, pos, prefix, callback) {
|
||||
var wordScore = wordDistance(session, pos, prefix);
|
||||
var wordList = Object.keys(wordScore);
|
||||
callback(null, wordList.map(function(word) {
|
||||
return {
|
||||
caption: word,
|
||||
value: word,
|
||||
score: wordScore[word],
|
||||
meta: "local"
|
||||
};
|
||||
}));
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,74 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2012, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
exports.parForEach = function(array, fn, callback) {
|
||||
var completed = 0;
|
||||
var arLength = array.length;
|
||||
if (arLength === 0)
|
||||
callback();
|
||||
for (var i = 0; i < arLength; i++) {
|
||||
fn(array[i], function(result, err) {
|
||||
completed++;
|
||||
if (completed === arLength)
|
||||
callback(result, err);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var ID_REGEX = /[a-zA-Z_0-9\$\-\u00A2-\uFFFF]/;
|
||||
|
||||
exports.retrievePrecedingIdentifier = function(text, pos, regex) {
|
||||
regex = regex || ID_REGEX;
|
||||
var buf = [];
|
||||
for (var i = pos-1; i >= 0; i--) {
|
||||
if (regex.test(text[i]))
|
||||
buf.push(text[i]);
|
||||
else
|
||||
break;
|
||||
}
|
||||
return buf.reverse().join("");
|
||||
};
|
||||
|
||||
exports.retrieveFollowingIdentifier = function(text, pos, regex) {
|
||||
regex = regex || ID_REGEX;
|
||||
var buf = [];
|
||||
for (var i = pos; i < text.length; i++) {
|
||||
if (regex.test(text[i]))
|
||||
buf.push(text[i]);
|
||||
else
|
||||
break;
|
||||
}
|
||||
return buf;
|
||||
};
|
||||
|
||||
});
|
||||
@@ -34,12 +34,8 @@ define(function(require, exports, module) {
|
||||
var oop = require("./lib/oop");
|
||||
var EventEmitter = require("./lib/event_emitter").EventEmitter;
|
||||
|
||||
// tokenizing lines longer than this makes editor very slow
|
||||
var MAX_LINE_LENGTH = 5000;
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Tokenizes the current [[Document `Document`]] in the background, and caches the tokenized rows for future use.
|
||||
*
|
||||
* If a certain row is changed, everything below that row is re-tokenized.
|
||||
@@ -52,8 +48,6 @@ var MAX_LINE_LENGTH = 5000;
|
||||
* @param {Tokenizer} tokenizer The tokenizer to use
|
||||
* @param {Editor} editor The editor to associate with
|
||||
*
|
||||
*
|
||||
*
|
||||
* @constructor
|
||||
**/
|
||||
|
||||
@@ -70,29 +64,36 @@ var BackgroundTokenizer = function(tokenizer, editor) {
|
||||
if (!self.running) { return; }
|
||||
|
||||
var workerStart = new Date();
|
||||
var startLine = self.currentLine;
|
||||
var currentLine = self.currentLine;
|
||||
var endLine = -1;
|
||||
var doc = self.doc;
|
||||
|
||||
var processedLines = 0;
|
||||
while (self.lines[currentLine])
|
||||
currentLine++;
|
||||
|
||||
var startLine = currentLine;
|
||||
|
||||
var len = doc.getLength();
|
||||
while (self.currentLine < len) {
|
||||
self.$tokenizeRow(self.currentLine);
|
||||
while (self.lines[self.currentLine])
|
||||
self.currentLine++;
|
||||
var processedLines = 0;
|
||||
self.running = false;
|
||||
while (currentLine < len) {
|
||||
self.$tokenizeRow(currentLine);
|
||||
endLine = currentLine;
|
||||
do {
|
||||
currentLine++;
|
||||
} while (self.lines[currentLine]);
|
||||
|
||||
// only check every 5 lines
|
||||
processedLines ++;
|
||||
if ((processedLines % 5 == 0) && (new Date() - workerStart) > 20) {
|
||||
self.fireUpdateEvent(startLine, self.currentLine-1);
|
||||
if ((processedLines % 5 === 0) && (new Date() - workerStart) > 20) {
|
||||
self.running = setTimeout(self.$worker, 20);
|
||||
return;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.running = false;
|
||||
|
||||
self.fireUpdateEvent(startLine, len - 1);
|
||||
self.currentLine = currentLine;
|
||||
|
||||
if (startLine <= endLine)
|
||||
self.fireUpdateEvent(startLine, endLine);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -144,7 +145,7 @@ var BackgroundTokenizer = function(tokenizer, editor) {
|
||||
first: firstRow,
|
||||
last: lastRow
|
||||
};
|
||||
this._emit("update", {data: data});
|
||||
this._signal("update", {data: data});
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -164,6 +165,11 @@ var BackgroundTokenizer = function(tokenizer, editor) {
|
||||
// pretty long delay to prevent the tokenizer from interfering with the user
|
||||
this.running = setTimeout(this.$worker, 700);
|
||||
};
|
||||
|
||||
this.scheduleStart = function() {
|
||||
if (!this.running)
|
||||
this.running = setTimeout(this.$worker, 700);
|
||||
}
|
||||
|
||||
this.$updateOnChange = function(delta) {
|
||||
var range = delta.range;
|
||||
@@ -185,8 +191,6 @@ var BackgroundTokenizer = function(tokenizer, editor) {
|
||||
this.currentLine = Math.min(startRow, this.currentLine, this.doc.getLength());
|
||||
|
||||
this.stop();
|
||||
// pretty long delay to prevent the tokenizer from interfering with the user
|
||||
this.running = setTimeout(this.$worker, 700);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -226,17 +230,9 @@ var BackgroundTokenizer = function(tokenizer, editor) {
|
||||
var line = this.doc.getLine(row);
|
||||
var state = this.states[row - 1];
|
||||
|
||||
if (line.length > MAX_LINE_LENGTH) {
|
||||
var overflow = {value: line.substr(MAX_LINE_LENGTH), type: "text"};
|
||||
line = line.slice(0, MAX_LINE_LENGTH);
|
||||
}
|
||||
var data = this.tokenizer.getLineTokens(line, state);
|
||||
if (overflow) {
|
||||
data.tokens.push(overflow);
|
||||
data.state = "start";
|
||||
}
|
||||
var data = this.tokenizer.getLineTokens(line, state, row);
|
||||
|
||||
if (this.states[row] !== data.state) {
|
||||
if (this.states[row] + "" !== data.state + "") {
|
||||
this.states[row] = data.state;
|
||||
this.lines[row + 1] = null;
|
||||
if (this.currentLine > row + 1)
|
||||
|
||||
@@ -62,19 +62,19 @@ module.exports = {
|
||||
doc.setMode("./mode/javascript")
|
||||
|
||||
forceTokenize(doc)
|
||||
testStates(doc, ["comment", "start", "start"])
|
||||
testStates(doc, ["comment_regex_allowed", "start", "no_regex"])
|
||||
|
||||
doc.remove(new Range(0,2,1,2))
|
||||
testStates(doc, [null, "start"])
|
||||
testStates(doc, [null, "no_regex"])
|
||||
|
||||
forceTokenize(doc)
|
||||
testStates(doc, ["comment", "comment"])
|
||||
testStates(doc, ["comment_regex_allowed", "comment_regex_allowed"])
|
||||
|
||||
doc.insert({row:0, column:2}, "\n*/")
|
||||
testStates(doc, [undefined, undefined, "comment"])
|
||||
testStates(doc, [undefined, undefined, "comment_regex_allowed"])
|
||||
|
||||
forceTokenize(doc)
|
||||
testStates(doc, ["comment", "start", "start"])
|
||||
testStates(doc, ["comment_regex_allowed", "start", "no_regex"])
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -2,45 +2,44 @@ define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var oop = require("../lib/oop");
|
||||
var HashHandler = require("../keyboard/hash_handler").HashHandler;
|
||||
var MultiHashHandler = require("../keyboard/hash_handler").MultiHashHandler;
|
||||
var EventEmitter = require("../lib/event_emitter").EventEmitter;
|
||||
|
||||
/**
|
||||
* @class CommandManager
|
||||
*
|
||||
*
|
||||
**/
|
||||
|
||||
/**
|
||||
* new CommandManager(platform, commands)
|
||||
* @param {String} platform Identifier for the platform; must be either `'mac'` or `'win'`
|
||||
* @param {String} platform Identifier for the platform; must be either `"mac"` or `"win"`
|
||||
* @param {Array} commands A list of commands
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
**/
|
||||
|
||||
var CommandManager = function(platform, commands) {
|
||||
this.platform = platform;
|
||||
this.commands = this.byName = {};
|
||||
this.commmandKeyBinding = {};
|
||||
|
||||
this.addCommands(commands);
|
||||
|
||||
MultiHashHandler.call(this, commands, platform);
|
||||
this.byName = this.commands;
|
||||
this.setDefaultHandler("exec", function(e) {
|
||||
return e.command.exec(e.editor, e.args || {});
|
||||
});
|
||||
};
|
||||
|
||||
oop.inherits(CommandManager, HashHandler);
|
||||
oop.inherits(CommandManager, MultiHashHandler);
|
||||
|
||||
(function() {
|
||||
|
||||
oop.implement(this, EventEmitter);
|
||||
|
||||
this.exec = function(command, editor, args) {
|
||||
if (typeof command === 'string')
|
||||
if (Array.isArray(command)) {
|
||||
for (var i = command.length; i--; ) {
|
||||
if (this.exec(command[i], editor, args)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
if (typeof command === "string")
|
||||
command = this.commands[command];
|
||||
|
||||
if (!command)
|
||||
@@ -49,13 +48,11 @@ oop.inherits(CommandManager, HashHandler);
|
||||
if (editor && editor.$readOnly && !command.readOnly)
|
||||
return false;
|
||||
|
||||
var retvalue = this._emit("exec", {
|
||||
editor: editor,
|
||||
command: command,
|
||||
args: args
|
||||
});
|
||||
var e = {editor: editor, command: command, args: args};
|
||||
e.returnValue = this._emit("exec", e);
|
||||
this._signal("afterExec", e);
|
||||
|
||||
return retvalue === false ? false : true;
|
||||
return e.returnValue === false ? false : true;
|
||||
};
|
||||
|
||||
this.toggleRecording = function(editor) {
|
||||
|
||||
+1
-1
@@ -188,7 +188,7 @@ module.exports = {
|
||||
assert.equal(command, "cm2");
|
||||
|
||||
var command = this.cm.findKeyCommand(0, "return");
|
||||
assert.equal(command, "cm3");
|
||||
assert.equal(command + "", ["cm4", "cm3"] + "");
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -32,15 +32,48 @@ define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var lang = require("../lib/lang");
|
||||
var config = require("../config");
|
||||
var Range = require("../range").Range;
|
||||
|
||||
function bindKey(win, mac) {
|
||||
return {
|
||||
win: win,
|
||||
mac: mac
|
||||
};
|
||||
return {win: win, mac: mac};
|
||||
}
|
||||
|
||||
/*
|
||||
multiSelectAction: "forEach"|"forEachLine"|function|undefined,
|
||||
scrollIntoView: true|"cursor"|"center"|"selectionPart"
|
||||
*/
|
||||
exports.commands = [{
|
||||
name: "showSettingsMenu",
|
||||
bindKey: bindKey("Ctrl-,", "Command-,"),
|
||||
exec: function(editor) {
|
||||
config.loadModule("ace/ext/settings_menu", function(module) {
|
||||
module.init(editor);
|
||||
editor.showSettingsMenu();
|
||||
});
|
||||
},
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "goToNextError",
|
||||
bindKey: bindKey("Alt-E", "Ctrl-E"),
|
||||
exec: function(editor) {
|
||||
config.loadModule("ace/ext/error_marker", function(module) {
|
||||
module.showErrorMarker(editor, 1);
|
||||
});
|
||||
},
|
||||
scrollIntoView: "animate",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "goToPreviousError",
|
||||
bindKey: bindKey("Alt-Shift-E", "Ctrl-Shift-E"),
|
||||
exec: function(editor) {
|
||||
config.loadModule("ace/ext/error_marker", function(module) {
|
||||
module.showErrorMarker(editor, -1);
|
||||
});
|
||||
},
|
||||
scrollIntoView: "animate",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "selectall",
|
||||
bindKey: bindKey("Ctrl-A", "Command-A"),
|
||||
exec: function(editor) { editor.selectAll(); },
|
||||
@@ -64,38 +97,86 @@ exports.commands = [{
|
||||
name: "fold",
|
||||
bindKey: bindKey("Alt-L|Ctrl-F1", "Command-Alt-L|Command-F1"),
|
||||
exec: function(editor) { editor.session.toggleFold(false); },
|
||||
scrollIntoView: "center",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "unfold",
|
||||
bindKey: bindKey("Alt-Shift-L|Ctrl-Shift-F1", "Command-Alt-Shift-L|Command-Shift-F1"),
|
||||
exec: function(editor) { editor.session.toggleFold(true); },
|
||||
scrollIntoView: "center",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "toggleFoldWidget",
|
||||
bindKey: bindKey("F2", "F2"),
|
||||
exec: function(editor) { editor.session.toggleFoldWidget(); },
|
||||
scrollIntoView: "center",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "toggleParentFoldWidget",
|
||||
bindKey: bindKey("Alt-F2", "Alt-F2"),
|
||||
exec: function(editor) { editor.session.toggleFoldWidget(true); },
|
||||
scrollIntoView: "center",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "foldall",
|
||||
bindKey: bindKey("Alt-0", "Command-Option-0"),
|
||||
bindKey: bindKey("Ctrl-Alt-0", "Ctrl-Command-Option-0"),
|
||||
exec: function(editor) { editor.session.foldAll(); },
|
||||
scrollIntoView: "center",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "foldOther",
|
||||
bindKey: bindKey("Alt-0", "Command-Option-0"),
|
||||
exec: function(editor) {
|
||||
editor.session.foldAll();
|
||||
editor.session.unfold(editor.selection.getAllRanges());
|
||||
},
|
||||
scrollIntoView: "center",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "unfoldall",
|
||||
bindKey: bindKey("Alt-Shift-0", "Command-Option-Shift-0"),
|
||||
exec: function(editor) { editor.session.unfold(); },
|
||||
scrollIntoView: "center",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "findnext",
|
||||
bindKey: bindKey("Ctrl-K", "Command-G"),
|
||||
exec: function(editor) { editor.findNext(); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "center",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "findprevious",
|
||||
bindKey: bindKey("Ctrl-Shift-K", "Command-Shift-G"),
|
||||
exec: function(editor) { editor.findPrevious(); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "center",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "selectOrFindNext",
|
||||
bindKey: bindKey("Alt-K", "Ctrl-G"),
|
||||
exec: function(editor) {
|
||||
if (editor.selection.isEmpty())
|
||||
editor.selection.selectWord();
|
||||
else
|
||||
editor.findNext();
|
||||
},
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "selectOrFindPrevious",
|
||||
bindKey: bindKey("Alt-Shift-K", "Ctrl-Shift-G"),
|
||||
exec: function(editor) {
|
||||
if (editor.selection.isEmpty())
|
||||
editor.selection.selectWord();
|
||||
else
|
||||
editor.findPrevious();
|
||||
},
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "find",
|
||||
bindKey: bindKey("Ctrl-F", "Command-F"),
|
||||
exec: function(editor) {
|
||||
var needle = prompt("Find:", editor.getCopyText());
|
||||
editor.find(needle);
|
||||
config.loadModule("ace/ext/searchbox", function(e) {e.Search(editor)});
|
||||
},
|
||||
readOnly: true
|
||||
}, {
|
||||
@@ -108,120 +189,144 @@ exports.commands = [{
|
||||
bindKey: bindKey("Ctrl-Shift-Home", "Command-Shift-Up"),
|
||||
exec: function(editor) { editor.getSelection().selectFileStart(); },
|
||||
multiSelectAction: "forEach",
|
||||
readOnly: true
|
||||
readOnly: true,
|
||||
scrollIntoView: "animate",
|
||||
aceCommandGroup: "fileJump"
|
||||
}, {
|
||||
name: "gotostart",
|
||||
bindKey: bindKey("Ctrl-Home", "Command-Home|Command-Up"),
|
||||
exec: function(editor) { editor.navigateFileStart(); },
|
||||
multiSelectAction: "forEach",
|
||||
readOnly: true
|
||||
readOnly: true,
|
||||
scrollIntoView: "animate",
|
||||
aceCommandGroup: "fileJump"
|
||||
}, {
|
||||
name: "selectup",
|
||||
bindKey: bindKey("Shift-Up", "Shift-Up"),
|
||||
exec: function(editor) { editor.getSelection().selectUp(); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "golineup",
|
||||
bindKey: bindKey("Up", "Up|Ctrl-P"),
|
||||
exec: function(editor, args) { editor.navigateUp(args.times); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "selecttoend",
|
||||
bindKey: bindKey("Ctrl-Shift-End", "Command-Shift-Down"),
|
||||
exec: function(editor) { editor.getSelection().selectFileEnd(); },
|
||||
multiSelectAction: "forEach",
|
||||
readOnly: true
|
||||
readOnly: true,
|
||||
scrollIntoView: "animate",
|
||||
aceCommandGroup: "fileJump"
|
||||
}, {
|
||||
name: "gotoend",
|
||||
bindKey: bindKey("Ctrl-End", "Command-End|Command-Down"),
|
||||
exec: function(editor) { editor.navigateFileEnd(); },
|
||||
multiSelectAction: "forEach",
|
||||
readOnly: true
|
||||
readOnly: true,
|
||||
scrollIntoView: "animate",
|
||||
aceCommandGroup: "fileJump"
|
||||
}, {
|
||||
name: "selectdown",
|
||||
bindKey: bindKey("Shift-Down", "Shift-Down"),
|
||||
exec: function(editor) { editor.getSelection().selectDown(); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "golinedown",
|
||||
bindKey: bindKey("Down", "Down|Ctrl-N"),
|
||||
exec: function(editor, args) { editor.navigateDown(args.times); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "selectwordleft",
|
||||
bindKey: bindKey("Ctrl-Shift-Left", "Option-Shift-Left"),
|
||||
exec: function(editor) { editor.getSelection().selectWordLeft(); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "gotowordleft",
|
||||
bindKey: bindKey("Ctrl-Left", "Option-Left"),
|
||||
exec: function(editor) { editor.navigateWordLeft(); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "selecttolinestart",
|
||||
bindKey: bindKey("Alt-Shift-Left", "Command-Shift-Left"),
|
||||
exec: function(editor) { editor.getSelection().selectLineStart(); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "gotolinestart",
|
||||
bindKey: bindKey("Alt-Left|Home", "Command-Left|Home|Ctrl-A"),
|
||||
exec: function(editor) { editor.navigateLineStart(); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "selectleft",
|
||||
bindKey: bindKey("Shift-Left", "Shift-Left"),
|
||||
exec: function(editor) { editor.getSelection().selectLeft(); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "gotoleft",
|
||||
bindKey: bindKey("Left", "Left|Ctrl-B"),
|
||||
exec: function(editor, args) { editor.navigateLeft(args.times); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "selectwordright",
|
||||
bindKey: bindKey("Ctrl-Shift-Right", "Option-Shift-Right"),
|
||||
exec: function(editor) { editor.getSelection().selectWordRight(); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "gotowordright",
|
||||
bindKey: bindKey("Ctrl-Right", "Option-Right"),
|
||||
exec: function(editor) { editor.navigateWordRight(); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "selecttolineend",
|
||||
bindKey: bindKey("Alt-Shift-Right", "Command-Shift-Right"),
|
||||
exec: function(editor) { editor.getSelection().selectLineEnd(); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "gotolineend",
|
||||
bindKey: bindKey("Alt-Right|End", "Command-Right|End|Ctrl-E"),
|
||||
exec: function(editor) { editor.navigateLineEnd(); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "selectright",
|
||||
bindKey: bindKey("Shift-Right", "Shift-Right"),
|
||||
exec: function(editor) { editor.getSelection().selectRight(); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "gotoright",
|
||||
bindKey: bindKey("Right", "Right|Ctrl-F"),
|
||||
exec: function(editor, args) { editor.navigateRight(args.times); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "selectpagedown",
|
||||
@@ -268,12 +373,14 @@ exports.commands = [{
|
||||
bindKey: "Shift-Home",
|
||||
exec: function(editor) { editor.getSelection().selectLineStart(); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "selectlineend",
|
||||
bindKey: "Shift-End",
|
||||
exec: function(editor) { editor.getSelection().selectLineEnd(); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "togglerecording",
|
||||
@@ -287,16 +394,32 @@ exports.commands = [{
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "jumptomatching",
|
||||
bindKey: bindKey("Ctrl-P", "Ctrl-Shift-P"),
|
||||
bindKey: bindKey("Ctrl-P", "Ctrl-P"),
|
||||
exec: function(editor) { editor.jumpToMatching(); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "animate",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "selecttomatching",
|
||||
bindKey: bindKey("Ctrl-Shift-P", null),
|
||||
bindKey: bindKey("Ctrl-Shift-P", "Ctrl-Shift-P"),
|
||||
exec: function(editor) { editor.jumpToMatching(true); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "animate",
|
||||
readOnly: true
|
||||
},
|
||||
}, {
|
||||
name: "expandToMatching",
|
||||
bindKey: bindKey("Ctrl-Shift-M", "Ctrl-Shift-M"),
|
||||
exec: function(editor) { editor.jumpToMatching(true, true); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "animate",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "passKeysToBrowser",
|
||||
bindKey: bindKey("null", "null"),
|
||||
exec: function() {},
|
||||
passEvent: true,
|
||||
readOnly: true
|
||||
},
|
||||
|
||||
// commands disabled in readOnly mode
|
||||
{
|
||||
@@ -310,60 +433,55 @@ exports.commands = [{
|
||||
editor.clearSelection();
|
||||
}
|
||||
},
|
||||
scrollIntoView: "cursor",
|
||||
multiSelectAction: "forEach"
|
||||
}, {
|
||||
name: "removeline",
|
||||
bindKey: bindKey("Ctrl-D", "Command-D"),
|
||||
exec: function(editor) { editor.removeLines(); },
|
||||
multiSelectAction: "forEach"
|
||||
scrollIntoView: "cursor",
|
||||
multiSelectAction: "forEachLine"
|
||||
}, {
|
||||
name: "duplicateSelection",
|
||||
bindKey: bindKey("Ctrl-Shift-D", "Command-Shift-D"),
|
||||
exec: function(editor) { editor.duplicateSelection(); },
|
||||
scrollIntoView: "cursor",
|
||||
multiSelectAction: "forEach"
|
||||
}, {
|
||||
name: "sortlines",
|
||||
bindKey: bindKey("Ctrl-Alt-S", "Command-Alt-S"),
|
||||
exec: function(editor) { editor.sortLines(); },
|
||||
multiSelectAction: "forEach"
|
||||
scrollIntoView: "selection",
|
||||
multiSelectAction: "forEachLine"
|
||||
}, {
|
||||
name: "togglecomment",
|
||||
bindKey: bindKey("Ctrl-/", "Command-/"),
|
||||
exec: function(editor) { editor.toggleCommentLines(); },
|
||||
multiSelectAction: "forEach"
|
||||
multiSelectAction: "forEachLine",
|
||||
scrollIntoView: "selectionPart"
|
||||
}, {
|
||||
name: "toggleBlockComment",
|
||||
bindKey: bindKey("Ctrl-Shift-/", "Command-Shift-/"),
|
||||
exec: function(editor) { editor.toggleBlockComment(); },
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "selectionPart"
|
||||
}, {
|
||||
name: "modifyNumberUp",
|
||||
bindKey: bindKey("Ctrl-Shift-Up", "Alt-Shift-Up"),
|
||||
exec: function(editor) { editor.modifyNumber(1); },
|
||||
scrollIntoView: "cursor",
|
||||
multiSelectAction: "forEach"
|
||||
}, {
|
||||
name: "modifyNumberDown",
|
||||
bindKey: bindKey("Ctrl-Shift-Down", "Alt-Shift-Down"),
|
||||
exec: function(editor) { editor.modifyNumber(-1); },
|
||||
scrollIntoView: "cursor",
|
||||
multiSelectAction: "forEach"
|
||||
}, {
|
||||
name: "replace",
|
||||
bindKey: bindKey("Ctrl-R", "Command-Option-F"),
|
||||
bindKey: bindKey("Ctrl-H", "Command-Option-F"),
|
||||
exec: function(editor) {
|
||||
var needle = prompt("Find:", editor.getCopyText());
|
||||
if (!needle)
|
||||
return;
|
||||
var replacement = prompt("Replacement:");
|
||||
if (!replacement)
|
||||
return;
|
||||
editor.replace(replacement, {needle: needle});
|
||||
}
|
||||
}, {
|
||||
name: "replaceall",
|
||||
bindKey: bindKey("Ctrl-Shift-R", "Command-Shift-Option-F"),
|
||||
exec: function(editor) {
|
||||
var needle = prompt("Find:");
|
||||
if (!needle)
|
||||
return;
|
||||
var replacement = prompt("Replacement:");
|
||||
if (!replacement)
|
||||
return;
|
||||
editor.replaceAll(replacement, {needle: needle});
|
||||
config.loadModule("ace/ext/searchbox", function(e) {e.Search(editor, true)});
|
||||
}
|
||||
}, {
|
||||
name: "undo",
|
||||
@@ -376,92 +494,228 @@ exports.commands = [{
|
||||
}, {
|
||||
name: "copylinesup",
|
||||
bindKey: bindKey("Alt-Shift-Up", "Command-Option-Up"),
|
||||
exec: function(editor) { editor.copyLinesUp(); }
|
||||
exec: function(editor) { editor.copyLinesUp(); },
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "movelinesup",
|
||||
bindKey: bindKey("Alt-Up", "Option-Up"),
|
||||
exec: function(editor) { editor.moveLinesUp(); }
|
||||
exec: function(editor) { editor.moveLinesUp(); },
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "copylinesdown",
|
||||
bindKey: bindKey("Alt-Shift-Down", "Command-Option-Down"),
|
||||
exec: function(editor) { editor.copyLinesDown(); }
|
||||
exec: function(editor) { editor.copyLinesDown(); },
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "movelinesdown",
|
||||
bindKey: bindKey("Alt-Down", "Option-Down"),
|
||||
exec: function(editor) { editor.moveLinesDown(); }
|
||||
exec: function(editor) { editor.moveLinesDown(); },
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "del",
|
||||
bindKey: bindKey("Delete", "Delete|Ctrl-D"),
|
||||
bindKey: bindKey("Delete", "Delete|Ctrl-D|Shift-Delete"),
|
||||
exec: function(editor) { editor.remove("right"); },
|
||||
multiSelectAction: "forEach"
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "backspace",
|
||||
bindKey: bindKey(
|
||||
"Command-Backspace|Option-Backspace|Shift-Backspace|Backspace",
|
||||
"Ctrl-Backspace|Command-Backspace|Shift-Backspace|Backspace|Ctrl-H"
|
||||
"Shift-Backspace|Backspace",
|
||||
"Ctrl-Backspace|Shift-Backspace|Backspace|Ctrl-H"
|
||||
),
|
||||
exec: function(editor) { editor.remove("left"); },
|
||||
multiSelectAction: "forEach"
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "cut_or_delete",
|
||||
bindKey: bindKey("Shift-Delete", null),
|
||||
exec: function(editor) {
|
||||
if (editor.selection.isEmpty()) {
|
||||
editor.remove("left");
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "removetolinestart",
|
||||
bindKey: bindKey("Alt-Backspace", "Command-Backspace"),
|
||||
exec: function(editor) { editor.removeToLineStart(); },
|
||||
multiSelectAction: "forEach"
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "removetolineend",
|
||||
bindKey: bindKey("Alt-Delete", "Ctrl-K"),
|
||||
exec: function(editor) { editor.removeToLineEnd(); },
|
||||
multiSelectAction: "forEach"
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "removewordleft",
|
||||
bindKey: bindKey("Ctrl-Backspace", "Alt-Backspace|Ctrl-Alt-Backspace"),
|
||||
exec: function(editor) { editor.removeWordLeft(); },
|
||||
multiSelectAction: "forEach"
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "removewordright",
|
||||
bindKey: bindKey("Ctrl-Delete", "Alt-Delete"),
|
||||
exec: function(editor) { editor.removeWordRight(); },
|
||||
multiSelectAction: "forEach"
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "outdent",
|
||||
bindKey: bindKey("Shift-Tab", "Shift-Tab"),
|
||||
exec: function(editor) { editor.blockOutdent(); },
|
||||
multiSelectAction: "forEach"
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "selectionPart"
|
||||
}, {
|
||||
name: "indent",
|
||||
bindKey: bindKey("Tab", "Tab"),
|
||||
exec: function(editor) { editor.indent(); },
|
||||
multiSelectAction: "forEach"
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "selectionPart"
|
||||
}, {
|
||||
name: "blockoutdent",
|
||||
bindKey: bindKey("Ctrl-[", "Ctrl-["),
|
||||
exec: function(editor) { editor.blockOutdent(); },
|
||||
multiSelectAction: "forEachLine",
|
||||
scrollIntoView: "selectionPart"
|
||||
}, {
|
||||
name: "blockindent",
|
||||
bindKey: bindKey("Ctrl-]", "Ctrl-]"),
|
||||
exec: function(editor) { editor.blockIndent(); },
|
||||
multiSelectAction: "forEachLine",
|
||||
scrollIntoView: "selectionPart"
|
||||
}, {
|
||||
name: "insertstring",
|
||||
exec: function(editor, str) { editor.insert(str); },
|
||||
multiSelectAction: "forEach"
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "inserttext",
|
||||
exec: function(editor, args) {
|
||||
editor.insert(lang.stringRepeat(args.text || "", args.times || 1));
|
||||
},
|
||||
multiSelectAction: "forEach"
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "splitline",
|
||||
bindKey: bindKey(null, "Ctrl-O"),
|
||||
exec: function(editor) { editor.splitLine(); },
|
||||
multiSelectAction: "forEach"
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "transposeletters",
|
||||
bindKey: bindKey("Ctrl-T", "Ctrl-T"),
|
||||
exec: function(editor) { editor.transposeLetters(); },
|
||||
multiSelectAction: function(editor) {editor.transposeSelections(1); }
|
||||
multiSelectAction: function(editor) {editor.transposeSelections(1); },
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "touppercase",
|
||||
bindKey: bindKey("Ctrl-U", "Ctrl-U"),
|
||||
exec: function(editor) { editor.toUpperCase(); },
|
||||
multiSelectAction: "forEach"
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "tolowercase",
|
||||
bindKey: bindKey("Ctrl-Shift-U", "Ctrl-Shift-U"),
|
||||
exec: function(editor) { editor.toLowerCase(); },
|
||||
multiSelectAction: "forEach"
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "expandtoline",
|
||||
bindKey: bindKey("Ctrl-Shift-L", "Command-Shift-L"),
|
||||
exec: function(editor) {
|
||||
var range = editor.selection.getRange();
|
||||
|
||||
range.start.column = range.end.column = 0;
|
||||
range.end.row++;
|
||||
editor.selection.setRange(range, false);
|
||||
},
|
||||
multiSelectAction: "forEach",
|
||||
scrollIntoView: "cursor",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "joinlines",
|
||||
bindKey: bindKey(null, null),
|
||||
exec: function(editor) {
|
||||
var isBackwards = editor.selection.isBackwards();
|
||||
var selectionStart = isBackwards ? editor.selection.getSelectionLead() : editor.selection.getSelectionAnchor();
|
||||
var selectionEnd = isBackwards ? editor.selection.getSelectionAnchor() : editor.selection.getSelectionLead();
|
||||
var firstLineEndCol = editor.session.doc.getLine(selectionStart.row).length;
|
||||
var selectedText = editor.session.doc.getTextRange(editor.selection.getRange());
|
||||
var selectedCount = selectedText.replace(/\n\s*/, " ").length;
|
||||
var insertLine = editor.session.doc.getLine(selectionStart.row);
|
||||
|
||||
for (var i = selectionStart.row + 1; i <= selectionEnd.row + 1; i++) {
|
||||
var curLine = lang.stringTrimLeft(lang.stringTrimRight(editor.session.doc.getLine(i)));
|
||||
if (curLine.length !== 0) {
|
||||
curLine = " " + curLine;
|
||||
}
|
||||
insertLine += curLine;
|
||||
}
|
||||
|
||||
if (selectionEnd.row + 1 < (editor.session.doc.getLength() - 1)) {
|
||||
// Don't insert a newline at the end of the document
|
||||
insertLine += editor.session.doc.getNewLineCharacter();
|
||||
}
|
||||
|
||||
editor.clearSelection();
|
||||
editor.session.doc.replace(new Range(selectionStart.row, 0, selectionEnd.row + 2, 0), insertLine);
|
||||
|
||||
if (selectedCount > 0) {
|
||||
// Select the text that was previously selected
|
||||
editor.selection.moveCursorTo(selectionStart.row, selectionStart.column);
|
||||
editor.selection.selectTo(selectionStart.row, selectionStart.column + selectedCount);
|
||||
} else {
|
||||
// If the joined line had something in it, start the cursor at that something
|
||||
firstLineEndCol = editor.session.doc.getLine(selectionStart.row).length > firstLineEndCol ? (firstLineEndCol + 1) : firstLineEndCol;
|
||||
editor.selection.moveCursorTo(selectionStart.row, firstLineEndCol);
|
||||
}
|
||||
},
|
||||
multiSelectAction: "forEach",
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "invertSelection",
|
||||
bindKey: bindKey(null, null),
|
||||
exec: function(editor) {
|
||||
var endRow = editor.session.doc.getLength() - 1;
|
||||
var endCol = editor.session.doc.getLine(endRow).length;
|
||||
var ranges = editor.selection.rangeList.ranges;
|
||||
var newRanges = [];
|
||||
|
||||
// If multiple selections don't exist, rangeList will return 0 so replace with single range
|
||||
if (ranges.length < 1) {
|
||||
ranges = [editor.selection.getRange()];
|
||||
}
|
||||
|
||||
for (var i = 0; i < ranges.length; i++) {
|
||||
if (i == (ranges.length - 1)) {
|
||||
// The last selection must connect to the end of the document, unless it already does
|
||||
if (!(ranges[i].end.row === endRow && ranges[i].end.column === endCol)) {
|
||||
newRanges.push(new Range(ranges[i].end.row, ranges[i].end.column, endRow, endCol));
|
||||
}
|
||||
}
|
||||
|
||||
if (i === 0) {
|
||||
// The first selection must connect to the start of the document, unless it already does
|
||||
if (!(ranges[i].start.row === 0 && ranges[i].start.column === 0)) {
|
||||
newRanges.push(new Range(0, 0, ranges[i].start.row, ranges[i].start.column));
|
||||
}
|
||||
} else {
|
||||
newRanges.push(new Range(ranges[i-1].end.row, ranges[i-1].end.column, ranges[i].start.row, ranges[i].start.column));
|
||||
}
|
||||
}
|
||||
|
||||
editor.exitMultiSelectMode();
|
||||
editor.clearSelection();
|
||||
|
||||
for(var i = 0; i < newRanges.length; i++) {
|
||||
editor.selection.addRange(newRanges[i], false);
|
||||
}
|
||||
},
|
||||
readOnly: true,
|
||||
scrollIntoView: "none"
|
||||
}];
|
||||
|
||||
});
|
||||
|
||||
+232
@@ -0,0 +1,232 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
|
||||
var config = require("../config");
|
||||
var oop = require("../lib/oop");
|
||||
var HashHandler = require("../keyboard/hash_handler").HashHandler;
|
||||
var occurStartCommand = require("./occur_commands").occurStartCommand;
|
||||
|
||||
// These commands can be installed in a normal key handler to start iSearch:
|
||||
exports.iSearchStartCommands = [{
|
||||
name: "iSearch",
|
||||
bindKey: {win: "Ctrl-F", mac: "Command-F"},
|
||||
exec: function(editor, options) {
|
||||
config.loadModule(["core", "ace/incremental_search"], function(e) {
|
||||
var iSearch = e.iSearch = e.iSearch || new e.IncrementalSearch();
|
||||
iSearch.activate(editor, options.backwards);
|
||||
if (options.jumpToFirstMatch) iSearch.next(options);
|
||||
});
|
||||
},
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "iSearchBackwards",
|
||||
exec: function(editor, jumpToNext) { editor.execCommand('iSearch', {backwards: true}); },
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "iSearchAndGo",
|
||||
bindKey: {win: "Ctrl-K", mac: "Command-G"},
|
||||
exec: function(editor, jumpToNext) { editor.execCommand('iSearch', {jumpToFirstMatch: true, useCurrentOrPrevSearch: true}); },
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "iSearchBackwardsAndGo",
|
||||
bindKey: {win: "Ctrl-Shift-K", mac: "Command-Shift-G"},
|
||||
exec: function(editor) { editor.execCommand('iSearch', {jumpToFirstMatch: true, backwards: true, useCurrentOrPrevSearch: true}); },
|
||||
readOnly: true
|
||||
}];
|
||||
|
||||
// These commands are only available when incremental search mode is active:
|
||||
exports.iSearchCommands = [{
|
||||
name: "restartSearch",
|
||||
bindKey: {win: "Ctrl-F", mac: "Command-F"},
|
||||
exec: function(iSearch) {
|
||||
iSearch.cancelSearch(true);
|
||||
},
|
||||
readOnly: true,
|
||||
isIncrementalSearchCommand: true
|
||||
}, {
|
||||
name: "searchForward",
|
||||
bindKey: {win: "Ctrl-S|Ctrl-K", mac: "Ctrl-S|Command-G"},
|
||||
exec: function(iSearch, options) {
|
||||
options.useCurrentOrPrevSearch = true;
|
||||
iSearch.next(options);
|
||||
},
|
||||
readOnly: true,
|
||||
isIncrementalSearchCommand: true
|
||||
}, {
|
||||
name: "searchBackward",
|
||||
bindKey: {win: "Ctrl-R|Ctrl-Shift-K", mac: "Ctrl-R|Command-Shift-G"},
|
||||
exec: function(iSearch, options) {
|
||||
options.useCurrentOrPrevSearch = true;
|
||||
options.backwards = true;
|
||||
iSearch.next(options);
|
||||
},
|
||||
readOnly: true,
|
||||
isIncrementalSearchCommand: true
|
||||
}, {
|
||||
name: "extendSearchTerm",
|
||||
exec: function(iSearch, string) {
|
||||
iSearch.addString(string);
|
||||
},
|
||||
readOnly: true,
|
||||
isIncrementalSearchCommand: true
|
||||
}, {
|
||||
name: "extendSearchTermSpace",
|
||||
bindKey: "space",
|
||||
exec: function(iSearch) { iSearch.addString(' '); },
|
||||
readOnly: true,
|
||||
isIncrementalSearchCommand: true
|
||||
}, {
|
||||
name: "shrinkSearchTerm",
|
||||
bindKey: "backspace",
|
||||
exec: function(iSearch) {
|
||||
iSearch.removeChar();
|
||||
},
|
||||
readOnly: true,
|
||||
isIncrementalSearchCommand: true
|
||||
}, {
|
||||
name: 'confirmSearch',
|
||||
bindKey: 'return',
|
||||
exec: function(iSearch) { iSearch.deactivate(); },
|
||||
readOnly: true,
|
||||
isIncrementalSearchCommand: true
|
||||
}, {
|
||||
name: 'cancelSearch',
|
||||
bindKey: 'esc|Ctrl-G',
|
||||
exec: function(iSearch) { iSearch.deactivate(true); },
|
||||
readOnly: true,
|
||||
isIncrementalSearchCommand: true
|
||||
}, {
|
||||
name: 'occurisearch',
|
||||
bindKey: 'Ctrl-O',
|
||||
exec: function(iSearch) {
|
||||
var options = oop.mixin({}, iSearch.$options);
|
||||
iSearch.deactivate();
|
||||
occurStartCommand.exec(iSearch.$editor, options);
|
||||
},
|
||||
readOnly: true,
|
||||
isIncrementalSearchCommand: true
|
||||
}, {
|
||||
name: "yankNextWord",
|
||||
bindKey: "Ctrl-w",
|
||||
exec: function(iSearch) {
|
||||
var ed = iSearch.$editor,
|
||||
range = ed.selection.getRangeOfMovements(function(sel) { sel.moveCursorWordRight(); }),
|
||||
string = ed.session.getTextRange(range);
|
||||
iSearch.addString(string);
|
||||
},
|
||||
readOnly: true,
|
||||
isIncrementalSearchCommand: true
|
||||
}, {
|
||||
name: "yankNextChar",
|
||||
bindKey: "Ctrl-Alt-y",
|
||||
exec: function(iSearch) {
|
||||
var ed = iSearch.$editor,
|
||||
range = ed.selection.getRangeOfMovements(function(sel) { sel.moveCursorRight(); }),
|
||||
string = ed.session.getTextRange(range);
|
||||
iSearch.addString(string);
|
||||
},
|
||||
readOnly: true,
|
||||
isIncrementalSearchCommand: true
|
||||
}, {
|
||||
name: 'recenterTopBottom',
|
||||
bindKey: 'Ctrl-l',
|
||||
exec: function(iSearch) { iSearch.$editor.execCommand('recenterTopBottom'); },
|
||||
readOnly: true,
|
||||
isIncrementalSearchCommand: true
|
||||
}, {
|
||||
name: 'selectAllMatches',
|
||||
bindKey: 'Ctrl-space',
|
||||
exec: function(iSearch) {
|
||||
var ed = iSearch.$editor,
|
||||
hl = ed.session.$isearchHighlight,
|
||||
ranges = hl && hl.cache ? hl.cache
|
||||
.reduce(function(ranges, ea) {
|
||||
return ranges.concat(ea ? ea : []); }, []) : [];
|
||||
iSearch.deactivate(false);
|
||||
ranges.forEach(ed.selection.addRange.bind(ed.selection));
|
||||
},
|
||||
readOnly: true,
|
||||
isIncrementalSearchCommand: true
|
||||
}, {
|
||||
name: 'searchAsRegExp',
|
||||
bindKey: 'Alt-r',
|
||||
exec: function(iSearch) {
|
||||
iSearch.convertNeedleToRegExp();
|
||||
},
|
||||
readOnly: true,
|
||||
isIncrementalSearchCommand: true
|
||||
}];
|
||||
|
||||
function IncrementalSearchKeyboardHandler(iSearch) {
|
||||
this.$iSearch = iSearch;
|
||||
}
|
||||
|
||||
oop.inherits(IncrementalSearchKeyboardHandler, HashHandler);
|
||||
|
||||
;(function() {
|
||||
|
||||
this.attach = function(editor) {
|
||||
var iSearch = this.$iSearch;
|
||||
HashHandler.call(this, exports.iSearchCommands, editor.commands.platform);
|
||||
this.$commandExecHandler = editor.commands.addEventListener('exec', function(e) {
|
||||
if (!e.command.isIncrementalSearchCommand) return undefined;
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
return e.command.exec(iSearch, e.args || {});
|
||||
});
|
||||
}
|
||||
|
||||
this.detach = function(editor) {
|
||||
if (!this.$commandExecHandler) return;
|
||||
editor.commands.removeEventListener('exec', this.$commandExecHandler);
|
||||
delete this.$commandExecHandler;
|
||||
}
|
||||
|
||||
var handleKeyboard$super = this.handleKeyboard;
|
||||
this.handleKeyboard = function(data, hashId, key, keyCode) {
|
||||
if (((hashId === 1/*ctrl*/ || hashId === 8/*command*/) && key === 'v')
|
||||
|| (hashId === 1/*ctrl*/ && key === 'y')) return null;
|
||||
var cmd = handleKeyboard$super.call(this, data, hashId, key, keyCode);
|
||||
if (cmd.command) { return cmd; }
|
||||
if (hashId == -1) {
|
||||
var extendCmd = this.commands.extendSearchTerm;
|
||||
if (extendCmd) { return {command: extendCmd, args: key}; }
|
||||
}
|
||||
return {command: "null", passEvent: hashId == 0 || hashId == 4};
|
||||
}
|
||||
|
||||
}).call(IncrementalSearchKeyboardHandler.prototype);
|
||||
|
||||
|
||||
exports.IncrementalSearchKeyboardHandler = IncrementalSearchKeyboardHandler;
|
||||
|
||||
});
|
||||
+17
-1
@@ -35,41 +35,49 @@ exports.defaultCommands = [{
|
||||
name: "addCursorAbove",
|
||||
exec: function(editor) { editor.selectMoreLines(-1); },
|
||||
bindKey: {win: "Ctrl-Alt-Up", mac: "Ctrl-Alt-Up"},
|
||||
scrollIntoView: "cursor",
|
||||
readonly: true
|
||||
}, {
|
||||
name: "addCursorBelow",
|
||||
exec: function(editor) { editor.selectMoreLines(1); },
|
||||
bindKey: {win: "Ctrl-Alt-Down", mac: "Ctrl-Alt-Down"},
|
||||
scrollIntoView: "cursor",
|
||||
readonly: true
|
||||
}, {
|
||||
name: "addCursorAboveSkipCurrent",
|
||||
exec: function(editor) { editor.selectMoreLines(-1, true); },
|
||||
bindKey: {win: "Ctrl-Alt-Shift-Up", mac: "Ctrl-Alt-Shift-Up"},
|
||||
scrollIntoView: "cursor",
|
||||
readonly: true
|
||||
}, {
|
||||
name: "addCursorBelowSkipCurrent",
|
||||
exec: function(editor) { editor.selectMoreLines(1, true); },
|
||||
bindKey: {win: "Ctrl-Alt-Shift-Down", mac: "Ctrl-Alt-Shift-Down"},
|
||||
scrollIntoView: "cursor",
|
||||
readonly: true
|
||||
}, {
|
||||
name: "selectMoreBefore",
|
||||
exec: function(editor) { editor.selectMore(-1); },
|
||||
bindKey: {win: "Ctrl-Alt-Left", mac: "Ctrl-Alt-Left"},
|
||||
scrollIntoView: "cursor",
|
||||
readonly: true
|
||||
}, {
|
||||
name: "selectMoreAfter",
|
||||
exec: function(editor) { editor.selectMore(1); },
|
||||
bindKey: {win: "Ctrl-Alt-Right", mac: "Ctrl-Alt-Right"},
|
||||
scrollIntoView: "cursor",
|
||||
readonly: true
|
||||
}, {
|
||||
name: "selectNextBefore",
|
||||
exec: function(editor) { editor.selectMore(-1, true); },
|
||||
bindKey: {win: "Ctrl-Alt-Shift-Left", mac: "Ctrl-Alt-Shift-Left"},
|
||||
scrollIntoView: "cursor",
|
||||
readonly: true
|
||||
}, {
|
||||
name: "selectNextAfter",
|
||||
exec: function(editor) { editor.selectMore(1, true); },
|
||||
bindKey: {win: "Ctrl-Alt-Shift-Right", mac: "Ctrl-Alt-Shift-Right"},
|
||||
scrollIntoView: "cursor",
|
||||
readonly: true
|
||||
}, {
|
||||
name: "splitIntoLines",
|
||||
@@ -79,7 +87,14 @@ exports.defaultCommands = [{
|
||||
}, {
|
||||
name: "alignCursors",
|
||||
exec: function(editor) { editor.alignCursors(); },
|
||||
bindKey: {win: "Ctrl-Alt-A", mac: "Ctrl-Alt-A"}
|
||||
bindKey: {win: "Ctrl-Alt-A", mac: "Ctrl-Alt-A"},
|
||||
scrollIntoView: "cursor"
|
||||
}, {
|
||||
name: "findAll",
|
||||
exec: function(editor) { editor.findAll(); },
|
||||
bindKey: {win: "Ctrl-Alt-K", mac: "Ctrl-Alt-G"},
|
||||
scrollIntoView: "cursor",
|
||||
readonly: true
|
||||
}];
|
||||
|
||||
// commands active only in multiselect mode
|
||||
@@ -87,6 +102,7 @@ exports.multiSelectCommands = [{
|
||||
name: "singleSelection",
|
||||
bindKey: "esc",
|
||||
exec: function(editor) { editor.exitMultiSelectMode(); },
|
||||
scrollIntoView: "cursor",
|
||||
readonly: true,
|
||||
isAvailable: function(editor) {return editor && editor.inMultiSelectMode}
|
||||
}];
|
||||
|
||||
@@ -0,0 +1,110 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
|
||||
var config = require("../config"),
|
||||
Occur = require("../occur").Occur;
|
||||
|
||||
// These commands can be installed in a normal command handler to start occur:
|
||||
var occurStartCommand = {
|
||||
name: "occur",
|
||||
exec: function(editor, options) {
|
||||
var alreadyInOccur = !!editor.session.$occur;
|
||||
var occurSessionActive = new Occur().enter(editor, options);
|
||||
if (occurSessionActive && !alreadyInOccur)
|
||||
OccurKeyboardHandler.installIn(editor);
|
||||
},
|
||||
readOnly: true
|
||||
};
|
||||
|
||||
var occurCommands = [{
|
||||
name: "occurexit",
|
||||
bindKey: 'esc|Ctrl-G',
|
||||
exec: function(editor) {
|
||||
var occur = editor.session.$occur;
|
||||
if (!occur) return;
|
||||
occur.exit(editor, {});
|
||||
if (!editor.session.$occur) OccurKeyboardHandler.uninstallFrom(editor);
|
||||
},
|
||||
readOnly: true
|
||||
}, {
|
||||
name: "occuraccept",
|
||||
bindKey: 'enter',
|
||||
exec: function(editor) {
|
||||
var occur = editor.session.$occur;
|
||||
if (!occur) return;
|
||||
occur.exit(editor, {translatePosition: true});
|
||||
if (!editor.session.$occur) OccurKeyboardHandler.uninstallFrom(editor);
|
||||
},
|
||||
readOnly: true
|
||||
}];
|
||||
|
||||
var HashHandler = require("../keyboard/hash_handler").HashHandler;
|
||||
var oop = require("../lib/oop");
|
||||
|
||||
|
||||
function OccurKeyboardHandler() {}
|
||||
|
||||
oop.inherits(OccurKeyboardHandler, HashHandler);
|
||||
|
||||
;(function() {
|
||||
|
||||
this.isOccurHandler = true;
|
||||
|
||||
this.attach = function(editor) {
|
||||
HashHandler.call(this, occurCommands, editor.commands.platform);
|
||||
this.$editor = editor;
|
||||
}
|
||||
|
||||
var handleKeyboard$super = this.handleKeyboard;
|
||||
this.handleKeyboard = function(data, hashId, key, keyCode) {
|
||||
var cmd = handleKeyboard$super.call(this, data, hashId, key, keyCode);
|
||||
return (cmd && cmd.command) ? cmd : undefined;
|
||||
}
|
||||
|
||||
}).call(OccurKeyboardHandler.prototype);
|
||||
|
||||
OccurKeyboardHandler.installIn = function(editor) {
|
||||
var handler = new this();
|
||||
editor.keyBinding.addKeyboardHandler(handler);
|
||||
editor.commands.addCommands(occurCommands);
|
||||
}
|
||||
|
||||
OccurKeyboardHandler.uninstallFrom = function(editor) {
|
||||
editor.commands.removeCommands(occurCommands);
|
||||
var handler = editor.getKeyboardHandler();
|
||||
if (handler.isOccurHandler)
|
||||
editor.keyBinding.removeKeyboardHandler(handler);
|
||||
}
|
||||
|
||||
exports.occurStartCommand = occurStartCommand;
|
||||
|
||||
});
|
||||
@@ -32,6 +32,11 @@ define(function(require, exports, module) {
|
||||
"no use strict";
|
||||
|
||||
var lang = require("./lib/lang");
|
||||
var oop = require("./lib/oop");
|
||||
var net = require("./lib/net");
|
||||
var AppConfig = require("./lib/app_config").AppConfig;
|
||||
|
||||
module.exports = exports = new AppConfig();
|
||||
|
||||
var global = (function() {
|
||||
return this;
|
||||
@@ -65,30 +70,81 @@ exports.all = function() {
|
||||
return lang.copyObject(options);
|
||||
};
|
||||
|
||||
// module loading
|
||||
exports.moduleUrl = function(name, component) {
|
||||
if (options.$moduleUrls[name])
|
||||
return options.$moduleUrls[name];
|
||||
|
||||
var parts = name.split("/");
|
||||
component = component || parts[parts.length - 2] || "";
|
||||
var base = parts[parts.length - 1].replace(component, "").replace(/(^[\-_])|([\-_]$)/, "");
|
||||
|
||||
// todo make this configurable or get rid of '-'
|
||||
var sep = component == "snippets" ? "/" : "-";
|
||||
var base = parts[parts.length - 1];
|
||||
if (component == "worker" && sep == "-") {
|
||||
var re = new RegExp("^" + component + "[\\-_]|[\\-_]" + component + "$", "g");
|
||||
base = base.replace(re, "");
|
||||
}
|
||||
|
||||
if (!base && parts.length > 1)
|
||||
if ((!base || base == component) && parts.length > 1)
|
||||
base = parts[parts.length - 2];
|
||||
var path = options[component + "Path"];
|
||||
if (path == null)
|
||||
if (path == null) {
|
||||
path = options.basePath;
|
||||
} else if (sep == "/") {
|
||||
component = sep = "";
|
||||
}
|
||||
if (path && path.slice(-1) != "/")
|
||||
path += "/";
|
||||
return path + component + "-" + base + this.get("suffix");
|
||||
return path + component + sep + base + this.get("suffix");
|
||||
};
|
||||
|
||||
exports.setModuleUrl = function(name, subst) {
|
||||
return options.$moduleUrls[name] = subst;
|
||||
};
|
||||
|
||||
exports.init = function() {
|
||||
options.packaged = require.packaged || module.packaged || (global.define && define.packaged);
|
||||
exports.$loading = {};
|
||||
exports.loadModule = function(moduleName, onLoad) {
|
||||
var module, moduleType;
|
||||
if (Array.isArray(moduleName)) {
|
||||
moduleType = moduleName[0];
|
||||
moduleName = moduleName[1];
|
||||
}
|
||||
|
||||
try {
|
||||
module = require(moduleName);
|
||||
} catch (e) {}
|
||||
// require(moduleName) can return empty object if called after require([moduleName], callback)
|
||||
if (module && !exports.$loading[moduleName])
|
||||
return onLoad && onLoad(module);
|
||||
|
||||
if (!exports.$loading[moduleName])
|
||||
exports.$loading[moduleName] = [];
|
||||
|
||||
exports.$loading[moduleName].push(onLoad);
|
||||
|
||||
if (exports.$loading[moduleName].length > 1)
|
||||
return;
|
||||
|
||||
var afterLoad = function() {
|
||||
require([moduleName], function(module) {
|
||||
exports._emit("load.module", {name: moduleName, module: module});
|
||||
var listeners = exports.$loading[moduleName];
|
||||
exports.$loading[moduleName] = null;
|
||||
listeners.forEach(function(onLoad) {
|
||||
onLoad && onLoad(module);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
if (!exports.get("packaged"))
|
||||
return afterLoad();
|
||||
net.loadScript(exports.moduleUrl(moduleName, moduleType), afterLoad);
|
||||
};
|
||||
|
||||
// initialization
|
||||
function init(packaged) {
|
||||
options.packaged = packaged || require.packaged || module.packaged || (global.define && define.packaged);
|
||||
|
||||
if (!global.document)
|
||||
return "";
|
||||
@@ -96,7 +152,11 @@ exports.init = function() {
|
||||
var scriptOptions = {};
|
||||
var scriptUrl = "";
|
||||
|
||||
var scripts = document.getElementsByTagName("script");
|
||||
// Use currentScript.ownerDocument in case this file was loaded from imported document. (HTML Imports)
|
||||
var currentScript = (document.currentScript || document._currentScript ); // native or polyfill
|
||||
var currentDocument = currentScript && currentScript.ownerDocument || document;
|
||||
|
||||
var scripts = currentDocument.getElementsByTagName("script");
|
||||
for (var i=0; i<scripts.length; i++) {
|
||||
var script = scripts[i];
|
||||
|
||||
@@ -122,6 +182,7 @@ exports.init = function() {
|
||||
scriptOptions.packaged = true;
|
||||
}
|
||||
|
||||
scriptOptions.basePath = scriptOptions.base;
|
||||
scriptOptions.workerPath = scriptOptions.workerPath || scriptOptions.base;
|
||||
scriptOptions.modePath = scriptOptions.modePath || scriptOptions.base;
|
||||
scriptOptions.themePath = scriptOptions.themePath || scriptOptions.base;
|
||||
@@ -132,6 +193,8 @@ exports.init = function() {
|
||||
exports.set(key, scriptOptions[key]);
|
||||
};
|
||||
|
||||
exports.init = init;
|
||||
|
||||
function deHyphenate(str) {
|
||||
return str.replace(/-(.)/g, function(m, m1) { return m1.toUpperCase(); });
|
||||
}
|
||||
|
||||
@@ -40,10 +40,10 @@ var assert = require("./test/assertions");
|
||||
|
||||
module.exports = {
|
||||
|
||||
"test path resolution" : function() {
|
||||
"test: path resolution" : function() {
|
||||
config.set("packaged", "true");
|
||||
var url = config.moduleUrl("kr_theme", "theme");
|
||||
assert.equal(url, "theme-kr.js");
|
||||
assert.equal(url, "theme-kr_theme.js");
|
||||
|
||||
config.set("basePath", "a/b");
|
||||
url = config.moduleUrl("m/theme", "theme");
|
||||
@@ -60,12 +60,76 @@ module.exports = {
|
||||
url = config.moduleUrl("foo/1", "theme");
|
||||
assert.equal(url, "a/b1.js");
|
||||
|
||||
url = config.moduleUrl("snippets/js");
|
||||
assert.equal(url, "a/b/snippets/js.js");
|
||||
|
||||
config.setModuleUrl("snippets/js", "_.js");
|
||||
url = config.moduleUrl("snippets/js");
|
||||
assert.equal(url, "_.js");
|
||||
|
||||
url = config.moduleUrl("ace/ext/textarea");
|
||||
assert.equal(url, "a/b/ext-textarea.js");
|
||||
|
||||
assert.equal();
|
||||
},
|
||||
"test: define options" : function() {
|
||||
var o = {};
|
||||
config.defineOptions(o, "test_object", {
|
||||
opt1: {
|
||||
set: function(val) {
|
||||
this.x = val;
|
||||
},
|
||||
value: 7,
|
||||
},
|
||||
initialValue: {
|
||||
set: function(val) {
|
||||
this.x = val;
|
||||
},
|
||||
initialValue: 8,
|
||||
},
|
||||
opt2: {
|
||||
get: function(val) {
|
||||
return this.x;
|
||||
}
|
||||
},
|
||||
forwarded: "model"
|
||||
});
|
||||
o.model = {};
|
||||
config.defineOptions(o.model, "model", {
|
||||
forwarded: {value: 1}
|
||||
});
|
||||
|
||||
config.resetOptions(o);
|
||||
config.resetOptions(o.model);
|
||||
assert.equal(o.getOption("opt1"), 7);
|
||||
assert.equal(o.getOption("opt2"), 7);
|
||||
o.setOption("opt1", 8);
|
||||
assert.equal(o.getOption("opt1"), 8);
|
||||
assert.equal(o.getOption("opt2"), 8);
|
||||
|
||||
assert.equal(o.getOption("forwarded"), 1);
|
||||
|
||||
assert.equal(o.getOption("new"), undefined);
|
||||
o.setOption("new", 0);
|
||||
assert.equal(o.getOption("new"), undefined);
|
||||
|
||||
|
||||
assert.equal(o.getOption("initialValue"), 8);
|
||||
o.setOption("initialValue", 7);
|
||||
assert.equal(o.getOption("opt2"), 7);
|
||||
|
||||
config.setDefaultValues("test_object", {
|
||||
opt1: 1,
|
||||
forwarded: 2
|
||||
});
|
||||
config.resetOptions(o);
|
||||
assert.equal(o.getOption("opt1"), 1);
|
||||
assert.equal(o.getOption("forwarded"), 2);
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
if (typeof module !== "undefined" && module === require.main) {
|
||||
require("asyncjs").test.testcase(module.exports).exec()
|
||||
require("asyncjs").test.testcase(module.exports).exec();
|
||||
}
|
||||
|
||||
@@ -1,13 +1,21 @@
|
||||
.ace_editor {
|
||||
position: absolute;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;
|
||||
font-size: 12px;
|
||||
font: 12px/normal 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', monospace;
|
||||
direction: ltr;
|
||||
}
|
||||
|
||||
.ace_scroller {
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
background-color: inherit;
|
||||
-ms-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
cursor: text;
|
||||
}
|
||||
|
||||
.ace_content {
|
||||
@@ -15,16 +23,40 @@
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
cursor: text;
|
||||
min-width: 100%;
|
||||
}
|
||||
|
||||
.ace_dragging .ace_scroller:before{
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
content: '';
|
||||
background: rgba(250, 250, 250, 0.01);
|
||||
z-index: 1000;
|
||||
}
|
||||
.ace_dragging.ace_dark .ace_scroller:before{
|
||||
background: rgba(0, 0, 0, 0.01);
|
||||
}
|
||||
|
||||
.ace_selecting, .ace_selecting * {
|
||||
cursor: text !important;
|
||||
}
|
||||
|
||||
.ace_gutter {
|
||||
position: absolute;
|
||||
overflow : hidden;
|
||||
height: 100%;
|
||||
width: auto;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
cursor: default;
|
||||
z-index: 4;
|
||||
-ms-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.ace_gutter-active-line {
|
||||
@@ -44,34 +76,47 @@
|
||||
}
|
||||
|
||||
.ace_gutter-cell.ace_error {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUM2OEZDQTQ4RTU0MTFFMUEzM0VFRTM2RUY1M0RBMjYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUM2OEZDQTU4RTU0MTFFMUEzM0VFRTM2RUY1M0RBMjYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQzY4RkNBMjhFNTQxMUUxQTMzRUVFMzZFRjUzREEyNiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQzY4RkNBMzhFNTQxMUUxQTMzRUVFMzZFRjUzREEyNiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PkgXxbAAAAJbSURBVHjapFNNaBNBFH4zs5vdZLP5sQmNpT82QY209heh1ioWisaDRcSKF0WKJ0GQnrzrxasHsR6EnlrwD0TagxJabaVEpFYxLWlLSS822tr87m66ccfd2GKyVhA6MMybgfe97/vmPUQphd0sZjto9XIn9OOsvlu2nkqRzVU+6vvlzPf8W6bk8dxQ0NPbxAALgCgg2JkaQuhzQau/El0zbmUA7U0Es8v2CiYmKQJHGO1QICCLoqilMhkmurDAyapKgqItezi/USRdJqEYY4D5jCy03ht2yMkkvL91jTTX10qzyyu2hruPRN7jgbH+EOsXcMLgYiThEgAMhABW85oqy1DXdRIdvP1AHJ2acQXvDIrVHcdQNrEKNYSVMSZGMjEzIIAwDXIo+6G/FxcGnzkC3T2oMhLjre49sBB+RRcHLqdafK6sYdE/GGBwU1VpFNj0aN8pJbe+BkZyevUrvLl6Xmm0W9IuTc0DxrDNAJd5oEvI/KRsNC3bQyNjPO9yQ1YHcfj2QvfQc/5TUhJTBc2iM0U7AWDQtc1nJHvD/cfO2s7jaGkiTEfa/Ep8coLu7zmNmh8+dc5lZDuUeFAGUNA/OY6JVaypQ0vjr7XYjUvJM37vt+j1vuTK5DgVfVUoTjVe+y3/LxMxY2GgU+CSLy4cpfsYorRXuXIOi0Vt40h67uZFTdIo6nLaZcwUJWAzwNS0tBnqqKzQDnjdG/iPyZxo46HaKUpbvYkj8qYRTZsBhge+JHhZyh0x9b95JqjVJkT084kZIPwu/mPWqPgfQ5jXh2+92Ay7HedfAgwA6KDWafb4w3cAAAAASUVORK5CYII=");
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAABOFBMVEX/////////QRswFAb/Ui4wFAYwFAYwFAaWGAfDRymzOSH/PxswFAb/SiUwFAYwFAbUPRvjQiDllog5HhHdRybsTi3/Tyv9Tir+Syj/UC3////XurebMBIwFAb/RSHbPx/gUzfdwL3kzMivKBAwFAbbvbnhPx66NhowFAYwFAaZJg8wFAaxKBDZurf/RB6mMxb/SCMwFAYwFAbxQB3+RB4wFAb/Qhy4Oh+4QifbNRcwFAYwFAYwFAb/QRzdNhgwFAYwFAbav7v/Uy7oaE68MBK5LxLewr/r2NXewLswFAaxJw4wFAbkPRy2PyYwFAaxKhLm1tMwFAazPiQwFAaUGAb/QBrfOx3bvrv/VC/maE4wFAbRPBq6MRO8Qynew8Dp2tjfwb0wFAbx6eju5+by6uns4uH9/f36+vr/GkHjAAAAYnRSTlMAGt+64rnWu/bo8eAA4InH3+DwoN7j4eLi4xP99Nfg4+b+/u9B/eDs1MD1mO7+4PHg2MXa347g7vDizMLN4eG+Pv7i5evs/v79yu7S3/DV7/498Yv24eH+4ufQ3Ozu/v7+y13sRqwAAADLSURBVHjaZc/XDsFgGIBhtDrshlitmk2IrbHFqL2pvXf/+78DPokj7+Fz9qpU/9UXJIlhmPaTaQ6QPaz0mm+5gwkgovcV6GZzd5JtCQwgsxoHOvJO15kleRLAnMgHFIESUEPmawB9ngmelTtipwwfASilxOLyiV5UVUyVAfbG0cCPHig+GBkzAENHS0AstVF6bacZIOzgLmxsHbt2OecNgJC83JERmePUYq8ARGkJx6XtFsdddBQgZE2nPR6CICZhawjA4Fb/chv+399kfR+MMMDGOQAAAABJRU5ErkJggg==");
|
||||
background-repeat: no-repeat;
|
||||
background-position: 2px center;
|
||||
}
|
||||
|
||||
.ace_gutter-cell.ace_warning {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QUM2OEZDQTg4RTU0MTFFMUEzM0VFRTM2RUY1M0RBMjYiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QUM2OEZDQTk4RTU0MTFFMUEzM0VFRTM2RUY1M0RBMjYiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDpBQzY4RkNBNjhFNTQxMUUxQTMzRUVFMzZFRjUzREEyNiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBQzY4RkNBNzhFNTQxMUUxQTMzRUVFMzZFRjUzREEyNiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pgd7PfIAAAGmSURBVHjaYvr//z8DJZiJgUIANoCRkREb9gLiSVAaQx4OQM7AAkwd7XU2/v++/rOttdYGEB9dASEvOMydGKfH8Gv/p4XTkvRBfLxeQAP+1cUhXopyvzhP7P/IoSj7g7Mw09cNKO6J1QQ0L4gICPIv/veg/8W+JdFvQNLHVsW9/nmn9zk7B+cCkDwhL7gt6knSZnx9/LuCEOcvkIAMP+cvto9nfqyZmmUAksfnBUtbM60gX/3/kgyv3/xSFOL5DZT+L8vP+Yfh5cvfPvp/xUHyQHXGyAYwgpwBjZYFT3Y1OEl/OfCH4ffv3wzc4iwMvNIsDJ+f/mH4+vIPAxsb631WW0Yln6ZpQLXdMK/DXGDflh+sIv37EivD5x//Gb7+YWT4y86sl7BCCkSD+Z++/1dkvsFRl+HnD1Rvje4F8whjMXmGj58YGf5zsDMwcnAwfPvKcml62DsQDeaDxN+/Y0qwlpEHqrdB94IRNIDUgfgfKJChGK4OikEW3gTiXUB950ASLFAF54AC94A0G9QAfOnmF9DCDzABFqS08IHYDIScdijOjQABBgC+/9awBH96jwAAAABJRU5ErkJggg==");
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAAmVBMVEX///8AAAD///8AAAAAAABPSzb/5sAAAAB/blH/73z/ulkAAAAAAAD85pkAAAAAAAACAgP/vGz/rkDerGbGrV7/pkQICAf////e0IsAAAD/oED/qTvhrnUAAAD/yHD/njcAAADuv2r/nz//oTj/p064oGf/zHAAAAA9Nir/tFIAAAD/tlTiuWf/tkIAAACynXEAAAAAAAAtIRW7zBpBAAAAM3RSTlMAABR1m7RXO8Ln31Z36zT+neXe5OzooRDfn+TZ4p3h2hTf4t3k3ucyrN1K5+Xaks52Sfs9CXgrAAAAjklEQVR42o3PbQ+CIBQFYEwboPhSYgoYunIqqLn6/z8uYdH8Vmdnu9vz4WwXgN/xTPRD2+sgOcZjsge/whXZgUaYYvT8QnuJaUrjrHUQreGczuEafQCO/SJTufTbroWsPgsllVhq3wJEk2jUSzX3CUEDJC84707djRc5MTAQxoLgupWRwW6UB5fS++NV8AbOZgnsC7BpEAAAAABJRU5ErkJggg==");
|
||||
background-position: 2px center;
|
||||
}
|
||||
|
||||
.ace_gutter-cell.ace_info {
|
||||
background-image: url("data:image/gif;base64,R0lGODlhEAAQAMQAAAAAAEFBQVJSUl5eXmRkZGtra39/f4WFhYmJiZGRkaampry8vMPDw8zMzNXV1dzc3OTk5Orq6vDw8P///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABQALAAAAAAQABAAAAUuICWOZGmeaBml5XGwFCQSBGyXRSAwtqQIiRuiwIM5BoYVbEFIyGCQoeJGrVptIQA7");
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAAAAAA6mKC9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAAJ0Uk5TAAB2k804AAAAPklEQVQY02NgIB68QuO3tiLznjAwpKTgNyDbMegwisCHZUETUZV0ZqOquBpXj2rtnpSJT1AEnnRmL2OgGgAAIKkRQap2htgAAAAASUVORK5CYII=");
|
||||
background-position: 2px center;
|
||||
}
|
||||
.ace_dark .ace_gutter-cell.ace_info {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyRpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoTWFjaW50b3NoKSIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpGRTk5MTVGREIxNDkxMUUxOTc5Q0FFREQyMTNGMjBFQyIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpGRTk5MTVGRUIxNDkxMUUxOTc5Q0FFREQyMTNGMjBFQyI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOkZFOTkxNUZCQjE0OTExRTE5NzlDQUVERDIxM0YyMEVDIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkZFOTkxNUZDQjE0OTExRTE5NzlDQUVERDIxM0YyMEVDIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+SIDkjAAAAJ1JREFUeNpi/P//PwMlgImBQkB7A6qrq/+DMC55FkIGKCoq4pVnpFkgTp069f/+/fv/r1u37r+tre1/kg0A+ptn9uzZYLaRkRHpLvjw4cNXWVlZhufPnzOcO3eOdAO0tbVPAjHDmzdvGA4fPsxIsgGSkpJmv379Ynj37h2DjIyMCMkG3LhxQ/T27dsMampqDHZ2dq/pH41DxwCAAAMAFdc68dUsFZgAAAAASUVORK5CYII=");
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQBAMAAADt3eJSAAAAJFBMVEUAAAChoaGAgIAqKiq+vr6tra1ZWVmUlJSbm5s8PDxubm56enrdgzg3AAAAAXRSTlMAQObYZgAAAClJREFUeNpjYMAPdsMYHegyJZFQBlsUlMFVCWUYKkAZMxZAGdxlDMQBAG+TBP4B6RyJAAAAAElFTkSuQmCC");
|
||||
}
|
||||
|
||||
.ace_scrollbar {
|
||||
position: absolute;
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 6;
|
||||
}
|
||||
|
||||
.ace_scrollbar-inner {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
cursor: text;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.ace_scrollbar-v{
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
.ace_scrollbar-h {
|
||||
overflow-x: scroll;
|
||||
overflow-y: hidden;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
@@ -93,22 +138,29 @@
|
||||
resize: none;
|
||||
outline: none;
|
||||
overflow: hidden;
|
||||
font: inherit;
|
||||
padding: 0 1px;
|
||||
margin: 0 -1px;
|
||||
text-indent: -1em;
|
||||
-ms-user-select: text;
|
||||
-moz-user-select: text;
|
||||
-webkit-user-select: text;
|
||||
user-select: text;
|
||||
}
|
||||
|
||||
.ace_text-input.ace_composition {
|
||||
background: #fff;
|
||||
color: #000;
|
||||
background: inherit;
|
||||
color: inherit;
|
||||
z-index: 1000;
|
||||
opacity: 1;
|
||||
border: solid lightgray 1px;
|
||||
margin: -1px
|
||||
text-indent: 0;
|
||||
}
|
||||
|
||||
.ace_layer {
|
||||
z-index: 1;
|
||||
position: absolute;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
white-space: pre;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
-moz-box-sizing: border-box;
|
||||
@@ -127,7 +179,6 @@
|
||||
}
|
||||
|
||||
.ace_text-layer {
|
||||
color: black;
|
||||
font: inherit !important;
|
||||
}
|
||||
|
||||
@@ -146,6 +197,16 @@
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
border-left: 2px solid
|
||||
}
|
||||
|
||||
.ace_slim-cursors .ace_cursor {
|
||||
border-left-width: 1px;
|
||||
}
|
||||
|
||||
.ace_overwrite-cursors .ace_cursor {
|
||||
border-left-width: 0;
|
||||
border-bottom: 1px solid;
|
||||
}
|
||||
|
||||
.ace_hidden-cursors .ace_cursor {
|
||||
@@ -153,26 +214,15 @@
|
||||
}
|
||||
|
||||
.ace_smooth-blinking .ace_cursor {
|
||||
-moz-transition: opacity 0.18s;
|
||||
-webkit-transition: opacity 0.18s;
|
||||
-o-transition: opacity 0.18s;
|
||||
-ms-transition: opacity 0.18s;
|
||||
transition: opacity 0.18s;
|
||||
}
|
||||
|
||||
.ace_cursor[style*="opacity: 0"]{
|
||||
-ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=0)";
|
||||
}
|
||||
|
||||
.ace_editor.ace_multiselect .ace_cursor {
|
||||
border-left-width: 1px;
|
||||
}
|
||||
|
||||
.ace_line {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ace_marker-layer .ace_step {
|
||||
.ace_marker-layer .ace_step, .ace_marker-layer .ace_stack {
|
||||
position: absolute;
|
||||
z-index: 3;
|
||||
}
|
||||
@@ -211,15 +261,13 @@
|
||||
vertical-align: middle;
|
||||
|
||||
background-image:
|
||||
url("data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%11%00%00%00%09%08%06%00%00%00%D4%E8%C7%0C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%B5IDAT(%15%A5%91%3D%0E%02!%10%85ac%E1%05%D6%CE%D6%C6%CE%D2%E8%ED%CD%DE%C0%C6%D6N.%E0V%F8%3D%9Ca%891XH%C2%BE%D9y%3F%90!%E6%9C%C3%BFk%E5%011%C6-%F5%C8N%04%DF%BD%FF%89%DFt%83DN%60%3E%F3%AB%A0%DE%1A%5Dg%BE%10Q%97%1B%40%9C%A8o%10%8F%5E%828%B4%1B%60%87%F6%02%26%85%1Ch%1E%C1%2B%5Bk%FF%86%EE%B7j%09%9A%DA%9B%ACe%A3%F9%EC%DA!9%B4%D5%A6%81%86%86%98%CC%3C%5B%40%FA%81%B3%E9%CB%23%94%C16Azo%05%D4%E1%C1%95a%3B%8A'%A0%E8%CC%17%22%85%1D%BA%00%A2%FA%DC%0A%94%D1%D1%8D%8B%3A%84%17B%C7%60%1A%25Z%FC%8D%00%00%00%00IEND%AEB%60%82"),
|
||||
url("data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%007%08%06%00%00%00%C4%DD%80C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%3AIDAT8%11c%FC%FF%FF%7F%18%03%1A%60%01%F2%3F%A0%891%80%04%FF%11-%F8%17%9BJ%E2%05%B1ZD%81v%26t%E7%80%F8%A3%82h%A12%1A%20%A3%01%02%0F%01%BA%25%06%00%19%C0%0D%AEF%D5%3ES%00%00%00%00IEND%AEB%60%82");
|
||||
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),
|
||||
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACJJREFUeNpi+P//fxgTAwPDBxDxD078RSX+YeEyDFMCIMAAI3INmXiwf2YAAAAASUVORK5CYII=");
|
||||
background-repeat: no-repeat, repeat-x;
|
||||
background-position: center center, top left;
|
||||
color: transparent;
|
||||
|
||||
border: 1px solid black;
|
||||
-moz-border-radius: 2px;
|
||||
-webkit-border-radius: 2px;
|
||||
border-radius: 2px;
|
||||
|
||||
cursor: pointer;
|
||||
@@ -231,31 +279,33 @@
|
||||
|
||||
.ace_fold:hover{
|
||||
background-image:
|
||||
url("data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%11%00%00%00%09%08%06%00%00%00%D4%E8%C7%0C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%00%B5IDAT(%15%A5%91%3D%0E%02!%10%85ac%E1%05%D6%CE%D6%C6%CE%D2%E8%ED%CD%DE%C0%C6%D6N.%E0V%F8%3D%9Ca%891XH%C2%BE%D9y%3F%90!%E6%9C%C3%BFk%E5%011%C6-%F5%C8N%04%DF%BD%FF%89%DFt%83DN%60%3E%F3%AB%A0%DE%1A%5Dg%BE%10Q%97%1B%40%9C%A8o%10%8F%5E%828%B4%1B%60%87%F6%02%26%85%1Ch%1E%C1%2B%5Bk%FF%86%EE%B7j%09%9A%DA%9B%ACe%A3%F9%EC%DA!9%B4%D5%A6%81%86%86%98%CC%3C%5B%40%FA%81%B3%E9%CB%23%94%C16Azo%05%D4%E1%C1%95a%3B%8A'%A0%E8%CC%17%22%85%1D%BA%00%A2%FA%DC%0A%94%D1%D1%8D%8B%3A%84%17B%C7%60%1A%25Z%FC%8D%00%00%00%00IEND%AEB%60%82"),
|
||||
url("data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%007%08%06%00%00%00%C4%DD%80C%00%00%03%1EiCCPICC%20Profile%00%00x%01%85T%DFk%D3P%14%FE%DAe%9D%B0%E1%8B%3Ag%11%09%3Eh%91ndStC%9C%B6kW%BA%CDZ%EA6%B7!H%9B%A6m%5C%9A%C6%24%ED~%B0%07%D9%8Bo%3A%C5w%F1%07%3E%F9%07%0C%D9%83o%7B%92%0D%C6%14a%F8%AC%88%22L%F6%22%B3%9E%9B4M'S%03%B9%F7%BB%DF%F9%EE9'%E7%E4%5E%A0%F9qZ%D3%14%2F%0F%14USO%C5%C2%FC%C4%E4%14%DF%F2%01%5E%1CC%2B%FChM%8B%86%16J%26G%40%0F%D3%B2y%EF%B3%F3%0E%1E%C6lt%EEo%DF%AB%FEc%D5%9A%95%0C%11%F0%1C%20%BE%945%C4%22%E1Y%A0i%5C%D4t%13%E0%D6%89%EF%9D15%C2%CDLsX%A7%04%09%1Fg8oc%81%E1%8C%8D%23%96f45%40%9A%09%C2%07%C5B%3AK%B8%408%98i%E0%F3%0D%D8%CE%81%14%E4'%26%A9%92.%8B%3C%ABER%2F%E5dE%B2%0C%F6%F0%1Fs%83%F2_%B0%A8%94%E9%9B%AD%E7%10%8Dm%9A%19N%D1%7C%8A%DE%1F9%7Dp%8C%E6%00%D5%C1%3F_%18%BDA%B8%9DpX6%E3%A35~B%CD%24%AE%11%26%BD%E7%EEti%98%EDe%9A%97Y)%12%25%1C%24%BCbT%AE3li%E6%0B%03%89%9A%E6%D3%ED%F4P%92%B0%9F4%BF43Y%F3%E3%EDP%95%04%EB1%C5%F5%F6KF%F4%BA%BD%D7%DB%91%93%07%E35%3E%A7)%D6%7F%40%FE%BD%F7%F5r%8A%E5y%92%F0%EB%B4%1E%8D%D5%F4%5B%92%3AV%DB%DB%E4%CD%A6%23%C3%C4wQ%3F%03HB%82%8E%1Cd(%E0%91B%0Ca%9Ac%C4%AA%F8L%16%19%22J%A4%D2itTy%B28%D6%3B(%93%96%ED%1CGx%C9_%0E%B8%5E%16%F5%5B%B2%B8%F6%E0%FB%9E%DD%25%D7%8E%BC%15%85%C5%B7%A3%D8Q%ED%B5%81%E9%BA%B2%13%9A%1B%7Fua%A5%A3n%E17%B9%E5%9B%1Bm%AB%0B%08Q%FE%8A%E5%B1H%5Ee%CAO%82Q%D7u6%E6%90S%97%FCu%0B%CF2%94%EE%25v%12X%0C%BA%AC%F0%5E%F8*l%0AO%85%17%C2%97%BF%D4%C8%CE%DE%AD%11%CB%80q%2C%3E%AB%9ES%CD%C6%EC%25%D2L%D2%EBd%B8%BF%8A%F5B%C6%18%F9%901CZ%9D%BE%24M%9C%8A9%F2%DAP%0B'%06w%82%EB%E6%E2%5C%2F%D7%07%9E%BB%CC%5D%E1%FA%B9%08%AD.r%23%8E%C2%17%F5E%7C!%F0%BE3%BE%3E_%B7o%88a%A7%DB%BE%D3d%EB%A31Z%EB%BB%D3%91%BA%A2%B1z%94%8F%DB'%F6%3D%8E%AA%13%19%B2%B1%BE%B1~V%08%2B%B4%A2cjJ%B3tO%00%03%25mN%97%F3%05%93%EF%11%84%0B%7C%88%AE-%89%8F%ABbW%90O%2B%0Ao%99%0C%5E%97%0CI%AFH%D9.%B0%3B%8F%ED%03%B6S%D6%5D%E6i_s9%F3*p%E9%1B%FD%C3%EB.7U%06%5E%19%C0%D1s.%17%A03u%E4%09%B0%7C%5E%2C%EB%15%DB%1F%3C%9E%B7%80%91%3B%DBc%AD%3Dma%BA%8B%3EV%AB%DBt.%5B%1E%01%BB%0F%AB%D5%9F%CF%AA%D5%DD%E7%E4%7F%0Bx%A3%FC%06%A9%23%0A%D6%C2%A1_2%00%00%00%09pHYs%00%00%0B%13%00%00%0B%13%01%00%9A%9C%18%00%00%003IDAT8%11c%FC%FF%FF%7F%3E%03%1A%60%01%F2%3F%A3%891%80%04%FFQ%26%F8w%C0%B43%A1%DB%0C%E2%8F%0A%A2%85%CAh%80%8C%06%08%3C%04%E8%96%18%00%A3S%0D%CD%CF%D8%C1%9D%00%00%00%00IEND%AEB%60%82");
|
||||
background-repeat: no-repeat, repeat-x;
|
||||
background-position: center center, top left;
|
||||
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABEAAAAJCAYAAADU6McMAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAJpJREFUeNpi/P//PwOlgAXGYGRklAVSokD8GmjwY1wasKljQpYACtpCFeADcHVQfQyMQAwzwAZI3wJKvCLkfKBaMSClBlR7BOQikCFGQEErIH0VqkabiGCAqwUadAzZJRxQr/0gwiXIal8zQQPnNVTgJ1TdawL0T5gBIP1MUJNhBv2HKoQHHjqNrA4WO4zY0glyNKLT2KIfIMAAQsdgGiXvgnYAAAAASUVORK5CYII="),
|
||||
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAA3CAYAAADNNiA5AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAACBJREFUeNpi+P//fz4TAwPDZxDxD5X4i5fLMEwJgAADAEPVDbjNw87ZAAAAAElFTkSuQmCC");
|
||||
}
|
||||
|
||||
.ace_editor.ace_dragging .ace_content {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.ace_gutter-tooltip {
|
||||
background-color: #FFFFD5;
|
||||
.ace_tooltip {
|
||||
background-color: #FFF;
|
||||
background-image: -webkit-linear-gradient(top, transparent, rgba(0, 0, 0, 0.1));
|
||||
background-image: linear-gradient(to bottom, transparent, rgba(0, 0, 0, 0.1));
|
||||
border: 1px solid gray;
|
||||
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.4);
|
||||
border-radius: 1px;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
|
||||
color: black;
|
||||
display: inline-block;
|
||||
padding: 4px;
|
||||
position: absolute;
|
||||
z-index: 300;
|
||||
max-width: 100%;
|
||||
padding: 3px 4px;
|
||||
position: fixed;
|
||||
z-index: 999999;
|
||||
-moz-box-sizing: border-box;
|
||||
-webkit-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
cursor: default;
|
||||
white-space: pre-line;
|
||||
white-space: pre;
|
||||
word-wrap: break-word;
|
||||
line-height: normal;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
letter-spacing: normal;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.ace_folding-enabled > .ace_gutter-cell {
|
||||
@@ -268,40 +318,41 @@
|
||||
box-sizing: border-box;
|
||||
|
||||
margin: 0 -12px 0 1px;
|
||||
display: inline-block;
|
||||
display: none;
|
||||
width: 11px;
|
||||
vertical-align: top;
|
||||
|
||||
background-image: url("data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%00%05%08%06%00%00%00%8Do%26%E5%00%00%004IDATx%DAe%8A%B1%0D%000%0C%C2%F2%2CK%96%BC%D0%8F9%81%88H%E9%D0%0E%96%C0%10%92%3E%02%80%5E%82%E4%A9*-%EEsw%C8%CC%11%EE%96w%D8%DC%E9*Eh%0C%151(%00%00%00%00IEND%AEB%60%82");
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42mWKsQ0AMAzC8ixLlrzQjzmBiEjp0A6WwBCSPgKAXoLkqSot7nN3yMwR7pZ32NzpKkVoDBUxKAAAAABJRU5ErkJggg==");
|
||||
background-repeat: no-repeat;
|
||||
background-position: center;
|
||||
|
||||
border-radius: 3px;
|
||||
|
||||
border: 1px solid transparent;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ace_folding-enabled .ace_fold-widget {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.ace_fold-widget.ace_end {
|
||||
background-image: url("data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%05%00%00%00%05%08%06%00%00%00%8Do%26%E5%00%00%004IDATx%DAm%C7%C1%09%000%08C%D1%8C%ECE%C8E(%8E%EC%02)%1EZJ%F1%C1'%04%07I%E1%E5%EE%CAL%F5%A2%99%99%22%E2%D6%1FU%B5%FE0%D9x%A7%26Wz5%0E%D5%00%00%00%00IEND%AEB%60%82");
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAANElEQVR42m3HwQkAMAhD0YzsRchFKI7sAikeWkrxwScEB0nh5e7KTPWimZki4tYfVbX+MNl4pyZXejUO1QAAAABJRU5ErkJggg==");
|
||||
}
|
||||
|
||||
.ace_fold-widget.ace_closed {
|
||||
background-image: url("data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%03%00%00%00%06%08%06%00%00%00%06%E5%24%0C%00%00%009IDATx%DA5%CA%C1%09%000%08%03%C0%AC*(%3E%04%C1%0D%BA%B1%23%A4Uh%E0%20%81%C0%CC%F8%82%81%AA%A2%AArGfr%88%08%11%11%1C%DD%7D%E0%EE%5B%F6%F6%CB%B8%05Q%2F%E9tai%D9%00%00%00%00IEND%AEB%60%82");
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAGCAYAAAAG5SQMAAAAOUlEQVR42jXKwQkAMAgDwKwqKD4EwQ26sSOkVWjgIIHAzPiCgaqiqnJHZnKICBERHN194O5b9vbLuAVRL+l0YWnZAAAAAElFTkSuQmCCXA==");
|
||||
}
|
||||
|
||||
.ace_fold-widget:hover {
|
||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||
background-color: rgba(255, 255, 255, 0.2);
|
||||
-moz-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);
|
||||
-webkit-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);
|
||||
box-shadow: 0 1px 1px rgba(255, 255, 255, 0.7);
|
||||
}
|
||||
|
||||
.ace_fold-widget:active {
|
||||
border: 1px solid rgba(0, 0, 0, 0.4);
|
||||
background-color: rgba(0, 0, 0, 0.05);
|
||||
-moz-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);
|
||||
-webkit-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);
|
||||
box-shadow: 0 1px 1px rgba(255, 255, 255, 0.8);
|
||||
}
|
||||
/**
|
||||
@@ -321,32 +372,22 @@
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
.ace_dark .ace_fold-widget:active {
|
||||
-moz-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);
|
||||
-webkit-box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);
|
||||
box-shadow: 0 1px 1px rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
.ace_fold-widget.ace_invalid {
|
||||
background-color: #FFB4B4;
|
||||
border-color: #DE5555;
|
||||
}
|
||||
|
||||
.ace_fade-fold-widgets .ace_fold-widget {
|
||||
-moz-transition: opacity 0.4s ease 0.05s;
|
||||
-webkit-transition: opacity 0.4s ease 0.05s;
|
||||
-o-transition: opacity 0.4s ease 0.05s;
|
||||
-ms-transition: opacity 0.4s ease 0.05s;
|
||||
transition: opacity 0.4s ease 0.05s;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.ace_fade-fold-widgets:hover .ace_fold-widget {
|
||||
-moz-transition: opacity 0.05s ease 0.05s;
|
||||
-webkit-transition: opacity 0.05s ease 0.05s;
|
||||
-o-transition: opacity 0.05s ease 0.05s;
|
||||
-ms-transition: opacity 0.05s ease 0.05s;
|
||||
transition: opacity 0.05s ease 0.05s;
|
||||
opacity:1;
|
||||
}
|
||||
@@ -366,3 +407,16 @@
|
||||
.ace_italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
||||
.ace_error-marker {
|
||||
background-color: rgba(255, 0, 0,0.2);
|
||||
position: absolute;
|
||||
z-index: 9;
|
||||
}
|
||||
|
||||
.ace_highlight-marker {
|
||||
background-color: rgba(255, 255, 0,0.2);
|
||||
position: absolute;
|
||||
z-index: 8;
|
||||
}
|
||||
|
||||
@@ -56,10 +56,10 @@ var Document = function(text) {
|
||||
|
||||
// There has to be one line at least in the document. If you pass an empty
|
||||
// string to the insert function, nothing will happen. Workaround.
|
||||
if (text.length == 0) {
|
||||
if (text.length === 0) {
|
||||
this.$lines = [""];
|
||||
} else if (Array.isArray(text)) {
|
||||
this.insertLines(0, text);
|
||||
this._insertLines(0, text);
|
||||
} else {
|
||||
this.insert({row: 0, column:0}, text);
|
||||
}
|
||||
@@ -81,7 +81,7 @@ var Document = function(text) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns all the lines in the document as a single string, split by the new line character.
|
||||
* Returns all the lines in the document as a single string, joined by the new line character.
|
||||
**/
|
||||
this.getValue = function() {
|
||||
return this.getAllLines().join(this.getNewLineCharacter());
|
||||
@@ -92,7 +92,6 @@ var Document = function(text) {
|
||||
* @param {Number} row The row number to use
|
||||
* @param {Number} column The column number to use
|
||||
*
|
||||
*
|
||||
**/
|
||||
this.createAnchor = function(row, column) {
|
||||
return new Anchor(this, row, column);
|
||||
@@ -105,28 +104,23 @@ var Document = function(text) {
|
||||
* @param {String} text The text to work with
|
||||
* @returns {String} A String array, with each index containing a piece of the original `text` string.
|
||||
*
|
||||
*
|
||||
**/
|
||||
|
||||
// check for IE split bug
|
||||
if ("aaa".split(/a/).length == 0)
|
||||
if ("aaa".split(/a/).length === 0)
|
||||
this.$split = function(text) {
|
||||
return text.replace(/\r\n|\r/g, "\n").split("\n");
|
||||
}
|
||||
};
|
||||
else
|
||||
this.$split = function(text) {
|
||||
return text.split(/\r\n|\r|\n/);
|
||||
};
|
||||
|
||||
|
||||
|
||||
this.$detectNewLine = function(text) {
|
||||
var match = text.match(/^.*?(\r\n|\r|\n)/m);
|
||||
if (match) {
|
||||
this.$autoNewLine = match[1];
|
||||
} else {
|
||||
this.$autoNewLine = "\n";
|
||||
}
|
||||
this.$autoNewLine = match ? match[1] : "\n";
|
||||
this._signal("changeNewLineMode");
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -134,30 +128,24 @@ var Document = function(text) {
|
||||
* @returns {String} If `newLineMode == windows`, `\r\n` is returned.
|
||||
* If `newLineMode == unix`, `\n` is returned.
|
||||
* If `newLineMode == auto`, the value of `autoNewLine` is returned.
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
**/
|
||||
this.getNewLineCharacter = function() {
|
||||
switch (this.$newLineMode) {
|
||||
switch (this.$newLineMode) {
|
||||
case "windows":
|
||||
return "\r\n";
|
||||
|
||||
return "\r\n";
|
||||
case "unix":
|
||||
return "\n";
|
||||
|
||||
case "auto":
|
||||
return this.$autoNewLine;
|
||||
}
|
||||
return "\n";
|
||||
default:
|
||||
return this.$autoNewLine || "\n";
|
||||
}
|
||||
};
|
||||
|
||||
this.$autoNewLine = "\n";
|
||||
this.$autoNewLine = "";
|
||||
this.$newLineMode = "auto";
|
||||
/**
|
||||
* [Sets the new line mode.]{: #Document.setNewLineMode.desc}
|
||||
* @param {String} newLineMode [The newline mode to use; can be either `windows`, `unix`, or `auto`]{: #Document.setNewLineMode.param}
|
||||
*
|
||||
*
|
||||
**/
|
||||
this.setNewLineMode = function(newLineMode) {
|
||||
@@ -165,11 +153,12 @@ var Document = function(text) {
|
||||
return;
|
||||
|
||||
this.$newLineMode = newLineMode;
|
||||
this._signal("changeNewLineMode");
|
||||
};
|
||||
|
||||
/**
|
||||
* [Returns the type of newlines being used; either `windows`, `unix`, or `auto`]{: #Document.getNewLineMode}
|
||||
* @returns String
|
||||
* @returns {String}
|
||||
**/
|
||||
this.getNewLineMode = function() {
|
||||
return this.$newLineMode;
|
||||
@@ -179,8 +168,6 @@ var Document = function(text) {
|
||||
* Returns `true` if `text` is a newline character (either `\r\n`, `\r`, or `\n`).
|
||||
* @param {String} text The text to check
|
||||
*
|
||||
*
|
||||
*
|
||||
**/
|
||||
this.isNewLine = function(text) {
|
||||
return (text == "\r\n" || text == "\r" || text == "\n");
|
||||
@@ -190,8 +177,6 @@ var Document = function(text) {
|
||||
* Returns a verbatim copy of the given line as it is in the document
|
||||
* @param {Number} row The row index to retrieve
|
||||
*
|
||||
*
|
||||
*
|
||||
**/
|
||||
this.getLine = function(row) {
|
||||
return this.$lines[row] || "";
|
||||
@@ -202,15 +187,13 @@ var Document = function(text) {
|
||||
* @param {Number} firstRow The first row index to retrieve
|
||||
* @param {Number} lastRow The final row index to retrieve
|
||||
*
|
||||
*
|
||||
*
|
||||
**/
|
||||
this.getLines = function(firstRow, lastRow) {
|
||||
return this.$lines.slice(firstRow, lastRow + 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns all lines in the document as string array. Warning: The caller should not modify this array!
|
||||
* Returns all lines in the document as string array.
|
||||
**/
|
||||
this.getAllLines = function() {
|
||||
return this.getLines(0, this.getLength());
|
||||
@@ -227,19 +210,19 @@ var Document = function(text) {
|
||||
* [Given a range within the document, this function returns all the text within that range as a single string.]{: #Document.getTextRange.desc}
|
||||
* @param {Range} range The range to work with
|
||||
*
|
||||
*
|
||||
* @returns {String}
|
||||
**/
|
||||
this.getTextRange = function(range) {
|
||||
if (range.start.row == range.end.row) {
|
||||
return this.$lines[range.start.row].substring(range.start.column,
|
||||
range.end.column);
|
||||
}
|
||||
else {
|
||||
var lines = this.getLines(range.start.row+1, range.end.row-1);
|
||||
lines.unshift((this.$lines[range.start.row] || "").substring(range.start.column));
|
||||
lines.push((this.$lines[range.end.row] || "").substring(0, range.end.column));
|
||||
return lines.join(this.getNewLineCharacter());
|
||||
return this.getLine(range.start.row)
|
||||
.substring(range.start.column, range.end.column);
|
||||
}
|
||||
var lines = this.getLines(range.start.row, range.end.row);
|
||||
lines[0] = (lines[0] || "").substring(range.start.column);
|
||||
var l = lines.length - 1;
|
||||
if (range.end.row - range.start.row == l)
|
||||
lines[l] = lines[l].substring(0, range.end.column);
|
||||
return lines.join(this.getNewLineCharacter());
|
||||
};
|
||||
|
||||
this.$clipPosition = function(position) {
|
||||
@@ -247,13 +230,14 @@ var Document = function(text) {
|
||||
if (position.row >= length) {
|
||||
position.row = Math.max(0, length - 1);
|
||||
position.column = this.getLine(length-1).length;
|
||||
}
|
||||
} else if (position.row < 0)
|
||||
position.row = 0;
|
||||
return position;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inserts a block of `text` and the indicated `position`.
|
||||
* @param {Object} position The position to start inserting at
|
||||
* Inserts a block of `text` at the indicated `position`.
|
||||
* @param {Object} position The position to start inserting at; it's an object that looks like `{ row: row, column: column}`
|
||||
* @param {String} text A chunk of text to insert
|
||||
* @returns {Object} The position ({row, column}) of the last line of `text`. If the length of `text` is 0, this function simply returns `position`.
|
||||
*
|
||||
@@ -275,7 +259,7 @@ var Document = function(text) {
|
||||
position = this.insertInLine(position, firstLine);
|
||||
if (lastLine !== null) {
|
||||
position = this.insertNewLine(position); // terminate first line
|
||||
position = this.insertLines(position.row, lines);
|
||||
position = this._insertLines(position.row, lines);
|
||||
position = this.insertInLine(position, lastLine || "");
|
||||
}
|
||||
return position;
|
||||
@@ -317,19 +301,22 @@ var Document = function(text) {
|
||||
* {row: row, column: 0}
|
||||
* ```
|
||||
*
|
||||
*
|
||||
*
|
||||
*
|
||||
**/
|
||||
this.insertLines = function(row, lines) {
|
||||
if (row >= this.getLength())
|
||||
return this.insert({row: row, column: 0}, "\n" + lines.join("\n"));
|
||||
return this._insertLines(Math.max(row, 0), lines);
|
||||
};
|
||||
this._insertLines = function(row, lines) {
|
||||
if (lines.length == 0)
|
||||
return {row: row, column: 0};
|
||||
|
||||
// apply doesn't work for big arrays (smallest threshold is on safari 0xFFFF)
|
||||
// to circumvent that we have to break huge inserts into smaller chunks here
|
||||
if (lines.length > 0xFFFF) {
|
||||
var end = this.insertLines(row, lines.slice(0xFFFF));
|
||||
lines = lines.slice(0, 0xFFFF);
|
||||
while (lines.length > 0xF000) {
|
||||
var end = this._insertLines(row, lines.slice(0, 0xF000));
|
||||
lines = lines.slice(0xF000);
|
||||
row = end.row;
|
||||
}
|
||||
|
||||
var args = [row, 0];
|
||||
@@ -342,8 +329,8 @@ var Document = function(text) {
|
||||
range: range,
|
||||
lines: lines
|
||||
};
|
||||
this._emit("change", { data: delta });
|
||||
return end || range.end;
|
||||
this._signal("change", { data: delta });
|
||||
return range.end;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -372,14 +359,14 @@ var Document = function(text) {
|
||||
range: Range.fromPoints(position, end),
|
||||
text: this.getNewLineCharacter()
|
||||
};
|
||||
this._emit("change", { data: delta });
|
||||
this._signal("change", { data: delta });
|
||||
|
||||
return end;
|
||||
};
|
||||
|
||||
/**
|
||||
* Inserts `text` into the `position` at the current row. This method also triggers the `'change'` event.
|
||||
* @param {Object} position The position to insert at
|
||||
* @param {Object} position The position to insert at; it's an object that looks like `{ row: row, column: column}`
|
||||
* @param {String} text A chunk of text
|
||||
* @returns {Object} Returns an object containing the final row and column, like this:
|
||||
* ```
|
||||
@@ -406,7 +393,7 @@ var Document = function(text) {
|
||||
range: Range.fromPoints(position, end),
|
||||
text: text
|
||||
};
|
||||
this._emit("change", { data: delta });
|
||||
this._signal("change", { data: delta });
|
||||
|
||||
return end;
|
||||
};
|
||||
@@ -416,9 +403,10 @@ var Document = function(text) {
|
||||
* @param {Range} range A specified Range to remove
|
||||
* @returns {Object} Returns the new `start` property of the range, which contains `startRow` and `startColumn`. If `range` is empty, this function returns the unmodified value of `range.start`.
|
||||
*
|
||||
*
|
||||
**/
|
||||
this.remove = function(range) {
|
||||
if (!(range instanceof Range))
|
||||
range = Range.fromPoints(range.start, range.end);
|
||||
// clip to document
|
||||
range.start = this.$clipPosition(range.start);
|
||||
range.end = this.$clipPosition(range.end);
|
||||
@@ -437,7 +425,7 @@ var Document = function(text) {
|
||||
this.removeInLine(lastRow, 0, range.end.column);
|
||||
|
||||
if (lastFullRow >= firstFullRow)
|
||||
this.removeLines(firstFullRow, lastFullRow);
|
||||
this._removeLines(firstFullRow, lastFullRow);
|
||||
|
||||
if (firstFullRow != firstRow) {
|
||||
this.removeInLine(firstRow, range.start.column, this.getLine(firstRow).length);
|
||||
@@ -457,7 +445,6 @@ var Document = function(text) {
|
||||
* @param {Number} endColumn The column to stop removing at
|
||||
* @returns {Object} Returns an object containing `startRow` and `startColumn`, indicating the new row and column values.<br/>If `startColumn` is equal to `endColumn`, this function returns nothing.
|
||||
*
|
||||
*
|
||||
**/
|
||||
this.removeInLine = function(row, startColumn, endColumn) {
|
||||
if (startColumn == endColumn)
|
||||
@@ -474,7 +461,7 @@ var Document = function(text) {
|
||||
range: range,
|
||||
text: removed
|
||||
};
|
||||
this._emit("change", { data: delta });
|
||||
this._signal("change", { data: delta });
|
||||
return range.start;
|
||||
};
|
||||
|
||||
@@ -483,10 +470,15 @@ var Document = function(text) {
|
||||
* @param {Number} firstRow The first row to be removed
|
||||
* @param {Number} lastRow The last row to be removed
|
||||
* @returns {[String]} Returns all the removed lines.
|
||||
*
|
||||
*
|
||||
**/
|
||||
this.removeLines = function(firstRow, lastRow) {
|
||||
if (firstRow < 0 || lastRow >= this.getLength())
|
||||
return this.remove(new Range(firstRow, 0, lastRow + 1, 0));
|
||||
return this._removeLines(firstRow, lastRow);
|
||||
};
|
||||
|
||||
this._removeLines = function(firstRow, lastRow) {
|
||||
var range = new Range(firstRow, 0, lastRow + 1, 0);
|
||||
var removed = this.$lines.splice(firstRow, lastRow - firstRow + 1);
|
||||
|
||||
@@ -496,7 +488,7 @@ var Document = function(text) {
|
||||
nl: this.getNewLineCharacter(),
|
||||
lines: removed
|
||||
};
|
||||
this._emit("change", { data: delta });
|
||||
this._signal("change", { data: delta });
|
||||
return removed;
|
||||
};
|
||||
|
||||
@@ -519,7 +511,7 @@ var Document = function(text) {
|
||||
range: range,
|
||||
text: this.getNewLineCharacter()
|
||||
};
|
||||
this._emit("change", { data: delta });
|
||||
this._signal("change", { data: delta });
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -531,9 +523,10 @@ var Document = function(text) {
|
||||
* If the text and range are empty, this function returns an object containing the current `range.start` value.
|
||||
* If the text is the exact same as what currently exists, this function returns an object containing the current `range.end` value.
|
||||
*
|
||||
*
|
||||
**/
|
||||
this.replace = function(range, text) {
|
||||
if (!(range instanceof Range))
|
||||
range = Range.fromPoints(range.start, range.end);
|
||||
if (text.length == 0 && range.isEmpty())
|
||||
return range.start;
|
||||
|
||||
@@ -554,7 +547,7 @@ var Document = function(text) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Applies all the changes previously accumulated. These can be either `'includeText'`, `'insertLines'`, `'removeText'`, and `'removeLines'`.
|
||||
* Applies all the changes previously accumulated. These can be either `'insertText'`, `'insertLines'`, `'removeText'`, and `'removeLines'`.
|
||||
**/
|
||||
this.applyDeltas = function(deltas) {
|
||||
for (var i=0; i<deltas.length; i++) {
|
||||
@@ -566,14 +559,14 @@ var Document = function(text) {
|
||||
else if (delta.action == "insertText")
|
||||
this.insert(range.start, delta.text);
|
||||
else if (delta.action == "removeLines")
|
||||
this.removeLines(range.start.row, range.end.row - 1);
|
||||
this._removeLines(range.start.row, range.end.row - 1);
|
||||
else if (delta.action == "removeText")
|
||||
this.remove(range);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Reverts any changes previously applied. These can be either `'includeText'`, `'insertLines'`, `'removeText'`, and `'removeLines'`.
|
||||
* Reverts any changes previously applied. These can be either `'insertText'`, `'insertLines'`, `'removeText'`, and `'removeLines'`.
|
||||
**/
|
||||
this.revertDeltas = function(deltas) {
|
||||
for (var i=deltas.length-1; i>=0; i--) {
|
||||
@@ -582,16 +575,70 @@ var Document = function(text) {
|
||||
var range = Range.fromPoints(delta.range.start, delta.range.end);
|
||||
|
||||
if (delta.action == "insertLines")
|
||||
this.removeLines(range.start.row, range.end.row - 1);
|
||||
this._removeLines(range.start.row, range.end.row - 1);
|
||||
else if (delta.action == "insertText")
|
||||
this.remove(range);
|
||||
else if (delta.action == "removeLines")
|
||||
this.insertLines(range.start.row, delta.lines);
|
||||
this._insertLines(range.start.row, delta.lines);
|
||||
else if (delta.action == "removeText")
|
||||
this.insert(range.start, delta.text);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts an index position in a document to a `{row, column}` object.
|
||||
*
|
||||
* Index refers to the "absolute position" of a character in the document. For example:
|
||||
*
|
||||
* ```javascript
|
||||
* var x = 0; // 10 characters, plus one for newline
|
||||
* var y = -1;
|
||||
* ```
|
||||
*
|
||||
* Here, `y` is an index 15: 11 characters for the first row, and 5 characters until `y` in the second.
|
||||
*
|
||||
* @param {Number} index An index to convert
|
||||
* @param {Number} startRow=0 The row from which to start the conversion
|
||||
* @returns {Object} A `{row, column}` object of the `index` position
|
||||
*/
|
||||
this.indexToPosition = function(index, startRow) {
|
||||
var lines = this.$lines || this.getAllLines();
|
||||
var newlineLength = this.getNewLineCharacter().length;
|
||||
for (var i = startRow || 0, l = lines.length; i < l; i++) {
|
||||
index -= lines[i].length + newlineLength;
|
||||
if (index < 0)
|
||||
return {row: i, column: index + lines[i].length + newlineLength};
|
||||
}
|
||||
return {row: l-1, column: lines[l-1].length};
|
||||
};
|
||||
|
||||
/**
|
||||
* Converts the `{row, column}` position in a document to the character's index.
|
||||
*
|
||||
* Index refers to the "absolute position" of a character in the document. For example:
|
||||
*
|
||||
* ```javascript
|
||||
* var x = 0; // 10 characters, plus one for newline
|
||||
* var y = -1;
|
||||
* ```
|
||||
*
|
||||
* Here, `y` is an index 15: 11 characters for the first row, and 5 characters until `y` in the second.
|
||||
*
|
||||
* @param {Object} pos The `{row, column}` to convert
|
||||
* @param {Number} startRow=0 The row from which to start the conversion
|
||||
* @returns {Number} The index position in the document
|
||||
*/
|
||||
this.positionToIndex = function(pos, startRow) {
|
||||
var lines = this.$lines || this.getAllLines();
|
||||
var newlineLength = this.getNewLineCharacter().length;
|
||||
var index = 0;
|
||||
var row = Math.min(pos.row, lines.length);
|
||||
for (var i = startRow || 0; i < row; ++i)
|
||||
index += lines[i].length + newlineLength;
|
||||
|
||||
return index + pos.column;
|
||||
};
|
||||
|
||||
}).call(Document.prototype);
|
||||
|
||||
exports.Document = Document;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -37,10 +37,10 @@ var Range = require("../range").Range;
|
||||
|
||||
function BracketMatch() {
|
||||
|
||||
this.findMatchingBracket = function(position, char) {
|
||||
this.findMatchingBracket = function(position, chr) {
|
||||
if (position.column == 0) return null;
|
||||
|
||||
var charBeforeCursor = char || this.getLine(position.row).charAt(position.column-1);
|
||||
var charBeforeCursor = chr || this.getLine(position.row).charAt(position.column-1);
|
||||
if (charBeforeCursor == "") return null;
|
||||
|
||||
var match = charBeforeCursor.match(/([\(\[\{])|([\)\]\}])/);
|
||||
@@ -117,6 +117,7 @@ function BracketMatch() {
|
||||
typeRe = new RegExp(
|
||||
"(\\.?" +
|
||||
token.type.replace(".", "\\.").replace("rparen", ".paren")
|
||||
.replace(/\b(?:end|start|begin)\b/, "")
|
||||
+ ")+"
|
||||
);
|
||||
}
|
||||
@@ -173,6 +174,7 @@ function BracketMatch() {
|
||||
typeRe = new RegExp(
|
||||
"(\\.?" +
|
||||
token.type.replace(".", "\\.").replace("lparen", ".paren")
|
||||
.replace(/\b(?:end|start|begin)\b/, "")
|
||||
+ ")+"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,9 @@
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var Range = require("../range").Range;
|
||||
var RangeList = require("../range_list").RangeList;
|
||||
var oop = require("../lib/oop")
|
||||
/*
|
||||
* Simple fold-data struct.
|
||||
**/
|
||||
@@ -42,9 +45,11 @@ var Fold = exports.Fold = function(range, placeholder) {
|
||||
this.end = range.end;
|
||||
|
||||
this.sameRow = range.start.row == range.end.row;
|
||||
this.subFolds = [];
|
||||
this.subFolds = this.ranges = [];
|
||||
};
|
||||
|
||||
oop.inherits(Fold, RangeList);
|
||||
|
||||
(function() {
|
||||
|
||||
this.toString = function() {
|
||||
@@ -64,17 +69,21 @@ var Fold = exports.Fold = function(range, placeholder) {
|
||||
this.subFolds.forEach(function(subFold) {
|
||||
fold.subFolds.push(subFold.clone());
|
||||
});
|
||||
fold.collapseChildren = this.collapseChildren;
|
||||
return fold;
|
||||
};
|
||||
|
||||
this.addSubFold = function(fold) {
|
||||
if (this.range.isEqual(fold))
|
||||
return this;
|
||||
return;
|
||||
|
||||
if (!this.range.containsRange(fold))
|
||||
throw "A fold can't intersect already existing fold" + fold.range + this.range;
|
||||
throw new Error("A fold can't intersect already existing fold" + fold.range + this.range);
|
||||
|
||||
var row = fold.range.start.row, column = fold.range.start.column;
|
||||
// transform fold to local coordinates
|
||||
consumeRange(fold, this.start);
|
||||
|
||||
var row = fold.start.row, column = fold.start.column;
|
||||
for (var i = 0, cmp = -1; i < this.subFolds.length; i++) {
|
||||
cmp = this.subFolds[i].range.compare(row, column);
|
||||
if (cmp != 1)
|
||||
@@ -95,14 +104,37 @@ var Fold = exports.Fold = function(range, placeholder) {
|
||||
var afterEnd = this.subFolds[j];
|
||||
|
||||
if (cmp == 0)
|
||||
throw "A fold can't intersect already existing fold" + fold.range + this.range;
|
||||
throw new Error("A fold can't intersect already existing fold" + fold.range + this.range);
|
||||
|
||||
var consumedFolds = this.subFolds.splice(i, j - i, fold);
|
||||
fold.setFoldLine(this.foldLine);
|
||||
|
||||
return fold;
|
||||
};
|
||||
|
||||
this.restoreRange = function(range) {
|
||||
return restoreRange(range, this.start);
|
||||
};
|
||||
|
||||
}).call(Fold.prototype);
|
||||
|
||||
function consumePoint(point, anchor) {
|
||||
point.row -= anchor.row;
|
||||
if (point.row == 0)
|
||||
point.column -= anchor.column;
|
||||
}
|
||||
function consumeRange(range, anchor) {
|
||||
consumePoint(range.start, anchor);
|
||||
consumePoint(range.end, anchor);
|
||||
}
|
||||
function restorePoint(point, anchor) {
|
||||
if (point.row == 0)
|
||||
point.column += anchor.column;
|
||||
point.row += anchor.row;
|
||||
}
|
||||
function restoreRange(range, anchor) {
|
||||
restorePoint(range.start, anchor);
|
||||
restorePoint(range.end, anchor);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
@@ -44,7 +44,7 @@ function FoldLine(foldData, folds) {
|
||||
folds = this.folds = [ folds ];
|
||||
}
|
||||
|
||||
var last = folds[folds.length - 1]
|
||||
var last = folds[folds.length - 1];
|
||||
this.range = new Range(folds[0].start.row, folds[0].start.column,
|
||||
last.end.row, last.end.column);
|
||||
this.start = this.range.start;
|
||||
@@ -66,12 +66,12 @@ function FoldLine(foldData, folds) {
|
||||
fold.start.row += shift;
|
||||
fold.end.row += shift;
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
this.addFold = function(fold) {
|
||||
if (fold.sameRow) {
|
||||
if (fold.start.row < this.startRow || fold.endRow > this.endRow) {
|
||||
throw "Can't add a fold to this FoldLine as it has no connection";
|
||||
throw new Error("Can't add a fold to this FoldLine as it has no connection");
|
||||
}
|
||||
this.folds.push(fold);
|
||||
this.folds.sort(function(a, b) {
|
||||
@@ -93,20 +93,20 @@ function FoldLine(foldData, folds) {
|
||||
this.start.row = fold.start.row;
|
||||
this.start.column = fold.start.column;
|
||||
} else {
|
||||
throw "Trying to add fold to FoldRow that doesn't have a matching row";
|
||||
throw new Error("Trying to add fold to FoldRow that doesn't have a matching row");
|
||||
}
|
||||
fold.foldLine = this;
|
||||
}
|
||||
};
|
||||
|
||||
this.containsRow = function(row) {
|
||||
return row >= this.start.row && row <= this.end.row;
|
||||
}
|
||||
};
|
||||
|
||||
this.walk = function(callback, endRow, endColumn) {
|
||||
var lastEnd = 0,
|
||||
folds = this.folds,
|
||||
fold,
|
||||
comp, stop, isNewRow = true;
|
||||
cmp, stop, isNewRow = true;
|
||||
|
||||
if (endRow == null) {
|
||||
endRow = this.end.row;
|
||||
@@ -116,9 +116,9 @@ function FoldLine(foldData, folds) {
|
||||
for (var i = 0; i < folds.length; i++) {
|
||||
fold = folds[i];
|
||||
|
||||
comp = fold.range.compareStart(endRow, endColumn);
|
||||
cmp = fold.range.compareStart(endRow, endColumn);
|
||||
// This fold is after the endRow/Column.
|
||||
if (comp == -1) {
|
||||
if (cmp == -1) {
|
||||
callback(null, endRow, endColumn, lastEnd, isNewRow);
|
||||
return;
|
||||
}
|
||||
@@ -127,8 +127,8 @@ function FoldLine(foldData, folds) {
|
||||
stop = !stop && callback(fold.placeholder, fold.start.row, fold.start.column, lastEnd);
|
||||
|
||||
// If the user requested to stop the walk or endRow/endColumn is
|
||||
// inside of this fold (comp == 0), then end here.
|
||||
if (stop || comp == 0) {
|
||||
// inside of this fold (cmp == 0), then end here.
|
||||
if (stop || cmp === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ function FoldLine(foldData, folds) {
|
||||
lastEnd = fold.end.column;
|
||||
}
|
||||
callback(null, endRow, endColumn, lastEnd, isNewRow);
|
||||
}
|
||||
};
|
||||
|
||||
this.getNextFoldTo = function(row, column) {
|
||||
var fold, cmp;
|
||||
@@ -150,15 +150,15 @@ function FoldLine(foldData, folds) {
|
||||
fold: fold,
|
||||
kind: "after"
|
||||
};
|
||||
} else if (cmp == 0) {
|
||||
} else if (cmp === 0) {
|
||||
return {
|
||||
fold: fold,
|
||||
kind: "inside"
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
this.addRemoveChars = function(row, column, len) {
|
||||
var ret = this.getNextFoldTo(row, column),
|
||||
@@ -175,7 +175,7 @@ function FoldLine(foldData, folds) {
|
||||
} else if (fold.start.row == row) {
|
||||
folds = this.folds;
|
||||
var i = folds.indexOf(fold);
|
||||
if (i == 0) {
|
||||
if (i === 0) {
|
||||
this.start.column += len;
|
||||
}
|
||||
for (i; i < folds.length; i++) {
|
||||
@@ -189,16 +189,18 @@ function FoldLine(foldData, folds) {
|
||||
this.end.column += len;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.split = function(row, column) {
|
||||
var fold = this.getNextFoldTo(row, column).fold,
|
||||
folds = this.folds;
|
||||
var foldData = this.foldData;
|
||||
|
||||
if (!fold) {
|
||||
var pos = this.getNextFoldTo(row, column);
|
||||
|
||||
if (!pos || pos.kind == "inside")
|
||||
return null;
|
||||
}
|
||||
|
||||
var fold = pos.fold;
|
||||
var folds = this.folds;
|
||||
var foldData = this.foldData;
|
||||
|
||||
var i = folds.indexOf(fold);
|
||||
var foldBefore = folds[i - 1];
|
||||
this.end.row = foldBefore.end.row;
|
||||
@@ -211,7 +213,7 @@ function FoldLine(foldData, folds) {
|
||||
var newFoldLine = new FoldLine(foldData, folds);
|
||||
foldData.splice(foldData.indexOf(this) + 1, 0, newFoldLine);
|
||||
return newFoldLine;
|
||||
}
|
||||
};
|
||||
|
||||
this.merge = function(foldLineNext) {
|
||||
var folds = foldLineNext.folds;
|
||||
@@ -222,7 +224,7 @@ function FoldLine(foldData, folds) {
|
||||
// it's merged now with foldLineNext.
|
||||
var foldData = this.foldData;
|
||||
foldData.splice(foldData.indexOf(foldLineNext), 1);
|
||||
}
|
||||
};
|
||||
|
||||
this.toString = function() {
|
||||
var ret = [this.range.toString() + ": [" ];
|
||||
@@ -230,13 +232,12 @@ function FoldLine(foldData, folds) {
|
||||
this.folds.forEach(function(fold) {
|
||||
ret.push(" " + fold.toString());
|
||||
});
|
||||
ret.push("]")
|
||||
ret.push("]");
|
||||
return ret.join("\n");
|
||||
}
|
||||
};
|
||||
|
||||
this.idxToPosition = function(idx) {
|
||||
var lastFoldEndColumn = 0;
|
||||
var fold;
|
||||
|
||||
for (var i = 0; i < this.folds.length; i++) {
|
||||
var fold = this.folds[i];
|
||||
@@ -261,7 +262,7 @@ function FoldLine(foldData, folds) {
|
||||
row: this.end.row,
|
||||
column: this.end.column + idx
|
||||
};
|
||||
}
|
||||
};
|
||||
}).call(FoldLine.prototype);
|
||||
|
||||
exports.FoldLine = FoldLine;
|
||||
|
||||
@@ -66,7 +66,6 @@ function Folding() {
|
||||
*
|
||||
*/
|
||||
this.getFoldsInRange = function(range) {
|
||||
range = range.clone();
|
||||
var start = range.start;
|
||||
var end = range.end;
|
||||
var foldLines = this.$foldData;
|
||||
@@ -104,8 +103,23 @@ function Folding() {
|
||||
foundFolds.push(fold);
|
||||
}
|
||||
}
|
||||
start.column -= 1;
|
||||
end.column += 1;
|
||||
|
||||
return foundFolds;
|
||||
};
|
||||
|
||||
this.getFoldsInRangeList = function(ranges) {
|
||||
if (Array.isArray(ranges)) {
|
||||
var folds = [];
|
||||
ranges.forEach(function(range) {
|
||||
folds = folds.concat(this.getFoldsInRange(range));
|
||||
}, this);
|
||||
} else {
|
||||
var folds = this.getFoldsInRange(ranges);
|
||||
}
|
||||
return folds;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns all folds in the document
|
||||
@@ -114,18 +128,9 @@ function Folding() {
|
||||
var folds = [];
|
||||
var foldLines = this.$foldData;
|
||||
|
||||
function addFold(fold) {
|
||||
folds.push(fold);
|
||||
if (!fold.subFolds)
|
||||
return;
|
||||
|
||||
for (var i = 0; i < fold.subFolds.length; i++)
|
||||
addFold(fold.subFolds[i]);
|
||||
}
|
||||
|
||||
for (var i = 0; i < foldLines.length; i++)
|
||||
for (var j = 0; j < foldLines[i].folds.length; j++)
|
||||
addFold(foldLines[i].folds[j]);
|
||||
folds.push(foldLines[i].folds[j]);
|
||||
|
||||
return folds;
|
||||
};
|
||||
@@ -249,7 +254,7 @@ function Folding() {
|
||||
return foldLine;
|
||||
};
|
||||
|
||||
/*
|
||||
/**
|
||||
* Adds a new fold.
|
||||
*
|
||||
* @returns
|
||||
@@ -263,9 +268,10 @@ function Folding() {
|
||||
|
||||
if (placeholder instanceof Fold)
|
||||
fold = placeholder;
|
||||
else
|
||||
else {
|
||||
fold = new Fold(range, placeholder);
|
||||
|
||||
fold.collapseChildren = range.collapseChildren;
|
||||
}
|
||||
this.$clipRangeToDocument(fold.range);
|
||||
|
||||
var startRow = fold.start.row;
|
||||
@@ -274,28 +280,30 @@ function Folding() {
|
||||
var endColumn = fold.end.column;
|
||||
|
||||
// --- Some checking ---
|
||||
if (startRow == endRow && endColumn - startColumn < 2)
|
||||
throw "The range has to be at least 2 characters width";
|
||||
if (!(startRow < endRow ||
|
||||
startRow == endRow && startColumn <= endColumn - 2))
|
||||
throw new Error("The range has to be at least 2 characters width");
|
||||
|
||||
var startFold = this.getFoldAt(startRow, startColumn, 1);
|
||||
var endFold = this.getFoldAt(endRow, endColumn, -1);
|
||||
if (startFold && endFold == startFold)
|
||||
return startFold.addSubFold(fold);
|
||||
|
||||
if (
|
||||
(startFold && !startFold.range.isStart(startRow, startColumn))
|
||||
|| (endFold && !endFold.range.isEnd(endRow, endColumn))
|
||||
) {
|
||||
throw "A fold can't intersect already existing fold" + fold.range + startFold.range;
|
||||
}
|
||||
|
||||
if (startFold && !startFold.range.isStart(startRow, startColumn))
|
||||
this.removeFold(startFold);
|
||||
|
||||
if (endFold && !endFold.range.isEnd(endRow, endColumn))
|
||||
this.removeFold(endFold);
|
||||
|
||||
// Check if there are folds in the range we create the new fold for.
|
||||
var folds = this.getFoldsInRange(fold.range);
|
||||
if (folds.length > 0) {
|
||||
// Remove the folds from fold data.
|
||||
this.removeFolds(folds);
|
||||
// Add the removed folds as subfolds on the new fold.
|
||||
fold.subFolds = folds;
|
||||
folds.forEach(function(subFold) {
|
||||
fold.addSubFold(subFold);
|
||||
});
|
||||
}
|
||||
|
||||
for (var i = 0; i < foldData.length; i++) {
|
||||
@@ -304,8 +312,7 @@ function Folding() {
|
||||
foldLine.addFold(fold);
|
||||
added = true;
|
||||
break;
|
||||
}
|
||||
else if (startRow == foldLine.end.row) {
|
||||
} else if (startRow == foldLine.end.row) {
|
||||
foldLine.addFold(fold);
|
||||
added = true;
|
||||
if (!fold.sameRow) {
|
||||
@@ -318,8 +325,7 @@ function Folding() {
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
else if (endRow <= foldLine.start.row) {
|
||||
} else if (endRow <= foldLine.start.row) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -334,7 +340,7 @@ function Folding() {
|
||||
|
||||
// Notify that fold data has changed.
|
||||
this.$modified = true;
|
||||
this._emit("changeFold", { data: fold });
|
||||
this._emit("changeFold", { data: fold, action: "add" });
|
||||
|
||||
return fold;
|
||||
};
|
||||
@@ -386,14 +392,16 @@ function Folding() {
|
||||
newFoldLine.start.column = folds[0].start.column;
|
||||
}
|
||||
|
||||
if (this.$useWrapMode)
|
||||
this.$updateWrapData(startRow, endRow);
|
||||
else
|
||||
this.$updateRowLengthCache(startRow, endRow);
|
||||
|
||||
if (!this.$updating) {
|
||||
if (this.$useWrapMode)
|
||||
this.$updateWrapData(startRow, endRow);
|
||||
else
|
||||
this.$updateRowLengthCache(startRow, endRow);
|
||||
}
|
||||
|
||||
// Notify that fold data has changed.
|
||||
this.$modified = true;
|
||||
this._emit("changeFold", { data: fold });
|
||||
this._emit("changeFold", { data: fold, action: "remove" });
|
||||
};
|
||||
|
||||
this.removeFolds = function(folds) {
|
||||
@@ -413,9 +421,13 @@ function Folding() {
|
||||
|
||||
this.expandFold = function(fold) {
|
||||
this.removeFold(fold);
|
||||
fold.subFolds.forEach(function(fold) {
|
||||
this.addFold(fold);
|
||||
fold.subFolds.forEach(function(subFold) {
|
||||
fold.restoreRange(subFold);
|
||||
this.addFold(subFold);
|
||||
}, this);
|
||||
if (fold.collapseChildren > 0) {
|
||||
this.foldAll(fold.start.row+1, fold.end.row, fold.collapseChildren-1);
|
||||
}
|
||||
fold.subFolds = [];
|
||||
};
|
||||
|
||||
@@ -427,26 +439,30 @@ function Folding() {
|
||||
|
||||
this.unfold = function(location, expandInner) {
|
||||
var range, folds;
|
||||
if (location == null)
|
||||
if (location == null) {
|
||||
range = new Range(0, 0, this.getLength(), 0);
|
||||
else if (typeof location == "number")
|
||||
expandInner = true;
|
||||
} else if (typeof location == "number")
|
||||
range = new Range(location, 0, location, this.getLine(location).length);
|
||||
else if ("row" in location)
|
||||
range = Range.fromPoints(location, location);
|
||||
else
|
||||
range = location;
|
||||
|
||||
folds = this.getFoldsInRange(range);
|
||||
|
||||
folds = this.getFoldsInRangeList(range);
|
||||
if (expandInner) {
|
||||
this.removeFolds(folds);
|
||||
} else {
|
||||
// TODO: might need to remove and add folds in one go instead of using
|
||||
var subFolds = folds;
|
||||
// TODO: might be better to remove and add folds in one go instead of using
|
||||
// expandFolds several times.
|
||||
while (folds.length) {
|
||||
this.expandFolds(folds);
|
||||
folds = this.getFoldsInRange(range);
|
||||
while (subFolds.length) {
|
||||
this.expandFolds(subFolds);
|
||||
subFolds = this.getFoldsInRangeList(range);
|
||||
}
|
||||
}
|
||||
if (folds.length)
|
||||
return folds;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -462,36 +478,41 @@ function Folding() {
|
||||
return foldLine ? foldLine.end.row : docRow;
|
||||
};
|
||||
|
||||
this.getFoldDisplayLine = function(foldLine, endRow, endColumn, startRow, startColumn) {
|
||||
if (startRow == null) {
|
||||
startRow = foldLine.start.row;
|
||||
startColumn = 0;
|
||||
}
|
||||
this.getRowFoldStart = function(docRow, startFoldRow) {
|
||||
var foldLine = this.getFoldLine(docRow, startFoldRow);
|
||||
return foldLine ? foldLine.start.row : docRow;
|
||||
};
|
||||
|
||||
if (endRow == null) {
|
||||
this.getFoldDisplayLine = function(foldLine, endRow, endColumn, startRow, startColumn) {
|
||||
if (startRow == null)
|
||||
startRow = foldLine.start.row;
|
||||
if (startColumn == null)
|
||||
startColumn = 0;
|
||||
if (endRow == null)
|
||||
endRow = foldLine.end.row;
|
||||
if (endColumn == null)
|
||||
endColumn = this.getLine(endRow).length;
|
||||
}
|
||||
|
||||
|
||||
// Build the textline using the FoldLine walker.
|
||||
var doc = this.doc;
|
||||
var textLine = "";
|
||||
|
||||
foldLine.walk(function(placeholder, row, column, lastColumn) {
|
||||
if (row < startRow) {
|
||||
if (row < startRow)
|
||||
return;
|
||||
} else if (row == startRow) {
|
||||
if (column < startColumn) {
|
||||
if (row == startRow) {
|
||||
if (column < startColumn)
|
||||
return;
|
||||
}
|
||||
lastColumn = Math.max(startColumn, lastColumn);
|
||||
}
|
||||
|
||||
if (placeholder != null) {
|
||||
textLine += placeholder;
|
||||
} else {
|
||||
textLine += doc.getLine(row).substring(lastColumn, column);
|
||||
}
|
||||
}.bind(this), endRow, endColumn);
|
||||
}, endRow, endColumn);
|
||||
return textLine;
|
||||
};
|
||||
|
||||
@@ -533,26 +554,22 @@ function Folding() {
|
||||
if (fold) {
|
||||
this.expandFold(fold);
|
||||
return;
|
||||
}
|
||||
else if (bracketPos = this.findMatchingBracket(cursor)) {
|
||||
} else if (bracketPos = this.findMatchingBracket(cursor)) {
|
||||
if (range.comparePoint(bracketPos) == 1) {
|
||||
range.end = bracketPos;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
range.start = bracketPos;
|
||||
range.start.column++;
|
||||
range.end.column--;
|
||||
}
|
||||
}
|
||||
else if (bracketPos = this.findMatchingBracket({row: cursor.row, column: cursor.column + 1})) {
|
||||
} else if (bracketPos = this.findMatchingBracket({row: cursor.row, column: cursor.column + 1})) {
|
||||
if (range.comparePoint(bracketPos) == 1)
|
||||
range.end = bracketPos;
|
||||
else
|
||||
range.start = bracketPos;
|
||||
|
||||
range.start.column++;
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
range = this.getCommentFoldRange(cursor.row, cursor.column) || range;
|
||||
}
|
||||
} else {
|
||||
@@ -560,8 +577,7 @@ function Folding() {
|
||||
if (tryToUnfold && folds.length) {
|
||||
this.expandFolds(folds);
|
||||
return;
|
||||
}
|
||||
else if (folds.length == 1 ) {
|
||||
} else if (folds.length == 1 ) {
|
||||
fold = folds[0];
|
||||
}
|
||||
}
|
||||
@@ -617,10 +633,15 @@ function Folding() {
|
||||
}
|
||||
};
|
||||
|
||||
this.foldAll = function(startRow, endRow) {
|
||||
this.foldAll = function(startRow, endRow, depth) {
|
||||
if (depth == undefined)
|
||||
depth = 100000; // JSON.stringify doesn't hanle Infinity
|
||||
var foldWidgets = this.foldWidgets;
|
||||
if (!foldWidgets)
|
||||
return; // mode doesn't support folding
|
||||
endRow = endRow || this.getLength();
|
||||
for (var row = startRow || 0; row < endRow; row++) {
|
||||
startRow = startRow || 0;
|
||||
for (var row = startRow; row < endRow; row++) {
|
||||
if (foldWidgets[row] == null)
|
||||
foldWidgets[row] = this.getFoldWidget(row);
|
||||
if (foldWidgets[row] != "start")
|
||||
@@ -628,13 +649,23 @@ function Folding() {
|
||||
|
||||
var range = this.getFoldWidgetRange(row);
|
||||
// sometimes range can be incompatible with existing fold
|
||||
// wouldn't it be better for addFold to return null istead of throwing?
|
||||
if (range && range.end.row <= endRow) try {
|
||||
this.addFold("...", range);
|
||||
} catch(e) {}
|
||||
// TODO change addFold to return null istead of throwing
|
||||
if (range && range.isMultiLine()
|
||||
&& range.end.row <= endRow
|
||||
&& range.start.row >= startRow
|
||||
) {
|
||||
row = range.end.row;
|
||||
try {
|
||||
// addFold can change the range
|
||||
var fold = this.addFold("...", range);
|
||||
if (fold)
|
||||
fold.collapseChildren = depth;
|
||||
} catch(e) {}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// structured folding
|
||||
this.$foldStyles = {
|
||||
"manual": 1,
|
||||
"markbegin": 1,
|
||||
@@ -659,14 +690,14 @@ function Folding() {
|
||||
this.$setFolding(mode);
|
||||
};
|
||||
|
||||
// structured folding
|
||||
this.$setFolding = function(foldMode) {
|
||||
if (this.$foldMode == foldMode)
|
||||
return;
|
||||
|
||||
this.$foldMode = foldMode;
|
||||
|
||||
this.removeListener('change', this.$updateFoldWidgets);
|
||||
this.off('change', this.$updateFoldWidgets);
|
||||
this.off('tokenizerUpdate', this.$tokenizerUpdateFoldWidgets);
|
||||
this._emit("changeAnnotation");
|
||||
|
||||
if (!foldMode || this.$foldStyle == "manual") {
|
||||
@@ -679,54 +710,125 @@ function Folding() {
|
||||
this.getFoldWidgetRange = foldMode.getFoldWidgetRange.bind(foldMode, this, this.$foldStyle);
|
||||
|
||||
this.$updateFoldWidgets = this.updateFoldWidgets.bind(this);
|
||||
this.$tokenizerUpdateFoldWidgets = this.tokenizerUpdateFoldWidgets.bind(this);
|
||||
this.on('change', this.$updateFoldWidgets);
|
||||
|
||||
this.on('tokenizerUpdate', this.$tokenizerUpdateFoldWidgets);
|
||||
};
|
||||
|
||||
this.getParentFoldRangeData = function (row, ignoreCurrent) {
|
||||
var fw = this.foldWidgets;
|
||||
if (!fw || (ignoreCurrent && fw[row]))
|
||||
return {};
|
||||
|
||||
var i = row - 1, firstRange;
|
||||
while (i >= 0) {
|
||||
var c = fw[i];
|
||||
if (c == null)
|
||||
c = fw[i] = this.getFoldWidget(i);
|
||||
|
||||
if (c == "start") {
|
||||
var range = this.getFoldWidgetRange(i);
|
||||
if (!firstRange)
|
||||
firstRange = range;
|
||||
if (range && range.end.row >= row)
|
||||
break;
|
||||
}
|
||||
i--;
|
||||
}
|
||||
|
||||
return {
|
||||
range: i !== -1 && range,
|
||||
firstRange: firstRange
|
||||
};
|
||||
}
|
||||
|
||||
this.onFoldWidgetClick = function(row, e) {
|
||||
e = e.domEvent;
|
||||
var options = {
|
||||
children: e.shiftKey,
|
||||
all: e.ctrlKey || e.metaKey,
|
||||
siblings: e.altKey
|
||||
};
|
||||
|
||||
var range = this.$toggleFoldWidget(row, options);
|
||||
if (!range) {
|
||||
var el = (e.target || e.srcElement)
|
||||
if (el && /ace_fold-widget/.test(el.className))
|
||||
el.className += " ace_invalid";
|
||||
}
|
||||
};
|
||||
|
||||
this.$toggleFoldWidget = function(row, options) {
|
||||
if (!this.getFoldWidget)
|
||||
return;
|
||||
var type = this.getFoldWidget(row);
|
||||
var line = this.getLine(row);
|
||||
var onlySubfolds = e.shiftKey;
|
||||
var addSubfolds = onlySubfolds || e.ctrlKey || e.altKey || e.metaKey;
|
||||
var fold;
|
||||
|
||||
if (type == "end")
|
||||
fold = this.getFoldAt(row, 0, -1);
|
||||
else
|
||||
fold = this.getFoldAt(row, line.length, 1);
|
||||
var dir = type === "end" ? -1 : 1;
|
||||
var fold = this.getFoldAt(row, dir === -1 ? 0 : line.length, dir);
|
||||
|
||||
if (fold) {
|
||||
if (addSubfolds)
|
||||
if (options.children || options.all)
|
||||
this.removeFold(fold);
|
||||
else
|
||||
this.expandFold(fold);
|
||||
return;
|
||||
}
|
||||
|
||||
var range = this.getFoldWidgetRange(row);
|
||||
if (range) {
|
||||
// sometimes singleline folds can be missed by the code above
|
||||
if (!range.isMultiLine()) {
|
||||
fold = this.getFoldAt(range.start.row, range.start.column, 1);
|
||||
if (fold && range.isEqual(fold.range)) {
|
||||
this.removeFold(fold);
|
||||
return;
|
||||
}
|
||||
var range = this.getFoldWidgetRange(row, true);
|
||||
// sometimes singleline folds can be missed by the code above
|
||||
if (range && !range.isMultiLine()) {
|
||||
fold = this.getFoldAt(range.start.row, range.start.column, 1);
|
||||
if (fold && range.isEqual(fold.range)) {
|
||||
this.removeFold(fold);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!onlySubfolds)
|
||||
this.addFold("...", range);
|
||||
|
||||
if (addSubfolds)
|
||||
this.foldAll(range.start.row + 1, range.end.row);
|
||||
} else {
|
||||
if (addSubfolds)
|
||||
this.foldAll(row + 1, this.getLength());
|
||||
(e.target || e.srcElement).className += " ace_invalid"
|
||||
}
|
||||
|
||||
if (options.siblings) {
|
||||
var data = this.getParentFoldRangeData(row);
|
||||
if (data.range) {
|
||||
var startRow = data.range.start.row + 1;
|
||||
var endRow = data.range.end.row;
|
||||
}
|
||||
this.foldAll(startRow, endRow, options.all ? 10000 : 0);
|
||||
} else if (options.children) {
|
||||
endRow = range ? range.end.row : this.getLength();
|
||||
this.foldAll(row + 1, range.end.row, options.all ? 10000 : 0);
|
||||
} else if (range) {
|
||||
if (options.all)
|
||||
range.collapseChildren = 10000;
|
||||
this.addFold("...", range);
|
||||
}
|
||||
|
||||
return range;
|
||||
};
|
||||
|
||||
|
||||
|
||||
this.toggleFoldWidget = function(toggleParent) {
|
||||
var row = this.selection.getCursor().row;
|
||||
row = this.getRowFoldStart(row);
|
||||
var range = this.$toggleFoldWidget(row, {});
|
||||
|
||||
if (range)
|
||||
return;
|
||||
// handle toggleParent
|
||||
var data = this.getParentFoldRangeData(row, true);
|
||||
range = data.range || data.firstRange;
|
||||
|
||||
if (range) {
|
||||
row = range.start.row;
|
||||
var fold = this.getFoldAt(row, this.getLine(row).length, 1);
|
||||
|
||||
if (fold) {
|
||||
this.removeFold(fold);
|
||||
} else {
|
||||
this.addFold("...", range);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.updateFoldWidgets = function(e) {
|
||||
var delta = e.data;
|
||||
var range = delta.range;
|
||||
@@ -743,7 +845,13 @@ function Folding() {
|
||||
this.foldWidgets.splice.apply(this.foldWidgets, args);
|
||||
}
|
||||
};
|
||||
|
||||
this.tokenizerUpdateFoldWidgets = function(e) {
|
||||
var rows = e.data;
|
||||
if (rows.first != rows.last) {
|
||||
if (this.foldWidgets.length > rows.first)
|
||||
this.foldWidgets.splice(rows.first, this.foldWidgets.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exports.Folding = Folding;
|
||||
|
||||
@@ -62,6 +62,7 @@ function createFoldTestSession() {
|
||||
}
|
||||
|
||||
function assertArray(a, b) {
|
||||
assert.equal(a+"", b+"");
|
||||
assert.ok(a.length == b.length);
|
||||
for (var i = 0; i < a.length; i++) {
|
||||
assert.equal(a[i], b[i]);
|
||||
@@ -385,11 +386,12 @@ module.exports = {
|
||||
assert.ok(splits[i] == assertEqual[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EditSession.prototype.$wrapAsCode = true;
|
||||
// Basic splitting.
|
||||
computeAndAssert("foo bar foo bar", [ 12 ]);
|
||||
computeAndAssert("foo bar f bar", [ 12 ]);
|
||||
computeAndAssert("foo bar f r", [ 14 ]);
|
||||
computeAndAssert("foo bar f r", [ 12 ]); // 14 if we enable
|
||||
computeAndAssert("foo bar foo bar foo bara foo", [12, 25]);
|
||||
|
||||
// Don't split if there is only whitespaces/tabs at the end of the line.
|
||||
@@ -405,7 +407,7 @@ module.exports = {
|
||||
computeAndAssert("foo \t \tbar", [ 7 ]);
|
||||
|
||||
// Ignore spaces/tabs at beginning of split.
|
||||
computeAndAssert("foo \t \t \t \t bar", [ 14 ]);
|
||||
computeAndAssert("foo \t \t \t \t bar", [ 7 ]); // 14
|
||||
|
||||
// Test wrapping for asian characters.
|
||||
computeAndAssert("ぁぁ", [1], 2);
|
||||
@@ -417,6 +419,10 @@ module.exports = {
|
||||
computeAndAssert(" ab.c;ef++", [1, 3, 5, 7, 8], 2);
|
||||
computeAndAssert(" a.b", [1, 2, 3], 1);
|
||||
computeAndAssert("#>>", [1, 2], 1);
|
||||
|
||||
// Test wrapping for punctuation in
|
||||
EditSession.prototype.$wrapAsCode = false;
|
||||
computeAndAssert("ab cde, Juhu kinners", [3, 8, 13, 19], 6);
|
||||
},
|
||||
|
||||
"test get longest line" : function() {
|
||||
@@ -898,7 +904,27 @@ module.exports = {
|
||||
|
||||
return session;
|
||||
},
|
||||
|
||||
|
||||
"test delete fold with wrap enabled": function() {
|
||||
var session = new EditSession("");
|
||||
session.setValue([
|
||||
"This is some placeholder text that will be folded inline.",
|
||||
"This is some placeholder text that will be folded inline.",
|
||||
"More text.",
|
||||
"<p>The cursor in this paragraph text will be offset by 1 row.<p>",
|
||||
"<p>Everything after this will be offset as well due to the folds in the row before too.</p>"
|
||||
].join("\n"));
|
||||
session.addFold('...', new Range(0, 8, 0, 42));
|
||||
session.addFold('...', new Range(1, 8, 1, 42));
|
||||
session.addFold('...', new Range(3, 7, 3, 51));
|
||||
session.setOption("wrap", 40);
|
||||
session.remove(new Range(0,0, 2, 5));
|
||||
// needed because adjustWrapLimit is called async from renderer
|
||||
session.adjustWrapLimit(80);
|
||||
|
||||
assert.equal(session.$wrapData + "", [[], [], [40, 76]] + "");
|
||||
},
|
||||
|
||||
"test add fold": function() {
|
||||
var session = createFoldTestSession();
|
||||
var fold;
|
||||
@@ -911,7 +937,7 @@ module.exports = {
|
||||
fail = true;
|
||||
}
|
||||
if (fail != shouldFail) {
|
||||
throw "Expected to get an exception";
|
||||
throw new Error("Expected to get an exception");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -980,7 +1006,8 @@ module.exports = {
|
||||
assertArray(session.$docRowCache, [1,3,4]);
|
||||
assertArray(session.$screenRowCache, [1,2,3]);
|
||||
|
||||
session.screenToDocumentPosition(0,0);
|
||||
var pos = session.screenToDocumentPosition(0,0);
|
||||
assert.equal(pos.row, 0);
|
||||
assertArray(session.$docRowCache, [1,3,4]);
|
||||
assertArray(session.$screenRowCache, [1,2,3]);
|
||||
|
||||
@@ -991,11 +1018,58 @@ module.exports = {
|
||||
session.$resetRowCache();
|
||||
assertArray(session.$docRowCache, []);
|
||||
assertArray(session.$screenRowCache, []);
|
||||
|
||||
session.screenToDocumentPosition(1,3);
|
||||
assertArray(session.$docRowCache, [1]);
|
||||
assertArray(session.$screenRowCache, [1]);
|
||||
|
||||
session.screenToDocumentPosition(5,3);
|
||||
assertArray(session.$docRowCache, [1,3,4]);
|
||||
assertArray(session.$screenRowCache, [1,2,3]);
|
||||
|
||||
session = new EditSession(new Array(30).join("\n"));
|
||||
session.documentToScreenPosition(2,0);
|
||||
session.documentToScreenPosition(2,0);
|
||||
assertArray(session.$docRowCache, [1,2]);
|
||||
assertArray(session.$screenRowCache, [1,2]);
|
||||
},
|
||||
|
||||
"test annotations": function() {
|
||||
var session = new EditSession([]),
|
||||
annotation = {row: 0, type: 'info', text: "This is a test."};
|
||||
|
||||
session.clearAnnotations();
|
||||
assertArray(session.getAnnotations(), []);
|
||||
session.setAnnotations([annotation]);
|
||||
assertArray(session.getAnnotations(), [annotation]);
|
||||
},
|
||||
|
||||
"test: mode loading" : function(next) {
|
||||
if (!require.undef) {
|
||||
console.log("Skipping test: This test only runs in the browser");
|
||||
next();
|
||||
return;
|
||||
}
|
||||
var session = new EditSession([]);
|
||||
session.setMode("ace/mode/javascript");
|
||||
assert.equal(session.$modeid, "ace/mode/javascript");
|
||||
session.on("changeMode", function() {
|
||||
assert.equal(session.$modeid, "ace/mode/javascript");
|
||||
});
|
||||
session.setMode("ace/mode/sh", function(mode) {
|
||||
assert.ok(!mode);
|
||||
});
|
||||
setTimeout(function() {
|
||||
session.setMode("ace/mode/javascript", function(mode) {
|
||||
session.setMode("ace/mode/javascript");
|
||||
assert.equal(session.$modeid, "ace/mode/javascript");
|
||||
next();
|
||||
});
|
||||
}, 0);
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
if (typeof module !== "undefined" && module === require.main) {
|
||||
require("asyncjs").test.testcase(module.exports).exec()
|
||||
require("asyncjs").test.testcase(module.exports).exec();
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+2
-2
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
@@ -14,7 +14,7 @@
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
|
||||
@@ -42,6 +42,7 @@ var JavaScriptMode = require("./mode/javascript").Mode;
|
||||
var UndoManager = require("./undomanager").UndoManager;
|
||||
var MockRenderer = require("./test/mockrenderer").MockRenderer;
|
||||
var assert = require("./test/assertions");
|
||||
var whitespace = require("./ext/whitespace");
|
||||
|
||||
module.exports = {
|
||||
"test: delete line from the middle" : function() {
|
||||
@@ -184,21 +185,23 @@ module.exports = {
|
||||
"test: comment lines should perserve selection" : function() {
|
||||
var session = new EditSession([" abc", "cde"].join("\n"), new JavaScriptMode());
|
||||
var editor = new Editor(new MockRenderer(), session);
|
||||
|
||||
whitespace.detectIndentation(session);
|
||||
|
||||
editor.moveCursorTo(0, 2);
|
||||
editor.getSelection().selectDown();
|
||||
editor.toggleCommentLines();
|
||||
|
||||
assert.equal(["// abc", "//cde"].join("\n"), session.toString());
|
||||
assert.equal(["// abc", "// cde"].join("\n"), session.toString());
|
||||
|
||||
var selection = editor.getSelectionRange();
|
||||
assert.position(selection.start, 0, 4);
|
||||
assert.position(selection.end, 1, 4);
|
||||
assert.position(selection.start, 0, 5);
|
||||
assert.position(selection.end, 1, 5);
|
||||
},
|
||||
|
||||
"test: uncomment lines should perserve selection" : function() {
|
||||
var session = new EditSession(["// abc", "//cde"].join("\n"), new JavaScriptMode());
|
||||
var session = new EditSession(["// abc", "//cde"].join("\n"), new JavaScriptMode());
|
||||
var editor = new Editor(new MockRenderer(), session);
|
||||
session.setTabSize(2);
|
||||
|
||||
editor.moveCursorTo(0, 1);
|
||||
editor.getSelection().selectDown();
|
||||
@@ -235,7 +238,7 @@ module.exports = {
|
||||
editor.getSelection().selectDown();
|
||||
|
||||
editor.toggleCommentLines();
|
||||
assert.range(editor.getSelectionRange(), 0, 2, 1, 0);
|
||||
assert.range(editor.getSelectionRange(), 0, 3, 1, 0);
|
||||
|
||||
// select up
|
||||
var session = new EditSession(["abc", "cde"].join("\n"), new JavaScriptMode());
|
||||
@@ -245,10 +248,10 @@ module.exports = {
|
||||
editor.getSelection().selectUp();
|
||||
|
||||
editor.toggleCommentLines();
|
||||
assert.range(editor.getSelectionRange(), 0, 2, 1, 0);
|
||||
assert.range(editor.getSelectionRange(), 0, 3, 1, 0);
|
||||
},
|
||||
|
||||
"test: move lines down should select moved lines" : function() {
|
||||
"test: move lines down should keep selection on moved lines" : function() {
|
||||
var session = new EditSession(["11", "22", "33", "44"].join("\n"));
|
||||
var editor = new Editor(new MockRenderer(), session);
|
||||
|
||||
@@ -257,25 +260,25 @@ module.exports = {
|
||||
|
||||
editor.moveLinesDown();
|
||||
assert.equal(["33", "11", "22", "44"].join("\n"), session.toString());
|
||||
assert.position(editor.getCursorPosition(), 1, 0);
|
||||
assert.position(editor.getSelection().getSelectionAnchor(), 3, 0);
|
||||
assert.position(editor.getSelection().getSelectionLead(), 1, 0);
|
||||
assert.position(editor.getCursorPosition(), 2, 1);
|
||||
assert.position(editor.getSelection().getSelectionAnchor(), 1, 1);
|
||||
assert.position(editor.getSelection().getSelectionLead(), 2, 1);
|
||||
|
||||
editor.moveLinesDown();
|
||||
assert.equal(["33", "44", "11", "22"].join("\n"), session.toString());
|
||||
assert.position(editor.getCursorPosition(), 2, 0);
|
||||
assert.position(editor.getSelection().getSelectionAnchor(), 3, 2);
|
||||
assert.position(editor.getSelection().getSelectionLead(), 2, 0);
|
||||
assert.position(editor.getCursorPosition(), 3, 1);
|
||||
assert.position(editor.getSelection().getSelectionAnchor(), 2, 1);
|
||||
assert.position(editor.getSelection().getSelectionLead(), 3, 1);
|
||||
|
||||
// moving again should have no effect
|
||||
editor.moveLinesDown();
|
||||
assert.equal(["33", "44", "11", "22"].join("\n"), session.toString());
|
||||
assert.position(editor.getCursorPosition(), 2, 0);
|
||||
assert.position(editor.getSelection().getSelectionAnchor(), 3, 2);
|
||||
assert.position(editor.getSelection().getSelectionLead(), 2, 0);
|
||||
assert.position(editor.getCursorPosition(), 3, 1);
|
||||
assert.position(editor.getSelection().getSelectionAnchor(), 2, 1);
|
||||
assert.position(editor.getSelection().getSelectionLead(), 3, 1);
|
||||
},
|
||||
|
||||
"test: move lines up should select moved lines" : function() {
|
||||
"test: move lines up should keep selection on moved lines" : function() {
|
||||
var session = new EditSession(["11", "22", "33", "44"].join("\n"));
|
||||
var editor = new Editor(new MockRenderer(), session);
|
||||
|
||||
@@ -284,19 +287,18 @@ module.exports = {
|
||||
|
||||
editor.moveLinesUp();
|
||||
assert.equal(session.toString(), ["11", "33", "44", "22"].join("\n"));
|
||||
assert.position(editor.getCursorPosition(), 1, 0);
|
||||
assert.position(editor.getSelection().getSelectionAnchor(), 3, 0);
|
||||
assert.position(editor.getSelection().getSelectionLead(), 1, 0);
|
||||
assert.position(editor.getCursorPosition(), 2, 1);
|
||||
assert.position(editor.getSelection().getSelectionAnchor(), 1, 1);
|
||||
assert.position(editor.getSelection().getSelectionLead(), 2, 1);
|
||||
|
||||
editor.moveLinesUp();
|
||||
assert.equal(session.toString(), ["33", "44", "11", "22"].join("\n"));
|
||||
assert.position(editor.getCursorPosition(), 0, 0);
|
||||
assert.position(editor.getSelection().getSelectionAnchor(), 2, 0);
|
||||
assert.position(editor.getSelection().getSelectionLead(), 0, 0);
|
||||
assert.position(editor.getCursorPosition(), 1, 1);
|
||||
assert.position(editor.getSelection().getSelectionAnchor(), 0, 1);
|
||||
assert.position(editor.getSelection().getSelectionLead(), 1, 1);
|
||||
},
|
||||
|
||||
"test: move line without active selection should not move cursor relative to the moved line" : function()
|
||||
{
|
||||
"test: move line without active selection should not move cursor relative to the moved line" : function() {
|
||||
var session = new EditSession(["11", "22", "33", "44"].join("\n"));
|
||||
var editor = new Editor(new MockRenderer(), session);
|
||||
|
||||
@@ -314,7 +316,7 @@ module.exports = {
|
||||
assert.position(editor.getCursorPosition(), 1, 1);
|
||||
},
|
||||
|
||||
"test: copy lines down should select lines and place cursor at the selection start" : function() {
|
||||
"test: copy lines down should keep selection" : function() {
|
||||
var session = new EditSession(["11", "22", "33", "44"].join("\n"));
|
||||
var editor = new Editor(new MockRenderer(), session);
|
||||
|
||||
@@ -324,12 +326,12 @@ module.exports = {
|
||||
editor.copyLinesDown();
|
||||
assert.equal(["11", "22", "33", "22", "33", "44"].join("\n"), session.toString());
|
||||
|
||||
assert.position(editor.getCursorPosition(), 3, 0);
|
||||
assert.position(editor.getSelection().getSelectionAnchor(), 5, 0);
|
||||
assert.position(editor.getSelection().getSelectionLead(), 3, 0);
|
||||
assert.position(editor.getCursorPosition(), 4, 1);
|
||||
assert.position(editor.getSelection().getSelectionAnchor(), 3, 1);
|
||||
assert.position(editor.getSelection().getSelectionLead(), 4, 1);
|
||||
},
|
||||
|
||||
"test: copy lines up should select lines and place cursor at the selection start" : function() {
|
||||
"test: copy lines up should keep selection" : function() {
|
||||
var session = new EditSession(["11", "22", "33", "44"].join("\n"));
|
||||
var editor = new Editor(new MockRenderer(), session);
|
||||
|
||||
@@ -339,9 +341,9 @@ module.exports = {
|
||||
editor.copyLinesUp();
|
||||
assert.equal(["11", "22", "33", "22", "33", "44"].join("\n"), session.toString());
|
||||
|
||||
assert.position(editor.getCursorPosition(), 1, 0);
|
||||
assert.position(editor.getSelection().getSelectionAnchor(), 3, 0);
|
||||
assert.position(editor.getSelection().getSelectionLead(), 1, 0);
|
||||
assert.position(editor.getCursorPosition(), 2, 1);
|
||||
assert.position(editor.getSelection().getSelectionAnchor(), 1, 1);
|
||||
assert.position(editor.getSelection().getSelectionLead(), 2, 1);
|
||||
},
|
||||
|
||||
"test: input a tab with soft tab should convert it to spaces" : function() {
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2012, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
// [WIP]
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
var TokenIterator = require("ace/token_iterator").TokenIterator;
|
||||
|
||||
var phpTransform = require("./beautify/php_rules").transform;
|
||||
|
||||
exports.beautify = function(session) {
|
||||
var iterator = new TokenIterator(session, 0, 0);
|
||||
var token = iterator.getCurrentToken();
|
||||
|
||||
var context = session.$modeId.split("/").pop();
|
||||
|
||||
var code = phpTransform(iterator, context);
|
||||
session.doc.setValue(code);
|
||||
};
|
||||
|
||||
exports.commands = [{
|
||||
name: "beautify",
|
||||
exec: function(editor) {
|
||||
exports.beautify(editor.session);
|
||||
},
|
||||
bindKey: "Ctrl-Shift-B"
|
||||
}]
|
||||
|
||||
});
|
||||
@@ -0,0 +1,366 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2012, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
var TokenIterator = require("ace/token_iterator").TokenIterator;
|
||||
exports.newLines = [{
|
||||
type: 'support.php_tag',
|
||||
value: '<?php'
|
||||
}, {
|
||||
type: 'support.php_tag',
|
||||
value: '<?'
|
||||
}, {
|
||||
type: 'support.php_tag',
|
||||
value: '?>'
|
||||
}, {
|
||||
type: 'paren.lparen',
|
||||
value: '{',
|
||||
indent: true
|
||||
}, {
|
||||
type: 'paren.rparen',
|
||||
breakBefore: true,
|
||||
value: '}',
|
||||
indent: false
|
||||
}, {
|
||||
type: 'paren.rparen',
|
||||
breakBefore: true,
|
||||
value: '})',
|
||||
indent: false,
|
||||
dontBreak: true
|
||||
}, {
|
||||
type: 'comment'
|
||||
}, {
|
||||
type: 'text',
|
||||
value: ';'
|
||||
}, {
|
||||
type: 'text',
|
||||
value: ':',
|
||||
context: 'php'
|
||||
}, {
|
||||
type: 'keyword',
|
||||
value: 'case',
|
||||
indent: true,
|
||||
dontBreak: true
|
||||
}, {
|
||||
type: 'keyword',
|
||||
value: 'default',
|
||||
indent: true,
|
||||
dontBreak: true
|
||||
}, {
|
||||
type: 'keyword',
|
||||
value: 'break',
|
||||
indent: false,
|
||||
dontBreak: true
|
||||
}, {
|
||||
type: 'punctuation.doctype.end',
|
||||
value: '>'
|
||||
}, {
|
||||
type: 'meta.tag.punctuation.end',
|
||||
value: '>'
|
||||
}, {
|
||||
type: 'meta.tag.punctuation.begin',
|
||||
value: '<',
|
||||
blockTag: true,
|
||||
indent: true,
|
||||
dontBreak: true
|
||||
}, {
|
||||
type: 'meta.tag.punctuation.begin',
|
||||
value: '</',
|
||||
indent: false,
|
||||
breakBefore: true,
|
||||
dontBreak: true
|
||||
}, {
|
||||
type: 'punctuation.operator',
|
||||
value: ';'
|
||||
}];
|
||||
|
||||
exports.spaces = [{
|
||||
type: 'xml-pe',
|
||||
prepend: true
|
||||
},{
|
||||
type: 'entity.other.attribute-name',
|
||||
prepend: true
|
||||
}, {
|
||||
type: 'storage.type',
|
||||
value: 'var',
|
||||
append: true
|
||||
}, {
|
||||
type: 'storage.type',
|
||||
value: 'function',
|
||||
append: true
|
||||
}, {
|
||||
type: 'keyword.operator',
|
||||
value: '='
|
||||
}, {
|
||||
type: 'keyword',
|
||||
value: 'as',
|
||||
prepend: true,
|
||||
append: true
|
||||
}, {
|
||||
type: 'keyword',
|
||||
value: 'function',
|
||||
append: true
|
||||
}, {
|
||||
type: 'support.function',
|
||||
next: /[^\(]/,
|
||||
append: true
|
||||
}, {
|
||||
type: 'keyword',
|
||||
value: 'or',
|
||||
append: true,
|
||||
prepend: true
|
||||
}, {
|
||||
type: 'keyword',
|
||||
value: 'and',
|
||||
append: true,
|
||||
prepend: true
|
||||
}, {
|
||||
type: 'keyword',
|
||||
value: 'case',
|
||||
append: true
|
||||
}, {
|
||||
type: 'keyword.operator',
|
||||
value: '||',
|
||||
append: true,
|
||||
prepend: true
|
||||
}, {
|
||||
type: 'keyword.operator',
|
||||
value: '&&',
|
||||
append: true,
|
||||
prepend: true
|
||||
}];
|
||||
exports.singleTags = ['!doctype','area','base','br','hr','input','img','link','meta'];
|
||||
|
||||
exports.transform = function(iterator, maxPos, context) {
|
||||
var token = iterator.getCurrentToken();
|
||||
|
||||
var newLines = exports.newLines;
|
||||
var spaces = exports.spaces;
|
||||
var singleTags = exports.singleTags;
|
||||
|
||||
var code = '';
|
||||
|
||||
var indentation = 0;
|
||||
var dontBreak = false;
|
||||
var tag;
|
||||
var lastTag;
|
||||
var lastToken = {};
|
||||
var nextTag;
|
||||
var nextToken = {};
|
||||
var breakAdded = false;
|
||||
var value = '';
|
||||
|
||||
while (token!==null) {
|
||||
console.log(token);
|
||||
|
||||
if( !token ){
|
||||
token = iterator.stepForward();
|
||||
continue;
|
||||
}
|
||||
|
||||
//change syntax
|
||||
//php
|
||||
if( token.type == 'support.php_tag' && token.value != '?>' ){
|
||||
context = 'php';
|
||||
}
|
||||
else if( token.type == 'support.php_tag' && token.value == '?>' ){
|
||||
context = 'html';
|
||||
}
|
||||
//css
|
||||
else if( token.type == 'meta.tag.name.style' && context != 'css' ){
|
||||
context = 'css';
|
||||
}
|
||||
else if( token.type == 'meta.tag.name.style' && context == 'css' ){
|
||||
context = 'html';
|
||||
}
|
||||
//js
|
||||
else if( token.type == 'meta.tag.name.script' && context != 'js' ){
|
||||
context = 'js';
|
||||
}
|
||||
else if( token.type == 'meta.tag.name.script' && context == 'js' ){
|
||||
context = 'html';
|
||||
}
|
||||
|
||||
nextToken = iterator.stepForward();
|
||||
|
||||
//tag name
|
||||
if (nextToken && nextToken.type.indexOf('meta.tag.name') == 0) {
|
||||
nextTag = nextToken.value;
|
||||
}
|
||||
|
||||
//don't linebreak
|
||||
if ( lastToken.type == 'support.php_tag' && lastToken.value == '<?=') {
|
||||
dontBreak = true;
|
||||
}
|
||||
|
||||
//lowercase
|
||||
if (token.type == 'meta.tag.name') {
|
||||
token.value = token.value.toLowerCase();
|
||||
}
|
||||
|
||||
//trim spaces
|
||||
if (token.type == 'text') {
|
||||
token.value = token.value.trim();
|
||||
}
|
||||
|
||||
//skip empty tokens
|
||||
if (!token.value) {
|
||||
token = nextToken;
|
||||
continue;
|
||||
}
|
||||
|
||||
//put spaces back in
|
||||
value = token.value;
|
||||
for (var i in spaces) {
|
||||
if (
|
||||
token.type == spaces[i].type &&
|
||||
(!spaces[i].value || token.value == spaces[i].value) &&
|
||||
(
|
||||
nextToken &&
|
||||
(!spaces[i].next || spaces[i].next.test(nextToken.value))
|
||||
)
|
||||
) {
|
||||
if (spaces[i].prepend) {
|
||||
value = ' ' + token.value;
|
||||
}
|
||||
|
||||
if (spaces[i].append) {
|
||||
value += ' ';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//tag name
|
||||
if (token.type.indexOf('meta.tag.name') == 0) {
|
||||
tag = token.value;
|
||||
//console.log(tag);
|
||||
}
|
||||
|
||||
//new line before
|
||||
breakAdded = false;
|
||||
|
||||
//outdent
|
||||
for (i in newLines) {
|
||||
if (
|
||||
token.type == newLines[i].type &&
|
||||
(
|
||||
!newLines[i].value ||
|
||||
token.value == newLines[i].value
|
||||
) &&
|
||||
(
|
||||
!newLines[i].blockTag ||
|
||||
singleTags.indexOf(nextTag) === -1
|
||||
) &&
|
||||
(
|
||||
!newLines[i].context ||
|
||||
newLines[i].context === context
|
||||
)
|
||||
) {
|
||||
if (newLines[i].indent === false) {
|
||||
indentation--;
|
||||
}
|
||||
|
||||
if (
|
||||
newLines[i].breakBefore &&
|
||||
( !newLines[i].prev || newLines[i].prev.test(lastToken.value) )
|
||||
) {
|
||||
code += "\n";
|
||||
breakAdded = true;
|
||||
|
||||
//indent
|
||||
for (i = 0; i < indentation; i++) {
|
||||
code += "\t";
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (dontBreak===false) {
|
||||
for (i in newLines) {
|
||||
if (
|
||||
lastToken.type == newLines[i].type &&
|
||||
(
|
||||
!newLines[i].value || lastToken.value == newLines[i].value
|
||||
) &&
|
||||
(
|
||||
!newLines[i].blockTag ||
|
||||
singleTags.indexOf(tag) === -1
|
||||
) &&
|
||||
(
|
||||
!newLines[i].context ||
|
||||
newLines[i].context === context
|
||||
)
|
||||
) {
|
||||
if (newLines[i].indent === true) {
|
||||
indentation++;
|
||||
}
|
||||
|
||||
if (!newLines[i].dontBreak && !breakAdded) {
|
||||
code += "\n";
|
||||
|
||||
//indent
|
||||
for (i = 0; i < indentation; i++) {
|
||||
code += "\t";
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
code += value;
|
||||
|
||||
//linebreaks back on after end short php tag
|
||||
if ( lastToken.type == 'support.php_tag' && lastToken.value == '?>' ) {
|
||||
dontBreak = false;
|
||||
}
|
||||
|
||||
//next token
|
||||
lastTag = tag;
|
||||
|
||||
lastToken = token;
|
||||
|
||||
token = nextToken;
|
||||
|
||||
if (token===null) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return code;
|
||||
};
|
||||
|
||||
|
||||
|
||||
});
|
||||
@@ -0,0 +1,980 @@
|
||||
define(function(require, exports, module) {
|
||||
|
||||
/* ChromeVox Ace namespace. */
|
||||
var cvoxAce = {};
|
||||
|
||||
/* Typedefs for Closure compiler. */
|
||||
/**
|
||||
* @typedef {{
|
||||
rate: number,
|
||||
pitch: number,
|
||||
volume: number,
|
||||
relativePitch: number,
|
||||
punctuationEcho: string
|
||||
}}
|
||||
*/
|
||||
/* TODO(peterxiao): Export this typedef through cvox.Api. */
|
||||
cvoxAce.SpeechProperty;
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
* row: number,
|
||||
* column: number
|
||||
* }}
|
||||
*/
|
||||
cvoxAce.Cursor;
|
||||
|
||||
/**
|
||||
* @typedef {{
|
||||
type: string,
|
||||
value: string
|
||||
}}
|
||||
}
|
||||
*/
|
||||
cvoxAce.Token;
|
||||
|
||||
/**
|
||||
* These are errors and information that Ace will display in the gutter.
|
||||
* @typedef {{
|
||||
row: number,
|
||||
column: number,
|
||||
value: string
|
||||
}}
|
||||
}
|
||||
*/
|
||||
cvoxAce.Annotation;
|
||||
|
||||
/* Speech Properties. */
|
||||
/**
|
||||
* Speech property for speaking constant tokens.
|
||||
* @type {cvoxAce.SpeechProperty}
|
||||
*/
|
||||
var CONSTANT_PROP = {
|
||||
'rate': 0.8,
|
||||
'pitch': 0.4,
|
||||
'volume': 0.9
|
||||
};
|
||||
|
||||
/**
|
||||
* Default speech property for speaking tokens.
|
||||
* @type {cvoxAce.SpeechProperty}
|
||||
*/
|
||||
var DEFAULT_PROP = {
|
||||
'rate': 1,
|
||||
'pitch': 0.5,
|
||||
'volume': 0.9
|
||||
};
|
||||
|
||||
/**
|
||||
* Speech property for speaking entity tokens.
|
||||
* @type {cvoxAce.SpeechProperty}
|
||||
*/
|
||||
var ENTITY_PROP = {
|
||||
'rate': 0.8,
|
||||
'pitch': 0.8,
|
||||
'volume': 0.9
|
||||
};
|
||||
|
||||
/**
|
||||
* Speech property for speaking keywords.
|
||||
* @type {cvoxAce.SpeechProperty}
|
||||
*/
|
||||
var KEYWORD_PROP = {
|
||||
'rate': 0.8,
|
||||
'pitch': 0.3,
|
||||
'volume': 0.9
|
||||
};
|
||||
|
||||
/**
|
||||
* Speech property for speaking storage tokens.
|
||||
* @type {cvoxAce.SpeechProperty}
|
||||
*/
|
||||
var STORAGE_PROP = {
|
||||
'rate': 0.8,
|
||||
'pitch': 0.7,
|
||||
'volume': 0.9
|
||||
};
|
||||
|
||||
/**
|
||||
* Speech property for speaking variable tokens.
|
||||
* @type {cvoxAce.SpeechProperty}
|
||||
*/
|
||||
var VARIABLE_PROP = {
|
||||
'rate': 0.8,
|
||||
'pitch': 0.8,
|
||||
'volume': 0.9
|
||||
};
|
||||
|
||||
/**
|
||||
* Speech property for speaking deleted text.
|
||||
* @type {cvoxAce.SpeechProperty}
|
||||
*/
|
||||
var DELETED_PROP = {
|
||||
'punctuationEcho': 'none',
|
||||
'relativePitch': -0.6
|
||||
};
|
||||
|
||||
/* Constants for Earcons. */
|
||||
var ERROR_EARCON = 'ALERT_NONMODAL';
|
||||
var MODE_SWITCH_EARCON = 'ALERT_MODAL';
|
||||
var NO_MATCH_EARCON = 'INVALID_KEYPRESS';
|
||||
|
||||
/* Constants for vim state. */
|
||||
var INSERT_MODE_STATE = 'insertMode';
|
||||
var COMMAND_MODE_STATE = 'start';
|
||||
|
||||
var REPLACE_LIST = [
|
||||
{
|
||||
substr: ';',
|
||||
newSubstr: ' semicolon '
|
||||
},
|
||||
{
|
||||
substr: ':',
|
||||
newSubstr: ' colon '
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* Context menu commands.
|
||||
*/
|
||||
var Command = {
|
||||
SPEAK_ANNOT: 'annots',
|
||||
SPEAK_ALL_ANNOTS: 'all_annots',
|
||||
TOGGLE_LOCATION: 'toggle_location',
|
||||
SPEAK_MODE: 'mode',
|
||||
SPEAK_ROW_COL: 'row_col',
|
||||
TOGGLE_DISPLACEMENT: 'toggle_displacement',
|
||||
FOCUS_TEXT: 'focus_text'
|
||||
};
|
||||
|
||||
/**
|
||||
* Key prefix for each shortcut.
|
||||
*/
|
||||
var KEY_PREFIX = 'CONTROL + SHIFT ';
|
||||
|
||||
/* Globals. */
|
||||
cvoxAce.editor = null;
|
||||
/**
|
||||
* Last cursor position.
|
||||
* @type {cvoxAce.Cursor}
|
||||
*/
|
||||
var lastCursor = null;
|
||||
|
||||
/**
|
||||
* Table of annotations.
|
||||
* @typedef {!Object.<number, Object<number, cvoxAce.Annotation>>}
|
||||
*/
|
||||
var annotTable = {};
|
||||
|
||||
/**
|
||||
* Whether to speak character, word, and then line. This allows blind users
|
||||
* to know the location of the cursor when they change lines.
|
||||
* @typedef {boolean}
|
||||
*/
|
||||
var shouldSpeakRowLocation = false;
|
||||
|
||||
/**
|
||||
* Whether to speak displacement.
|
||||
* @typedef {boolean}
|
||||
*/
|
||||
var shouldSpeakDisplacement = false;
|
||||
|
||||
/**
|
||||
* Whether text was changed to cause a cursor change event.
|
||||
* @typedef {boolean}
|
||||
*/
|
||||
var changed = false;
|
||||
|
||||
/**
|
||||
* Current state vim is in.
|
||||
*/
|
||||
var vimState = null;
|
||||
|
||||
/**
|
||||
* Mapping from key code to shortcut.
|
||||
*/
|
||||
var keyCodeToShortcutMap = {};
|
||||
|
||||
/**
|
||||
* Mapping from command to shortcut.
|
||||
*/
|
||||
var cmdToShortcutMap = {};
|
||||
|
||||
/**
|
||||
* Get shortcut string from keyCode.
|
||||
* @param {number} keyCode Key code of shortcut.
|
||||
* @return {string} String representation of shortcut.
|
||||
*/
|
||||
var getKeyShortcutString = function(keyCode) {
|
||||
return KEY_PREFIX + String.fromCharCode(keyCode);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return if in vim mode.
|
||||
* @return {boolean} True if in Vim mode.
|
||||
*/
|
||||
var isVimMode = function() {
|
||||
var keyboardHandler = cvoxAce.editor.keyBinding.getKeyboardHandler();
|
||||
return keyboardHandler.$id === 'ace/keyboard/vim';
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the current token.
|
||||
* @param {!cvoxAce.Cursor} cursor Current position of the cursor.
|
||||
* @return {!cvoxAce.Token} Token at the current position.
|
||||
*/
|
||||
var getCurrentToken = function(cursor) {
|
||||
return cvoxAce.editor.getSession().getTokenAt(cursor.row, cursor.column + 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Gets the current line the cursor is under.
|
||||
* @param {!cvoxAce.Cursor} cursor Current cursor position.
|
||||
*/
|
||||
var getCurrentLine = function(cursor) {
|
||||
return cvoxAce.editor.getSession().getLine(cursor.row);
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler for row changes. When the user changes rows we want to speak
|
||||
* the line so the user can work on this line. If shouldSpeakRowLocation is on
|
||||
* then we speak the character, then the row, then the line so the user knows
|
||||
* where the cursor is.
|
||||
* @param {!cvoxAce.Cursor} currCursor Current cursor position.
|
||||
*/
|
||||
var onRowChange = function(currCursor) {
|
||||
/* Notify that this line has an annotation. */
|
||||
if (annotTable[currCursor.row]) {
|
||||
cvox.Api.playEarcon(ERROR_EARCON);
|
||||
}
|
||||
if (shouldSpeakRowLocation) {
|
||||
cvox.Api.stop();
|
||||
speakChar(currCursor);
|
||||
speakTokenQueue(getCurrentToken(currCursor));
|
||||
speakLine(currCursor.row, 1);
|
||||
} else {
|
||||
speakLine(currCursor.row, 0);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether the cursor is at the beginning of a word. A word is
|
||||
* a grouping of alphanumeric characters including underscores.
|
||||
* @param {!cvoxAce.Cursor} cursor Current cursor position.
|
||||
* @return {boolean} Whether there is word.
|
||||
*/
|
||||
var isWord = function(cursor) {
|
||||
var line = getCurrentLine(cursor);
|
||||
var lineSuffix = line.substr(cursor.column - 1);
|
||||
if (cursor.column === 0) {
|
||||
lineSuffix = ' ' + line;
|
||||
}
|
||||
/* Use regex to tell if the suffix is at the start of a new word. */
|
||||
var firstWordRegExp = /^\W(\w+)/;
|
||||
var words = firstWordRegExp.exec(lineSuffix);
|
||||
return words !== null;
|
||||
};
|
||||
|
||||
/**
|
||||
* A mapping of syntax type to speech properties / expanding rules.
|
||||
*/
|
||||
var rules = {
|
||||
'constant': {
|
||||
prop: CONSTANT_PROP
|
||||
},
|
||||
'entity': {
|
||||
prop: ENTITY_PROP
|
||||
},
|
||||
'keyword': {
|
||||
prop: KEYWORD_PROP
|
||||
},
|
||||
'storage': {
|
||||
prop: STORAGE_PROP
|
||||
},
|
||||
'variable': {
|
||||
prop: VARIABLE_PROP
|
||||
},
|
||||
'meta': {
|
||||
prop: DEFAULT_PROP,
|
||||
replace: [
|
||||
{
|
||||
substr: '</',
|
||||
newSubstr: ' closing tag '
|
||||
},
|
||||
{
|
||||
substr: '/>',
|
||||
newSubstr: ' close tag '
|
||||
},
|
||||
{
|
||||
substr: '<',
|
||||
newSubstr: ' tag start '
|
||||
},
|
||||
{
|
||||
substr: '>',
|
||||
newSubstr: ' tag end '
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Default rule to be used.
|
||||
*/
|
||||
var DEFAULT_RULE = {
|
||||
prop: DEFAULT_RULE
|
||||
};
|
||||
|
||||
/**
|
||||
* Expands substrings to how they are read based on the given rules.
|
||||
* @param {string} value Text to be expanded.
|
||||
* @param {Array.<Object>} replaceRules Rules to determine expansion.
|
||||
* @return {string} New expanded value.
|
||||
*/
|
||||
var expand = function(value, replaceRules) {
|
||||
var newValue = value;
|
||||
for (var i = 0; i < replaceRules.length; i++) {
|
||||
var replaceRule = replaceRules[i];
|
||||
var regexp = new RegExp(replaceRule.substr, 'g');
|
||||
newValue = newValue.replace(regexp, replaceRule.newSubstr);
|
||||
}
|
||||
return newValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Merges tokens from start inclusive to end exclusive.
|
||||
* @param {Array.<cvoxAce.Token>} Tokens to be merged.
|
||||
* @param {number} start Start index inclusive.
|
||||
* @param {number} end End index exclusive.
|
||||
* @return {cvoxAce.Token} Merged token.
|
||||
*/
|
||||
var mergeTokens = function(tokens, start, end) {
|
||||
/* Different type of token found! Merge all previous like tokens. */
|
||||
var newToken = {};
|
||||
newToken.value = '';
|
||||
newToken.type = tokens[start].type;
|
||||
for (var j = start; j < end; j++) {
|
||||
newToken.value += tokens[j].value;
|
||||
}
|
||||
return newToken;
|
||||
};
|
||||
|
||||
/**
|
||||
* Merges tokens that use the same speech properties.
|
||||
* @param {Array.<cvoxAce.Token>} tokens Tokens to be merged.
|
||||
* @return {Array.<cvoxAce.Token>} Merged tokens.
|
||||
*/
|
||||
var mergeLikeTokens = function(tokens) {
|
||||
if (tokens.length <= 1) {
|
||||
return tokens;
|
||||
}
|
||||
var newTokens = [];
|
||||
var lastLikeIndex = 0;
|
||||
for (var i = 1; i < tokens.length; i++) {
|
||||
var lastLikeToken = tokens[lastLikeIndex];
|
||||
var currToken = tokens[i];
|
||||
if (getTokenRule(lastLikeToken) !== getTokenRule(currToken)) {
|
||||
newTokens.push(mergeTokens(tokens, lastLikeIndex, i));
|
||||
lastLikeIndex = i;
|
||||
}
|
||||
}
|
||||
newTokens.push(mergeTokens(tokens, lastLikeIndex, tokens.length));
|
||||
return newTokens;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns if given row is a whitespace row.
|
||||
* @param {number} row Row.
|
||||
* @return {boolean} True if row is whitespaces.
|
||||
*/
|
||||
var isRowWhiteSpace = function(row) {
|
||||
var line = cvoxAce.editor.getSession().getLine(row);
|
||||
var whiteSpaceRegexp = /^\s*$/;
|
||||
return whiteSpaceRegexp.exec(line) !== null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Speak the line with syntax properties.
|
||||
* @param {number} row Row to speak.
|
||||
* @param {number} queue Queue mode to speak.
|
||||
*/
|
||||
var speakLine = function(row, queue) {
|
||||
var tokens = cvoxAce.editor.getSession().getTokens(row);
|
||||
if (tokens.length === 0 || isRowWhiteSpace(row)) {
|
||||
cvox.Api.playEarcon('EDITABLE_TEXT');
|
||||
return;
|
||||
}
|
||||
tokens = mergeLikeTokens(tokens);
|
||||
var firstToken = tokens[0];
|
||||
/* Filter out first token. */
|
||||
tokens = tokens.filter(function(token) {
|
||||
return token !== firstToken;
|
||||
});
|
||||
/* Speak first token separately to flush if queue. */
|
||||
speakToken_(firstToken, queue);
|
||||
/* Speak rest of tokens. */
|
||||
tokens.forEach(speakTokenQueue);
|
||||
};
|
||||
|
||||
/**
|
||||
* Speak the token based on the syntax of the token, flushing.
|
||||
* @param {!cvoxAce.Token} token Token to speak.
|
||||
* @param {number} queue Queue mode.
|
||||
*/
|
||||
var speakTokenFlush = function(token) {
|
||||
speakToken_(token, 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Speak the token based on the syntax of the token, queueing.
|
||||
* @param {!cvoxAce.Token} token Token to speak.
|
||||
* @param {number} queue Queue mode.
|
||||
*/
|
||||
var speakTokenQueue = function(token) {
|
||||
speakToken_(token, 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {!cvoxAce.Token} token Token to speak.
|
||||
* Get the token speech property.
|
||||
*/
|
||||
var getTokenRule = function(token) {
|
||||
/* Types are period delimited. In this case, we only syntax speak the outer
|
||||
* most type of token. */
|
||||
if (!token || !token.type) {
|
||||
return;
|
||||
}
|
||||
var split = token.type.split('.');
|
||||
if (split.length === 0) {
|
||||
return;
|
||||
}
|
||||
var type = split[0];
|
||||
var rule = rules[type];
|
||||
if (!rule) {
|
||||
return DEFAULT_RULE;
|
||||
}
|
||||
return rule;
|
||||
};
|
||||
|
||||
/**
|
||||
* Speak the token based on the syntax of the token.
|
||||
* @private
|
||||
* @param {!cvoxAce.Token} token Token to speak.
|
||||
* @param {number} queue Queue mode.
|
||||
*/
|
||||
var speakToken_ = function(token, queue) {
|
||||
var rule = getTokenRule(token);
|
||||
var value = expand(token.value, REPLACE_LIST);
|
||||
if (rule.replace) {
|
||||
value = expand(value, rule.replace);
|
||||
}
|
||||
cvox.Api.speak(value, queue, rule.prop);
|
||||
};
|
||||
|
||||
/**
|
||||
* Speaks the character under the cursor. This is queued.
|
||||
* @param {!cvoxAce.Cursor} cursor Current cursor position.
|
||||
* @return {string} Character.
|
||||
*/
|
||||
var speakChar = function(cursor) {
|
||||
var line = getCurrentLine(cursor);
|
||||
cvox.Api.speak(line[cursor.column], 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Speaks the jump from lastCursor to currCursor. This function assumes the
|
||||
* jump takes place on the current line.
|
||||
* @param {!cvoxAce.Cursor} lastCursor Previous cursor position.
|
||||
* @param {!cvoxAce.Cursor} currCursor Current cursor position.
|
||||
*/
|
||||
var speakDisplacement = function(lastCursor, currCursor) {
|
||||
var line = getCurrentLine(currCursor);
|
||||
|
||||
/* Get the text that we jumped past. */
|
||||
var displace = line.substring(lastCursor.column, currCursor.column);
|
||||
|
||||
/* Speak out loud spaces. */
|
||||
displace = displace.replace(/ /g, ' space ');
|
||||
cvox.Api.speak(displace);
|
||||
};
|
||||
|
||||
/**
|
||||
* Speaks the word if the cursor jumped to a new word or to the beginning
|
||||
* of the line. Otherwise speak the charactor.
|
||||
* @param {!cvoxAce.Cursor} lastCursor Previous cursor position.
|
||||
* @param {!cvoxAce.Cursor} currCursor Current cursor position.
|
||||
*/
|
||||
var speakCharOrWordOrLine = function(lastCursor, currCursor) {
|
||||
/* Say word only if jump. */
|
||||
if (Math.abs(lastCursor.column - currCursor.column) !== 1) {
|
||||
var currLineLength = getCurrentLine(currCursor).length;
|
||||
/* Speak line if jumping to beginning or end of line. */
|
||||
if (currCursor.column === 0 || currCursor.column === currLineLength) {
|
||||
speakLine(currCursor.row, 0);
|
||||
return;
|
||||
}
|
||||
if (isWord(currCursor)) {
|
||||
cvox.Api.stop();
|
||||
speakTokenQueue(getCurrentToken(currCursor));
|
||||
return;
|
||||
}
|
||||
}
|
||||
speakChar(currCursor);
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler for column changes. If shouldSpeakDisplacement is on, then
|
||||
* we just speak displacements in row changes. Otherwise, we either speak
|
||||
* the character for single character movements, the word when jumping to the
|
||||
* next word, or the entire line if jumping to beginning or end of the line.
|
||||
* @param {!cvoxAce.Cursor} lastCursor Previous cursor position.
|
||||
* @param {!cvoxAce.Cursor} currCursor Current cursor position.
|
||||
*/
|
||||
var onColumnChange = function(lastCursor, currCursor) {
|
||||
if (!cvoxAce.editor.selection.isEmpty()) {
|
||||
speakDisplacement(lastCursor, currCursor);
|
||||
cvox.Api.speak('selected', 1);
|
||||
}
|
||||
else if (shouldSpeakDisplacement) {
|
||||
speakDisplacement(lastCursor, currCursor);
|
||||
} else {
|
||||
speakCharOrWordOrLine(lastCursor, currCursor);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler for cursor changes. Classify cursor changes as either row or
|
||||
* column changes, then delegate accordingly.
|
||||
* @param {!Event} evt The event.
|
||||
*/
|
||||
var onCursorChange = function(evt) {
|
||||
/* Do not speak if cursor change was a result of text insertion. We want to
|
||||
* speak the text that was inserted and not where the cursor lands. */
|
||||
if (changed) {
|
||||
changed = false;
|
||||
return;
|
||||
}
|
||||
var currCursor = cvoxAce.editor.selection.getCursor();
|
||||
if (currCursor.row !== lastCursor.row) {
|
||||
onRowChange(currCursor);
|
||||
} else {
|
||||
onColumnChange(lastCursor, currCursor);
|
||||
}
|
||||
lastCursor = currCursor;
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler for selection changes.
|
||||
* @param {!Event} evt The event.
|
||||
*/
|
||||
var onSelectionChange = function(evt) {
|
||||
/* Assumes that when selection changes to empty, the user has unselected. */
|
||||
if (cvoxAce.editor.selection.isEmpty()) {
|
||||
cvox.Api.speak('unselected');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler for source changes. We want auditory feedback for inserting
|
||||
* and deleting text.
|
||||
* @param {!Event} evt The event.
|
||||
*/
|
||||
var onChange = function(evt) {
|
||||
var data = evt.data;
|
||||
switch (data.action) {
|
||||
case 'removeText':
|
||||
cvox.Api.speak(data.text, 0, DELETED_PROP);
|
||||
/* Let the future cursor change event know it's from text change. */
|
||||
changed = true;
|
||||
break;
|
||||
case 'insertText':
|
||||
cvox.Api.speak(data.text, 0);
|
||||
/* Let the future cursor change event know it's from text change. */
|
||||
changed = true;
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns whether or not the annotation is new.
|
||||
* @param {!cvoxAce.Annotation} annot Annotation in question.
|
||||
* @return {boolean} Whether annot is new.
|
||||
*/
|
||||
var isNewAnnotation = function(annot) {
|
||||
var row = annot.row;
|
||||
var col = annot.column;
|
||||
return !annotTable[row] || !annotTable[row][col];
|
||||
};
|
||||
|
||||
/**
|
||||
* Populates the annotation table.
|
||||
* @param {!Array.<cvoxAce.Annotation>} annotations Array of annotations.
|
||||
*/
|
||||
var populateAnnotations = function(annotations) {
|
||||
annotTable = {};
|
||||
for (var i = 0; i < annotations.length; i++) {
|
||||
var annotation = annotations[i];
|
||||
var row = annotation.row;
|
||||
var col = annotation.column;
|
||||
if (!annotTable[row]) {
|
||||
annotTable[row] = {};
|
||||
}
|
||||
annotTable[row][col] = annotation;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler for annotation changes. We want to notify the user when an
|
||||
* a new annotation appears.
|
||||
* @param {!Event} evt Event.
|
||||
*/
|
||||
var onAnnotationChange = function(evt) {
|
||||
var annotations = cvoxAce.editor.getSession().getAnnotations();
|
||||
var newAnnotations = annotations.filter(isNewAnnotation);
|
||||
if (newAnnotations.length > 0) {
|
||||
cvox.Api.playEarcon(ERROR_EARCON);
|
||||
}
|
||||
populateAnnotations(annotations);
|
||||
};
|
||||
|
||||
/**
|
||||
* Speak annotation.
|
||||
* @param {!cvoxAce.Annotation} annot Annotation to speak.
|
||||
*/
|
||||
var speakAnnot = function(annot) {
|
||||
var annotText = annot.type + ' ' + annot.text + ' on ' +
|
||||
rowColToString(annot.row, annot.column);
|
||||
annotText = annotText.replace(';', 'semicolon');
|
||||
cvox.Api.speak(annotText, 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Speak annotations in a row.
|
||||
* @param {number} row Row of annotations to speak.
|
||||
*/
|
||||
var speakAnnotsByRow = function(row) {
|
||||
var annots = annotTable[row];
|
||||
for (var col in annots) {
|
||||
speakAnnot(annots[col]);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get a string representation of a row and column.
|
||||
* @param {boolean} row Zero indexed row.
|
||||
* @param {boolean} col Zero indexed column.
|
||||
* @return {string} Row and column to be spoken.
|
||||
*/
|
||||
var rowColToString = function(row, col) {
|
||||
return 'row ' + (row + 1) + ' column ' + (col + 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Speaks the row and column.
|
||||
*/
|
||||
var speakCurrRowAndCol = function() {
|
||||
cvox.Api.speak(rowColToString(lastCursor.row, lastCursor.column));
|
||||
};
|
||||
|
||||
/**
|
||||
* Speaks all annotations.
|
||||
*/
|
||||
var speakAllAnnots = function() {
|
||||
for (var row in annotTable) {
|
||||
speakAnnotsByRow(row);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Speak the vim mode. If no vim mode, this function does nothing.
|
||||
*/
|
||||
var speakMode = function() {
|
||||
if (!isVimMode()) {
|
||||
return;
|
||||
}
|
||||
switch (cvoxAce.editor.keyBinding.$data.state) {
|
||||
case INSERT_MODE_STATE:
|
||||
cvox.Api.speak('Insert mode');
|
||||
break;
|
||||
case COMMAND_MODE_STATE:
|
||||
cvox.Api.speak('Command mode');
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle speak location.
|
||||
*/
|
||||
var toggleSpeakRowLocation = function() {
|
||||
shouldSpeakRowLocation = !shouldSpeakRowLocation;
|
||||
/* Auditory feedback of the change. */
|
||||
if (shouldSpeakRowLocation) {
|
||||
cvox.Api.speak('Speak location on row change enabled.');
|
||||
} else {
|
||||
cvox.Api.speak('Speak location on row change disabled.');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Toggle speak displacement.
|
||||
*/
|
||||
var toggleSpeakDisplacement = function() {
|
||||
shouldSpeakDisplacement = !shouldSpeakDisplacement;
|
||||
/* Auditory feedback of the change. */
|
||||
if (shouldSpeakDisplacement) {
|
||||
cvox.Api.speak('Speak displacement on column changes.');
|
||||
} else {
|
||||
cvox.Api.speak('Speak current character or word on column changes.');
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler for key down events. Gets the right shortcut from the map,
|
||||
* and calls the associated function.
|
||||
* @param {!Event} evt Keyboard event.
|
||||
*/
|
||||
var onKeyDown = function(evt) {
|
||||
if (evt.ctrlKey && evt.shiftKey) {
|
||||
var shortcut = keyCodeToShortcutMap[evt.keyCode];
|
||||
if (shortcut) {
|
||||
shortcut.func();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler for status change events. Auditory feedback of changing
|
||||
* between vim states.
|
||||
* @param {!Event} evt Change status event.
|
||||
* @param {!Object} editor Editor state.
|
||||
*/
|
||||
var onChangeStatus = function(evt, editor) {
|
||||
if (!isVimMode()) {
|
||||
return;
|
||||
}
|
||||
var state = editor.keyBinding.$data.state;
|
||||
if (state === vimState) {
|
||||
/* State hasn't changed, do nothing. */
|
||||
return;
|
||||
}
|
||||
switch (state) {
|
||||
case INSERT_MODE_STATE:
|
||||
cvox.Api.playEarcon(MODE_SWITCH_EARCON);
|
||||
/* When in insert mode, we want to speak out keys as feedback. */
|
||||
cvox.Api.setKeyEcho(true);
|
||||
break;
|
||||
case COMMAND_MODE_STATE:
|
||||
cvox.Api.playEarcon(MODE_SWITCH_EARCON);
|
||||
/* When in command mode, we want don't speak out keys because those keys
|
||||
* are not being inserted in the document. */
|
||||
cvox.Api.setKeyEcho(false);
|
||||
break;
|
||||
}
|
||||
vimState = state;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles context menu events. This is a ChromeVox feature where hitting
|
||||
* the shortcut ChromeVox + comma will open up a search bar where you can
|
||||
* type in various commands. All keyboard shortcuts are also commands that
|
||||
* can be invoked. This handles the event that ChromeVox sends to the page.
|
||||
* @param {Event} evt Event received.
|
||||
*/
|
||||
var contextMenuHandler = function(evt) {
|
||||
var cmd = evt.detail['customCommand'];
|
||||
var shortcut = cmdToShortcutMap[cmd];
|
||||
if (shortcut) {
|
||||
shortcut.func();
|
||||
/* ChromeVox will bring focus to an element near the cursor instead of the
|
||||
* text input. */
|
||||
cvoxAce.editor.focus();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the ChromeVox context menu.
|
||||
*/
|
||||
var initContextMenu = function() {
|
||||
var ACTIONS = SHORTCUTS.map(function(shortcut) {
|
||||
return {
|
||||
desc: shortcut.desc + getKeyShortcutString(shortcut.keyCode),
|
||||
cmd: shortcut.cmd
|
||||
};
|
||||
});
|
||||
|
||||
/* Attach ContextMenuActions. */
|
||||
var body = document.querySelector('body');
|
||||
body.setAttribute('contextMenuActions', JSON.stringify(ACTIONS));
|
||||
|
||||
/* Listen for ContextMenu events. */
|
||||
body.addEventListener('ATCustomEvent', contextMenuHandler, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Event handler for find events. When there is a match, we want to speak the
|
||||
* line we are now at. Otherwise, we want to notify the user there was no
|
||||
* match
|
||||
* @param {!Event} evt The event.
|
||||
*/
|
||||
var onFindSearchbox = function(evt) {
|
||||
if (evt.match) {
|
||||
/* There is still a match! Speak the line. */
|
||||
speakLine(lastCursor.row, 0);
|
||||
} else {
|
||||
/* No match, give auditory feedback! */
|
||||
cvox.Api.playEarcon(NO_MATCH_EARCON);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Focus to text input.
|
||||
*/
|
||||
var focus = function() {
|
||||
cvoxAce.editor.focus();
|
||||
};
|
||||
|
||||
/**
|
||||
* Shortcut definitions.
|
||||
*/
|
||||
var SHORTCUTS = [
|
||||
{
|
||||
/* 1 key. */
|
||||
keyCode: 49,
|
||||
func: function() {
|
||||
speakAnnotsByRow(lastCursor.row);
|
||||
},
|
||||
cmd: Command.SPEAK_ANNOT,
|
||||
desc: 'Speak annotations on line'
|
||||
},
|
||||
{
|
||||
/* 2 key. */
|
||||
keyCode: 50,
|
||||
func: speakAllAnnots,
|
||||
cmd: Command.SPEAK_ALL_ANNOTS,
|
||||
desc: 'Speak all annotations'
|
||||
},
|
||||
{
|
||||
/* 3 key. */
|
||||
keyCode: 51,
|
||||
func: speakMode,
|
||||
cmd: Command.SPEAK_MODE,
|
||||
desc: 'Speak Vim mode'
|
||||
},
|
||||
{
|
||||
/* 4 key. */
|
||||
keyCode: 52,
|
||||
func: toggleSpeakRowLocation,
|
||||
cmd: Command.TOGGLE_LOCATION,
|
||||
desc: 'Toggle speak row location'
|
||||
},
|
||||
{
|
||||
/* 5 key. */
|
||||
keyCode: 53,
|
||||
func: speakCurrRowAndCol,
|
||||
cmd: Command.SPEAK_ROW_COL,
|
||||
desc: 'Speak row and column'
|
||||
},
|
||||
{
|
||||
/* 6 key. */
|
||||
keyCode: 54,
|
||||
func: toggleSpeakDisplacement,
|
||||
cmd: Command.TOGGLE_DISPLACEMENT,
|
||||
desc: 'Toggle speak displacement'
|
||||
},
|
||||
{
|
||||
/* 7 key. */
|
||||
keyCode: 55,
|
||||
func: focus,
|
||||
cmd: Command.FOCUS_TEXT,
|
||||
desc: 'Focus text'
|
||||
}
|
||||
];
|
||||
|
||||
/**
|
||||
* Event handler for focus events.
|
||||
*/
|
||||
var onFocus = function() {
|
||||
cvoxAce.editor = editor;
|
||||
|
||||
/* Set up listeners. */
|
||||
editor.getSession().selection.on('changeCursor', onCursorChange);
|
||||
editor.getSession().selection.on('changeSelection', onSelectionChange);
|
||||
editor.getSession().on('change', onChange);
|
||||
editor.getSession().on('changeAnnotation', onAnnotationChange);
|
||||
editor.on('changeStatus', onChangeStatus);
|
||||
editor.on('findSearchBox', onFindSearchbox);
|
||||
editor.container.addEventListener('keydown', onKeyDown);
|
||||
|
||||
lastCursor = editor.selection.getCursor();
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize the theme.
|
||||
* @param {Object} editor Editor to use.
|
||||
*/
|
||||
var init = function(editor) {
|
||||
onFocus();
|
||||
|
||||
/* Construct maps. */
|
||||
SHORTCUTS.forEach(function(shortcut) {
|
||||
keyCodeToShortcutMap[shortcut.keyCode] = shortcut;
|
||||
cmdToShortcutMap[shortcut.cmd] = shortcut;
|
||||
});
|
||||
|
||||
editor.on('focus', onFocus);
|
||||
|
||||
/* Assume we start in command mode if vim. */
|
||||
if (isVimMode()) {
|
||||
cvox.Api.setKeyEcho(false);
|
||||
}
|
||||
initContextMenu();
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns if cvox exists, and the api exists.
|
||||
* @return {boolean} Whether not Cvox Api exists.
|
||||
*/
|
||||
function cvoxApiExists() {
|
||||
return (typeof(cvox) !== 'undefined') && cvox && cvox.Api;
|
||||
}
|
||||
|
||||
/**
|
||||
* Number of tries for Cvox loading.
|
||||
* @type {number}
|
||||
*/
|
||||
var tries = 0;
|
||||
|
||||
/**
|
||||
* Max number of tries to watch for Cvox loading.
|
||||
* @type {number}
|
||||
*/
|
||||
var MAX_TRIES = 15;
|
||||
|
||||
/**
|
||||
* Check for ChromeVox load.
|
||||
* @param {Object} editor Editor to use.
|
||||
*/
|
||||
function watchForCvoxLoad(editor) {
|
||||
if (cvoxApiExists()) {
|
||||
init(editor);
|
||||
} else {
|
||||
tries++;
|
||||
if (tries >= MAX_TRIES) {
|
||||
return;
|
||||
}
|
||||
window.setTimeout(watchForCvoxLoad, 500, editor);
|
||||
}
|
||||
}
|
||||
|
||||
var Editor = require('../editor').Editor;
|
||||
require('../config').defineOptions(Editor.prototype, 'editor', {
|
||||
enableChromevoxEnhancements: {
|
||||
set: function(val) {
|
||||
if (val) {
|
||||
watchForCvoxLoad(this);
|
||||
}
|
||||
},
|
||||
value: true // turn it on by default or check for window.cvox
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,319 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2012, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var ElasticTabstopsLite = function(editor) {
|
||||
this.$editor = editor;
|
||||
var self = this;
|
||||
var changedRows = [];
|
||||
var recordChanges = false;
|
||||
this.onAfterExec = function() {
|
||||
recordChanges = false;
|
||||
self.processRows(changedRows);
|
||||
changedRows = [];
|
||||
};
|
||||
this.onExec = function() {
|
||||
recordChanges = true;
|
||||
};
|
||||
this.onChange = function(e) {
|
||||
var range = e.data.range
|
||||
if (recordChanges) {
|
||||
if (changedRows.indexOf(range.start.row) == -1)
|
||||
changedRows.push(range.start.row);
|
||||
if (range.end.row != range.start.row)
|
||||
changedRows.push(range.end.row);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
(function() {
|
||||
this.processRows = function(rows) {
|
||||
this.$inChange = true;
|
||||
var checkedRows = [];
|
||||
|
||||
for (var r = 0, rowCount = rows.length; r < rowCount; r++) {
|
||||
var row = rows[r];
|
||||
|
||||
if (checkedRows.indexOf(row) > -1)
|
||||
continue;
|
||||
|
||||
var cellWidthObj = this.$findCellWidthsForBlock(row);
|
||||
var cellWidths = this.$setBlockCellWidthsToMax(cellWidthObj.cellWidths);
|
||||
var rowIndex = cellWidthObj.firstRow;
|
||||
|
||||
for (var w = 0, l = cellWidths.length; w < l; w++) {
|
||||
var widths = cellWidths[w];
|
||||
checkedRows.push(rowIndex);
|
||||
this.$adjustRow(rowIndex, widths);
|
||||
rowIndex++;
|
||||
}
|
||||
}
|
||||
this.$inChange = false;
|
||||
};
|
||||
|
||||
this.$findCellWidthsForBlock = function(row) {
|
||||
var cellWidths = [], widths;
|
||||
|
||||
// starting row and backward
|
||||
var rowIter = row;
|
||||
while (rowIter >= 0) {
|
||||
widths = this.$cellWidthsForRow(rowIter);
|
||||
if (widths.length == 0)
|
||||
break;
|
||||
|
||||
cellWidths.unshift(widths);
|
||||
rowIter--;
|
||||
}
|
||||
var firstRow = rowIter + 1;
|
||||
|
||||
// forward (not including starting row)
|
||||
rowIter = row;
|
||||
var numRows = this.$editor.session.getLength();
|
||||
|
||||
while (rowIter < numRows - 1) {
|
||||
rowIter++;
|
||||
|
||||
widths = this.$cellWidthsForRow(rowIter);
|
||||
if (widths.length == 0)
|
||||
break;
|
||||
|
||||
cellWidths.push(widths);
|
||||
}
|
||||
|
||||
return { cellWidths: cellWidths, firstRow: firstRow };
|
||||
};
|
||||
|
||||
this.$cellWidthsForRow = function(row) {
|
||||
var selectionColumns = this.$selectionColumnsForRow(row);
|
||||
// todo: support multicursor
|
||||
|
||||
var tabs = [-1].concat(this.$tabsForRow(row));
|
||||
var widths = tabs.map(function(el) { return 0; } ).slice(1);
|
||||
var line = this.$editor.session.getLine(row);
|
||||
|
||||
for (var i = 0, len = tabs.length - 1; i < len; i++) {
|
||||
var leftEdge = tabs[i]+1;
|
||||
var rightEdge = tabs[i+1];
|
||||
|
||||
var rightmostSelection = this.$rightmostSelectionInCell(selectionColumns, rightEdge);
|
||||
var cell = line.substring(leftEdge, rightEdge);
|
||||
widths[i] = Math.max(cell.replace(/\s+$/g,'').length, rightmostSelection - leftEdge);
|
||||
}
|
||||
|
||||
return widths;
|
||||
};
|
||||
|
||||
this.$selectionColumnsForRow = function(row) {
|
||||
var selections = [], cursor = this.$editor.getCursorPosition();
|
||||
if (this.$editor.session.getSelection().isEmpty()) {
|
||||
// todo: support multicursor
|
||||
if (row == cursor.row)
|
||||
selections.push(cursor.column);
|
||||
}
|
||||
|
||||
return selections;
|
||||
};
|
||||
|
||||
this.$setBlockCellWidthsToMax = function(cellWidths) {
|
||||
var startingNewBlock = true, blockStartRow, blockEndRow, maxWidth;
|
||||
var columnInfo = this.$izip_longest(cellWidths);
|
||||
|
||||
for (var c = 0, l = columnInfo.length; c < l; c++) {
|
||||
var column = columnInfo[c];
|
||||
if (!column.push) {
|
||||
console.error(column);
|
||||
continue;
|
||||
}
|
||||
// add an extra None to the end so that the end of the column automatically
|
||||
// finishes a block
|
||||
column.push(NaN);
|
||||
|
||||
for (var r = 0, s = column.length; r < s; r++) {
|
||||
var width = column[r];
|
||||
if (startingNewBlock) {
|
||||
blockStartRow = r;
|
||||
maxWidth = 0;
|
||||
startingNewBlock = false;
|
||||
}
|
||||
if (isNaN(width)) {
|
||||
// block ended
|
||||
blockEndRow = r;
|
||||
|
||||
for (var j = blockStartRow; j < blockEndRow; j++) {
|
||||
cellWidths[j][c] = maxWidth;
|
||||
}
|
||||
startingNewBlock = true;
|
||||
}
|
||||
|
||||
maxWidth = Math.max(maxWidth, width);
|
||||
}
|
||||
}
|
||||
|
||||
return cellWidths;
|
||||
};
|
||||
|
||||
this.$rightmostSelectionInCell = function(selectionColumns, cellRightEdge) {
|
||||
var rightmost = 0;
|
||||
|
||||
if (selectionColumns.length) {
|
||||
var lengths = [];
|
||||
for (var s = 0, length = selectionColumns.length; s < length; s++) {
|
||||
if (selectionColumns[s] <= cellRightEdge)
|
||||
lengths.push(s);
|
||||
else
|
||||
lengths.push(0);
|
||||
}
|
||||
rightmost = Math.max.apply(Math, lengths);
|
||||
}
|
||||
|
||||
return rightmost;
|
||||
};
|
||||
|
||||
this.$tabsForRow = function(row) {
|
||||
var rowTabs = [], line = this.$editor.session.getLine(row),
|
||||
re = /\t/g, match;
|
||||
|
||||
while ((match = re.exec(line)) != null) {
|
||||
rowTabs.push(match.index);
|
||||
}
|
||||
|
||||
return rowTabs;
|
||||
};
|
||||
|
||||
this.$adjustRow = function(row, widths) {
|
||||
var rowTabs = this.$tabsForRow(row);
|
||||
|
||||
if (rowTabs.length == 0)
|
||||
return;
|
||||
|
||||
var bias = 0, location = -1;
|
||||
|
||||
// this always only contains two elements, so we're safe in the loop below
|
||||
var expandedSet = this.$izip(widths, rowTabs);
|
||||
|
||||
for (var i = 0, l = expandedSet.length; i < l; i++) {
|
||||
var w = expandedSet[i][0], it = expandedSet[i][1];
|
||||
location += 1 + w;
|
||||
it += bias;
|
||||
var difference = location - it;
|
||||
|
||||
if (difference == 0)
|
||||
continue;
|
||||
|
||||
var partialLine = this.$editor.session.getLine(row).substr(0, it );
|
||||
var strippedPartialLine = partialLine.replace(/\s*$/g, "");
|
||||
var ispaces = partialLine.length - strippedPartialLine.length;
|
||||
|
||||
if (difference > 0) {
|
||||
// put the spaces after the tab and then delete the tab, so any insertion
|
||||
// points behave as expected
|
||||
this.$editor.session.getDocument().insertInLine({row: row, column: it + 1}, Array(difference + 1).join(" ") + "\t");
|
||||
this.$editor.session.getDocument().removeInLine(row, it, it + 1);
|
||||
|
||||
bias += difference;
|
||||
}
|
||||
|
||||
if (difference < 0 && ispaces >= -difference) {
|
||||
this.$editor.session.getDocument().removeInLine(row, it + difference, it);
|
||||
bias += difference;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// the is a (naive) Python port--but works for these purposes
|
||||
this.$izip_longest = function(iterables) {
|
||||
if (!iterables[0])
|
||||
return [];
|
||||
var longest = iterables[0].length;
|
||||
var iterablesLength = iterables.length;
|
||||
|
||||
for (var i = 1; i < iterablesLength; i++) {
|
||||
var iLength = iterables[i].length;
|
||||
if (iLength > longest)
|
||||
longest = iLength;
|
||||
}
|
||||
|
||||
var expandedSet = [];
|
||||
|
||||
for (var l = 0; l < longest; l++) {
|
||||
var set = [];
|
||||
for (var i = 0; i < iterablesLength; i++) {
|
||||
if (iterables[i][l] === "")
|
||||
set.push(NaN);
|
||||
else
|
||||
set.push(iterables[i][l]);
|
||||
}
|
||||
|
||||
expandedSet.push(set);
|
||||
}
|
||||
|
||||
|
||||
return expandedSet;
|
||||
};
|
||||
|
||||
// an even more (naive) Python port
|
||||
this.$izip = function(widths, tabs) {
|
||||
// grab the shorter size
|
||||
var size = widths.length >= tabs.length ? tabs.length : widths.length;
|
||||
|
||||
var expandedSet = [];
|
||||
for (var i = 0; i < size; i++) {
|
||||
var set = [ widths[i], tabs[i] ];
|
||||
expandedSet.push(set);
|
||||
}
|
||||
return expandedSet;
|
||||
};
|
||||
|
||||
}).call(ElasticTabstopsLite.prototype);
|
||||
|
||||
exports.ElasticTabstopsLite = ElasticTabstopsLite;
|
||||
|
||||
var Editor = require("../editor").Editor;
|
||||
require("../config").defineOptions(Editor.prototype, "editor", {
|
||||
useElasticTabstops: {
|
||||
set: function(val) {
|
||||
if (val) {
|
||||
if (!this.elasticTabstops)
|
||||
this.elasticTabstops = new ElasticTabstopsLite(this);
|
||||
this.commands.on("afterExec", this.elasticTabstops.onAfterExec);
|
||||
this.commands.on("exec", this.elasticTabstops.onExec);
|
||||
this.on("change", this.elasticTabstops.onChange);
|
||||
} else if (this.elasticTabstops) {
|
||||
this.commands.removeListener("afterExec", this.elasticTabstops.onAfterExec);
|
||||
this.commands.removeListener("exec", this.elasticTabstops.onExec);
|
||||
this.removeListener("change", this.elasticTabstops.onChange);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,434 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
var HashHandler = require("ace/keyboard/hash_handler").HashHandler;
|
||||
var Editor = require("ace/editor").Editor;
|
||||
var snippetManager = require("ace/snippets").snippetManager;
|
||||
var Range = require("ace/range").Range;
|
||||
var emmet, emmetPath;
|
||||
|
||||
/**
|
||||
* Implementation of {@link IEmmetEditor} interface for Ace
|
||||
*/
|
||||
function AceEmmetEditor() {}
|
||||
|
||||
AceEmmetEditor.prototype = {
|
||||
setupContext: function(editor) {
|
||||
this.ace = editor;
|
||||
this.indentation = editor.session.getTabString();
|
||||
if (!emmet)
|
||||
emmet = window.emmet;
|
||||
emmet.require("resources").setVariable("indentation", this.indentation);
|
||||
this.$syntax = null;
|
||||
this.$syntax = this.getSyntax();
|
||||
},
|
||||
/**
|
||||
* Returns character indexes of selected text: object with <code>start</code>
|
||||
* and <code>end</code> properties. If there's no selection, should return
|
||||
* object with <code>start</code> and <code>end</code> properties referring
|
||||
* to current caret position
|
||||
* @return {Object}
|
||||
* @example
|
||||
* var selection = editor.getSelectionRange();
|
||||
* alert(selection.start + ', ' + selection.end);
|
||||
*/
|
||||
getSelectionRange: function() {
|
||||
// TODO should start be caret position instead?
|
||||
var range = this.ace.getSelectionRange();
|
||||
var doc = this.ace.session.doc;
|
||||
return {
|
||||
start: doc.positionToIndex(range.start),
|
||||
end: doc.positionToIndex(range.end)
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Creates selection from <code>start</code> to <code>end</code> character
|
||||
* indexes. If <code>end</code> is ommited, this method should place caret
|
||||
* and <code>start</code> index
|
||||
* @param {Number} start
|
||||
* @param {Number} [end]
|
||||
* @example
|
||||
* editor.createSelection(10, 40);
|
||||
*
|
||||
* //move caret to 15th character
|
||||
* editor.createSelection(15);
|
||||
*/
|
||||
createSelection: function(start, end) {
|
||||
var doc = this.ace.session.doc;
|
||||
this.ace.selection.setRange({
|
||||
start: doc.indexToPosition(start),
|
||||
end: doc.indexToPosition(end)
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns current line's start and end indexes as object with <code>start</code>
|
||||
* and <code>end</code> properties
|
||||
* @return {Object}
|
||||
* @example
|
||||
* var range = editor.getCurrentLineRange();
|
||||
* alert(range.start + ', ' + range.end);
|
||||
*/
|
||||
getCurrentLineRange: function() {
|
||||
var ace = this.ace;
|
||||
var row = ace.getCursorPosition().row;
|
||||
var lineLength = ace.session.getLine(row).length;
|
||||
var index = ace.session.doc.positionToIndex({row: row, column: 0});
|
||||
return {
|
||||
start: index,
|
||||
end: index + lineLength
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns current caret position
|
||||
* @return {Number|null}
|
||||
*/
|
||||
getCaretPos: function(){
|
||||
var pos = this.ace.getCursorPosition();
|
||||
return this.ace.session.doc.positionToIndex(pos);
|
||||
},
|
||||
|
||||
/**
|
||||
* Set new caret position
|
||||
* @param {Number} index Caret position
|
||||
*/
|
||||
setCaretPos: function(index){
|
||||
var pos = this.ace.session.doc.indexToPosition(index);
|
||||
this.ace.selection.moveToPosition(pos);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns content of current line
|
||||
* @return {String}
|
||||
*/
|
||||
getCurrentLine: function() {
|
||||
var row = this.ace.getCursorPosition().row;
|
||||
return this.ace.session.getLine(row);
|
||||
},
|
||||
|
||||
/**
|
||||
* Replace editor's content or it's part (from <code>start</code> to
|
||||
* <code>end</code> index). If <code>value</code> contains
|
||||
* <code>caret_placeholder</code>, the editor will put caret into
|
||||
* this position. If you skip <code>start</code> and <code>end</code>
|
||||
* arguments, the whole target's content will be replaced with
|
||||
* <code>value</code>.
|
||||
*
|
||||
* If you pass <code>start</code> argument only,
|
||||
* the <code>value</code> will be placed at <code>start</code> string
|
||||
* index of current content.
|
||||
*
|
||||
* If you pass <code>start</code> and <code>end</code> arguments,
|
||||
* the corresponding substring of current target's content will be
|
||||
* replaced with <code>value</code>.
|
||||
* @param {String} value Content you want to paste
|
||||
* @param {Number} [start] Start index of editor's content
|
||||
* @param {Number} [end] End index of editor's content
|
||||
* @param {Boolean} [noIndent] Do not auto indent <code>value</code>
|
||||
*/
|
||||
replaceContent: function(value, start, end, noIndent) {
|
||||
if (end == null)
|
||||
end = start == null ? this.getContent().length : start;
|
||||
if (start == null)
|
||||
start = 0;
|
||||
|
||||
var editor = this.ace;
|
||||
var doc = editor.session.doc;
|
||||
var range = Range.fromPoints(doc.indexToPosition(start), doc.indexToPosition(end));
|
||||
editor.session.remove(range);
|
||||
|
||||
range.end = range.start;
|
||||
//editor.selection.setRange(range);
|
||||
|
||||
value = this.$updateTabstops(value);
|
||||
snippetManager.insertSnippet(editor, value);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns editor's content
|
||||
* @return {String}
|
||||
*/
|
||||
getContent: function(){
|
||||
return this.ace.getValue();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns current editor's syntax mode
|
||||
* @return {String}
|
||||
*/
|
||||
getSyntax: function() {
|
||||
if (this.$syntax)
|
||||
return this.$syntax;
|
||||
var syntax = this.ace.session.$modeId.split("/").pop();
|
||||
if (syntax == "html" || syntax == "php") {
|
||||
var cursor = this.ace.getCursorPosition();
|
||||
var state = this.ace.session.getState(cursor.row);
|
||||
if (typeof state != "string")
|
||||
state = state[0];
|
||||
if (state) {
|
||||
state = state.split("-");
|
||||
if (state.length > 1)
|
||||
syntax = state[0];
|
||||
else if (syntax == "php")
|
||||
syntax = "html";
|
||||
}
|
||||
}
|
||||
return syntax;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns current output profile name (@see emmet#setupProfile)
|
||||
* @return {String}
|
||||
*/
|
||||
getProfileName: function() {
|
||||
switch(this.getSyntax()) {
|
||||
case "css": return "css";
|
||||
case "xml":
|
||||
case "xsl":
|
||||
return "xml";
|
||||
case "html":
|
||||
var profile = emmet.require("resources").getVariable("profile");
|
||||
// no forced profile, guess from content html or xhtml?
|
||||
if (!profile)
|
||||
profile = this.ace.session.getLines(0,2).join("").search(/<!DOCTYPE[^>]+XHTML/i) != -1 ? "xhtml": "html";
|
||||
return profile;
|
||||
}
|
||||
return "xhtml";
|
||||
},
|
||||
|
||||
/**
|
||||
* Ask user to enter something
|
||||
* @param {String} title Dialog title
|
||||
* @return {String} Entered data
|
||||
* @since 0.65
|
||||
*/
|
||||
prompt: function(title) {
|
||||
return prompt(title);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns current selection
|
||||
* @return {String}
|
||||
* @since 0.65
|
||||
*/
|
||||
getSelection: function() {
|
||||
return this.ace.session.getTextRange();
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns current editor's file path
|
||||
* @return {String}
|
||||
* @since 0.65
|
||||
*/
|
||||
getFilePath: function() {
|
||||
return "";
|
||||
},
|
||||
|
||||
// update tabstops: make sure all caret placeholders are unique
|
||||
// by default, abbreviation parser generates all unlinked (un-mirrored)
|
||||
// tabstops as ${0}, so we have upgrade all caret tabstops with unique
|
||||
// positions but make sure that all other tabstops are not linked accidentally
|
||||
// based on https://github.com/sergeche/emmet-sublime/blob/master/editor.js#L119-L171
|
||||
$updateTabstops: function(value) {
|
||||
var base = 1000;
|
||||
var zeroBase = 0;
|
||||
var lastZero = null;
|
||||
var range = emmet.require('range');
|
||||
var ts = emmet.require('tabStops');
|
||||
var settings = emmet.require('resources').getVocabulary("user");
|
||||
var tabstopOptions = {
|
||||
tabstop: function(data) {
|
||||
var group = parseInt(data.group, 10);
|
||||
var isZero = group === 0;
|
||||
if (isZero)
|
||||
group = ++zeroBase;
|
||||
else
|
||||
group += base;
|
||||
|
||||
var placeholder = data.placeholder;
|
||||
if (placeholder) {
|
||||
// recursively update nested tabstops
|
||||
placeholder = ts.processText(placeholder, tabstopOptions);
|
||||
}
|
||||
|
||||
var result = '${' + group + (placeholder ? ':' + placeholder : '') + '}';
|
||||
|
||||
if (isZero) {
|
||||
lastZero = range.create(data.start, result);
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
escape: function(ch) {
|
||||
if (ch == '$') return '\\$';
|
||||
if (ch == '\\') return '\\\\';
|
||||
return ch;
|
||||
}
|
||||
};
|
||||
|
||||
value = ts.processText(value, tabstopOptions);
|
||||
|
||||
if (settings.variables['insert_final_tabstop'] && !/\$\{0\}$/.test(value)) {
|
||||
value += '${0}';
|
||||
} else if (lastZero) {
|
||||
value = emmet.require('utils').replaceSubstring(value, '${0}', lastZero);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var keymap = {
|
||||
expand_abbreviation: {"mac": "ctrl+alt+e", "win": "alt+e"},
|
||||
match_pair_outward: {"mac": "ctrl+d", "win": "ctrl+,"},
|
||||
match_pair_inward: {"mac": "ctrl+j", "win": "ctrl+shift+0"},
|
||||
matching_pair: {"mac": "ctrl+alt+j", "win": "alt+j"},
|
||||
next_edit_point: "alt+right",
|
||||
prev_edit_point: "alt+left",
|
||||
toggle_comment: {"mac": "command+/", "win": "ctrl+/"},
|
||||
split_join_tag: {"mac": "shift+command+'", "win": "shift+ctrl+`"},
|
||||
remove_tag: {"mac": "command+'", "win": "shift+ctrl+;"},
|
||||
evaluate_math_expression: {"mac": "shift+command+y", "win": "shift+ctrl+y"},
|
||||
increment_number_by_1: "ctrl+up",
|
||||
decrement_number_by_1: "ctrl+down",
|
||||
increment_number_by_01: "alt+up",
|
||||
decrement_number_by_01: "alt+down",
|
||||
increment_number_by_10: {"mac": "alt+command+up", "win": "shift+alt+up"},
|
||||
decrement_number_by_10: {"mac": "alt+command+down", "win": "shift+alt+down"},
|
||||
select_next_item: {"mac": "shift+command+.", "win": "shift+ctrl+."},
|
||||
select_previous_item: {"mac": "shift+command+,", "win": "shift+ctrl+,"},
|
||||
reflect_css_value: {"mac": "shift+command+r", "win": "shift+ctrl+r"},
|
||||
|
||||
encode_decode_data_url: {"mac": "shift+ctrl+d", "win": "ctrl+'"},
|
||||
// update_image_size: {"mac": "shift+ctrl+i", "win": "ctrl+u"},
|
||||
// expand_as_you_type: "ctrl+alt+enter",
|
||||
// wrap_as_you_type: {"mac": "shift+ctrl+g", "win": "shift+ctrl+g"},
|
||||
expand_abbreviation_with_tab: "Tab",
|
||||
wrap_with_abbreviation: {"mac": "shift+ctrl+a", "win": "shift+ctrl+a"}
|
||||
};
|
||||
|
||||
var editorProxy = new AceEmmetEditor();
|
||||
exports.commands = new HashHandler();
|
||||
exports.runEmmetCommand = function(editor) {
|
||||
try {
|
||||
editorProxy.setupContext(editor);
|
||||
if (editorProxy.getSyntax() == "php")
|
||||
return false;
|
||||
var actions = emmet.require("actions");
|
||||
|
||||
if (this.action == "expand_abbreviation_with_tab") {
|
||||
if (!editor.selection.isEmpty())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.action == "wrap_with_abbreviation") {
|
||||
// without setTimeout prompt doesn't work on firefox
|
||||
return setTimeout(function() {
|
||||
actions.run("wrap_with_abbreviation", editorProxy);
|
||||
}, 0);
|
||||
}
|
||||
|
||||
var pos = editor.selection.lead;
|
||||
var token = editor.session.getTokenAt(pos.row, pos.column);
|
||||
if (token && /\btag\b/.test(token.type))
|
||||
return false;
|
||||
|
||||
var result = actions.run(this.action, editorProxy);
|
||||
} catch(e) {
|
||||
editor._signal("changeStatus", typeof e == "string" ? e : e.message);
|
||||
console.log(e);
|
||||
result = false;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
for (var command in keymap) {
|
||||
exports.commands.addCommand({
|
||||
name: "emmet:" + command,
|
||||
action: command,
|
||||
bindKey: keymap[command],
|
||||
exec: exports.runEmmetCommand,
|
||||
multiSelectAction: "forEach"
|
||||
});
|
||||
}
|
||||
|
||||
exports.updateCommands = function(editor, enabled) {
|
||||
if (enabled) {
|
||||
editor.keyBinding.addKeyboardHandler(exports.commands);
|
||||
} else {
|
||||
editor.keyBinding.removeKeyboardHandler(exports.commands);
|
||||
}
|
||||
};
|
||||
|
||||
exports.isSupportedMode = function(modeId) {
|
||||
return modeId && /css|less|scss|sass|stylus|html|php|twig|ejs/.test(modeId);
|
||||
};
|
||||
|
||||
var onChangeMode = function(e, target) {
|
||||
var editor = target;
|
||||
if (!editor)
|
||||
return;
|
||||
var enabled = exports.isSupportedMode(editor.session.$modeId);
|
||||
if (e.enableEmmet === false)
|
||||
enabled = false;
|
||||
if (enabled) {
|
||||
if (typeof emmetPath == "string") {
|
||||
require("ace/config").loadModule(emmetPath, function() {
|
||||
emmetPath = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
exports.updateCommands(editor, enabled);
|
||||
};
|
||||
|
||||
exports.AceEmmetEditor = AceEmmetEditor;
|
||||
require("ace/config").defineOptions(Editor.prototype, "editor", {
|
||||
enableEmmet: {
|
||||
set: function(val) {
|
||||
this[val ? "on" : "removeListener"]("changeMode", onChangeMode);
|
||||
onChangeMode({enableEmmet: !!val}, this);
|
||||
},
|
||||
value: true
|
||||
}
|
||||
});
|
||||
|
||||
exports.setCore = function(e) {
|
||||
if (typeof e == "string")
|
||||
emmetPath = e;
|
||||
else
|
||||
emmet = e;
|
||||
};
|
||||
});
|
||||
|
||||
@@ -0,0 +1,214 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2012, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
var LineWidgets = require("../line_widgets").LineWidgets;
|
||||
var dom = require("../lib/dom");
|
||||
var Range = require("../range").Range;
|
||||
|
||||
function binarySearch(array, needle, comparator) {
|
||||
var first = 0;
|
||||
var last = array.length - 1;
|
||||
|
||||
while (first <= last) {
|
||||
var mid = (first + last) >> 1;
|
||||
var c = comparator(needle, array[mid]);
|
||||
if (c > 0)
|
||||
first = mid + 1;
|
||||
else if (c < 0)
|
||||
last = mid - 1;
|
||||
else
|
||||
return mid;
|
||||
}
|
||||
|
||||
// Return the nearest lesser index, "-1" means "0, "-2" means "1", etc.
|
||||
return -(first + 1);
|
||||
}
|
||||
|
||||
function findAnnotations(session, row, dir) {
|
||||
var annotations = session.getAnnotations().sort(Range.comparePoints);
|
||||
if (!annotations.length)
|
||||
return;
|
||||
|
||||
var i = binarySearch(annotations, {row: row, column: -1}, Range.comparePoints);
|
||||
if (i < 0)
|
||||
i = -i - 1;
|
||||
|
||||
if (i >= annotations.length - 1)
|
||||
i = dir > 0 ? 0 : annotations.length - 1;
|
||||
else if (i === 0 && dir < 0)
|
||||
i = annotations.length - 1;
|
||||
|
||||
var annotation = annotations[i];
|
||||
if (!annotation || !dir)
|
||||
return;
|
||||
|
||||
if (annotation.row === row) {
|
||||
do {
|
||||
annotation = annotations[i += dir];
|
||||
} while (annotation && annotation.row === row);
|
||||
if (!annotation)
|
||||
return annotations.slice();
|
||||
}
|
||||
|
||||
|
||||
var matched = [];
|
||||
row = annotation.row;
|
||||
do {
|
||||
matched[dir < 0 ? "unshift" : "push"](annotation);
|
||||
annotation = annotations[i += dir];
|
||||
} while (annotation && annotation.row == row);
|
||||
return matched.length && matched;
|
||||
}
|
||||
|
||||
exports.showErrorMarker = function(editor, dir) {
|
||||
var session = editor.session;
|
||||
if (!session.widgetManager) {
|
||||
session.widgetManager = new LineWidgets(session);
|
||||
session.widgetManager.attach(editor);
|
||||
}
|
||||
|
||||
var pos = editor.getCursorPosition();
|
||||
var row = pos.row;
|
||||
var oldWidget = session.lineWidgets && session.lineWidgets[row];
|
||||
if (oldWidget) {
|
||||
oldWidget.destroy();
|
||||
} else {
|
||||
row -= dir;
|
||||
}
|
||||
var annotations = findAnnotations(session, row, dir);
|
||||
var gutterAnno;
|
||||
if (annotations) {
|
||||
var annotation = annotations[0];
|
||||
pos.column = (annotation.pos && typeof annotation.column != "number"
|
||||
? annotation.pos.sc
|
||||
: annotation.column) || 0;
|
||||
pos.row = annotation.row;
|
||||
gutterAnno = editor.renderer.$gutterLayer.$annotations[pos.row];
|
||||
} else if (oldWidget) {
|
||||
return;
|
||||
} else {
|
||||
gutterAnno = {
|
||||
text: ["Looks good!"],
|
||||
className: "ace_ok"
|
||||
};
|
||||
}
|
||||
editor.session.unfold(pos.row);
|
||||
editor.selection.moveToPosition(pos);
|
||||
|
||||
var w = {
|
||||
row: pos.row,
|
||||
fixedWidth: true,
|
||||
coverGutter: true,
|
||||
el: dom.createElement("div")
|
||||
};
|
||||
var el = w.el.appendChild(dom.createElement("div"));
|
||||
var arrow = w.el.appendChild(dom.createElement("div"));
|
||||
arrow.className = "error_widget_arrow " + gutterAnno.className;
|
||||
|
||||
var left = editor.renderer.$cursorLayer
|
||||
.getPixelPosition(pos).left;
|
||||
arrow.style.left = left + editor.renderer.gutterWidth - 5 + "px";
|
||||
|
||||
w.el.className = "error_widget_wrapper";
|
||||
el.className = "error_widget " + gutterAnno.className;
|
||||
el.innerHTML = gutterAnno.text.join("<br>");
|
||||
|
||||
el.appendChild(dom.createElement("div"));
|
||||
|
||||
var kb = function(_, hashId, keyString) {
|
||||
if (hashId === 0 && (keyString === "esc" || keyString === "return")) {
|
||||
w.destroy();
|
||||
return {command: "null"};
|
||||
}
|
||||
};
|
||||
|
||||
w.destroy = function() {
|
||||
if (editor.$mouseHandler.isMousePressed)
|
||||
return;
|
||||
editor.keyBinding.removeKeyboardHandler(kb);
|
||||
session.widgetManager.removeLineWidget(w);
|
||||
editor.off("changeSelection", w.destroy);
|
||||
editor.off("changeSession", w.destroy);
|
||||
editor.off("mouseup", w.destroy);
|
||||
editor.off("change", w.destroy);
|
||||
};
|
||||
|
||||
editor.keyBinding.addKeyboardHandler(kb);
|
||||
editor.on("changeSelection", w.destroy);
|
||||
editor.on("changeSession", w.destroy);
|
||||
editor.on("mouseup", w.destroy);
|
||||
editor.on("change", w.destroy);
|
||||
|
||||
editor.session.widgetManager.addLineWidget(w);
|
||||
|
||||
w.el.onmousedown = editor.focus.bind(editor);
|
||||
|
||||
editor.renderer.scrollCursorIntoView(null, 0.5, {bottom: w.el.offsetHeight});
|
||||
};
|
||||
|
||||
|
||||
dom.importCssString("\
|
||||
.error_widget_wrapper {\
|
||||
background: inherit;\
|
||||
color: inherit;\
|
||||
border:none\
|
||||
}\
|
||||
.error_widget {\
|
||||
border-top: solid 2px;\
|
||||
border-bottom: solid 2px;\
|
||||
margin: 5px 0;\
|
||||
padding: 10px 40px;\
|
||||
white-space: pre-wrap;\
|
||||
}\
|
||||
.error_widget.ace_error, .error_widget_arrow.ace_error{\
|
||||
border-color: #ff5a5a\
|
||||
}\
|
||||
.error_widget.ace_warning, .error_widget_arrow.ace_warning{\
|
||||
border-color: #F1D817\
|
||||
}\
|
||||
.error_widget.ace_info, .error_widget_arrow.ace_info{\
|
||||
border-color: #5a5a5a\
|
||||
}\
|
||||
.error_widget.ace_ok, .error_widget_arrow.ace_ok{\
|
||||
border-color: #5aaa5a\
|
||||
}\
|
||||
.error_widget_arrow {\
|
||||
position: absolute;\
|
||||
border: solid 5px;\
|
||||
border-top-color: transparent!important;\
|
||||
border-right-color: transparent!important;\
|
||||
border-left-color: transparent!important;\
|
||||
top: -5px;\
|
||||
}\
|
||||
", "");
|
||||
|
||||
});
|
||||
@@ -0,0 +1,86 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2013 Matthew Christopher Kastor-Inare III, Atropa Inc. Intl
|
||||
* All rights reserved.
|
||||
*
|
||||
* Contributed to Ajax.org under the BSD license.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/*jslint indent: 4, maxerr: 50, white: true, browser: true, vars: true*/
|
||||
/*global define, require */
|
||||
|
||||
/**
|
||||
* Show Keyboard Shortcuts
|
||||
* @fileOverview Show Keyboard Shortcuts <br />
|
||||
* Generates a menu which displays the keyboard shortcuts.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
*/
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
var Editor = require("ace/editor").Editor;
|
||||
/**
|
||||
* Generates a menu which displays the keyboard shortcuts.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
* @param {ace.Editor} editor An instance of the ace editor.
|
||||
*/
|
||||
function showKeyboardShortcuts (editor) {
|
||||
// make sure the menu isn't open already.
|
||||
if(!document.getElementById('kbshortcutmenu')) {
|
||||
var overlayPage = require('./menu_tools/overlay_page').overlayPage;
|
||||
var getEditorKeybordShortcuts = require('./menu_tools/get_editor_keyboard_shortcuts').getEditorKeybordShortcuts;
|
||||
var kb = getEditorKeybordShortcuts(editor);
|
||||
var el = document.createElement('div');
|
||||
var commands = kb.reduce(function(previous, current) {
|
||||
return previous + '<div class="ace_optionsMenuEntry"><span class="ace_optionsMenuCommand">'
|
||||
+ current.command + '</span> : '
|
||||
+ '<span class="ace_optionsMenuKey">' + current.key + '</span></div>';
|
||||
}, '');
|
||||
|
||||
el.id = 'kbshortcutmenu';
|
||||
el.innerHTML = '<h1>Keyboard Shortcuts</h1>' + commands + '</div>';
|
||||
overlayPage(editor, el, '0', '0', '0', null);
|
||||
}
|
||||
};
|
||||
module.exports.init = function(editor) {
|
||||
Editor.prototype.showKeyboardShortcuts = function() {
|
||||
showKeyboardShortcuts(this);
|
||||
};
|
||||
editor.commands.addCommands([{
|
||||
name: "showKeyboardShortcuts",
|
||||
bindKey: {win: "Ctrl-Alt-h", mac: "Command-Alt-h"},
|
||||
exec: function(editor, line) {
|
||||
editor.showKeyboardShortcuts();
|
||||
}
|
||||
}]);
|
||||
};
|
||||
|
||||
});
|
||||
@@ -0,0 +1,226 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2012, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var snippetManager = require("../snippets").snippetManager;
|
||||
var Autocomplete = require("../autocomplete").Autocomplete;
|
||||
var config = require("../config");
|
||||
var lang = require("../lib/lang");
|
||||
var util = require("../autocomplete/util");
|
||||
|
||||
var textCompleter = require("../autocomplete/text_completer");
|
||||
var keyWordCompleter = {
|
||||
getCompletions: function(editor, session, pos, prefix, callback) {
|
||||
if (session.$mode.completer) {
|
||||
return session.$mode.completer.getCompletions(editor, session, pos, prefix, callback);
|
||||
}
|
||||
var state = editor.session.getState(pos.row);
|
||||
var completions = session.$mode.getCompletions(state, session, pos, prefix);
|
||||
callback(null, completions);
|
||||
}
|
||||
};
|
||||
|
||||
var snippetCompleter = {
|
||||
getCompletions: function(editor, session, pos, prefix, callback) {
|
||||
var snippetMap = snippetManager.snippetMap;
|
||||
var completions = [];
|
||||
snippetManager.getActiveScopes(editor).forEach(function(scope) {
|
||||
var snippets = snippetMap[scope] || [];
|
||||
for (var i = snippets.length; i--;) {
|
||||
var s = snippets[i];
|
||||
var caption = s.name || s.tabTrigger;
|
||||
if (!caption)
|
||||
continue;
|
||||
completions.push({
|
||||
caption: caption,
|
||||
snippet: s.content,
|
||||
meta: s.tabTrigger && !s.name ? s.tabTrigger + "\u21E5 " : "snippet",
|
||||
type: "snippet"
|
||||
});
|
||||
}
|
||||
}, this);
|
||||
callback(null, completions);
|
||||
},
|
||||
getDocTooltip: function(item) {
|
||||
if (item.type == "snippet" && !item.docHTML) {
|
||||
item.docHTML = [
|
||||
"<b>", lang.escapeHTML(item.caption), "</b>", "<hr></hr>",
|
||||
lang.escapeHTML(item.snippet)
|
||||
].join("");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var completers = [snippetCompleter, textCompleter, keyWordCompleter];
|
||||
// Allows default completers to be removed or replaced with a explict set of completers
|
||||
// A null argument here will result in an empty completer array, not a null attribute
|
||||
exports.setCompleters = function(val) {
|
||||
completers = val || [];
|
||||
};
|
||||
exports.addCompleter = function(completer) {
|
||||
completers.push(completer);
|
||||
};
|
||||
|
||||
// Exports existing completer so that user can construct his own set of completers.
|
||||
exports.textCompleter = textCompleter;
|
||||
exports.keyWordCompleter = keyWordCompleter;
|
||||
exports.snippetCompleter = snippetCompleter;
|
||||
|
||||
var expandSnippet = {
|
||||
name: "expandSnippet",
|
||||
exec: function(editor) {
|
||||
return snippetManager.expandWithTab(editor);
|
||||
},
|
||||
bindKey: "Tab"
|
||||
};
|
||||
|
||||
var onChangeMode = function(e, editor) {
|
||||
loadSnippetsForMode(editor.session.$mode);
|
||||
};
|
||||
|
||||
var loadSnippetsForMode = function(mode) {
|
||||
var id = mode.$id;
|
||||
if (!snippetManager.files)
|
||||
snippetManager.files = {};
|
||||
loadSnippetFile(id);
|
||||
if (mode.modes)
|
||||
mode.modes.forEach(loadSnippetsForMode);
|
||||
};
|
||||
|
||||
var loadSnippetFile = function(id) {
|
||||
if (!id || snippetManager.files[id])
|
||||
return;
|
||||
var snippetFilePath = id.replace("mode", "snippets");
|
||||
snippetManager.files[id] = {};
|
||||
config.loadModule(snippetFilePath, function(m) {
|
||||
if (m) {
|
||||
snippetManager.files[id] = m;
|
||||
if (!m.snippets && m.snippetText)
|
||||
m.snippets = snippetManager.parseSnippetFile(m.snippetText);
|
||||
snippetManager.register(m.snippets || [], m.scope);
|
||||
if (m.includeScopes) {
|
||||
snippetManager.snippetMap[m.scope].includeScopes = m.includeScopes;
|
||||
m.includeScopes.forEach(function(x) {
|
||||
loadSnippetFile("ace/mode/" + x);
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function getCompletionPrefix(editor) {
|
||||
var pos = editor.getCursorPosition();
|
||||
var line = editor.session.getLine(pos.row);
|
||||
var prefix;
|
||||
// Try to find custom prefixes on the completers
|
||||
editor.completers.forEach(function(completer) {
|
||||
if (completer.identifierRegexps) {
|
||||
completer.identifierRegexps.forEach(function(identifierRegex) {
|
||||
if (!prefix && identifierRegex)
|
||||
prefix = util.retrievePrecedingIdentifier(line, pos.column, identifierRegex);
|
||||
});
|
||||
}
|
||||
});
|
||||
return prefix || util.retrievePrecedingIdentifier(line, pos.column);
|
||||
}
|
||||
|
||||
var doLiveAutocomplete = function(e) {
|
||||
var editor = e.editor;
|
||||
var text = e.args || "";
|
||||
var hasCompleter = editor.completer && editor.completer.activated;
|
||||
|
||||
// We don't want to autocomplete with no prefix
|
||||
if (e.command.name === "backspace") {
|
||||
if (hasCompleter && !getCompletionPrefix(editor))
|
||||
editor.completer.detach();
|
||||
}
|
||||
else if (e.command.name === "insertstring") {
|
||||
var prefix = getCompletionPrefix(editor);
|
||||
// Only autocomplete if there's a prefix that can be matched
|
||||
if (prefix && !hasCompleter) {
|
||||
if (!editor.completer) {
|
||||
// Create new autocompleter
|
||||
editor.completer = new Autocomplete();
|
||||
}
|
||||
// Disable autoInsert
|
||||
editor.completer.autoInsert = false;
|
||||
editor.completer.showPopup(editor);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var Editor = require("../editor").Editor;
|
||||
require("../config").defineOptions(Editor.prototype, "editor", {
|
||||
enableBasicAutocompletion: {
|
||||
set: function(val) {
|
||||
if (val) {
|
||||
if (!this.completers)
|
||||
this.completers = Array.isArray(val)? val: completers;
|
||||
this.commands.addCommand(Autocomplete.startCommand);
|
||||
} else {
|
||||
this.commands.removeCommand(Autocomplete.startCommand);
|
||||
}
|
||||
},
|
||||
value: false
|
||||
},
|
||||
/**
|
||||
* Enable live autocomplete. If the value is an array, it is assumed to be an array of completers
|
||||
* and will use them instead of the default completers.
|
||||
*/
|
||||
enableLiveAutocompletion: {
|
||||
set: function(val) {
|
||||
if (val) {
|
||||
if (!this.completers)
|
||||
this.completers = Array.isArray(val)? val: completers;
|
||||
// On each change automatically trigger the autocomplete
|
||||
this.commands.on('afterExec', doLiveAutocomplete);
|
||||
} else {
|
||||
this.commands.removeListener('afterExec', doLiveAutocomplete);
|
||||
}
|
||||
},
|
||||
value: false
|
||||
},
|
||||
enableSnippets: {
|
||||
set: function(val) {
|
||||
if (val) {
|
||||
this.commands.addCommand(expandSnippet);
|
||||
this.on("changeMode", onChangeMode);
|
||||
onChangeMode(null, this);
|
||||
} else {
|
||||
this.commands.removeCommand(expandSnippet);
|
||||
this.off("changeMode", onChangeMode);
|
||||
}
|
||||
},
|
||||
value: false
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,78 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
|
||||
var Editor = require("ace/editor").Editor;
|
||||
|
||||
require("../config").defineOptions(Editor.prototype, "editor", {
|
||||
enableLinking: {
|
||||
set: function(val) {
|
||||
if (val) {
|
||||
this.on("click", onClick);
|
||||
this.on("mousemove", onMouseMove);
|
||||
} else {
|
||||
this.off("click", onClick);
|
||||
this.off("mousemove", onMouseMove);
|
||||
}
|
||||
},
|
||||
value: false
|
||||
}
|
||||
})
|
||||
|
||||
function onMouseMove(e) {
|
||||
var editor = e.editor;
|
||||
var ctrl = e.getAccelKey();
|
||||
|
||||
if (ctrl) {
|
||||
var editor = e.editor;
|
||||
var docPos = e.getDocumentPosition();
|
||||
var session = editor.session;
|
||||
var token = session.getTokenAt(docPos.row, docPos.column);
|
||||
|
||||
editor._emit("linkHover", {position: docPos, token: token});
|
||||
}
|
||||
}
|
||||
|
||||
function onClick(e) {
|
||||
var ctrl = e.getAccelKey();
|
||||
var button = e.getButton();
|
||||
|
||||
if (button == 0 && ctrl) {
|
||||
var editor = e.editor;
|
||||
var docPos = e.getDocumentPosition();
|
||||
var session = editor.session;
|
||||
var token = session.getTokenAt(docPos.row, docPos.column);
|
||||
|
||||
editor._emit("linkClick", {position: docPos, token: token});
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2013 Matthew Christopher Kastor-Inare III, Atropa Inc. Intl
|
||||
* All rights reserved.
|
||||
*
|
||||
* Contributed to Ajax.org under the BSD license.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/*jslint indent: 4, maxerr: 50, white: true, browser: true, vars: true*/
|
||||
/*global define, require */
|
||||
|
||||
/**
|
||||
* Add Editor Menu Options
|
||||
* @fileOverview Add Editor Menu Options <br />
|
||||
* The menu options property needs to be added to the editor
|
||||
* so that the settings menu can know about options for
|
||||
* selection elements and track which option is selected.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
*/
|
||||
|
||||
define(function(require, exports, module) {
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* The menu options property needs to be added to the editor
|
||||
* so that the settings menu can know about options for
|
||||
* selection elements and track which option is selected.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
* @param {ace.Editor} editor An instance of the ace editor.
|
||||
*/
|
||||
module.exports.addEditorMenuOptions = function addEditorMenuOptions (editor) {
|
||||
var modelist = require('../modelist');
|
||||
var themelist = require('../themelist');
|
||||
editor.menuOptions = {
|
||||
setNewLineMode: [{
|
||||
textContent: "unix",
|
||||
value: "unix"
|
||||
}, {
|
||||
textContent: "windows",
|
||||
value: "windows"
|
||||
}, {
|
||||
textContent: "auto",
|
||||
value: "auto"
|
||||
}],
|
||||
setTheme: [],
|
||||
setMode: [],
|
||||
setKeyboardHandler: [{
|
||||
textContent: "ace",
|
||||
value: ""
|
||||
}, {
|
||||
textContent: "vim",
|
||||
value: "ace/keyboard/vim"
|
||||
}, {
|
||||
textContent: "emacs",
|
||||
value: "ace/keyboard/emacs"
|
||||
}, {
|
||||
textContent: "textarea",
|
||||
value: "ace/keyboard/textarea"
|
||||
}, {
|
||||
textContent: "sublime",
|
||||
value: "ace/keyboard/sublime"
|
||||
}]
|
||||
};
|
||||
|
||||
editor.menuOptions.setTheme = themelist.themes.map(function(theme) {
|
||||
return {
|
||||
textContent: theme.caption,
|
||||
value: theme.theme
|
||||
};
|
||||
});
|
||||
|
||||
editor.menuOptions.setMode = modelist.modes.map(function(mode) {
|
||||
return {
|
||||
textContent: mode.name,
|
||||
value: mode.mode
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2013 Matthew Christopher Kastor-Inare III, Atropa Inc. Intl
|
||||
* All rights reserved.
|
||||
*
|
||||
* Contributed to Ajax.org under the BSD license.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/*jslint indent: 4, maxerr: 50, white: true, browser: true, vars: true*/
|
||||
/*global define, require */
|
||||
|
||||
/**
|
||||
* Element Generator
|
||||
* @fileOverview Element Generator <br />
|
||||
* Contains methods for generating elements.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
*/
|
||||
|
||||
define(function(require, exports, module) {
|
||||
'use strict';
|
||||
/**
|
||||
* Creates a DOM option element
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
* @param {object} obj An object containing properties to add to the dom
|
||||
* element. If one of those properties is named `selected` then it will be
|
||||
* added as an attribute on the element instead.
|
||||
*/
|
||||
module.exports.createOption = function createOption (obj) {
|
||||
var attribute;
|
||||
var el = document.createElement('option');
|
||||
for(attribute in obj) {
|
||||
if(obj.hasOwnProperty(attribute)) {
|
||||
if(attribute === 'selected') {
|
||||
el.setAttribute(attribute, obj[attribute]);
|
||||
} else {
|
||||
el[attribute] = obj[attribute];
|
||||
}
|
||||
}
|
||||
}
|
||||
return el;
|
||||
};
|
||||
/**
|
||||
* Creates a DOM checkbox element.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
* @param {string} id The id of the element.
|
||||
* @param {boolean} checked Whether or not the element is checked.
|
||||
* @param {string} clss The class of the element.
|
||||
* @returns {DOMElement} Returns a checkbox element reference.
|
||||
*/
|
||||
module.exports.createCheckbox = function createCheckbox (id, checked, clss) {
|
||||
var el = document.createElement('input');
|
||||
el.setAttribute('type', 'checkbox');
|
||||
el.setAttribute('id', id);
|
||||
el.setAttribute('name', id);
|
||||
el.setAttribute('value', checked);
|
||||
el.setAttribute('class', clss);
|
||||
if(checked) {
|
||||
el.setAttribute('checked', 'checked');
|
||||
}
|
||||
return el;
|
||||
};
|
||||
/**
|
||||
* Creates a DOM text input element.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
* @param {string} id The id of the element.
|
||||
* @param {string} value The default value of the input element.
|
||||
* @param {string} clss The class of the element.
|
||||
* @returns {DOMElement} Returns an input element reference.
|
||||
*/
|
||||
module.exports.createInput = function createInput (id, value, clss) {
|
||||
var el = document.createElement('input');
|
||||
el.setAttribute('type', 'text');
|
||||
el.setAttribute('id', id);
|
||||
el.setAttribute('name', id);
|
||||
el.setAttribute('value', value);
|
||||
el.setAttribute('class', clss);
|
||||
return el;
|
||||
};
|
||||
/**
|
||||
* Creates a DOM label element.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
* @param {string} text The label text.
|
||||
* @param {string} labelFor The id of the element being labeled.
|
||||
* @returns {DOMElement} Returns a label element reference.
|
||||
*/
|
||||
module.exports.createLabel = function createLabel (text, labelFor) {
|
||||
var el = document.createElement('label');
|
||||
el.setAttribute('for', labelFor);
|
||||
el.textContent = text;
|
||||
return el;
|
||||
};
|
||||
/**
|
||||
* Creates a DOM selection element.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
* @param {string} id The id of the element.
|
||||
* @param {string} values An array of objects suitable for `createOption`
|
||||
* @param {string} clss The class of the element.
|
||||
* @returns {DOMElement} Returns a selection element reference.
|
||||
* @see ace/ext/element_generator.createOption
|
||||
*/
|
||||
module.exports.createSelection = function createSelection (id, values, clss) {
|
||||
var el = document.createElement('select');
|
||||
el.setAttribute('id', id);
|
||||
el.setAttribute('name', id);
|
||||
el.setAttribute('class', clss);
|
||||
values.forEach(function(item) {
|
||||
el.appendChild(module.exports.createOption(item));
|
||||
});
|
||||
return el;
|
||||
};
|
||||
|
||||
});
|
||||
+264
@@ -0,0 +1,264 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2013 Matthew Christopher Kastor-Inare III, Atropa Inc. Intl
|
||||
* All rights reserved.
|
||||
*
|
||||
* Contributed to Ajax.org under the BSD license.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/*jslint indent: 4, maxerr: 50, white: true, browser: true, vars: true*/
|
||||
/*global define*/
|
||||
|
||||
/**
|
||||
* Generates the settings menu
|
||||
* @fileOverview Generates the settings menu.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
*/
|
||||
|
||||
define(function(require, exports, module) {
|
||||
'use strict';
|
||||
var egen = require('./element_generator');
|
||||
var addEditorMenuOptions = require('./add_editor_menu_options').addEditorMenuOptions;
|
||||
var getSetFunctions = require('./get_set_functions').getSetFunctions;
|
||||
|
||||
/**
|
||||
* Generates an interactive menu with settings useful to end users.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
* @param {ace.Editor} editor An instance of the ace editor.
|
||||
*/
|
||||
module.exports.generateSettingsMenu = function generateSettingsMenu (editor) {
|
||||
/**
|
||||
* container for dom elements that will go in the menu.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
*/
|
||||
var elements = [];
|
||||
/**
|
||||
* Sorts the menu entries (elements var) so they'll appear in alphabetical order
|
||||
* the sort is performed based on the value of the contains property
|
||||
* of each element. Since this is an `array.sort` the array is sorted
|
||||
* in place.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
*/
|
||||
function cleanupElementsList() {
|
||||
elements.sort(function(a, b) {
|
||||
var x = a.getAttribute('contains');
|
||||
var y = b.getAttribute('contains');
|
||||
return x.localeCompare(y);
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Wraps all dom elements contained in the elements var with a single
|
||||
* div.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
*/
|
||||
function wrapElements() {
|
||||
var topmenu = document.createElement('div');
|
||||
topmenu.setAttribute('id', 'ace_settingsmenu');
|
||||
elements.forEach(function(element) {
|
||||
topmenu.appendChild(element);
|
||||
});
|
||||
|
||||
var el = topmenu.appendChild(document.createElement('div'));
|
||||
var version = "1.1.8";
|
||||
el.style.padding = "1em";
|
||||
el.textContent = "Ace version " + version;
|
||||
|
||||
return topmenu;
|
||||
}
|
||||
/**
|
||||
* Creates a new menu entry.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
* @param {object} obj This is a reference to the object containing the
|
||||
* set function. It is used to set up event listeners for when the
|
||||
* menu options change.
|
||||
* @param {string} clss Maps to the class of the dom element. This is
|
||||
* the name of the object containing the set function e.g. `editor`,
|
||||
* `session`, `renderer`.
|
||||
* @param {string} item This is the set function name. It maps to the
|
||||
* id of the dom element (check, select, input) and to the "contains"
|
||||
* attribute of the div holding both the element and its label.
|
||||
* @param {mixed} val This is the value of the setting. It is mapped to
|
||||
* the dom element's value, checked, or selected option accordingly.
|
||||
*/
|
||||
function createNewEntry(obj, clss, item, val) {
|
||||
var el;
|
||||
var div = document.createElement('div');
|
||||
div.setAttribute('contains', item);
|
||||
div.setAttribute('class', 'ace_optionsMenuEntry');
|
||||
div.setAttribute('style', 'clear: both;');
|
||||
|
||||
div.appendChild(egen.createLabel(
|
||||
item.replace(/^set/, '').replace(/([A-Z])/g, ' $1').trim(),
|
||||
item
|
||||
));
|
||||
|
||||
if (Array.isArray(val)) {
|
||||
el = egen.createSelection(item, val, clss);
|
||||
el.addEventListener('change', function(e) {
|
||||
try{
|
||||
editor.menuOptions[e.target.id].forEach(function(x) {
|
||||
if(x.textContent !== e.target.textContent) {
|
||||
delete x.selected;
|
||||
}
|
||||
});
|
||||
obj[e.target.id](e.target.value);
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
});
|
||||
} else if(typeof val === 'boolean') {
|
||||
el = egen.createCheckbox(item, val, clss);
|
||||
el.addEventListener('change', function(e) {
|
||||
try{
|
||||
// renderer['setHighlightGutterLine'](true);
|
||||
obj[e.target.id](!!e.target.checked);
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// this aids in giving the ability to specify settings through
|
||||
// post and get requests.
|
||||
// /ace_editor.html?setMode=ace/mode/html&setOverwrite=true
|
||||
el = egen.createInput(item, val, clss);
|
||||
el.addEventListener('change', function(e) {
|
||||
try{
|
||||
if(e.target.value === 'true') {
|
||||
obj[e.target.id](true);
|
||||
} else if(e.target.value === 'false') {
|
||||
obj[e.target.id](false);
|
||||
} else {
|
||||
obj[e.target.id](e.target.value);
|
||||
}
|
||||
} catch (err) {
|
||||
throw new Error(err);
|
||||
}
|
||||
});
|
||||
}
|
||||
el.style.cssText = 'float:right;';
|
||||
div.appendChild(el);
|
||||
return div;
|
||||
}
|
||||
/**
|
||||
* Generates selection fields for the menu and populates their options
|
||||
* using information from `editor.menuOptions`
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
* @param {string} item The set function name.
|
||||
* @param {object} esr A reference to the object having the set function.
|
||||
* @param {string} clss The name of the object containing the set function.
|
||||
* @param {string} fn The matching get function's function name.
|
||||
* @returns {DOMElement} Returns a dom element containing a selection
|
||||
* element populated with options. The option whose value matches that
|
||||
* returned from `esr[fn]()` will be selected.
|
||||
*/
|
||||
function makeDropdown(item, esr, clss, fn) {
|
||||
var val = editor.menuOptions[item];
|
||||
var currentVal = esr[fn]();
|
||||
if (typeof currentVal == 'object')
|
||||
currentVal = currentVal.$id;
|
||||
val.forEach(function(valuex) {
|
||||
if (valuex.value === currentVal)
|
||||
valuex.selected = 'selected';
|
||||
});
|
||||
return createNewEntry(esr, clss, item, val);
|
||||
}
|
||||
/**
|
||||
* Processes the set functions returned from `getSetFunctions`. First it
|
||||
* checks for menu options defined in `editor.menuOptions`. If no
|
||||
* options are specified then it checks whether there is a get function
|
||||
* (replace set with get) for the setting. When either of those
|
||||
* conditions are met it will attempt to create a new entry for the
|
||||
* settings menu and push it into the elements array defined above.
|
||||
* It can only do so for get functions which return
|
||||
* strings, numbers, and booleans. A special case is written in for
|
||||
* `getMode` where it looks at the returned objects `$id` property and
|
||||
* forwards that through instead. Other special cases could be written
|
||||
* in but that would get a bit ridiculous.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
* @param {object} setObj An item from the array returned by
|
||||
* `getSetFunctions`.
|
||||
*/
|
||||
function handleSet(setObj) {
|
||||
var item = setObj.functionName;
|
||||
var esr = setObj.parentObj;
|
||||
var clss = setObj.parentName;
|
||||
var val;
|
||||
var fn = item.replace(/^set/, 'get');
|
||||
if(editor.menuOptions[item] !== undefined) {
|
||||
// has options for select element
|
||||
elements.push(makeDropdown(item, esr, clss, fn));
|
||||
} else if(typeof esr[fn] === 'function') {
|
||||
// has get function
|
||||
try {
|
||||
val = esr[fn]();
|
||||
if(typeof val === 'object') {
|
||||
// setMode takes a string, getMode returns an object
|
||||
// the $id property of that object is the string
|
||||
// which may be given to setMode...
|
||||
val = val.$id;
|
||||
}
|
||||
// the rest of the get functions return strings,
|
||||
// booleans, or numbers.
|
||||
elements.push(
|
||||
createNewEntry(esr, clss, item, val)
|
||||
);
|
||||
} catch (e) {
|
||||
// if there are errors it is because the element
|
||||
// does not belong in the settings menu
|
||||
}
|
||||
}
|
||||
}
|
||||
addEditorMenuOptions(editor);
|
||||
// gather the set functions
|
||||
getSetFunctions(editor).forEach(function(setObj) {
|
||||
// populate the elements array with good stuff.
|
||||
handleSet(setObj);
|
||||
});
|
||||
// sort the menu entries in the elements list so people can find
|
||||
// the settings in alphabetical order.
|
||||
cleanupElementsList();
|
||||
// dump the entries from the elements list and wrap them up in a div
|
||||
return wrapElements();
|
||||
};
|
||||
|
||||
});
|
||||
+91
@@ -0,0 +1,91 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2013 Matthew Christopher Kastor-Inare III, Atropa Inc. Intl
|
||||
* All rights reserved.
|
||||
*
|
||||
* Contributed to Ajax.org under the BSD license.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/*jslint indent: 4, maxerr: 50, white: true, browser: true, vars: true*/
|
||||
/*global define, require */
|
||||
|
||||
/**
|
||||
* Get Editor Keyboard Shortcuts
|
||||
* @fileOverview Get Editor Keyboard Shortcuts <br />
|
||||
* Gets a map of keyboard shortcuts to command names for the current platform.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
*/
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
var keys = require("../../lib/keys");
|
||||
|
||||
/**
|
||||
* Gets a map of keyboard shortcuts to command names for the current platform.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
* @param {ace.Editor} editor An editor instance.
|
||||
* @returns {Array} Returns an array of objects representing the keyboard
|
||||
* shortcuts for the given editor.
|
||||
* @example
|
||||
* var getKbShortcuts = require('./get_keyboard_shortcuts');
|
||||
* console.log(getKbShortcuts(editor));
|
||||
* // [
|
||||
* // {'command' : aCommand, 'key' : 'Control-d'},
|
||||
* // {'command' : aCommand, 'key' : 'Control-d'}
|
||||
* // ]
|
||||
*/
|
||||
module.exports.getEditorKeybordShortcuts = function(editor) {
|
||||
var KEY_MODS = keys.KEY_MODS;
|
||||
var keybindings = [];
|
||||
var commandMap = {};
|
||||
editor.keyBinding.$handlers.forEach(function(handler) {
|
||||
var ckb = handler.commandKeyBinding;
|
||||
for (var i in ckb) {
|
||||
var key = i.replace(/(^|-)\w/g, function(x) { return x.toUpperCase(); });
|
||||
var commands = ckb[i];
|
||||
if (!Array.isArray(commands))
|
||||
commands = [commands];
|
||||
commands.forEach(function(command) {
|
||||
if (typeof command != "string")
|
||||
command = command.name
|
||||
if (commandMap[command]) {
|
||||
commandMap[command].key += "|" + key;
|
||||
} else {
|
||||
commandMap[command] = {key: key, command: command};
|
||||
keybindings.push(commandMap[command]);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
return keybindings;
|
||||
};
|
||||
|
||||
});
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2013 Matthew Christopher Kastor-Inare III, Atropa Inc. Intl
|
||||
* All rights reserved.
|
||||
*
|
||||
* Contributed to Ajax.org under the BSD license.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/*jslint indent: 4, maxerr: 50, white: true, browser: true, vars: true */
|
||||
/*global define*/
|
||||
|
||||
/**
|
||||
* Get Set Functions
|
||||
* @fileOverview Get Set Functions <br />
|
||||
* Gets various functions for setting settings.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
*/
|
||||
|
||||
define(function(require, exports, module) {
|
||||
'use strict';
|
||||
/**
|
||||
* Generates a list of set functions for the settings menu.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
* @param {object} editor The editor instance
|
||||
* @return {array} Returns an array of objects. Each object contains the
|
||||
* following properties: functionName, parentObj, and parentName. The
|
||||
* function name will be the name of a method beginning with the string
|
||||
* `set` which was found. The parent object will be a reference to the
|
||||
* object having the method matching the function name. The parent name
|
||||
* will be a string representing the identifier of the parent object e.g.
|
||||
* `editor`, `session`, or `renderer`.
|
||||
*/
|
||||
module.exports.getSetFunctions = function getSetFunctions (editor) {
|
||||
/**
|
||||
* Output array. Will hold the objects described above.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
*/
|
||||
var out = [];
|
||||
/**
|
||||
* This object provides a map between the objects which will be
|
||||
* traversed and the parent name which will appear in the output.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
*/
|
||||
var my = {
|
||||
'editor' : editor,
|
||||
'session' : editor.session,
|
||||
'renderer' : editor.renderer
|
||||
};
|
||||
/**
|
||||
* This array will hold the set function names which have already been
|
||||
* found so that they are not added to the output multiple times.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
*/
|
||||
var opts = [];
|
||||
/**
|
||||
* This is a list of set functions which will not appear in the settings
|
||||
* menu. I don't know what to do with setKeyboardHandler. When I tried
|
||||
* to use it, it didn't appear to be working. Someone who knows better
|
||||
* could remove it from this list and add it's options to
|
||||
* add_editor_menu_options.js
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
*/
|
||||
var skip = [
|
||||
'setOption',
|
||||
'setUndoManager',
|
||||
'setDocument',
|
||||
'setValue',
|
||||
'setBreakpoints',
|
||||
'setScrollTop',
|
||||
'setScrollLeft',
|
||||
'setSelectionStyle',
|
||||
'setWrapLimitRange'
|
||||
];
|
||||
|
||||
|
||||
/**
|
||||
* This will search the objects mapped to the `my` variable above. When
|
||||
* it finds a set function in the object that is not listed in the
|
||||
* `skip` list or the `opts` list it will push a new object to the
|
||||
* output array.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
*/
|
||||
['renderer', 'session', 'editor'].forEach(function(esra) {
|
||||
var esr = my[esra];
|
||||
var clss = esra;
|
||||
for(var fn in esr) {
|
||||
if(skip.indexOf(fn) === -1) {
|
||||
if(/^set/.test(fn) && opts.indexOf(fn) === -1) {
|
||||
// found set function
|
||||
opts.push(fn);
|
||||
out.push({
|
||||
'functionName' : fn,
|
||||
'parentObj' : esr,
|
||||
'parentName' : clss
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return out;
|
||||
};
|
||||
|
||||
});
|
||||
@@ -0,0 +1,116 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2013 Matthew Christopher Kastor-Inare III, Atropa Inc. Intl
|
||||
* All rights reserved.
|
||||
*
|
||||
* Contributed to Ajax.org under the BSD license.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/*jslint indent: 4, maxerr: 50, white: true, browser: true, vars: true*/
|
||||
/*global define, require */
|
||||
|
||||
/**
|
||||
* Overlay Page
|
||||
* @fileOverview Overlay Page <br />
|
||||
* Generates an overlay for displaying menus. The overlay is an absolutely
|
||||
* positioned div.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
*/
|
||||
|
||||
define(function(require, exports, module) {
|
||||
'use strict';
|
||||
var dom = require("../../lib/dom");
|
||||
var cssText = require("../../requirejs/text!./settings_menu.css");
|
||||
dom.importCssString(cssText);
|
||||
|
||||
/**
|
||||
* Generates an overlay for displaying menus. The overlay is an absolutely
|
||||
* positioned div.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
* @param {DOMElement} contentElement Any element which may be presented inside
|
||||
* a div.
|
||||
* @param {string|number} top absolute position value.
|
||||
* @param {string|number} right absolute position value.
|
||||
* @param {string|number} bottom absolute position value.
|
||||
* @param {string|number} left absolute position value.
|
||||
*/
|
||||
module.exports.overlayPage = function overlayPage(editor, contentElement, top, right, bottom, left) {
|
||||
top = top ? 'top: ' + top + ';' : '';
|
||||
bottom = bottom ? 'bottom: ' + bottom + ';' : '';
|
||||
right = right ? 'right: ' + right + ';' : '';
|
||||
left = left ? 'left: ' + left + ';' : '';
|
||||
|
||||
var closer = document.createElement('div');
|
||||
var contentContainer = document.createElement('div');
|
||||
|
||||
function documentEscListener(e) {
|
||||
if (e.keyCode === 27) {
|
||||
closer.click();
|
||||
}
|
||||
}
|
||||
|
||||
closer.style.cssText = 'margin: 0; padding: 0; ' +
|
||||
'position: fixed; top:0; bottom:0; left:0; right:0;' +
|
||||
'z-index: 9990; ' +
|
||||
'background-color: rgba(0, 0, 0, 0.3);';
|
||||
closer.addEventListener('click', function() {
|
||||
document.removeEventListener('keydown', documentEscListener);
|
||||
closer.parentNode.removeChild(closer);
|
||||
editor.focus();
|
||||
closer = null;
|
||||
});
|
||||
// click closer if esc key is pressed
|
||||
document.addEventListener('keydown', documentEscListener);
|
||||
|
||||
contentContainer.style.cssText = top + right + bottom + left;
|
||||
contentContainer.addEventListener('click', function(e) {
|
||||
e.stopPropagation();
|
||||
});
|
||||
|
||||
var wrapper = dom.createElement("div");
|
||||
wrapper.style.position = "relative";
|
||||
|
||||
var closeButton = dom.createElement("div");
|
||||
closeButton.className = "ace_closeButton";
|
||||
closeButton.addEventListener('click', function() {
|
||||
closer.click();
|
||||
});
|
||||
|
||||
wrapper.appendChild(closeButton);
|
||||
contentContainer.appendChild(wrapper);
|
||||
|
||||
contentContainer.appendChild(contentElement);
|
||||
closer.appendChild(contentContainer);
|
||||
document.body.appendChild(closer);
|
||||
editor.blur();
|
||||
};
|
||||
|
||||
});
|
||||
@@ -0,0 +1,48 @@
|
||||
#ace_settingsmenu, #kbshortcutmenu {
|
||||
background-color: #F7F7F7;
|
||||
color: black;
|
||||
box-shadow: -5px 4px 5px rgba(126, 126, 126, 0.55);
|
||||
padding: 1em 0.5em 2em 1em;
|
||||
overflow: auto;
|
||||
position: absolute;
|
||||
margin: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
z-index: 9991;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.ace_dark #ace_settingsmenu, .ace_dark #kbshortcutmenu {
|
||||
box-shadow: -20px 10px 25px rgba(126, 126, 126, 0.25);
|
||||
background-color: rgba(255, 255, 255, 0.6);
|
||||
color: black;
|
||||
}
|
||||
|
||||
.ace_optionsMenuEntry:hover {
|
||||
background-color: rgba(100, 100, 100, 0.1);
|
||||
-webkit-transition: all 0.5s;
|
||||
transition: all 0.3s
|
||||
}
|
||||
|
||||
.ace_closeButton {
|
||||
background: rgba(245, 146, 146, 0.5);
|
||||
border: 1px solid #F48A8A;
|
||||
border-radius: 50%;
|
||||
padding: 7px;
|
||||
position: absolute;
|
||||
right: -8px;
|
||||
top: -8px;
|
||||
z-index: 1000;
|
||||
}
|
||||
.ace_closeButton{
|
||||
background: rgba(245, 146, 146, 0.9);
|
||||
}
|
||||
.ace_optionsMenuKey {
|
||||
color: darkslateblue;
|
||||
font-weight: bold;
|
||||
}
|
||||
.ace_optionsMenuCommand {
|
||||
color: darkcyan;
|
||||
font-weight: normal;
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var modes = [];
|
||||
/**
|
||||
* Suggests a mode based on the file extension present in the given path
|
||||
* @param {string} path The path to the file
|
||||
* @returns {object} Returns an object containing information about the
|
||||
* suggested mode.
|
||||
*/
|
||||
function getModeForPath(path) {
|
||||
var mode = modesByName.text;
|
||||
var fileName = path.split(/[\/\\]/).pop();
|
||||
for (var i = 0; i < modes.length; i++) {
|
||||
if (modes[i].supportsFile(fileName)) {
|
||||
mode = modes[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
var Mode = function(name, caption, extensions) {
|
||||
this.name = name;
|
||||
this.caption = caption;
|
||||
this.mode = "ace/mode/" + name;
|
||||
this.extensions = extensions;
|
||||
if (/\^/.test(extensions)) {
|
||||
var re = extensions.replace(/\|(\^)?/g, function(a, b){
|
||||
return "$|" + (b ? "^" : "^.*\\.");
|
||||
}) + "$";
|
||||
} else {
|
||||
var re = "^.*\\.(" + extensions + ")$";
|
||||
}
|
||||
|
||||
this.extRe = new RegExp(re, "gi");
|
||||
};
|
||||
|
||||
Mode.prototype.supportsFile = function(filename) {
|
||||
return filename.match(this.extRe);
|
||||
};
|
||||
|
||||
// todo firstlinematch
|
||||
var supportedModes = {
|
||||
ABAP: ["abap"],
|
||||
ABC: ["abc"],
|
||||
ActionScript:["as"],
|
||||
ADA: ["ada|adb"],
|
||||
Apache_Conf: ["^htaccess|^htgroups|^htpasswd|^conf|htaccess|htgroups|htpasswd"],
|
||||
AsciiDoc: ["asciidoc"],
|
||||
Assembly_x86:["asm"],
|
||||
AutoHotKey: ["ahk"],
|
||||
BatchFile: ["bat|cmd"],
|
||||
C9Search: ["c9search_results"],
|
||||
C_Cpp: ["cpp|c|cc|cxx|h|hh|hpp"],
|
||||
Cirru: ["cirru|cr"],
|
||||
Clojure: ["clj|cljs"],
|
||||
Cobol: ["CBL|COB"],
|
||||
coffee: ["coffee|cf|cson|^Cakefile"],
|
||||
ColdFusion: ["cfm"],
|
||||
CSharp: ["cs"],
|
||||
CSS: ["css"],
|
||||
Curly: ["curly"],
|
||||
D: ["d|di"],
|
||||
Dart: ["dart"],
|
||||
Diff: ["diff|patch"],
|
||||
Dockerfile: ["^Dockerfile"],
|
||||
Dot: ["dot"],
|
||||
Dummy: ["dummy"],
|
||||
DummySyntax: ["dummy"],
|
||||
Eiffel: ["e"],
|
||||
EJS: ["ejs"],
|
||||
Elixir: ["ex|exs"],
|
||||
Elm: ["elm"],
|
||||
Erlang: ["erl|hrl"],
|
||||
Forth: ["frt|fs|ldr"],
|
||||
FTL: ["ftl"],
|
||||
Gcode: ["gcode"],
|
||||
Gherkin: ["feature"],
|
||||
Gitignore: ["^.gitignore"],
|
||||
Glsl: ["glsl|frag|vert"],
|
||||
golang: ["go"],
|
||||
Groovy: ["groovy"],
|
||||
HAML: ["haml"],
|
||||
Handlebars: ["hbs|handlebars|tpl|mustache"],
|
||||
Haskell: ["hs"],
|
||||
haXe: ["hx"],
|
||||
HTML: ["html|htm|xhtml"],
|
||||
HTML_Ruby: ["erb|rhtml|html.erb"],
|
||||
INI: ["ini|conf|cfg|prefs"],
|
||||
Io: ["io"],
|
||||
Jack: ["jack"],
|
||||
Jade: ["jade"],
|
||||
Java: ["java"],
|
||||
JavaScript: ["js|jsm"],
|
||||
JSON: ["json"],
|
||||
JSONiq: ["jq"],
|
||||
JSP: ["jsp"],
|
||||
JSX: ["jsx"],
|
||||
Julia: ["jl"],
|
||||
LaTeX: ["tex|latex|ltx|bib"],
|
||||
LESS: ["less"],
|
||||
Liquid: ["liquid"],
|
||||
Lisp: ["lisp"],
|
||||
LiveScript: ["ls"],
|
||||
LogiQL: ["logic|lql"],
|
||||
LSL: ["lsl"],
|
||||
Lua: ["lua"],
|
||||
LuaPage: ["lp"],
|
||||
Lucene: ["lucene"],
|
||||
Makefile: ["^Makefile|^GNUmakefile|^makefile|^OCamlMakefile|make"],
|
||||
Markdown: ["md|markdown"],
|
||||
Mask: ["mask"],
|
||||
MATLAB: ["matlab"],
|
||||
MEL: ["mel"],
|
||||
MUSHCode: ["mc|mush"],
|
||||
MySQL: ["mysql"],
|
||||
Nix: ["nix"],
|
||||
ObjectiveC: ["m|mm"],
|
||||
OCaml: ["ml|mli"],
|
||||
Pascal: ["pas|p"],
|
||||
Perl: ["pl|pm"],
|
||||
pgSQL: ["pgsql"],
|
||||
PHP: ["php|phtml"],
|
||||
Powershell: ["ps1"],
|
||||
Praat: ["praat|praatscript|psc|proc"],
|
||||
Prolog: ["plg|prolog"],
|
||||
Properties: ["properties"],
|
||||
Protobuf: ["proto"],
|
||||
Python: ["py"],
|
||||
R: ["r"],
|
||||
RDoc: ["Rd"],
|
||||
RHTML: ["Rhtml"],
|
||||
Ruby: ["rb|ru|gemspec|rake|^Guardfile|^Rakefile|^Gemfile"],
|
||||
Rust: ["rs"],
|
||||
SASS: ["sass"],
|
||||
SCAD: ["scad"],
|
||||
Scala: ["scala"],
|
||||
Scheme: ["scm|rkt"],
|
||||
SCSS: ["scss"],
|
||||
SH: ["sh|bash|^.bashrc"],
|
||||
SJS: ["sjs"],
|
||||
Smarty: ["smarty|tpl"],
|
||||
snippets: ["snippets"],
|
||||
Soy_Template:["soy"],
|
||||
Space: ["space"],
|
||||
SQL: ["sql"],
|
||||
Stylus: ["styl|stylus"],
|
||||
SVG: ["svg"],
|
||||
Tcl: ["tcl"],
|
||||
Tex: ["tex"],
|
||||
Text: ["txt"],
|
||||
Textile: ["textile"],
|
||||
Toml: ["toml"],
|
||||
Twig: ["twig"],
|
||||
Typescript: ["ts|typescript|str"],
|
||||
Vala: ["vala"],
|
||||
VBScript: ["vbs|vb"],
|
||||
Velocity: ["vm"],
|
||||
Verilog: ["v|vh|sv|svh"],
|
||||
VHDL: ["vhd|vhdl"],
|
||||
XML: ["xml|rdf|rss|wsdl|xslt|atom|mathml|mml|xul|xbl"],
|
||||
XQuery: ["xq"],
|
||||
YAML: ["yaml|yml"],
|
||||
// Add the missing mode "Django" to ext-modelist
|
||||
Django: ["html"]
|
||||
};
|
||||
|
||||
var nameOverrides = {
|
||||
ObjectiveC: "Objective-C",
|
||||
CSharp: "C#",
|
||||
golang: "Go",
|
||||
C_Cpp: "C and C++",
|
||||
coffee: "CoffeeScript",
|
||||
HTML_Ruby: "HTML (Ruby)",
|
||||
FTL: "FreeMarker"
|
||||
};
|
||||
var modesByName = {};
|
||||
for (var name in supportedModes) {
|
||||
var data = supportedModes[name];
|
||||
var displayName = (nameOverrides[name] || name).replace(/_/g, " ");
|
||||
var filename = name.toLowerCase();
|
||||
var mode = new Mode(filename, displayName, data[0]);
|
||||
modesByName[filename] = mode;
|
||||
modes.push(mode);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
getModeForPath: getModeForPath,
|
||||
modes: modes,
|
||||
modesByName: modesByName
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
var MAX_TOKEN_COUNT = 1000;
|
||||
var useragent = require("../lib/useragent");
|
||||
var TokenizerModule = require("../tokenizer");
|
||||
|
||||
function patch(obj, name, regexp, replacement) {
|
||||
eval("obj['" + name + "']=" + obj[name].toString().replace(
|
||||
regexp, replacement
|
||||
));
|
||||
}
|
||||
|
||||
if (useragent.isIE && useragent.isIE < 10 && window.top.document.compatMode === "BackCompat")
|
||||
useragent.isOldIE = true;
|
||||
|
||||
if (typeof document != "undefined" && !document.documentElement.querySelector) {
|
||||
useragent.isOldIE = true;
|
||||
var qs = function(el, selector) {
|
||||
if (selector.charAt(0) == ".") {
|
||||
var classNeme = selector.slice(1);
|
||||
} else {
|
||||
var m = selector.match(/(\w+)=(\w+)/);
|
||||
var attr = m && m[1];
|
||||
var attrVal = m && m[2];
|
||||
}
|
||||
for (var i = 0; i < el.all.length; i++) {
|
||||
var ch = el.all[i];
|
||||
if (classNeme) {
|
||||
if (ch.className.indexOf(classNeme) != -1)
|
||||
return ch;
|
||||
} else if (attr) {
|
||||
if (ch.getAttribute(attr) == attrVal)
|
||||
return ch;
|
||||
}
|
||||
}
|
||||
};
|
||||
var sb = require("./searchbox").SearchBox.prototype;
|
||||
patch(
|
||||
sb, "$initElements",
|
||||
/([^\s=]*).querySelector\((".*?")\)/g,
|
||||
"qs($1, $2)"
|
||||
);
|
||||
}
|
||||
|
||||
var compliantExecNpcg = /()??/.exec("")[1] === undefined;
|
||||
if (compliantExecNpcg)
|
||||
return;
|
||||
var proto = TokenizerModule.Tokenizer.prototype;
|
||||
TokenizerModule.Tokenizer_orig = TokenizerModule.Tokenizer;
|
||||
proto.getLineTokens_orig = proto.getLineTokens;
|
||||
|
||||
patch(
|
||||
TokenizerModule, "Tokenizer",
|
||||
"ruleRegExps.push(adjustedregex);\n",
|
||||
function(m) {
|
||||
return m + '\
|
||||
if (state[i].next && RegExp(adjustedregex).test(""))\n\
|
||||
rule._qre = RegExp(adjustedregex, "g");\n\
|
||||
';
|
||||
}
|
||||
);
|
||||
TokenizerModule.Tokenizer.prototype = proto;
|
||||
patch(
|
||||
proto, "getLineTokens",
|
||||
/if \(match\[i \+ 1\] === undefined\)\s*continue;/,
|
||||
"if (!match[i + 1]) {\n\
|
||||
if (value)continue;\n\
|
||||
var qre = state[mapping[i]]._qre;\n\
|
||||
if (!qre) continue;\n\
|
||||
qre.lastIndex = lastIndex;\n\
|
||||
if (!qre.exec(line) || qre.lastIndex != lastIndex)\n\
|
||||
continue;\n\
|
||||
}"
|
||||
);
|
||||
|
||||
patch(
|
||||
require("../mode/text").Mode.prototype, "getTokenizer",
|
||||
/Tokenizer/,
|
||||
"TokenizerModule.Tokenizer"
|
||||
);
|
||||
|
||||
useragent.isOldIE = true;
|
||||
|
||||
});
|
||||
@@ -0,0 +1,77 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
if (typeof process !== "undefined") {
|
||||
require("amd-loader");
|
||||
}
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var assert = require("../test/assertions");
|
||||
|
||||
module.exports = {
|
||||
"test: getTokenizer() (smoke test)" : function() {
|
||||
var exec = RegExp.prototype.exec
|
||||
var brokenExec = function(str) {
|
||||
var result = exec.call(this, str);
|
||||
if (result) {
|
||||
for (var i = result.length; i--;)
|
||||
if (!result[i])
|
||||
result[i] = "";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
try {
|
||||
// break this to emulate old ie
|
||||
RegExp.prototype.exec = brokenExec;
|
||||
require("./old_ie");
|
||||
var Tokenizer = require("../tokenizer").Tokenizer;
|
||||
var JavaScriptHighlightRules = require("../mode/javascript_highlight_rules").JavaScriptHighlightRules;
|
||||
var tokenizer = new Tokenizer((new JavaScriptHighlightRules).getRules());
|
||||
|
||||
var tokens = tokenizer.getLineTokens("'juhu'", "start").tokens;
|
||||
assert.equal("string", tokens[0].type);
|
||||
} finally {
|
||||
// restore modified functions
|
||||
RegExp.prototype.exec = exec;
|
||||
var module = require("../tokenizer");
|
||||
module.Tokenizer = module.Tokenizer_orig;
|
||||
module.Tokenizer.prototype.getLineTokens = module.Tokenizer.prototype.getLineTokens_orig;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
if (typeof module !== "undefined" && module === require.main) {
|
||||
require("asyncjs").test.testcase(module.exports).exec()
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------
|
||||
* Editor Search Form
|
||||
* --------------------------------------------------------------------------------------- */
|
||||
.ace_search {
|
||||
background-color: #ddd;
|
||||
border: 1px solid #cbcbcb;
|
||||
border-top: 0 none;
|
||||
max-width: 325px;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
padding-right: 6px;
|
||||
padding-bottom: 0;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
z-index: 99;
|
||||
white-space: normal;
|
||||
}
|
||||
.ace_search.left {
|
||||
border-left: 0 none;
|
||||
border-radius: 0px 0px 5px 0px;
|
||||
left: 0;
|
||||
}
|
||||
.ace_search.right {
|
||||
border-radius: 0px 0px 0px 5px;
|
||||
border-right: 0 none;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.ace_search_form, .ace_replace_form {
|
||||
border-radius: 3px;
|
||||
border: 1px solid #cbcbcb;
|
||||
float: left;
|
||||
margin-bottom: 4px;
|
||||
overflow: hidden;
|
||||
}
|
||||
.ace_search_form.ace_nomatch {
|
||||
outline: 1px solid red;
|
||||
}
|
||||
|
||||
.ace_search_field {
|
||||
background-color: white;
|
||||
border-right: 1px solid #cbcbcb;
|
||||
border: 0 none;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
float: left;
|
||||
height: 22px;
|
||||
outline: 0;
|
||||
padding: 0 7px;
|
||||
width: 214px;
|
||||
margin: 0;
|
||||
}
|
||||
.ace_searchbtn,
|
||||
.ace_replacebtn {
|
||||
background: #fff;
|
||||
border: 0 none;
|
||||
border-left: 1px solid #dcdcdc;
|
||||
cursor: pointer;
|
||||
float: left;
|
||||
height: 22px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
position: relative;
|
||||
}
|
||||
.ace_searchbtn:last-child,
|
||||
.ace_replacebtn:last-child {
|
||||
border-top-right-radius: 3px;
|
||||
border-bottom-right-radius: 3px;
|
||||
}
|
||||
.ace_searchbtn:disabled {
|
||||
background: none;
|
||||
cursor: default;
|
||||
}
|
||||
.ace_searchbtn {
|
||||
background-position: 50% 50%;
|
||||
background-repeat: no-repeat;
|
||||
width: 27px;
|
||||
}
|
||||
.ace_searchbtn.prev {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADFJREFUeNpiSU1NZUAC/6E0I0yACYskCpsJiySKIiY0SUZk40FyTEgCjGgKwTRAgAEAQJUIPCE+qfkAAAAASUVORK5CYII=);
|
||||
}
|
||||
.ace_searchbtn.next {
|
||||
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAFCAYAAAB4ka1VAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAADRJREFUeNpiTE1NZQCC/0DMyIAKwGJMUAYDEo3M/s+EpvM/mkKwCQxYjIeLMaELoLMBAgwAU7UJObTKsvAAAAAASUVORK5CYII=);
|
||||
}
|
||||
.ace_searchbtn_close {
|
||||
background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAcCAYAAABRVo5BAAAAZ0lEQVR42u2SUQrAMAhDvazn8OjZBilCkYVVxiis8H4CT0VrAJb4WHT3C5xU2a2IQZXJjiQIRMdkEoJ5Q2yMqpfDIo+XY4k6h+YXOyKqTIj5REaxloNAd0xiKmAtsTHqW8sR2W5f7gCu5nWFUpVjZwAAAABJRU5ErkJggg==) no-repeat 50% 0;
|
||||
border-radius: 50%;
|
||||
border: 0 none;
|
||||
color: #656565;
|
||||
cursor: pointer;
|
||||
float: right;
|
||||
font: 16px/16px Arial;
|
||||
height: 14px;
|
||||
margin: 5px 1px 9px 5px;
|
||||
padding: 0;
|
||||
text-align: center;
|
||||
width: 14px;
|
||||
}
|
||||
.ace_searchbtn_close:hover {
|
||||
background-color: #656565;
|
||||
background-position: 50% 100%;
|
||||
color: white;
|
||||
}
|
||||
.ace_replacebtn.prev {
|
||||
width: 54px
|
||||
}
|
||||
.ace_replacebtn.next {
|
||||
width: 27px
|
||||
}
|
||||
|
||||
.ace_button {
|
||||
margin-left: 2px;
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-o-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
overflow: hidden;
|
||||
opacity: 0.7;
|
||||
border: 1px solid rgba(100,100,100,0.23);
|
||||
padding: 1px;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
color: black;
|
||||
}
|
||||
|
||||
.ace_button:hover {
|
||||
background-color: #eee;
|
||||
opacity:1;
|
||||
}
|
||||
.ace_button:active {
|
||||
background-color: #ddd;
|
||||
}
|
||||
|
||||
.ace_button.checked {
|
||||
border-color: #3399ff;
|
||||
opacity:1;
|
||||
}
|
||||
|
||||
.ace_search_options{
|
||||
margin-bottom: 3px;
|
||||
text-align: right;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-o-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var dom = require("../lib/dom");
|
||||
var lang = require("../lib/lang");
|
||||
var event = require("../lib/event");
|
||||
var searchboxCss = require("../requirejs/text!./searchbox.css");
|
||||
var HashHandler = require("../keyboard/hash_handler").HashHandler;
|
||||
var keyUtil = require("../lib/keys");
|
||||
|
||||
dom.importCssString(searchboxCss, "ace_searchbox");
|
||||
|
||||
var html = '<div class="ace_search right">\
|
||||
<button type="button" action="hide" class="ace_searchbtn_close"></button>\
|
||||
<div class="ace_search_form">\
|
||||
<input class="ace_search_field" placeholder="Search for" spellcheck="false"></input>\
|
||||
<button type="button" action="findNext" class="ace_searchbtn next"></button>\
|
||||
<button type="button" action="findPrev" class="ace_searchbtn prev"></button>\
|
||||
<button type="button" action="findAll" class="ace_searchbtn" title="Alt-Enter">All</button>\
|
||||
</div>\
|
||||
<div class="ace_replace_form">\
|
||||
<input class="ace_search_field" placeholder="Replace with" spellcheck="false"></input>\
|
||||
<button type="button" action="replaceAndFindNext" class="ace_replacebtn">Replace</button>\
|
||||
<button type="button" action="replaceAll" class="ace_replacebtn">All</button>\
|
||||
</div>\
|
||||
<div class="ace_search_options">\
|
||||
<span action="toggleRegexpMode" class="ace_button" title="RegExp Search">.*</span>\
|
||||
<span action="toggleCaseSensitive" class="ace_button" title="CaseSensitive Search">Aa</span>\
|
||||
<span action="toggleWholeWords" class="ace_button" title="Whole Word Search">\\b</span>\
|
||||
</div>\
|
||||
</div>'.replace(/>\s+/g, ">");
|
||||
|
||||
var SearchBox = function(editor, range, showReplaceForm) {
|
||||
var div = dom.createElement("div");
|
||||
div.innerHTML = html;
|
||||
this.element = div.firstChild;
|
||||
|
||||
this.$init();
|
||||
this.setEditor(editor);
|
||||
};
|
||||
|
||||
(function() {
|
||||
this.setEditor = function(editor) {
|
||||
editor.searchBox = this;
|
||||
editor.container.appendChild(this.element);
|
||||
this.editor = editor;
|
||||
};
|
||||
|
||||
this.$initElements = function(sb) {
|
||||
this.searchBox = sb.querySelector(".ace_search_form");
|
||||
this.replaceBox = sb.querySelector(".ace_replace_form");
|
||||
this.searchOptions = sb.querySelector(".ace_search_options");
|
||||
this.regExpOption = sb.querySelector("[action=toggleRegexpMode]");
|
||||
this.caseSensitiveOption = sb.querySelector("[action=toggleCaseSensitive]");
|
||||
this.wholeWordOption = sb.querySelector("[action=toggleWholeWords]");
|
||||
this.searchInput = this.searchBox.querySelector(".ace_search_field");
|
||||
this.replaceInput = this.replaceBox.querySelector(".ace_search_field");
|
||||
};
|
||||
|
||||
this.$init = function() {
|
||||
var sb = this.element;
|
||||
|
||||
this.$initElements(sb);
|
||||
|
||||
var _this = this;
|
||||
event.addListener(sb, "mousedown", function(e) {
|
||||
setTimeout(function(){
|
||||
_this.activeInput.focus();
|
||||
}, 0);
|
||||
event.stopPropagation(e);
|
||||
});
|
||||
event.addListener(sb, "click", function(e) {
|
||||
var t = e.target || e.srcElement;
|
||||
var action = t.getAttribute("action");
|
||||
if (action && _this[action])
|
||||
_this[action]();
|
||||
else if (_this.$searchBarKb.commands[action])
|
||||
_this.$searchBarKb.commands[action].exec(_this);
|
||||
event.stopPropagation(e);
|
||||
});
|
||||
|
||||
event.addCommandKeyListener(sb, function(e, hashId, keyCode) {
|
||||
var keyString = keyUtil.keyCodeToString(keyCode);
|
||||
var command = _this.$searchBarKb.findKeyCommand(hashId, keyString);
|
||||
if (command && command.exec) {
|
||||
command.exec(_this);
|
||||
event.stopEvent(e);
|
||||
}
|
||||
});
|
||||
|
||||
this.$onChange = lang.delayedCall(function() {
|
||||
_this.find(false, false);
|
||||
});
|
||||
|
||||
event.addListener(this.searchInput, "input", function() {
|
||||
_this.$onChange.schedule(20);
|
||||
});
|
||||
event.addListener(this.searchInput, "focus", function() {
|
||||
_this.activeInput = _this.searchInput;
|
||||
_this.searchInput.value && _this.highlight();
|
||||
});
|
||||
event.addListener(this.replaceInput, "focus", function() {
|
||||
_this.activeInput = _this.replaceInput;
|
||||
_this.searchInput.value && _this.highlight();
|
||||
});
|
||||
};
|
||||
|
||||
//keybinging outsite of the searchbox
|
||||
this.$closeSearchBarKb = new HashHandler([{
|
||||
bindKey: "Esc",
|
||||
name: "closeSearchBar",
|
||||
exec: function(editor) {
|
||||
editor.searchBox.hide();
|
||||
}
|
||||
}]);
|
||||
|
||||
//keybinging outsite of the searchbox
|
||||
this.$searchBarKb = new HashHandler();
|
||||
this.$searchBarKb.bindKeys({
|
||||
"Ctrl-f|Command-f|Ctrl-H|Command-Option-F": function(sb) {
|
||||
var isReplace = sb.isReplace = !sb.isReplace;
|
||||
sb.replaceBox.style.display = isReplace ? "" : "none";
|
||||
sb[isReplace ? "replaceInput" : "searchInput"].focus();
|
||||
},
|
||||
"Ctrl-G|Command-G": function(sb) {
|
||||
sb.findNext();
|
||||
},
|
||||
"Ctrl-Shift-G|Command-Shift-G": function(sb) {
|
||||
sb.findPrev();
|
||||
},
|
||||
"esc": function(sb) {
|
||||
setTimeout(function() { sb.hide();});
|
||||
},
|
||||
"Return": function(sb) {
|
||||
if (sb.activeInput == sb.replaceInput)
|
||||
sb.replace();
|
||||
sb.findNext();
|
||||
},
|
||||
"Shift-Return": function(sb) {
|
||||
if (sb.activeInput == sb.replaceInput)
|
||||
sb.replace();
|
||||
sb.findPrev();
|
||||
},
|
||||
"Alt-Return": function(sb) {
|
||||
if (sb.activeInput == sb.replaceInput)
|
||||
sb.replaceAll();
|
||||
sb.findAll();
|
||||
},
|
||||
"Tab": function(sb) {
|
||||
(sb.activeInput == sb.replaceInput ? sb.searchInput : sb.replaceInput).focus();
|
||||
}
|
||||
});
|
||||
|
||||
this.$searchBarKb.addCommands([{
|
||||
name: "toggleRegexpMode",
|
||||
bindKey: {win: "Alt-R|Alt-/", mac: "Ctrl-Alt-R|Ctrl-Alt-/"},
|
||||
exec: function(sb) {
|
||||
sb.regExpOption.checked = !sb.regExpOption.checked;
|
||||
sb.$syncOptions();
|
||||
}
|
||||
}, {
|
||||
name: "toggleCaseSensitive",
|
||||
bindKey: {win: "Alt-C|Alt-I", mac: "Ctrl-Alt-R|Ctrl-Alt-I"},
|
||||
exec: function(sb) {
|
||||
sb.caseSensitiveOption.checked = !sb.caseSensitiveOption.checked;
|
||||
sb.$syncOptions();
|
||||
}
|
||||
}, {
|
||||
name: "toggleWholeWords",
|
||||
bindKey: {win: "Alt-B|Alt-W", mac: "Ctrl-Alt-B|Ctrl-Alt-W"},
|
||||
exec: function(sb) {
|
||||
sb.wholeWordOption.checked = !sb.wholeWordOption.checked;
|
||||
sb.$syncOptions();
|
||||
}
|
||||
}]);
|
||||
|
||||
this.$syncOptions = function() {
|
||||
dom.setCssClass(this.regExpOption, "checked", this.regExpOption.checked);
|
||||
dom.setCssClass(this.wholeWordOption, "checked", this.wholeWordOption.checked);
|
||||
dom.setCssClass(this.caseSensitiveOption, "checked", this.caseSensitiveOption.checked);
|
||||
this.find(false, false);
|
||||
};
|
||||
|
||||
this.highlight = function(re) {
|
||||
this.editor.session.highlight(re || this.editor.$search.$options.re);
|
||||
this.editor.renderer.updateBackMarkers()
|
||||
};
|
||||
this.find = function(skipCurrent, backwards) {
|
||||
var range = this.editor.find(this.searchInput.value, {
|
||||
skipCurrent: skipCurrent,
|
||||
backwards: backwards,
|
||||
wrap: true,
|
||||
regExp: this.regExpOption.checked,
|
||||
caseSensitive: this.caseSensitiveOption.checked,
|
||||
wholeWord: this.wholeWordOption.checked
|
||||
});
|
||||
var noMatch = !range && this.searchInput.value;
|
||||
dom.setCssClass(this.searchBox, "ace_nomatch", noMatch);
|
||||
this.editor._emit("findSearchBox", { match: !noMatch });
|
||||
this.highlight();
|
||||
};
|
||||
this.findNext = function() {
|
||||
this.find(true, false);
|
||||
};
|
||||
this.findPrev = function() {
|
||||
this.find(true, true);
|
||||
};
|
||||
this.findAll = function(){
|
||||
var range = this.editor.findAll(this.searchInput.value, {
|
||||
regExp: this.regExpOption.checked,
|
||||
caseSensitive: this.caseSensitiveOption.checked,
|
||||
wholeWord: this.wholeWordOption.checked
|
||||
});
|
||||
var noMatch = !range && this.searchInput.value;
|
||||
dom.setCssClass(this.searchBox, "ace_nomatch", noMatch);
|
||||
this.editor._emit("findSearchBox", { match: !noMatch });
|
||||
this.highlight();
|
||||
this.hide();
|
||||
};
|
||||
this.replace = function() {
|
||||
if (!this.editor.getReadOnly())
|
||||
this.editor.replace(this.replaceInput.value);
|
||||
};
|
||||
this.replaceAndFindNext = function() {
|
||||
if (!this.editor.getReadOnly()) {
|
||||
this.editor.replace(this.replaceInput.value);
|
||||
this.findNext()
|
||||
}
|
||||
};
|
||||
this.replaceAll = function() {
|
||||
if (!this.editor.getReadOnly())
|
||||
this.editor.replaceAll(this.replaceInput.value);
|
||||
};
|
||||
|
||||
this.hide = function() {
|
||||
this.element.style.display = "none";
|
||||
this.editor.keyBinding.removeKeyboardHandler(this.$closeSearchBarKb);
|
||||
this.editor.focus();
|
||||
};
|
||||
this.show = function(value, isReplace) {
|
||||
this.element.style.display = "";
|
||||
this.replaceBox.style.display = isReplace ? "" : "none";
|
||||
|
||||
this.isReplace = isReplace;
|
||||
|
||||
if (value)
|
||||
this.searchInput.value = value;
|
||||
this.searchInput.focus();
|
||||
this.searchInput.select();
|
||||
|
||||
this.editor.keyBinding.addKeyboardHandler(this.$closeSearchBarKb);
|
||||
};
|
||||
|
||||
this.isFocused = function() {
|
||||
var el = document.activeElement;
|
||||
return el == this.searchInput || el == this.replaceInput;
|
||||
}
|
||||
}).call(SearchBox.prototype);
|
||||
|
||||
exports.SearchBox = SearchBox;
|
||||
|
||||
exports.Search = function(editor, isReplace) {
|
||||
var sb = editor.searchBox || new SearchBox(editor);
|
||||
sb.show(editor.session.getTextRange(), isReplace);
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
|
||||
/* ------------------------------------------------------------------------------------------
|
||||
* TODO
|
||||
* --------------------------------------------------------------------------------------- */
|
||||
/*
|
||||
- move search form to the left if it masks current word
|
||||
- includ all options that search has. ex: regex
|
||||
- searchbox.searchbox is not that pretty. we should have just searchbox
|
||||
- disable prev button if it makes sence
|
||||
*/
|
||||
@@ -0,0 +1,76 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2013 Matthew Christopher Kastor-Inare III, Atropa Inc. Intl
|
||||
* All rights reserved.
|
||||
*
|
||||
* Contributed to Ajax.org under the BSD license.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
/*jslint indent: 4, maxerr: 50, white: true, browser: true, vars: true*/
|
||||
/*global define, require */
|
||||
|
||||
/**
|
||||
* Show Settings Menu
|
||||
* @fileOverview Show Settings Menu <br />
|
||||
* Displays an interactive settings menu mostly generated on the fly based on
|
||||
* the current state of the editor.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
*/
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
var generateSettingsMenu = require('./menu_tools/generate_settings_menu').generateSettingsMenu;
|
||||
var overlayPage = require('./menu_tools/overlay_page').overlayPage;
|
||||
/**
|
||||
* This displays the settings menu if it is not already being shown.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
* @param {ace.Editor} editor An instance of the ace editor.
|
||||
*/
|
||||
function showSettingsMenu(editor) {
|
||||
// make sure the menu isn't open already.
|
||||
var sm = document.getElementById('ace_settingsmenu');
|
||||
if (!sm)
|
||||
overlayPage(editor, generateSettingsMenu(editor), '0', '0', '0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the settings menu extension. It adds the showSettingsMenu
|
||||
* method to the given editor object and adds the showSettingsMenu command
|
||||
* to the editor with appropriate keyboard shortcuts.
|
||||
* @param {ace.Editor} editor An instance of the Editor.
|
||||
*/
|
||||
module.exports.init = function(editor) {
|
||||
var Editor = require("ace/editor").Editor;
|
||||
Editor.prototype.showSettingsMenu = function() {
|
||||
showSettingsMenu(this);
|
||||
};
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,69 @@
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
var event = require("../lib/event");
|
||||
|
||||
exports.contextMenuHandler = function(e){
|
||||
var host = e.target;
|
||||
var text = host.textInput.getElement();
|
||||
if (!host.selection.isEmpty())
|
||||
return;
|
||||
var c = host.getCursorPosition();
|
||||
var r = host.session.getWordRange(c.row, c.column);
|
||||
var w = host.session.getTextRange(r);
|
||||
|
||||
host.session.tokenRe.lastIndex = 0;
|
||||
if (!host.session.tokenRe.test(w))
|
||||
return;
|
||||
var PLACEHOLDER = "\x01\x01";
|
||||
var value = w + " " + PLACEHOLDER;
|
||||
text.value = value;
|
||||
text.setSelectionRange(w.length, w.length + 1);
|
||||
text.setSelectionRange(0, 0);
|
||||
text.setSelectionRange(0, w.length);
|
||||
|
||||
var afterKeydown = false;
|
||||
event.addListener(text, "keydown", function onKeydown() {
|
||||
event.removeListener(text, "keydown", onKeydown);
|
||||
afterKeydown = true;
|
||||
});
|
||||
|
||||
host.textInput.setInputHandler(function(newVal) {
|
||||
console.log(newVal , value, text.selectionStart, text.selectionEnd)
|
||||
if (newVal == value)
|
||||
return '';
|
||||
if (newVal.lastIndexOf(value, 0) === 0)
|
||||
return newVal.slice(value.length);
|
||||
if (newVal.substr(text.selectionEnd) == value)
|
||||
return newVal.slice(0, -value.length);
|
||||
if (newVal.slice(-2) == PLACEHOLDER) {
|
||||
var val = newVal.slice(0, -2);
|
||||
if (val.slice(-1) == " ") {
|
||||
if (afterKeydown)
|
||||
return val.substring(0, text.selectionEnd);
|
||||
val = val.slice(0, -1);
|
||||
host.session.replace(r, val);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
return newVal;
|
||||
});
|
||||
};
|
||||
// todo support highlighting with typo.js
|
||||
var Editor = require("../editor").Editor;
|
||||
require("../config").defineOptions(Editor.prototype, "editor", {
|
||||
spellcheck: {
|
||||
set: function(val) {
|
||||
var text = this.textInput.getElement();
|
||||
text.spellcheck = !!val;
|
||||
if (!val)
|
||||
this.removeListener("nativecontextmenu", exports.contextMenuHandler);
|
||||
else
|
||||
this.on("nativecontextmenu", exports.contextMenuHandler);
|
||||
},
|
||||
value: true
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
+8
-10
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
@@ -14,7 +14,7 @@
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
@@ -29,14 +29,12 @@
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
"never use strict";
|
||||
|
||||
module.exports = {
|
||||
_default: {
|
||||
text: "",
|
||||
isLine: false
|
||||
}
|
||||
};
|
||||
/**
|
||||
* this is experimental, and subject to change, use at your own risk!
|
||||
*/
|
||||
module.exports = require("../split");
|
||||
|
||||
});
|
||||
|
||||
@@ -1,22 +1,31 @@
|
||||
.ace_editor {
|
||||
font-family: 'Monaco', 'Menlo', 'Droid Sans Mono', 'Courier New', monospace;
|
||||
.ace_static_highlight {
|
||||
font-family: 'Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', 'Droid Sans Mono', monospace;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.ace_editor .ace_gutter {
|
||||
.ace_static_highlight .ace_gutter {
|
||||
width: 25px !important;
|
||||
display: block;
|
||||
float: left;
|
||||
text-align: right;
|
||||
padding: 0 3px 0 0;
|
||||
text-align: right;
|
||||
padding: 0 3px 0 0;
|
||||
margin-right: 3px;
|
||||
position: static !important;
|
||||
}
|
||||
|
||||
.ace_line { clear: both; }
|
||||
.ace_static_highlight .ace_line { clear: both; }
|
||||
|
||||
*.ace_gutter-cell {
|
||||
.ace_static_highlight .ace_gutter-cell {
|
||||
-moz-user-select: -moz-none;
|
||||
-khtml-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.ace_static_highlight .ace_gutter-cell:before {
|
||||
content: counter(ace_line, decimal);
|
||||
counter-increment: ace_line;
|
||||
}
|
||||
.ace_static_highlight {
|
||||
counter-reset: ace_line;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
@@ -14,7 +14,7 @@
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
@@ -34,55 +34,157 @@ define(function(require, exports, module) {
|
||||
var EditSession = require("../edit_session").EditSession;
|
||||
var TextLayer = require("../layer/text").Text;
|
||||
var baseStyles = require("../requirejs/text!./static.css");
|
||||
var config = require("../config");
|
||||
var dom = require("../lib/dom");
|
||||
|
||||
/* Transforms a given input code snippet into HTML using the given mode
|
||||
*
|
||||
* @param {string} input Code snippet
|
||||
* @param {mode} mode Mode loaded from /ace/mode (use 'ServerSideHiglighter.getMode')
|
||||
* @param {string} r Code snippet
|
||||
* @returns {object} An object containing: html, css
|
||||
*/
|
||||
|
||||
exports.render = function(input, mode, theme, lineStart, disableGutter) {
|
||||
var highlight = function(el, opts, callback) {
|
||||
var m = el.className.match(/lang-(\w+)/);
|
||||
var mode = opts.mode || m && ("ace/mode/" + m[1]);
|
||||
if (!mode)
|
||||
return false;
|
||||
var theme = opts.theme || "ace/theme/textmate";
|
||||
|
||||
var data = "";
|
||||
var nodes = [];
|
||||
|
||||
if (el.firstElementChild) {
|
||||
var textLen = 0;
|
||||
for (var i = 0; i < el.childNodes.length; i++) {
|
||||
var ch = el.childNodes[i];
|
||||
if (ch.nodeType == 3) {
|
||||
textLen += ch.data.length;
|
||||
data += ch.data;
|
||||
} else {
|
||||
nodes.push(textLen, ch);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
data = dom.getInnerText(el);
|
||||
if (opts.trim)
|
||||
data = data.trim();
|
||||
}
|
||||
|
||||
highlight.render(data, mode, theme, opts.firstLineNumber, !opts.showGutter, function (highlighted) {
|
||||
dom.importCssString(highlighted.css, "ace_highlight");
|
||||
el.innerHTML = highlighted.html;
|
||||
var container = el.firstChild.firstChild;
|
||||
for (var i = 0; i < nodes.length; i += 2) {
|
||||
var pos = highlighted.session.doc.indexToPosition(nodes[i]);
|
||||
var node = nodes[i + 1];
|
||||
var lineEl = container.children[pos.row];
|
||||
lineEl && lineEl.appendChild(node);
|
||||
}
|
||||
callback && callback();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Transforms a given input code snippet into HTML using the given mode
|
||||
*
|
||||
* @param {string} input Code snippet
|
||||
* @param {string|mode} mode String specifying the mode to load such as
|
||||
* `ace/mode/javascript` or, a mode loaded from `/ace/mode`
|
||||
* (use 'ServerSideHiglighter.getMode').
|
||||
* @param {string|theme} theme String specifying the theme to load such as
|
||||
* `ace/theme/twilight` or, a theme loaded from `/ace/theme`.
|
||||
* @param {number} lineStart A number indicating the first line number. Defaults
|
||||
* to 1.
|
||||
* @param {boolean} disableGutter Specifies whether or not to disable the gutter.
|
||||
* `true` disables the gutter, `false` enables the gutter. Defaults to `false`.
|
||||
* @param {function} callback When specifying the mode or theme as a string,
|
||||
* this method has no return value and you must specify a callback function. The
|
||||
* callback will receive the rendered object containing the properties `html`
|
||||
* and `css`.
|
||||
* @returns {object} An object containing the properties `html` and `css`.
|
||||
*/
|
||||
highlight.render = function(input, mode, theme, lineStart, disableGutter, callback) {
|
||||
var waiting = 1;
|
||||
var modeCache = EditSession.prototype.$modes;
|
||||
|
||||
// if either the theme or the mode were specified as objects
|
||||
// then we need to lazily load them.
|
||||
if (typeof theme == "string") {
|
||||
waiting++;
|
||||
config.loadModule(['theme', theme], function(m) {
|
||||
theme = m;
|
||||
--waiting || done();
|
||||
});
|
||||
}
|
||||
// allow setting mode options e.h {path: "ace/mode/php", inline:true}
|
||||
var modeOptions;
|
||||
if (mode && typeof mode === "object" && !mode.getTokenizer) {
|
||||
modeOptions = mode;
|
||||
mode = modeOptions.path;
|
||||
}
|
||||
if (typeof mode == "string") {
|
||||
waiting++;
|
||||
config.loadModule(['mode', mode], function(m) {
|
||||
if (!modeCache[mode] || modeOptions)
|
||||
modeCache[mode] = new m.Mode(modeOptions);
|
||||
mode = modeCache[mode];
|
||||
--waiting || done();
|
||||
});
|
||||
}
|
||||
|
||||
// loads or passes the specified mode module then calls renderer
|
||||
function done() {
|
||||
var result = highlight.renderSync(input, mode, theme, lineStart, disableGutter);
|
||||
return callback ? callback(result) : result;
|
||||
}
|
||||
return --waiting || done();
|
||||
};
|
||||
|
||||
/**
|
||||
* Transforms a given input code snippet into HTML using the given mode
|
||||
* @param {string} input Code snippet
|
||||
* @param {mode} mode Mode loaded from /ace/mode (use 'ServerSideHiglighter.getMode')
|
||||
* @param {string} r Code snippet
|
||||
* @returns {object} An object containing: html, css
|
||||
*/
|
||||
highlight.renderSync = function(input, mode, theme, lineStart, disableGutter) {
|
||||
lineStart = parseInt(lineStart || 1, 10);
|
||||
|
||||
|
||||
var session = new EditSession("");
|
||||
session.setMode(mode);
|
||||
session.setUseWorker(false);
|
||||
|
||||
session.setMode(mode);
|
||||
|
||||
var textLayer = new TextLayer(document.createElement("div"));
|
||||
textLayer.setSession(session);
|
||||
textLayer.config = {
|
||||
characterWidth: 10,
|
||||
lineHeight: 20
|
||||
};
|
||||
|
||||
|
||||
session.setValue(input);
|
||||
|
||||
|
||||
var stringBuilder = [];
|
||||
var length = session.getLength();
|
||||
|
||||
for(var ix = 0; ix < length; ix++) {
|
||||
stringBuilder.push("<div class='ace_line'>");
|
||||
if (!disableGutter)
|
||||
stringBuilder.push("<span class='ace_gutter ace_gutter-cell' unselectable='on'>" + (ix + lineStart) + "</span>");
|
||||
stringBuilder.push("<span class='ace_gutter ace_gutter-cell' unselectable='on'>" + /*(ix + lineStart) + */ "</span>");
|
||||
textLayer.$renderLine(stringBuilder, ix, true, false);
|
||||
stringBuilder.push("</div>");
|
||||
stringBuilder.push("\n</div>");
|
||||
}
|
||||
|
||||
|
||||
// let's prepare the whole html
|
||||
var html = "<div class=':cssClass'>\
|
||||
<div class='ace_editor ace_scroller ace_text-layer'>\
|
||||
:code\
|
||||
</div>\
|
||||
</div>".replace(/:cssClass/, theme.cssClass).replace(/:code/, stringBuilder.join(""));
|
||||
|
||||
var html = "<div class='" + theme.cssClass + "'>" +
|
||||
"<div class='ace_static_highlight' style='counter-reset:ace_line " + (lineStart - 1) + "'>" +
|
||||
stringBuilder.join("") +
|
||||
"</div>" +
|
||||
"</div>";
|
||||
|
||||
textLayer.destroy();
|
||||
|
||||
|
||||
return {
|
||||
css: baseStyles + theme.cssText,
|
||||
html: html
|
||||
html: html,
|
||||
session: session
|
||||
};
|
||||
};
|
||||
|
||||
module.exports = highlight;
|
||||
module.exports.highlight =highlight;
|
||||
});
|
||||
|
||||
@@ -9,62 +9,82 @@ define(function(require, exports, module) {
|
||||
var assert = require("assert");
|
||||
var highlighter = require("./static_highlight");
|
||||
var JavaScriptMode = require("../mode/javascript").Mode;
|
||||
var TextMode = require("../mode/text").Mode;
|
||||
|
||||
// Execution ORDER: test.setUpSuite, setUp, testFn, tearDown, test.tearDownSuite
|
||||
module.exports = {
|
||||
timeout: 10000,
|
||||
|
||||
|
||||
"test simple snippet": function(next) {
|
||||
var theme = require("../theme/tomorrow");
|
||||
var snippet = "\
|
||||
/** this is a function\n\
|
||||
*\n\
|
||||
*/\n\
|
||||
function hello (a, b, c) {\n\
|
||||
console.log(a * b + c + 'sup?');\n\
|
||||
}";
|
||||
var snippet = [
|
||||
"/** this is a function",
|
||||
"*",
|
||||
"*/",
|
||||
"function hello (a, b, c) {",
|
||||
" console.log(a * b + c + 'sup$');",
|
||||
"}"
|
||||
].join("\n");
|
||||
var mode = new JavaScriptMode();
|
||||
|
||||
|
||||
var result = highlighter.render(snippet, mode, theme);
|
||||
assert.equal(result.html, "<div class='ace-tomorrow'> <div class='ace_editor ace_scroller ace_text-layer'> <div class='ace_line'><span class='ace_gutter ace_gutter-cell' unselectable='on'>1</span><span class='ace_comment ace_doc'>/** this is a function</span></div><div class='ace_line'><span class='ace_gutter ace_gutter-cell' unselectable='on'>2</span><span class='ace_comment ace_doc'>*</span></div><div class='ace_line'><span class='ace_gutter ace_gutter-cell' unselectable='on'>3</span><span class='ace_comment ace_doc'>*/</span></div><div class='ace_line'><span class='ace_gutter ace_gutter-cell' unselectable='on'>4</span><span class='ace_storage ace_type'>function</span> <span class='ace_entity ace_name ace_function'>hello</span> <span class='ace_paren ace_lparen'>(</span><span class='ace_variable ace_parameter'>a</span><span class='ace_punctuation ace_operator'>, </span><span class='ace_variable ace_parameter'>b</span><span class='ace_punctuation ace_operator'>, </span><span class='ace_variable ace_parameter'>c</span><span class='ace_paren ace_rparen'>)</span> <span class='ace_paren ace_lparen'>{</span></div><div class='ace_line'><span class='ace_gutter ace_gutter-cell' unselectable='on'>5</span>    <span class='ace_storage ace_type'>console</span><span class='ace_punctuation ace_operator'>.</span><span class='ace_support ace_function ace_firebug'>log</span><span class='ace_paren ace_lparen'>(</span><span class='ace_identifier'>a</span> <span class='ace_keyword ace_operator'>*</span> <span class='ace_identifier'>b</span> <span class='ace_keyword ace_operator'>+</span> <span class='ace_identifier'>c</span> <span class='ace_keyword ace_operator'>+</span> <span class='ace_string'>'sup?'</span><span class='ace_paren ace_rparen'>)</span><span class='ace_punctuation ace_operator'>;</span></div><div class='ace_line'><span class='ace_gutter ace_gutter-cell' unselectable='on'>6</span><span class='ace_paren ace_rparen'>}</span></div> </div> </div>");
|
||||
assert.equal(result.html, "<div class='ace-tomorrow'><div class='ace_static_highlight' style='counter-reset:ace_line 0'>"
|
||||
+ "<div class='ace_line'><span class='ace_gutter ace_gutter-cell' unselectable='on'></span><span class='ace_comment ace_doc'>/**\xa0this\xa0is\xa0a\xa0function</span>\n</div>"
|
||||
+ "<div class='ace_line'><span class='ace_gutter ace_gutter-cell' unselectable='on'></span><span class='ace_comment ace_doc'>*</span>\n</div>"
|
||||
+ "<div class='ace_line'><span class='ace_gutter ace_gutter-cell' unselectable='on'></span><span class='ace_comment ace_doc'>*/</span>\n</div>"
|
||||
+ "<div class='ace_line'><span class='ace_gutter ace_gutter-cell' unselectable='on'></span><span class='ace_storage ace_type'>function</span>\xa0<span class='ace_entity ace_name ace_function'>hello</span>\xa0<span class='ace_paren ace_lparen'>(</span><span class='ace_variable ace_parameter'>a</span><span class='ace_punctuation ace_operator'>,\xa0</span><span class='ace_variable ace_parameter'>b</span><span class='ace_punctuation ace_operator'>,\xa0</span><span class='ace_variable ace_parameter'>c</span><span class='ace_paren ace_rparen'>)</span>\xa0<span class='ace_paren ace_lparen'>{</span>\n</div>"
|
||||
+ "<div class='ace_line'><span class='ace_gutter ace_gutter-cell' unselectable='on'></span>\xa0\xa0\xa0\xa0<span class='ace_storage ace_type'>console</span><span class='ace_punctuation ace_operator'>.</span><span class='ace_support ace_function ace_firebug'>log</span><span class='ace_paren ace_lparen'>(</span><span class='ace_identifier'>a</span>\xa0<span class='ace_keyword ace_operator'>*</span>\xa0<span class='ace_identifier'>b</span>\xa0<span class='ace_keyword ace_operator'>+</span>\xa0<span class='ace_identifier'>c</span>\xa0<span class='ace_keyword ace_operator'>+</span>\xa0<span class='ace_string'>'sup$'</span><span class='ace_paren ace_rparen'>)</span><span class='ace_punctuation ace_operator'>;</span>\n</div>"
|
||||
+ "<div class='ace_line'><span class='ace_gutter ace_gutter-cell' unselectable='on'></span><span class='ace_paren ace_rparen'>}</span>\n</div>"
|
||||
+ "</div></div>");
|
||||
assert.ok(!!result.css);
|
||||
next();
|
||||
},
|
||||
|
||||
|
||||
"test css from theme is used": function(next) {
|
||||
var theme = require("../theme/tomorrow");
|
||||
var snippet = "\
|
||||
/** this is a function\n\
|
||||
*\n\
|
||||
*/\n\
|
||||
function hello (a, b, c) {\n\
|
||||
console.log(a * b + c + 'sup?');\n\
|
||||
}";
|
||||
var snippet = [
|
||||
"/** this is a function",
|
||||
"*",
|
||||
"*/",
|
||||
"function hello (a, b, c) {",
|
||||
" console.log(a * b + c + 'sup?');",
|
||||
"}"
|
||||
].join("\n");
|
||||
var mode = new JavaScriptMode();
|
||||
|
||||
var isError = false, result;
|
||||
result = highlighter.render(snippet, mode, theme);
|
||||
|
||||
|
||||
var result = highlighter.render(snippet, mode, theme);
|
||||
|
||||
assert.ok(result.css.indexOf(theme.cssText) !== -1);
|
||||
|
||||
|
||||
next();
|
||||
},
|
||||
|
||||
"test theme classname should be in output html": function(next) {
|
||||
var theme = require("../theme/tomorrow");
|
||||
var snippet = [
|
||||
"/** this is a function",
|
||||
"*",
|
||||
"*/",
|
||||
"function hello (a, b, c) {",
|
||||
" console.log(a * b + c + 'sup?');",
|
||||
"}"
|
||||
].join("\n");
|
||||
var mode = new JavaScriptMode();
|
||||
|
||||
var result = highlighter.render(snippet, mode, theme);
|
||||
assert.equal(!!result.html.match(/<div class='ace-tomorrow'>/), true);
|
||||
|
||||
next();
|
||||
},
|
||||
|
||||
"test theme classname should be in output html": function (next) {
|
||||
"test js string replace specials": function(next) {
|
||||
var theme = require("../theme/tomorrow");
|
||||
var snippet = "\
|
||||
/** this is a function\n\
|
||||
*\n\
|
||||
*/\n\
|
||||
function hello (a, b, c) {\n\
|
||||
console.log(a * b + c + 'sup?');\n\
|
||||
}";
|
||||
var mode = new JavaScriptMode();
|
||||
|
||||
var isError = false, result;
|
||||
result = highlighter.render(snippet, mode, theme);
|
||||
assert.equal(!!result.html.match(/<div class='ace-tomorrow'>/), true);
|
||||
|
||||
var snippet = "$'$1$2$$$&";
|
||||
var mode = new TextMode();
|
||||
|
||||
var result = highlighter.render(snippet, mode, theme);
|
||||
assert.ok(result.html.indexOf(snippet) != -1);
|
||||
|
||||
next();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
/** simple statusbar **/
|
||||
var dom = require("ace/lib/dom");
|
||||
var lang = require("ace/lib/lang");
|
||||
|
||||
var StatusBar = function(editor, parentNode) {
|
||||
this.element = dom.createElement("div");
|
||||
this.element.className = "ace_status-indicator";
|
||||
this.element.style.cssText = "display: inline-block;";
|
||||
parentNode.appendChild(this.element);
|
||||
|
||||
var statusUpdate = lang.delayedCall(function(){
|
||||
this.updateStatus(editor)
|
||||
}.bind(this));
|
||||
editor.on("changeStatus", function() {
|
||||
statusUpdate.schedule(100);
|
||||
});
|
||||
editor.on("changeSelection", function() {
|
||||
statusUpdate.schedule(100);
|
||||
});
|
||||
};
|
||||
|
||||
(function(){
|
||||
this.updateStatus = function(editor) {
|
||||
var status = [];
|
||||
function add(str, separator) {
|
||||
str && status.push(str, separator || "|");
|
||||
}
|
||||
|
||||
add(editor.keyBinding.getStatusText(editor));
|
||||
if (editor.commands.recording)
|
||||
add("REC");
|
||||
|
||||
var c = editor.selection.lead;
|
||||
add(c.row + ":" + c.column, " ");
|
||||
if (!editor.selection.isEmpty()) {
|
||||
var r = editor.getSelectionRange();
|
||||
add("(" + (r.end.row - r.start.row) + ":" +(r.end.column - r.start.column) + ")");
|
||||
}
|
||||
status.pop();
|
||||
this.element.textContent = status.join("");
|
||||
};
|
||||
}).call(StatusBar.prototype);
|
||||
|
||||
exports.StatusBar = StatusBar;
|
||||
|
||||
});
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
@@ -14,7 +14,7 @@
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
@@ -73,8 +73,8 @@ function applyStyles(elm, styles) {
|
||||
}
|
||||
|
||||
function setupContainer(element, getValue) {
|
||||
if (element.type != 'textarea') {
|
||||
throw "Textarea required!";
|
||||
if (element.type != 'textarea') {
|
||||
throw new Error("Textarea required!");
|
||||
}
|
||||
|
||||
var parentNode = element.parentNode;
|
||||
@@ -132,11 +132,7 @@ function setupContainer(element, getValue) {
|
||||
resizeEvent();
|
||||
|
||||
// Insert the div container after the element.
|
||||
if (element.nextSibling) {
|
||||
parentNode.insertBefore(container, element.nextSibling);
|
||||
} else {
|
||||
parentNode.appendChild(container);
|
||||
}
|
||||
parentNode.insertBefore(container, element.nextSibling);
|
||||
|
||||
// Override the forms onsubmit function. Set the innerHTML and value
|
||||
// of the textarea before submitting.
|
||||
@@ -145,7 +141,6 @@ function setupContainer(element, getValue) {
|
||||
var oldSumit = parentNode.onsubmit;
|
||||
// Override the onsubmit function of the form.
|
||||
parentNode.onsubmit = function(evt) {
|
||||
element.innerHTML = getValue();
|
||||
element.value = getValue();
|
||||
// If there is a onsubmit function already, then call
|
||||
// it with the current context and pass the event.
|
||||
@@ -160,7 +155,7 @@ function setupContainer(element, getValue) {
|
||||
return container;
|
||||
}
|
||||
|
||||
exports.transformTextarea = function(element, loader) {
|
||||
exports.transformTextarea = function(element, options) {
|
||||
var session;
|
||||
var container = setupContainer(element, function() {
|
||||
return session.getValue();
|
||||
@@ -177,7 +172,8 @@ exports.transformTextarea = function(element, loader) {
|
||||
left: "0px",
|
||||
right: "0px",
|
||||
bottom: "0px",
|
||||
border: "1px solid gray"
|
||||
border: "1px solid gray",
|
||||
position: "absolute"
|
||||
});
|
||||
container.appendChild(editorDiv);
|
||||
|
||||
@@ -198,7 +194,7 @@ exports.transformTextarea = function(element, loader) {
|
||||
var settingDiv = document.createElement("div");
|
||||
var settingDivStyles = {
|
||||
top: "0px",
|
||||
left: "0px",
|
||||
left: "20%",
|
||||
right: "0px",
|
||||
bottom: "0px",
|
||||
position: "absolute",
|
||||
@@ -207,7 +203,8 @@ exports.transformTextarea = function(element, loader) {
|
||||
color: "white",
|
||||
display: "none",
|
||||
overflow: "auto",
|
||||
fontSize: "14px"
|
||||
fontSize: "14px",
|
||||
boxShadow: "-5px 2px 3px gray"
|
||||
};
|
||||
if (!UA.isOldIE) {
|
||||
settingDivStyles.backgroundColor = "rgba(0, 0, 0, 0.6)";
|
||||
@@ -218,9 +215,8 @@ exports.transformTextarea = function(element, loader) {
|
||||
applyStyles(settingDiv, settingDivStyles);
|
||||
container.appendChild(settingDiv);
|
||||
|
||||
options = options || exports.defaultOptions;
|
||||
// Power up ace on the textarea:
|
||||
var options = {};
|
||||
|
||||
var editor = ace.edit(editorDiv);
|
||||
session = editor.getSession();
|
||||
|
||||
@@ -228,14 +224,14 @@ exports.transformTextarea = function(element, loader) {
|
||||
editor.focus();
|
||||
|
||||
// Add the settingPanel opener to the editor's div.
|
||||
editorDiv.appendChild(settingOpener);
|
||||
container.appendChild(settingOpener);
|
||||
|
||||
// Create the API.
|
||||
setupApi(editor, editorDiv, settingDiv, ace, options, loader);
|
||||
setupApi(editor, editorDiv, settingDiv, ace, options, load);
|
||||
|
||||
// Create the setting's panel.
|
||||
setupSettingPanel(settingDiv, settingOpener, editor, options);
|
||||
|
||||
setupSettingPanel(settingDiv, settingOpener, editor);
|
||||
|
||||
var state = "";
|
||||
event.addListener(settingOpener, "mousemove", function(e) {
|
||||
var rect = this.getBoundingClientRect();
|
||||
@@ -248,7 +244,7 @@ exports.transformTextarea = function(element, loader) {
|
||||
this.style.cursor = "nw-resize";
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
event.addListener(settingOpener, "mousedown", function(e) {
|
||||
if (state == "toggle") {
|
||||
editor.setDisplaySettings();
|
||||
@@ -280,117 +276,98 @@ function setupApi(editor, editorDiv, settingDiv, ace, options, loader) {
|
||||
loader = loader || load;
|
||||
|
||||
function toBool(value) {
|
||||
return value == "true";
|
||||
return value === "true" || value == true;
|
||||
}
|
||||
|
||||
editor.setDisplaySettings = function(display) {
|
||||
if (display == null)
|
||||
display = settingDiv.style.display == "none";
|
||||
settingDiv.style.display = display ? "block" : "none";
|
||||
if (display) {
|
||||
settingDiv.style.display = "block";
|
||||
settingDiv.hideButton.focus();
|
||||
editor.on("focus", function onFocus() {
|
||||
editor.removeListener("focus", onFocus);
|
||||
settingDiv.style.display = "none";
|
||||
});
|
||||
} else {
|
||||
editor.focus();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
editor.$setOption = editor.setOption;
|
||||
editor.$getOption = editor.getOption;
|
||||
editor.setOption = function(key, value) {
|
||||
if (options[key] == value) return;
|
||||
|
||||
switch (key) {
|
||||
case "gutter":
|
||||
renderer.setShowGutter(toBool(value));
|
||||
break;
|
||||
|
||||
case "mode":
|
||||
if (value != "text") {
|
||||
// Load the required mode file. Files get loaded only once.
|
||||
loader("mode-" + value + ".js", "ace/mode/" + value, function() {
|
||||
var aceMode = require("../mode/" + value).Mode;
|
||||
session.setMode(new aceMode());
|
||||
});
|
||||
} else {
|
||||
session.setMode(new (require("../mode/text").Mode));
|
||||
}
|
||||
editor.$setOption("mode", "ace/mode/" + value)
|
||||
break;
|
||||
|
||||
case "theme":
|
||||
if (value != "textmate") {
|
||||
// Load the required theme file. Files get loaded only once.
|
||||
loader("theme-" + value + ".js", "ace/theme/" + value, function() {
|
||||
editor.setTheme("ace/theme/" + value);
|
||||
});
|
||||
} else {
|
||||
editor.setTheme("ace/theme/textmate");
|
||||
}
|
||||
editor.$setOption("theme", "ace/theme/" + value)
|
||||
break;
|
||||
|
||||
case "fontSize":
|
||||
editorDiv.style.fontSize = value;
|
||||
case "keybindings":
|
||||
switch (value) {
|
||||
case "vim":
|
||||
editor.setKeyboardHandler("ace/keyboard/vim");
|
||||
break;
|
||||
case "emacs":
|
||||
editor.setKeyboardHandler("ace/keyboard/emacs");
|
||||
break;
|
||||
default:
|
||||
editor.setKeyboardHandler(null);
|
||||
}
|
||||
break;
|
||||
|
||||
case "softWrap":
|
||||
switch (value) {
|
||||
case "off":
|
||||
session.setUseWrapMode(false);
|
||||
renderer.setPrintMarginColumn(80);
|
||||
break;
|
||||
case "40":
|
||||
session.setUseWrapMode(true);
|
||||
session.setWrapLimitRange(40, 40);
|
||||
renderer.setPrintMarginColumn(40);
|
||||
break;
|
||||
case "80":
|
||||
session.setUseWrapMode(true);
|
||||
session.setWrapLimitRange(80, 80);
|
||||
renderer.setPrintMarginColumn(80);
|
||||
break;
|
||||
case "free":
|
||||
session.setUseWrapMode(true);
|
||||
session.setWrapLimitRange(null, null);
|
||||
renderer.setPrintMarginColumn(80);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case "useSoftTabs":
|
||||
session.setUseSoftTabs(toBool(value));
|
||||
break;
|
||||
|
||||
case "showPrintMargin":
|
||||
renderer.setShowPrintMargin(toBool(value));
|
||||
break;
|
||||
|
||||
case "showInvisibles":
|
||||
editor.setShowInvisibles(toBool(value));
|
||||
case "fontSize":
|
||||
editor.$setOption(key, value);
|
||||
break;
|
||||
|
||||
default:
|
||||
editor.$setOption(key, toBool(value));
|
||||
}
|
||||
|
||||
options[key] = value;
|
||||
};
|
||||
|
||||
editor.getOption = function(key) {
|
||||
return options[key];
|
||||
switch (key) {
|
||||
case "mode":
|
||||
return editor.$getOption("mode").substr("ace/mode/".length)
|
||||
break;
|
||||
|
||||
case "theme":
|
||||
return editor.$getOption("theme").substr("ace/theme/".length)
|
||||
break;
|
||||
|
||||
case "keybindings":
|
||||
var value = editor.getKeyboardHandler()
|
||||
switch (value && value.$id) {
|
||||
case "ace/keyboard/vim":
|
||||
return "vim";
|
||||
case "ace/keyboard/emacs":
|
||||
return "emacs";
|
||||
default:
|
||||
return "ace";
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
return editor.$getOption(key);
|
||||
}
|
||||
};
|
||||
|
||||
editor.getOptions = function() {
|
||||
return options;
|
||||
};
|
||||
|
||||
for (var option in exports.options) {
|
||||
editor.setOption(option, exports.options[option]);
|
||||
}
|
||||
|
||||
editor.setOptions(options);
|
||||
return editor;
|
||||
}
|
||||
|
||||
function setupSettingPanel(settingDiv, settingOpener, editor, options) {
|
||||
var BOOL = {
|
||||
"true": true,
|
||||
"false": false
|
||||
};
|
||||
function setupSettingPanel(settingDiv, settingOpener, editor) {
|
||||
var BOOL = null;
|
||||
|
||||
var desc = {
|
||||
mode: "Mode:",
|
||||
gutter: "Display Gutter:",
|
||||
wrap: "Soft Wrap:",
|
||||
theme: "Theme:",
|
||||
fontSize: "Font Size:",
|
||||
softWrap: "Soft Wrap:",
|
||||
showGutter: "Display Gutter:",
|
||||
keybindings: "Keyboard",
|
||||
showPrintMargin: "Show Print Margin:",
|
||||
useSoftTabs: "Use Soft Tabs:",
|
||||
showInvisibles: "Show Invisibles"
|
||||
@@ -442,7 +419,7 @@ function setupSettingPanel(settingDiv, settingOpener, editor, options) {
|
||||
twilight: "Twilight",
|
||||
vibrant_ink: "Vibrant Ink"
|
||||
},
|
||||
gutter: BOOL,
|
||||
showGutter: BOOL,
|
||||
fontSize: {
|
||||
"10px": "10px",
|
||||
"11px": "11px",
|
||||
@@ -450,12 +427,17 @@ function setupSettingPanel(settingDiv, settingOpener, editor, options) {
|
||||
"14px": "14px",
|
||||
"16px": "16px"
|
||||
},
|
||||
softWrap: {
|
||||
wrap: {
|
||||
off: "Off",
|
||||
40: "40",
|
||||
80: "80",
|
||||
free: "Free"
|
||||
},
|
||||
keybindings: {
|
||||
ace: "ace",
|
||||
vim: "vim",
|
||||
emacs: "emacs"
|
||||
},
|
||||
showPrintMargin: BOOL,
|
||||
useSoftTabs: BOOL,
|
||||
showInvisibles: BOOL
|
||||
@@ -465,6 +447,14 @@ function setupSettingPanel(settingDiv, settingOpener, editor, options) {
|
||||
table.push("<table><tr><th>Setting</th><th>Value</th></tr>");
|
||||
|
||||
function renderOption(builder, option, obj, cValue) {
|
||||
if (!obj) {
|
||||
builder.push(
|
||||
"<input type='checkbox' title='", option, "' ",
|
||||
cValue + "" == "true" ? "checked='true'" : "",
|
||||
"'></input>"
|
||||
);
|
||||
return;
|
||||
}
|
||||
builder.push("<select title='" + option + "'>");
|
||||
for (var value in obj) {
|
||||
builder.push("<option value='" + value + "' ");
|
||||
@@ -480,27 +470,30 @@ function setupSettingPanel(settingDiv, settingOpener, editor, options) {
|
||||
builder.push("</select>");
|
||||
}
|
||||
|
||||
for (var option in options) {
|
||||
for (var option in exports.defaultOptions) {
|
||||
table.push("<tr><td>", desc[option], "</td>");
|
||||
table.push("<td>");
|
||||
renderOption(table, option, optionValues[option], options[option]);
|
||||
renderOption(table, option, optionValues[option], editor.getOption(option));
|
||||
table.push("</td></tr>");
|
||||
}
|
||||
table.push("</table>");
|
||||
settingDiv.innerHTML = table.join("");
|
||||
|
||||
var onChange = function(e) {
|
||||
var select = e.currentTarget;
|
||||
editor.setOption(select.title, select.value);
|
||||
};
|
||||
var onClick = function(e) {
|
||||
var cb = e.currentTarget;
|
||||
editor.setOption(cb.title, cb.checked);
|
||||
};
|
||||
var selects = settingDiv.getElementsByTagName("select");
|
||||
for (var i = 0; i < selects.length; i++) {
|
||||
var onChange = (function() {
|
||||
var select = selects[i];
|
||||
return function() {
|
||||
var option = select.title;
|
||||
var value = select.value;
|
||||
editor.setOption(option, value);
|
||||
};
|
||||
})();
|
||||
for (var i = 0; i < selects.length; i++)
|
||||
selects[i].onchange = onChange;
|
||||
}
|
||||
var cbs = settingDiv.getElementsByTagName("input");
|
||||
for (var i = 0; i < cbs.length; i++)
|
||||
cbs[i].onclick = onClick;
|
||||
|
||||
|
||||
var button = document.createElement("input");
|
||||
button.type = "button";
|
||||
@@ -509,18 +502,20 @@ function setupSettingPanel(settingDiv, settingOpener, editor, options) {
|
||||
editor.setDisplaySettings(false);
|
||||
});
|
||||
settingDiv.appendChild(button);
|
||||
settingDiv.hideButton = button;
|
||||
}
|
||||
|
||||
// Default startup options.
|
||||
exports.options = {
|
||||
mode: "text",
|
||||
exports.defaultOptions = {
|
||||
mode: "javascript",
|
||||
theme: "textmate",
|
||||
gutter: "false",
|
||||
wrap: "off",
|
||||
fontSize: "12px",
|
||||
softWrap: "off",
|
||||
showGutter: "false",
|
||||
keybindings: "ace",
|
||||
showPrintMargin: "false",
|
||||
useSoftTabs: "true",
|
||||
showInvisibles: "true"
|
||||
showInvisibles: "false"
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2013 Matthew Christopher Kastor-Inare III, Atropa Inc. Intl
|
||||
* All rights reserved.
|
||||
*
|
||||
* Contributed to Ajax.org under the BSD license.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
/**
|
||||
* Generates a list of themes available when ace was built.
|
||||
* @fileOverview Generates a list of themes available when ace was built.
|
||||
* @author <a href="mailto:matthewkastor@gmail.com">
|
||||
* Matthew Christopher Kastor-Inare III </a><br />
|
||||
* ☭ Hial Atropa!! ☭
|
||||
*/
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
require("ace/lib/fixoldbrowsers");
|
||||
|
||||
var themeData = [
|
||||
["Chrome" ],
|
||||
["Clouds" ],
|
||||
["Crimson Editor" ],
|
||||
["Dawn" ],
|
||||
["Dreamweaver" ],
|
||||
["Eclipse" ],
|
||||
["GitHub" ],
|
||||
["Solarized Light"],
|
||||
["TextMate" ],
|
||||
["Tomorrow" ],
|
||||
["XCode" ],
|
||||
["Kuroir"],
|
||||
["KatzenMilch"],
|
||||
["Ambiance" ,"ambiance" , "dark"],
|
||||
["Chaos" ,"chaos" , "dark"],
|
||||
["Clouds Midnight" ,"clouds_midnight" , "dark"],
|
||||
["Cobalt" ,"cobalt" , "dark"],
|
||||
["idle Fingers" ,"idle_fingers" , "dark"],
|
||||
["krTheme" ,"kr_theme" , "dark"],
|
||||
["Merbivore" ,"merbivore" , "dark"],
|
||||
["Merbivore Soft" ,"merbivore_soft" , "dark"],
|
||||
["Mono Industrial" ,"mono_industrial" , "dark"],
|
||||
["Monokai" ,"monokai" , "dark"],
|
||||
["Pastel on dark" ,"pastel_on_dark" , "dark"],
|
||||
["Solarized Dark" ,"solarized_dark" , "dark"],
|
||||
["Terminal" ,"terminal" , "dark"],
|
||||
["Tomorrow Night" ,"tomorrow_night" , "dark"],
|
||||
["Tomorrow Night Blue" ,"tomorrow_night_blue" , "dark"],
|
||||
["Tomorrow Night Bright","tomorrow_night_bright" , "dark"],
|
||||
["Tomorrow Night 80s" ,"tomorrow_night_eighties" , "dark"],
|
||||
["Twilight" ,"twilight" , "dark"],
|
||||
["Vibrant Ink" ,"vibrant_ink" , "dark"]
|
||||
];
|
||||
|
||||
|
||||
exports.themesByName = {};
|
||||
|
||||
/**
|
||||
* An array containing information about available themes.
|
||||
*/
|
||||
exports.themes = themeData.map(function(data) {
|
||||
var name = data[1] || data[0].replace(/ /g, "_").toLowerCase();
|
||||
var theme = {
|
||||
caption: data[0],
|
||||
theme: "ace/theme/" + name,
|
||||
isDark: data[2] == "dark",
|
||||
name: name
|
||||
};
|
||||
exports.themesByName[name] = theme;
|
||||
return theme;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var lang = require("../lib/lang");
|
||||
|
||||
// based on http://www.freehackers.org/Indent_Finder
|
||||
exports.$detectIndentation = function(lines, fallback) {
|
||||
var stats = [];
|
||||
var changes = [];
|
||||
var tabIndents = 0;
|
||||
var prevSpaces = 0;
|
||||
var max = Math.min(lines.length, 1000);
|
||||
for (var i = 0; i < max; i++) {
|
||||
var line = lines[i];
|
||||
// ignore empty and comment lines
|
||||
if (!/^\s*[^*+\-\s]/.test(line))
|
||||
continue;
|
||||
|
||||
if (line[0] == "\t")
|
||||
tabIndents++;
|
||||
|
||||
var spaces = line.match(/^ */)[0].length;
|
||||
if (spaces && line[spaces] != "\t") {
|
||||
var diff = spaces - prevSpaces;
|
||||
if (diff > 0 && !(prevSpaces%diff) && !(spaces%diff))
|
||||
changes[diff] = (changes[diff] || 0) + 1;
|
||||
|
||||
stats[spaces] = (stats[spaces] || 0) + 1;
|
||||
}
|
||||
prevSpaces = spaces;
|
||||
|
||||
// ignore lines ending with backslash
|
||||
while (i < max && line[line.length - 1] == "\\")
|
||||
line = lines[i++];
|
||||
}
|
||||
|
||||
function getScore(indent) {
|
||||
var score = 0;
|
||||
for (var i = indent; i < stats.length; i += indent)
|
||||
score += stats[i] || 0;
|
||||
return score;
|
||||
}
|
||||
|
||||
var changesTotal = changes.reduce(function(a,b){return a+b}, 0);
|
||||
|
||||
var first = {score: 0, length: 0};
|
||||
var spaceIndents = 0;
|
||||
for (var i = 1; i < 12; i++) {
|
||||
var score = getScore(i);
|
||||
if (i == 1) {
|
||||
spaceIndents = score;
|
||||
score = stats[1] ? 0.9 : 0.8;
|
||||
if (!stats.length)
|
||||
score = 0
|
||||
} else
|
||||
score /= spaceIndents;
|
||||
|
||||
if (changes[i])
|
||||
score += changes[i] / changesTotal;
|
||||
|
||||
if (score > first.score)
|
||||
first = {score: score, length: i};
|
||||
}
|
||||
|
||||
if (first.score && first.score > 1.4)
|
||||
var tabLength = first.length;
|
||||
|
||||
if (tabIndents > spaceIndents + 1)
|
||||
return {ch: "\t", length: tabLength};
|
||||
|
||||
if (spaceIndents > tabIndents + 1)
|
||||
return {ch: " ", length: tabLength};
|
||||
};
|
||||
|
||||
exports.detectIndentation = function(session) {
|
||||
var lines = session.getLines(0, 1000);
|
||||
var indent = exports.$detectIndentation(lines) || {};
|
||||
|
||||
if (indent.ch)
|
||||
session.setUseSoftTabs(indent.ch == " ");
|
||||
|
||||
if (indent.length)
|
||||
session.setTabSize(indent.length);
|
||||
return indent;
|
||||
};
|
||||
|
||||
exports.trimTrailingSpace = function(session, trimEmpty) {
|
||||
var doc = session.getDocument();
|
||||
var lines = doc.getAllLines();
|
||||
|
||||
var min = trimEmpty ? -1 : 0;
|
||||
|
||||
for (var i = 0, l=lines.length; i < l; i++) {
|
||||
var line = lines[i];
|
||||
var index = line.search(/\s+$/);
|
||||
|
||||
if (index > min)
|
||||
doc.removeInLine(i, index, line.length);
|
||||
}
|
||||
};
|
||||
|
||||
exports.convertIndentation = function(session, ch, len) {
|
||||
var oldCh = session.getTabString()[0];
|
||||
var oldLen = session.getTabSize();
|
||||
if (!len) len = oldLen;
|
||||
if (!ch) ch = oldCh;
|
||||
|
||||
var tab = ch == "\t" ? ch: lang.stringRepeat(ch, len);
|
||||
|
||||
var doc = session.doc;
|
||||
var lines = doc.getAllLines();
|
||||
|
||||
var cache = {};
|
||||
var spaceCache = {};
|
||||
for (var i = 0, l=lines.length; i < l; i++) {
|
||||
var line = lines[i];
|
||||
var match = line.match(/^\s*/)[0];
|
||||
if (match) {
|
||||
var w = session.$getStringScreenWidth(match)[0];
|
||||
var tabCount = Math.floor(w/oldLen);
|
||||
var reminder = w%oldLen;
|
||||
var toInsert = cache[tabCount] || (cache[tabCount] = lang.stringRepeat(tab, tabCount));
|
||||
toInsert += spaceCache[reminder] || (spaceCache[reminder] = lang.stringRepeat(" ", reminder));
|
||||
|
||||
if (toInsert != match) {
|
||||
doc.removeInLine(i, 0, match.length);
|
||||
doc.insertInLine({row: i, column: 0}, toInsert);
|
||||
}
|
||||
}
|
||||
}
|
||||
session.setTabSize(len);
|
||||
session.setUseSoftTabs(ch == " ");
|
||||
};
|
||||
|
||||
exports.$parseStringArg = function(text) {
|
||||
var indent = {};
|
||||
if (/t/.test(text))
|
||||
indent.ch = "\t";
|
||||
else if (/s/.test(text))
|
||||
indent.ch = " ";
|
||||
var m = text.match(/\d+/);
|
||||
if (m)
|
||||
indent.length = parseInt(m[0], 10);
|
||||
return indent;
|
||||
};
|
||||
|
||||
exports.$parseArg = function(arg) {
|
||||
if (!arg)
|
||||
return {};
|
||||
if (typeof arg == "string")
|
||||
return exports.$parseStringArg(arg);
|
||||
if (typeof arg.text == "string")
|
||||
return exports.$parseStringArg(arg.text);
|
||||
return arg;
|
||||
};
|
||||
|
||||
exports.commands = [{
|
||||
name: "detectIndentation",
|
||||
exec: function(editor) {
|
||||
exports.detectIndentation(editor.session);
|
||||
// todo show message?
|
||||
}
|
||||
}, {
|
||||
name: "trimTrailingSpace",
|
||||
exec: function(editor) {
|
||||
exports.trimTrailingSpace(editor.session);
|
||||
}
|
||||
}, {
|
||||
name: "convertIndentation",
|
||||
exec: function(editor, arg) {
|
||||
var indent = exports.$parseArg(arg);
|
||||
exports.convertIndentation(editor.session, indent.ch, indent.length);
|
||||
}
|
||||
}, {
|
||||
name: "setIndentation",
|
||||
exec: function(editor, arg) {
|
||||
var indent = exports.$parseArg(arg);
|
||||
indent.length && editor.session.setTabSize(indent.length);
|
||||
indent.ch && editor.session.setUseSoftTabs(indent.ch == " ");
|
||||
}
|
||||
}];
|
||||
|
||||
});
|
||||
@@ -0,0 +1,116 @@
|
||||
if (typeof process !== "undefined") {
|
||||
require("amd-loader");
|
||||
require("../test/mockdom");
|
||||
}
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var assert = require("assert");
|
||||
var EditSession = require("../edit_session").EditSession;
|
||||
var whitespace = require("./whitespace");
|
||||
|
||||
// Execution ORDER: test.setUpSuite, setUp, testFn, tearDown, test.tearDownSuite
|
||||
module.exports = {
|
||||
timeout: 10000,
|
||||
|
||||
"test tab detection": function(next) {
|
||||
var s = new EditSession([
|
||||
"define({",
|
||||
"\tfoo:1,",
|
||||
"\tbar:2,",
|
||||
"\tbaz:{,",
|
||||
"\t\tx:3",
|
||||
"\t}",
|
||||
"})"
|
||||
]);
|
||||
|
||||
var indent = whitespace.$detectIndentation(s.doc.$lines);
|
||||
assert.equal(indent.ch, "\t");
|
||||
assert.equal(indent.length, undefined);
|
||||
|
||||
s.insert({row: 0, column: 0}, " ");
|
||||
indent = whitespace.$detectIndentation(s.doc.$lines);
|
||||
assert.equal(indent.ch, "\t");
|
||||
assert.equal(indent.length, 4);
|
||||
|
||||
s.setValue("");
|
||||
indent = whitespace.$detectIndentation(s.doc.$lines);
|
||||
assert.ok(!indent);
|
||||
|
||||
next();
|
||||
},
|
||||
|
||||
"test empty session": function(next) {
|
||||
var s = new EditSession([
|
||||
"define({",
|
||||
"foo:1,",
|
||||
"})"
|
||||
]);
|
||||
var indent = whitespace.$detectIndentation(s.doc.$lines);
|
||||
assert.ok(!indent);
|
||||
s.insert({row: 1, column: 0}, " x\n ");
|
||||
|
||||
indent = whitespace.$detectIndentation(s.doc.$lines);
|
||||
assert.equal(indent.ch, " ");
|
||||
assert.equal(indent.length, 4);
|
||||
|
||||
next();
|
||||
},
|
||||
|
||||
"!test one line": function(next) {
|
||||
var s = new EditSession([
|
||||
"define({",
|
||||
" foo:1,",
|
||||
"})"
|
||||
]);
|
||||
var indent = whitespace.$detectIndentation(s.doc.$lines);
|
||||
assert.equal(indent.ch, " ");
|
||||
assert.equal(indent.length, 4);
|
||||
|
||||
next();
|
||||
},
|
||||
|
||||
"test 1 width indents": function(next) {
|
||||
var s = new EditSession([
|
||||
"define({",
|
||||
" foo:1,",
|
||||
"})",
|
||||
"define({",
|
||||
" bar:1,",
|
||||
"})",
|
||||
" t",
|
||||
" t",
|
||||
" t",
|
||||
" t",
|
||||
" t",
|
||||
" t",
|
||||
" t",
|
||||
" t"
|
||||
]);
|
||||
var indent = whitespace.$detectIndentation(s.doc.$lines);
|
||||
// assert.equal(indent.ch, " ");
|
||||
// assert.equal(indent.length, 4);
|
||||
|
||||
s = new EditSession([
|
||||
"{",
|
||||
" foo:1,",
|
||||
" bar: {",
|
||||
" baz:2",
|
||||
" }",
|
||||
"}"
|
||||
]);
|
||||
indent = whitespace.$detectIndentation(s.doc.$lines);
|
||||
assert.equal(indent.ch, " ");
|
||||
assert.equal(indent.length, 1);
|
||||
|
||||
next();
|
||||
},
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
if (typeof module !== "undefined" && module === require.main) {
|
||||
require("asyncjs").test.testcase(module.exports).exec();
|
||||
}
|
||||
@@ -0,0 +1,317 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var oop = require("./lib/oop");
|
||||
var Range = require("./range").Range;
|
||||
var Search = require("./search").Search;
|
||||
var SearchHighlight = require("./search_highlight").SearchHighlight;
|
||||
var iSearchCommandModule = require("./commands/incremental_search_commands");
|
||||
var ISearchKbd = iSearchCommandModule.IncrementalSearchKeyboardHandler;
|
||||
|
||||
/**
|
||||
* @class IncrementalSearch
|
||||
*
|
||||
* Implements immediate searching while the user is typing. When incremental
|
||||
* search is activated, keystrokes into the editor will be used for composing
|
||||
* a search term. Immediately after every keystroke the search is updated:
|
||||
* - so-far-matching characters are highlighted
|
||||
* - the cursor is moved to the next match
|
||||
*
|
||||
**/
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* Creates a new `IncrementalSearch` object.
|
||||
*
|
||||
* @constructor
|
||||
**/
|
||||
function IncrementalSearch() {
|
||||
this.$options = {wrap: false, skipCurrent: false};
|
||||
this.$keyboardHandler = new ISearchKbd(this);
|
||||
}
|
||||
|
||||
oop.inherits(IncrementalSearch, Search);
|
||||
|
||||
// regexp handling
|
||||
|
||||
function isRegExp(obj) {
|
||||
return obj instanceof RegExp;
|
||||
}
|
||||
|
||||
function regExpToObject(re) {
|
||||
var string = String(re),
|
||||
start = string.indexOf('/'),
|
||||
flagStart = string.lastIndexOf('/');
|
||||
return {
|
||||
expression: string.slice(start+1, flagStart),
|
||||
flags: string.slice(flagStart+1)
|
||||
}
|
||||
}
|
||||
|
||||
function stringToRegExp(string, flags) {
|
||||
try {
|
||||
return new RegExp(string, flags);
|
||||
} catch (e) { return string; }
|
||||
}
|
||||
|
||||
function objectToRegExp(obj) {
|
||||
return stringToRegExp(obj.expression, obj.flags);
|
||||
}
|
||||
|
||||
// iSearch class
|
||||
|
||||
;(function() {
|
||||
|
||||
this.activate = function(ed, backwards) {
|
||||
this.$editor = ed;
|
||||
this.$startPos = this.$currentPos = ed.getCursorPosition();
|
||||
this.$options.needle = '';
|
||||
this.$options.backwards = backwards;
|
||||
ed.keyBinding.addKeyboardHandler(this.$keyboardHandler);
|
||||
// we need to completely intercept paste, just registering an event handler does not work
|
||||
this.$originalEditorOnPaste = ed.onPaste; ed.onPaste = this.onPaste.bind(this);
|
||||
this.$mousedownHandler = ed.addEventListener('mousedown', this.onMouseDown.bind(this));
|
||||
this.selectionFix(ed);
|
||||
this.statusMessage(true);
|
||||
}
|
||||
|
||||
this.deactivate = function(reset) {
|
||||
this.cancelSearch(reset);
|
||||
var ed = this.$editor;
|
||||
ed.keyBinding.removeKeyboardHandler(this.$keyboardHandler);
|
||||
if (this.$mousedownHandler) {
|
||||
ed.removeEventListener('mousedown', this.$mousedownHandler);
|
||||
delete this.$mousedownHandler;
|
||||
}
|
||||
ed.onPaste = this.$originalEditorOnPaste;
|
||||
this.message('');
|
||||
}
|
||||
|
||||
this.selectionFix = function(editor) {
|
||||
// Fix selection bug: When clicked inside the editor
|
||||
// editor.selection.$isEmpty is false even if the mouse click did not
|
||||
// open a selection. This is interpreted by the move commands to
|
||||
// extend the selection. To only extend the selection when there is
|
||||
// one, we clear it here
|
||||
if (editor.selection.isEmpty() && !editor.session.$emacsMark) {
|
||||
editor.clearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
this.highlight = function(regexp) {
|
||||
var sess = this.$editor.session,
|
||||
hl = sess.$isearchHighlight = sess.$isearchHighlight || sess.addDynamicMarker(
|
||||
new SearchHighlight(null, "ace_isearch-result", "text"));
|
||||
hl.setRegexp(regexp);
|
||||
sess._emit("changeBackMarker"); // force highlight layer redraw
|
||||
}
|
||||
|
||||
this.cancelSearch = function(reset) {
|
||||
var e = this.$editor;
|
||||
this.$prevNeedle = this.$options.needle;
|
||||
this.$options.needle = '';
|
||||
if (reset) {
|
||||
e.moveCursorToPosition(this.$startPos);
|
||||
this.$currentPos = this.$startPos;
|
||||
} else {
|
||||
e.pushEmacsMark && e.pushEmacsMark(this.$startPos, false);
|
||||
}
|
||||
this.highlight(null);
|
||||
return Range.fromPoints(this.$currentPos, this.$currentPos);
|
||||
}
|
||||
|
||||
this.highlightAndFindWithNeedle = function(moveToNext, needleUpdateFunc) {
|
||||
if (!this.$editor) return null;
|
||||
var options = this.$options;
|
||||
|
||||
// get search term
|
||||
if (needleUpdateFunc) {
|
||||
options.needle = needleUpdateFunc.call(this, options.needle || '') || '';
|
||||
}
|
||||
if (options.needle.length === 0) {
|
||||
this.statusMessage(true);
|
||||
return this.cancelSearch(true);
|
||||
};
|
||||
|
||||
// try to find the next occurence and enable highlighting marker
|
||||
options.start = this.$currentPos;
|
||||
var session = this.$editor.session,
|
||||
found = this.find(session),
|
||||
shouldSelect = this.$editor.emacsMark ?
|
||||
!!this.$editor.emacsMark() : !this.$editor.selection.isEmpty();
|
||||
if (found) {
|
||||
if (options.backwards) found = Range.fromPoints(found.end, found.start);
|
||||
this.$editor.selection.setRange(Range.fromPoints(shouldSelect ? this.$startPos : found.end, found.end));
|
||||
if (moveToNext) this.$currentPos = found.end;
|
||||
// highlight after cursor move, so selection works properly
|
||||
this.highlight(options.re)
|
||||
}
|
||||
|
||||
this.statusMessage(found);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
this.addString = function(s) {
|
||||
return this.highlightAndFindWithNeedle(false, function(needle) {
|
||||
if (!isRegExp(needle))
|
||||
return needle + s;
|
||||
var reObj = regExpToObject(needle);
|
||||
reObj.expression += s;
|
||||
return objectToRegExp(reObj);
|
||||
});
|
||||
}
|
||||
|
||||
this.removeChar = function(c) {
|
||||
return this.highlightAndFindWithNeedle(false, function(needle) {
|
||||
if (!isRegExp(needle))
|
||||
return needle.substring(0, needle.length-1);
|
||||
var reObj = regExpToObject(needle);
|
||||
reObj.expression = reObj.expression.substring(0, reObj.expression.length-1);
|
||||
return objectToRegExp(reObj);
|
||||
});
|
||||
}
|
||||
|
||||
this.next = function(options) {
|
||||
// try to find the next occurence of whatever we have searched for
|
||||
// earlier.
|
||||
// options = {[backwards: BOOL], [useCurrentOrPrevSearch: BOOL]}
|
||||
options = options || {};
|
||||
this.$options.backwards = !!options.backwards;
|
||||
this.$currentPos = this.$editor.getCursorPosition();
|
||||
return this.highlightAndFindWithNeedle(true, function(needle) {
|
||||
return options.useCurrentOrPrevSearch && needle.length === 0 ?
|
||||
this.$prevNeedle || '' : needle;
|
||||
});
|
||||
}
|
||||
|
||||
this.onMouseDown = function(evt) {
|
||||
// when mouse interaction happens then we quit incremental search
|
||||
this.deactivate();
|
||||
return true;
|
||||
}
|
||||
|
||||
this.onPaste = function(text) {
|
||||
this.addString(text);
|
||||
}
|
||||
|
||||
this.convertNeedleToRegExp = function() {
|
||||
return this.highlightAndFindWithNeedle(false, function(needle) {
|
||||
return isRegExp(needle) ? needle : stringToRegExp(needle, 'ig');
|
||||
});
|
||||
}
|
||||
|
||||
this.convertNeedleToString = function() {
|
||||
return this.highlightAndFindWithNeedle(false, function(needle) {
|
||||
return isRegExp(needle) ? regExpToObject(needle).expression : needle;
|
||||
});
|
||||
}
|
||||
|
||||
this.statusMessage = function(found) {
|
||||
var options = this.$options, msg = '';
|
||||
msg += options.backwards ? 'reverse-' : '';
|
||||
msg += 'isearch: ' + options.needle;
|
||||
msg += found ? '' : ' (not found)';
|
||||
this.message(msg);
|
||||
}
|
||||
|
||||
this.message = function(msg) {
|
||||
if (this.$editor.showCommandLine) {
|
||||
this.$editor.showCommandLine(msg);
|
||||
this.$editor.focus();
|
||||
} else {
|
||||
console.log(msg);
|
||||
}
|
||||
}
|
||||
|
||||
}).call(IncrementalSearch.prototype);
|
||||
|
||||
|
||||
exports.IncrementalSearch = IncrementalSearch;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* Config settings for enabling/disabling [[IncrementalSearch `IncrementalSearch`]].
|
||||
*
|
||||
**/
|
||||
|
||||
var dom = require('./lib/dom');
|
||||
dom.importCssString && dom.importCssString("\
|
||||
.ace_marker-layer .ace_isearch-result {\
|
||||
position: absolute;\
|
||||
z-index: 6;\
|
||||
-moz-box-sizing: border-box;\
|
||||
-webkit-box-sizing: border-box;\
|
||||
box-sizing: border-box;\
|
||||
}\
|
||||
div.ace_isearch-result {\
|
||||
border-radius: 4px;\
|
||||
background-color: rgba(255, 200, 0, 0.5);\
|
||||
box-shadow: 0 0 4px rgb(255, 200, 0);\
|
||||
}\
|
||||
.ace_dark div.ace_isearch-result {\
|
||||
background-color: rgb(100, 110, 160);\
|
||||
box-shadow: 0 0 4px rgb(80, 90, 140);\
|
||||
}", "incremental-search-highlighting");
|
||||
|
||||
// support for default keyboard handler
|
||||
var commands = require("./commands/command_manager");
|
||||
(function() {
|
||||
this.setupIncrementalSearch = function(editor, val) {
|
||||
if (this.usesIncrementalSearch == val) return;
|
||||
this.usesIncrementalSearch = val;
|
||||
var iSearchCommands = iSearchCommandModule.iSearchStartCommands;
|
||||
var method = val ? 'addCommands' : 'removeCommands';
|
||||
this[method](iSearchCommands);
|
||||
};
|
||||
}).call(commands.CommandManager.prototype);
|
||||
|
||||
// incremental search config option
|
||||
var Editor = require("./editor").Editor;
|
||||
require("./config").defineOptions(Editor.prototype, "editor", {
|
||||
useIncrementalSearch: {
|
||||
set: function(val) {
|
||||
this.keyBinding.$handlers.forEach(function(handler) {
|
||||
if (handler.setupIncrementalSearch) {
|
||||
handler.setupIncrementalSearch(this, val);
|
||||
}
|
||||
});
|
||||
this._emit('incrementalSearchSettingChanged', {isEnabled: val});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
@@ -0,0 +1,213 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
if (typeof process !== "undefined") {
|
||||
require("amd-loader");
|
||||
}
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var emacs = require('./keyboard/emacs');
|
||||
var EditSession = require("./edit_session").EditSession;
|
||||
var Editor = require("./editor").Editor;
|
||||
var MockRenderer = require("./test/mockrenderer").MockRenderer;
|
||||
var Range = require("./range").Range;
|
||||
var MultiSelect = require("./multi_select").MultiSelect;
|
||||
var assert = require("./test/assertions");
|
||||
var IncrementalSearch = require("./incremental_search").IncrementalSearch;
|
||||
|
||||
require("./multi_select");
|
||||
|
||||
var editor, iSearch;
|
||||
function testRanges(str, ranges) {
|
||||
ranges = ranges || editor.selection.getAllRanges();
|
||||
assert.equal(ranges + "", str + "");
|
||||
}
|
||||
|
||||
// force "rerender"
|
||||
function callHighlighterUpdate() {
|
||||
var session = editor.session,
|
||||
ranges = [],
|
||||
mockMarkerLayer = {
|
||||
drawSingleLineMarker: function(_, markerRanges) {
|
||||
ranges = ranges.concat(markerRanges);
|
||||
}
|
||||
}
|
||||
session.$isearchHighlight.update([], mockMarkerLayer, session, {
|
||||
firstRow: 0, lastRow: session.getRowLength()});
|
||||
return ranges;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
name: "ACE incremental_search.js",
|
||||
|
||||
setUp: function() {
|
||||
var session = new EditSession(["abc123", "xyz124"]);
|
||||
editor = new Editor(new MockRenderer(), session);
|
||||
new MultiSelect(editor);
|
||||
iSearch = new IncrementalSearch();
|
||||
},
|
||||
|
||||
"test: keyboard handler setup" : function() {
|
||||
iSearch.activate(editor);
|
||||
assert.equal(editor.getKeyboardHandler(), iSearch.$keyboardHandler);
|
||||
iSearch.deactivate();
|
||||
assert.notEqual(editor.getKeyboardHandler(), iSearch.$keyboardHandler);
|
||||
},
|
||||
|
||||
"test: isearch highlight setup" : function() {
|
||||
var sess = editor.session;
|
||||
iSearch.activate(editor);
|
||||
iSearch.highlight('foo');
|
||||
var highl = sess.$isearchHighlight.id;
|
||||
assert.ok(sess.$isearchHighlight, 'session has no isearch highlighter');
|
||||
assert.equal(sess.getMarkers()[highl.id], highl.id, 'isearch highlight not in markers');
|
||||
iSearch.deactivate();
|
||||
iSearch.activate(editor);
|
||||
iSearch.highlight('bar');
|
||||
var highl2 = sess.$isearchHighlight.id;
|
||||
assert.equal(highl2, highl, 'multiple isearch highlights');
|
||||
},
|
||||
|
||||
"test: find simple text incrementally" : function() {
|
||||
iSearch.activate(editor);
|
||||
var range = iSearch.addString('1'), // "1"
|
||||
highlightRanges = callHighlighterUpdate(editor.session);
|
||||
testRanges("Range: [0/3] -> [0/4]", [range], "range");
|
||||
testRanges("Range: [0/3] -> [0/4],Range: [1/3] -> [1/4]", highlightRanges, "highlight");
|
||||
|
||||
range = iSearch.addString('2'); // "12"
|
||||
highlightRanges = callHighlighterUpdate(editor.session);
|
||||
testRanges("Range: [0/3] -> [0/5]", [range], "range");
|
||||
testRanges("Range: [0/3] -> [0/5],Range: [1/3] -> [1/5]", highlightRanges, "highlight");
|
||||
|
||||
range = iSearch.addString('3'); // "123"
|
||||
highlightRanges = callHighlighterUpdate(editor.session);
|
||||
testRanges("Range: [0/3] -> [0/6]", [range], "range");
|
||||
testRanges("Range: [0/3] -> [0/6]", highlightRanges, "highlight");
|
||||
|
||||
range = iSearch.removeChar(); // "12"
|
||||
highlightRanges = callHighlighterUpdate(editor.session);
|
||||
testRanges("Range: [0/3] -> [0/5]", [range], "range");
|
||||
testRanges("Range: [0/3] -> [0/5],Range: [1/3] -> [1/5]", highlightRanges, "highlight");
|
||||
},
|
||||
|
||||
"test: forward / backward" : function() {
|
||||
iSearch.activate(editor);
|
||||
iSearch.addString('1'); iSearch.addString('2');
|
||||
var range = iSearch.next();
|
||||
testRanges("Range: [1/3] -> [1/5]", [range], "range");
|
||||
|
||||
range = iSearch.next(); // nothing to find
|
||||
testRanges("", [range], "range");
|
||||
|
||||
range = iSearch.next({backwards: true}); // backwards
|
||||
testRanges("Range: [1/5] -> [1/3]", [range], "range");
|
||||
},
|
||||
|
||||
"test: cancelSearch" : function() {
|
||||
iSearch.activate(editor);
|
||||
iSearch.addString('1'); iSearch.addString('2');
|
||||
var range = iSearch.cancelSearch(true);
|
||||
testRanges("Range: [0/0] -> [0/0]", [range], "range");
|
||||
|
||||
iSearch.addString('1'); range = iSearch.addString('2');
|
||||
testRanges("Range: [0/3] -> [0/5]", [range], "range");
|
||||
},
|
||||
|
||||
"test: failing search keeps pos" : function() {
|
||||
iSearch.activate(editor);
|
||||
iSearch.addString('1'); iSearch.addString('2');
|
||||
var range = iSearch.addString('x');
|
||||
testRanges("", [range], "range");
|
||||
assert.position(editor.getCursorPosition(), 0, 5);
|
||||
},
|
||||
|
||||
"test: backwards search" : function() {
|
||||
editor.moveCursorTo(1,0);
|
||||
iSearch.activate(editor, true);
|
||||
iSearch.addString('1'); var range = iSearch.addString('2');;
|
||||
testRanges("Range: [0/5] -> [0/3]", [range], "range");
|
||||
assert.position(editor.getCursorPosition(), 0, 3);
|
||||
},
|
||||
|
||||
"test: forwards then backwards, same result, reoriented range" : function() {
|
||||
iSearch.activate(editor);
|
||||
iSearch.addString('1'); var range = iSearch.addString('2');;
|
||||
testRanges("Range: [0/3] -> [0/5]", [range], "range");
|
||||
assert.position(editor.getCursorPosition(), 0, 5);
|
||||
|
||||
range = iSearch.next({backwards: true});
|
||||
testRanges("Range: [0/5] -> [0/3]", [range], "range");
|
||||
assert.position(editor.getCursorPosition(), 0, 3);
|
||||
},
|
||||
|
||||
"test: reuse prev search via option" : function() {
|
||||
iSearch.activate(editor);
|
||||
iSearch.addString('1'); iSearch.addString('2');;
|
||||
assert.position(editor.getCursorPosition(), 0, 5);
|
||||
iSearch.deactivate();
|
||||
|
||||
iSearch.activate(editor);
|
||||
iSearch.next({backwards: false, useCurrentOrPrevSearch: true});
|
||||
assert.position(editor.getCursorPosition(), 1, 5);
|
||||
},
|
||||
|
||||
"test: don't extend selection range if selection is empty" : function() {
|
||||
iSearch.activate(editor);
|
||||
iSearch.addString('1'); iSearch.addString('2');;
|
||||
testRanges("Range: [0/5] -> [0/5]", [editor.getSelectionRange()], "sel range");
|
||||
},
|
||||
|
||||
"test: extend selection range if selection exists" : function() {
|
||||
iSearch.activate(editor);
|
||||
editor.selection.selectTo(0, 1);
|
||||
iSearch.addString('1'); iSearch.addString('2');;
|
||||
testRanges("Range: [0/0] -> [0/5]", [editor.getSelectionRange()], "sel range");
|
||||
},
|
||||
|
||||
"test: extend selection in emacs mark mode" : function() {
|
||||
var emacs = require('./keyboard/emacs');
|
||||
editor.keyBinding.addKeyboardHandler(emacs.handler);
|
||||
emacs.handler.commands.setMark.exec(editor);
|
||||
iSearch.activate(editor);
|
||||
iSearch.addString('1'); iSearch.addString('2');
|
||||
testRanges("Range: [0/0] -> [0/5]", [editor.getSelectionRange()], "sel range");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
if (typeof module !== "undefined" && module === require.main) {
|
||||
require("asyncjs").test.testcase(module.exports).exec()
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
@@ -14,7 +14,7 @@
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
@@ -32,15 +32,18 @@ define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var dom = require("../lib/dom");
|
||||
require("../incremental_search");
|
||||
var iSearchCommandModule = require("../commands/incremental_search_commands");
|
||||
|
||||
var screenToTextBlockCoordinates = function(pageX, pageY) {
|
||||
|
||||
var screenToTextBlockCoordinates = function(x, y) {
|
||||
var canvasPos = this.scroller.getBoundingClientRect();
|
||||
|
||||
var col = Math.floor(
|
||||
(pageX + this.scrollLeft - canvasPos.left - this.$padding - dom.getPageScrollLeft()) / this.characterWidth
|
||||
(x + this.scrollLeft - canvasPos.left - this.$padding) / this.characterWidth
|
||||
);
|
||||
var row = Math.floor(
|
||||
(pageY + this.scrollTop - canvasPos.top - dom.getPageScrollTop()) / this.lineHeight
|
||||
(y + this.scrollTop - canvasPos.top) / this.lineHeight
|
||||
);
|
||||
|
||||
return this.session.screenToDocumentPosition(row, col);
|
||||
@@ -49,20 +52,26 @@ var screenToTextBlockCoordinates = function(pageX, pageY) {
|
||||
var HashHandler = require("./hash_handler").HashHandler;
|
||||
exports.handler = new HashHandler();
|
||||
|
||||
exports.handler.isEmacs = true;
|
||||
exports.handler.$id = "ace/keyboard/emacs";
|
||||
|
||||
var initialized = false;
|
||||
var $formerLongWords;
|
||||
var $formerLineStart;
|
||||
|
||||
exports.handler.attach = function(editor) {
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
dom.importCssString('\
|
||||
.emacs-mode .ace_cursor{\
|
||||
border: 2px rgba(50,250,50,0.8) solid!important;\
|
||||
border: 1px rgba(50,250,50,0.8) solid!important;\
|
||||
-moz-box-sizing: border-box!important;\
|
||||
-webkit-box-sizing: border-box!important;\
|
||||
box-sizing: border-box!important;\
|
||||
background-color: rgba(0,250,0,0.9);\
|
||||
opacity: 0.5;\
|
||||
}\
|
||||
.emacs-mode .ace_cursor.ace_hidden{\
|
||||
.emacs-mode .ace_hidden-cursors .ace_cursor{\
|
||||
opacity: 1;\
|
||||
background-color: transparent;\
|
||||
}\
|
||||
@@ -79,110 +88,268 @@ exports.handler.attach = function(editor) {
|
||||
}', 'emacsMode'
|
||||
);
|
||||
}
|
||||
// in emacs, gotowordleft/right should not count a space as a word..
|
||||
$formerLongWords = editor.session.$selectLongWords;
|
||||
editor.session.$selectLongWords = true;
|
||||
// CTRL-A should go to actual beginning of line
|
||||
$formerLineStart = editor.session.$useEmacsStyleLineStart;
|
||||
editor.session.$useEmacsStyleLineStart = true;
|
||||
|
||||
editor.session.$emacsMark = null; // the active mark
|
||||
editor.session.$emacsMarkRing = editor.session.$emacsMarkRing || [];
|
||||
|
||||
editor.emacsMark = function() {
|
||||
return this.session.$emacsMark;
|
||||
};
|
||||
|
||||
editor.setEmacsMark = function(p) {
|
||||
// to deactivate pass in a falsy value
|
||||
this.session.$emacsMark = p;
|
||||
};
|
||||
|
||||
editor.pushEmacsMark = function(p, activate) {
|
||||
var prevMark = this.session.$emacsMark;
|
||||
if (prevMark)
|
||||
this.session.$emacsMarkRing.push(prevMark);
|
||||
if (!p || activate) this.setEmacsMark(p);
|
||||
else this.session.$emacsMarkRing.push(p);
|
||||
};
|
||||
|
||||
editor.popEmacsMark = function() {
|
||||
var mark = this.emacsMark();
|
||||
if (mark) { this.setEmacsMark(null); return mark; }
|
||||
return this.session.$emacsMarkRing.pop();
|
||||
};
|
||||
|
||||
editor.getLastEmacsMark = function(p) {
|
||||
return this.session.$emacsMark || this.session.$emacsMarkRing.slice(-1)[0];
|
||||
};
|
||||
|
||||
editor.emacsMarkForSelection = function(replacement) {
|
||||
// find the mark in $emacsMarkRing corresponding to the current
|
||||
// selection
|
||||
var sel = this.selection,
|
||||
multiRangeLength = this.multiSelect ?
|
||||
this.multiSelect.getAllRanges().length : 1,
|
||||
selIndex = sel.index || 0,
|
||||
markRing = this.session.$emacsMarkRing,
|
||||
markIndex = markRing.length - (multiRangeLength - selIndex),
|
||||
lastMark = markRing[markIndex] || sel.anchor;
|
||||
if (replacement) {
|
||||
markRing.splice(markIndex, 1,
|
||||
"row" in replacement && "column" in replacement ?
|
||||
replacement : undefined);
|
||||
}
|
||||
return lastMark;
|
||||
}
|
||||
|
||||
editor.on("click", $resetMarkMode);
|
||||
editor.on("changeSession", $kbSessionChange);
|
||||
editor.renderer.screenToTextCoordinates = screenToTextBlockCoordinates;
|
||||
editor.setStyle("emacs-mode");
|
||||
editor.commands.addCommands(commands);
|
||||
exports.handler.platform = editor.commands.platform;
|
||||
editor.$emacsModeHandler = this;
|
||||
editor.addEventListener('copy', this.onCopy);
|
||||
editor.addEventListener('paste', this.onPaste);
|
||||
};
|
||||
|
||||
exports.handler.detach = function(editor) {
|
||||
delete editor.renderer.screenToTextCoordinates;
|
||||
editor.session.$selectLongWords = $formerLongWords;
|
||||
editor.session.$useEmacsStyleLineStart = $formerLineStart;
|
||||
editor.removeEventListener("click", $resetMarkMode);
|
||||
editor.removeEventListener("changeSession", $kbSessionChange);
|
||||
editor.unsetStyle("emacs-mode");
|
||||
editor.commands.removeCommands(commands);
|
||||
editor.removeEventListener('copy', this.onCopy);
|
||||
editor.removeEventListener('paste', this.onPaste);
|
||||
editor.$emacsModeHandler = null;
|
||||
};
|
||||
|
||||
var $kbSessionChange = function(e) {
|
||||
if (e.oldSession) {
|
||||
e.oldSession.$selectLongWords = $formerLongWords;
|
||||
e.oldSession.$useEmacsStyleLineStart = $formerLineStart;
|
||||
}
|
||||
|
||||
$formerLongWords = e.session.$selectLongWords;
|
||||
e.session.$selectLongWords = true;
|
||||
$formerLineStart = e.session.$useEmacsStyleLineStart;
|
||||
e.session.$useEmacsStyleLineStart = true;
|
||||
|
||||
if (!e.session.hasOwnProperty('$emacsMark'))
|
||||
e.session.$emacsMark = null;
|
||||
if (!e.session.hasOwnProperty('$emacsMarkRing'))
|
||||
e.session.$emacsMarkRing = [];
|
||||
};
|
||||
|
||||
var $resetMarkMode = function(e) {
|
||||
e.editor.session.$emacsMark = null;
|
||||
};
|
||||
|
||||
var keys = require("../lib/keys").KEY_MODS;
|
||||
var eMods = {
|
||||
C: "ctrl", S: "shift", M: "alt"
|
||||
};
|
||||
["S-C-M", "S-C", "S-M", "C-M", "S", "C", "M"].forEach(function(c) {
|
||||
var eMods = {C: "ctrl", S: "shift", M: "alt", CMD: "command"};
|
||||
var combinations = ["C-S-M-CMD",
|
||||
"S-M-CMD", "C-M-CMD", "C-S-CMD", "C-S-M",
|
||||
"M-CMD", "S-CMD", "S-M", "C-CMD", "C-M", "C-S",
|
||||
"CMD", "M", "S", "C"];
|
||||
combinations.forEach(function(c) {
|
||||
var hashId = 0;
|
||||
c.split("-").forEach(function(c){
|
||||
c.split("-").forEach(function(c) {
|
||||
hashId = hashId | keys[eMods[c]];
|
||||
});
|
||||
eMods[hashId] = c.toLowerCase() + "-";
|
||||
});
|
||||
|
||||
exports.handler.onCopy = function(e, editor) {
|
||||
if (editor.$handlesEmacsOnCopy) return;
|
||||
editor.$handlesEmacsOnCopy = true;
|
||||
exports.handler.commands.killRingSave.exec(editor);
|
||||
editor.$handlesEmacsOnCopy = false;
|
||||
};
|
||||
|
||||
exports.handler.onPaste = function(e, editor) {
|
||||
editor.pushEmacsMark(editor.getCursorPosition());
|
||||
};
|
||||
|
||||
exports.handler.bindKey = function(key, command) {
|
||||
if (typeof key == "object")
|
||||
key = key[this.platform];
|
||||
if (!key)
|
||||
return;
|
||||
|
||||
var ckb = this.commmandKeyBinding;
|
||||
var ckb = this.commandKeyBinding;
|
||||
key.split("|").forEach(function(keyPart) {
|
||||
keyPart = keyPart.toLowerCase();
|
||||
ckb[keyPart] = command;
|
||||
keyPart = keyPart.split(" ")[0];
|
||||
if (!ckb[keyPart])
|
||||
ckb[keyPart] = "null";
|
||||
// register all partial key combos as null commands
|
||||
// to be able to activate key combos with arbitrary length
|
||||
// Example: if keyPart is "C-c C-l t" then "C-c C-l t" will
|
||||
// get command assigned and "C-c" and "C-c C-l" will get
|
||||
// a null command assigned in this.commandKeyBinding. For
|
||||
// the lookup logic see handleKeyboard()
|
||||
var keyParts = keyPart.split(" ").slice(0,-1);
|
||||
keyParts.reduce(function(keyMapKeys, keyPart, i) {
|
||||
var prefix = keyMapKeys[i-1] ? keyMapKeys[i-1] + ' ' : '';
|
||||
return keyMapKeys.concat([prefix + keyPart]);
|
||||
}, []).forEach(function(keyPart) {
|
||||
if (!ckb[keyPart]) ckb[keyPart] = "null";
|
||||
});
|
||||
}, this);
|
||||
};
|
||||
|
||||
exports.handler.getStatusText = function(editor, data) {
|
||||
var str = "";
|
||||
if (data.count)
|
||||
str += data.count;
|
||||
if (data.keyChain)
|
||||
str += " " + data.keyChain
|
||||
return str;
|
||||
};
|
||||
|
||||
exports.handler.handleKeyboard = function(data, hashId, key, keyCode) {
|
||||
// if keyCode == -1 a non-printable key was pressed, such as just
|
||||
// control. Handling those is currently not supported in this handler
|
||||
if (keyCode === -1) return undefined;
|
||||
|
||||
var editor = data.editor;
|
||||
editor._signal("changeStatus");
|
||||
// insertstring data.count times
|
||||
if (hashId == -1) {
|
||||
editor.pushEmacsMark();
|
||||
if (data.count) {
|
||||
var str = Array(data.count + 1).join(key);
|
||||
var str = new Array(data.count + 1).join(key);
|
||||
data.count = null;
|
||||
return {command: "insertstring", args: str};
|
||||
}
|
||||
}
|
||||
|
||||
if (key == "\x00")
|
||||
return;
|
||||
|
||||
var modifier = eMods[hashId];
|
||||
if (modifier == "c-" || data.universalArgument) {
|
||||
|
||||
// CTRL + number / universalArgument for setting data.count
|
||||
if (modifier == "c-" || data.count) {
|
||||
var count = parseInt(key[key.length - 1]);
|
||||
if (count) {
|
||||
data.count = count;
|
||||
if (typeof count === 'number' && !isNaN(count)) {
|
||||
data.count = Math.max(data.count, 0) || 0;
|
||||
data.count = 10 * data.count + count;
|
||||
return {command: "null"};
|
||||
}
|
||||
}
|
||||
data.universalArgument = false;
|
||||
|
||||
if (modifier)
|
||||
key = modifier + key;
|
||||
// this.commandKeyBinding maps key specs like "c-p" (for CTRL + P) to
|
||||
// command objects, for lookup key needs to include the modifier
|
||||
if (modifier) key = modifier + key;
|
||||
|
||||
if (data.keyChain)
|
||||
key = data.keyChain += " " + key;
|
||||
// Key combos like CTRL+X H build up the data.keyChain
|
||||
if (data.keyChain) key = data.keyChain += " " + key;
|
||||
|
||||
var command = this.commmandKeyBinding[key];
|
||||
// Key combo prefixes get stored as "null" (String!) in this
|
||||
// this.commandKeyBinding. When encountered no command is invoked but we
|
||||
// buld up data.keyChain
|
||||
var command = this.commandKeyBinding[key];
|
||||
data.keyChain = command == "null" ? key : "";
|
||||
|
||||
if (!command)
|
||||
return;
|
||||
// there really is no command
|
||||
if (!command) return undefined;
|
||||
|
||||
if (command == "null")
|
||||
return {command: "null"};
|
||||
// we pass b/c of key combo or universalArgument
|
||||
if (command === "null") return {command: "null"};
|
||||
|
||||
if (command == "universalArgument") {
|
||||
data.universalArgument = true;
|
||||
if (command === "universalArgument") {
|
||||
// if no number pressed emacs repeats action 4 times.
|
||||
// minus sign is needed to allow next keypress to replace it
|
||||
data.count = -4;
|
||||
return {command: "null"};
|
||||
}
|
||||
|
||||
if (typeof command != "string") {
|
||||
var args = command.args;
|
||||
command = command.command;
|
||||
// lookup command
|
||||
// TODO extract special handling of markmode
|
||||
// TODO special case command.command is really unnecessary, remove
|
||||
var args;
|
||||
if (typeof command !== "string") {
|
||||
args = command.args;
|
||||
if (command.command) command = command.command;
|
||||
if (command === "goorselect") {
|
||||
command = editor.emacsMark() ? args[1] : args[0];
|
||||
args = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof command == "string") {
|
||||
command = this.commands[command] || data.editor.commands.commands[command];
|
||||
if (typeof command === "string") {
|
||||
if (command === "insertstring" ||
|
||||
command === "splitline" ||
|
||||
command === "togglecomment") {
|
||||
editor.pushEmacsMark();
|
||||
}
|
||||
command = this.commands[command] || editor.commands.commands[command];
|
||||
if (!command) return undefined;
|
||||
}
|
||||
|
||||
if (!command.readonly && !command.isYank)
|
||||
if (!command.readOnly && !command.isYank)
|
||||
data.lastCommand = null;
|
||||
|
||||
if (!command.readOnly && editor.emacsMark())
|
||||
editor.setEmacsMark(null)
|
||||
|
||||
if (data.count) {
|
||||
var count = data.count;
|
||||
data.count = 0;
|
||||
return {
|
||||
args: args,
|
||||
command: {
|
||||
exec: function(editor, args) {
|
||||
for (var i = 0; i < count; i++)
|
||||
command.exec(editor, args);
|
||||
if (!command || !command.handlesCount) {
|
||||
return {
|
||||
args: args,
|
||||
command: {
|
||||
exec: function(editor, args) {
|
||||
for (var i = 0; i < count; i++)
|
||||
command.exec(editor, args);
|
||||
},
|
||||
multiSelectAction: command.multiSelectAction
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
} else {
|
||||
if (!args) args = {};
|
||||
if (typeof args === 'object') args.count = count;
|
||||
}
|
||||
}
|
||||
|
||||
return {command: command, args: args};
|
||||
@@ -190,16 +357,16 @@ exports.handler.handleKeyboard = function(data, hashId, key, keyCode) {
|
||||
|
||||
exports.emacsKeys = {
|
||||
// movement
|
||||
"Up|C-p" : "golineup",
|
||||
"Down|C-n" : "golinedown",
|
||||
"Left|C-b" : "gotoleft",
|
||||
"Right|C-f" : "gotoright",
|
||||
"C-Left|M-b" : "gotowordleft",
|
||||
"C-Right|M-f" : "gotowordright",
|
||||
"Home|C-a" : "gotolinestart",
|
||||
"End|C-e" : "gotolineend",
|
||||
"C-Home|S-M-,": "gotostart",
|
||||
"C-End|S-M-." : "gotoend",
|
||||
"Up|C-p" : {command: "goorselect", args: ["golineup","selectup"]},
|
||||
"Down|C-n" : {command: "goorselect", args: ["golinedown","selectdown"]},
|
||||
"Left|C-b" : {command: "goorselect", args: ["gotoleft","selectleft"]},
|
||||
"Right|C-f" : {command: "goorselect", args: ["gotoright","selectright"]},
|
||||
"C-Left|M-b" : {command: "goorselect", args: ["gotowordleft","selectwordleft"]},
|
||||
"C-Right|M-f" : {command: "goorselect", args: ["gotowordright","selectwordright"]},
|
||||
"Home|C-a" : {command: "goorselect", args: ["gotolinestart","selecttolinestart"]},
|
||||
"End|C-e" : {command: "goorselect", args: ["gotolineend","selecttolineend"]},
|
||||
"C-Home|S-M-,": {command: "goorselect", args: ["gotostart","selecttostart"]},
|
||||
"C-End|S-M-." : {command: "goorselect", args: ["gotoend","selecttoend"]},
|
||||
|
||||
// selection
|
||||
"S-Up|S-C-p" : "selectup",
|
||||
@@ -219,14 +386,16 @@ exports.emacsKeys = {
|
||||
"C-x C-p": "selectall",
|
||||
|
||||
// todo fix these
|
||||
"C-Down": "gotopagedown",
|
||||
"C-Up": "gotopageup",
|
||||
"PageDown|C-v": "gotopagedown",
|
||||
"PageUp|M-v": "gotopageup",
|
||||
"C-Down": {command: "goorselect", args: ["gotopagedown","selectpagedown"]},
|
||||
"C-Up": {command: "goorselect", args: ["gotopageup","selectpageup"]},
|
||||
"PageDown|C-v": {command: "goorselect", args: ["gotopagedown","selectpagedown"]},
|
||||
"PageUp|M-v": {command: "goorselect", args: ["gotopageup","selectpageup"]},
|
||||
"S-C-Down": "selectpagedown",
|
||||
"S-C-Up": "selectpageup",
|
||||
"C-s": "findnext",
|
||||
"C-r": "findprevious",
|
||||
|
||||
"C-s": "iSearch",
|
||||
"C-r": "iSearchBackwards",
|
||||
|
||||
"M-C-s": "findnext",
|
||||
"M-C-r": "findprevious",
|
||||
"S-M-5": "replace",
|
||||
@@ -245,27 +414,26 @@ exports.emacsKeys = {
|
||||
"M-y": "yankRotate",
|
||||
"C-g": "keyboardQuit",
|
||||
|
||||
"C-w": "killRegion",
|
||||
"C-w|C-S-W": "killRegion",
|
||||
"M-w": "killRingSave",
|
||||
|
||||
"C-Space": "setMark",
|
||||
"C-x C-x": "exchangePointAndMark",
|
||||
|
||||
"C-t": "transposeletters",
|
||||
|
||||
"M-u": "touppercase",
|
||||
"M-u": "touppercase", // Doesn't work
|
||||
"M-l": "tolowercase",
|
||||
"M-/": "autocomplete",
|
||||
"M-/": "autocomplete", // Doesn't work
|
||||
"C-u": "universalArgument",
|
||||
|
||||
"M-;": "togglecomment",
|
||||
|
||||
"C-/|C-x u|S-C--|C-z": "undo",
|
||||
"S-C-/|S-C-x u|C--|S-C-z": "redo", //infinite undo?
|
||||
// vertical editing
|
||||
"C-x r": "selectRectangularRegion"
|
||||
|
||||
"C-x r": "selectRectangularRegion",
|
||||
"M-x": {command: "focusCommandLine", args: "M-x "}
|
||||
// todo
|
||||
// "M-x" "C-x C-t" "M-t" "M-c" "F11" "C-M- "M-q"
|
||||
// "C-x C-t" "M-t" "M-c" "F11" "C-M- "M-q"
|
||||
};
|
||||
|
||||
|
||||
@@ -275,7 +443,7 @@ exports.handler.addCommands({
|
||||
recenterTopBottom: function(editor) {
|
||||
var renderer = editor.renderer;
|
||||
var pos = renderer.$cursorLayer.getPixelPosition();
|
||||
var h = renderer.$size.scrollerHeight - renderer.lineHeight;
|
||||
var h = renderer.$size.scrollerHeight - renderer.lineHeight;
|
||||
var scrollTop = renderer.scrollTop;
|
||||
if (Math.abs(pos.top - scrollTop) < 2) {
|
||||
scrollTop = pos.top - h;
|
||||
@@ -289,15 +457,74 @@ exports.handler.addCommands({
|
||||
selectRectangularRegion: function(editor) {
|
||||
editor.multiSelect.toggleBlockSelection();
|
||||
},
|
||||
setMark: function() {
|
||||
setMark: {
|
||||
exec: function(editor, args) {
|
||||
// Sets mark-mode and clears current selection.
|
||||
// When mark is set, keyboard cursor movement commands become
|
||||
// selection modification commands. That is,
|
||||
// "goto" commands become "select" commands.
|
||||
// Any insertion or mouse click resets mark-mode.
|
||||
// setMark twice in a row at the same place resets markmode.
|
||||
// in multi select mode, ea selection is handled individually
|
||||
|
||||
if (args && args.count) {
|
||||
if (editor.inMultiSelectMode) editor.forEachSelection(moveToMark);
|
||||
else moveToMark();
|
||||
moveToMark();
|
||||
return;
|
||||
}
|
||||
|
||||
var mark = editor.emacsMark(),
|
||||
ranges = editor.selection.getAllRanges(),
|
||||
rangePositions = ranges.map(function(r) { return {row: r.start.row, column: r.start.column}; }),
|
||||
transientMarkModeActive = true,
|
||||
hasNoSelection = ranges.every(function(range) { return range.isEmpty(); });
|
||||
// if transientMarkModeActive then mark behavior is a little
|
||||
// different. Deactivate the mark when setMark is run with active
|
||||
// mark
|
||||
if (transientMarkModeActive && (mark || !hasNoSelection)) {
|
||||
if (editor.inMultiSelectMode) editor.forEachSelection({exec: editor.clearSelection.bind(editor)})
|
||||
else editor.clearSelection();
|
||||
if (mark) editor.pushEmacsMark(null);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mark) {
|
||||
rangePositions.forEach(function(pos) { editor.pushEmacsMark(pos); });
|
||||
editor.setEmacsMark(rangePositions[rangePositions.length-1]);
|
||||
return;
|
||||
}
|
||||
|
||||
// -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||||
|
||||
function moveToMark() {
|
||||
var mark = editor.popEmacsMark();
|
||||
mark && editor.moveCursorToPosition(mark);
|
||||
}
|
||||
|
||||
},
|
||||
readOnly: true,
|
||||
handlesCount: true
|
||||
},
|
||||
exchangePointAndMark: {
|
||||
exec: function(editor) {
|
||||
var range = editor.selection.getRange();
|
||||
editor.selection.setSelectionRange(range, !editor.selection.isBackwards());
|
||||
exec: function exchangePointAndMark$exec(editor, args) {
|
||||
var sel = editor.selection;
|
||||
if (!args.count && !sel.isEmpty()) { // just invert selection
|
||||
sel.setSelectionRange(sel.getRange(), !sel.isBackwards());
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.count) { // replace mark and point
|
||||
var pos = {row: sel.lead.row, column: sel.lead.column};
|
||||
sel.clearSelection();
|
||||
sel.moveCursorToPosition(editor.emacsMarkForSelection(pos));
|
||||
} else { // create selection to last mark
|
||||
sel.selectToPosition(editor.emacsMarkForSelection());
|
||||
}
|
||||
},
|
||||
readonly: true,
|
||||
multiselectAction: "forEach"
|
||||
readOnly: true,
|
||||
handlesCount: true,
|
||||
multiSelectAction: "forEach"
|
||||
},
|
||||
killWord: {
|
||||
exec: function(editor, dir) {
|
||||
@@ -314,10 +541,23 @@ exports.handler.addCommands({
|
||||
editor.session.remove(range);
|
||||
editor.clearSelection();
|
||||
},
|
||||
multiselectAction: "forEach"
|
||||
multiSelectAction: "forEach"
|
||||
},
|
||||
killLine: function(editor) {
|
||||
editor.selection.selectLine();
|
||||
editor.pushEmacsMark(null);
|
||||
var pos = editor.getCursorPosition();
|
||||
if (pos.column === 0 &&
|
||||
editor.session.doc.getLine(pos.row).length === 0) {
|
||||
// If an already empty line is killed, remove
|
||||
// the line entirely
|
||||
editor.selection.selectLine();
|
||||
} else {
|
||||
// otherwise just remove from the current cursor position
|
||||
// to the end (but don't delete the selection if it's before
|
||||
// the cursor)
|
||||
editor.clearSelection();
|
||||
editor.selection.selectLineEnd();
|
||||
}
|
||||
var range = editor.getSelectionRange();
|
||||
var text = editor.session.getTextRange(range);
|
||||
exports.killRing.add(text);
|
||||
@@ -326,26 +566,63 @@ exports.handler.addCommands({
|
||||
editor.clearSelection();
|
||||
},
|
||||
yank: function(editor) {
|
||||
editor.onPaste(exports.killRing.get());
|
||||
editor.onPaste(exports.killRing.get() || '');
|
||||
editor.keyBinding.$data.lastCommand = "yank";
|
||||
},
|
||||
yankRotate: function(editor) {
|
||||
if (editor.keyBinding.$data.lastCommand != "yank")
|
||||
return;
|
||||
|
||||
editor.undo();
|
||||
editor.session.$emacsMarkRing.pop(); // also undo recording mark
|
||||
editor.onPaste(exports.killRing.rotate());
|
||||
editor.keyBinding.$data.lastCommand = "yank";
|
||||
},
|
||||
killRegion: function(editor) {
|
||||
exports.killRing.add(editor.getCopyText());
|
||||
editor.commands.byName.cut.exec(editor);
|
||||
killRegion: {
|
||||
exec: function(editor) {
|
||||
exports.killRing.add(editor.getCopyText());
|
||||
editor.commands.byName.cut.exec(editor);
|
||||
},
|
||||
readOnly: true,
|
||||
multiSelectAction: "forEach"
|
||||
},
|
||||
killRingSave: function(editor) {
|
||||
exports.killRing.add(editor.getCopyText());
|
||||
killRingSave: {
|
||||
exec: function(editor) {
|
||||
// copy text and deselect. will save marks for starts of the
|
||||
// selection(s)
|
||||
|
||||
editor.$handlesEmacsOnCopy = true;
|
||||
var marks = editor.session.$emacsMarkRing.slice(),
|
||||
deselectedMarks = [];
|
||||
exports.killRing.add(editor.getCopyText());
|
||||
|
||||
setTimeout(function() {
|
||||
function deselect() {
|
||||
var sel = editor.selection, range = sel.getRange(),
|
||||
pos = sel.isBackwards() ? range.end : range.start;
|
||||
deselectedMarks.push({row: pos.row, column: pos.column});
|
||||
sel.clearSelection();
|
||||
}
|
||||
editor.$handlesEmacsOnCopy = false;
|
||||
if (editor.inMultiSelectMode) editor.forEachSelection({exec: deselect});
|
||||
else deselect();
|
||||
editor.session.$emacsMarkRing = marks.concat(deselectedMarks.reverse());
|
||||
}, 0);
|
||||
},
|
||||
readOnly: true
|
||||
},
|
||||
keyboardQuit: function(editor) {
|
||||
editor.selection.clearSelection();
|
||||
editor.setEmacsMark(null);
|
||||
editor.keyBinding.$data.count = null;
|
||||
},
|
||||
focusCommandLine: function(editor, arg) {
|
||||
if (editor.showCommandLine)
|
||||
editor.showCommandLine(arg);
|
||||
}
|
||||
});
|
||||
|
||||
exports.handler.addCommands(iSearchCommandModule.iSearchStartCommands);
|
||||
|
||||
var commands = exports.handler.commands;
|
||||
commands.yank.isYank = true;
|
||||
commands.yankRotate.isYank = true;
|
||||
@@ -357,8 +634,9 @@ exports.killRing = {
|
||||
if (this.$data.length > 30)
|
||||
this.$data.shift();
|
||||
},
|
||||
get: function() {
|
||||
return this.$data[this.$data.length - 1] || "";
|
||||
get: function(n) {
|
||||
n = n || 1;
|
||||
return this.$data.slice(this.$data.length-n, this.$data.length).reverse().join('\n');
|
||||
},
|
||||
pop: function() {
|
||||
if (this.$data.length > 1)
|
||||
@@ -371,5 +649,4 @@ exports.killRing = {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
});
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
if (typeof process !== "undefined") {
|
||||
require("amd-loader");
|
||||
}
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
require("../multi_select");
|
||||
|
||||
var EditSession = require("./../edit_session").EditSession,
|
||||
Editor = require("./../editor").Editor,
|
||||
Range = require("./../range").Range,
|
||||
MockRenderer = require("./../test/mockrenderer").MockRenderer,
|
||||
emacs = require('./emacs'),
|
||||
assert = require("./../test/assertions"),
|
||||
editor, sel;
|
||||
|
||||
function initEditor(docString) {
|
||||
var doc = new EditSession(docString.split("\n"));
|
||||
editor = new Editor(new MockRenderer(), doc);
|
||||
editor.setKeyboardHandler(emacs.handler);
|
||||
sel = editor.selection;
|
||||
}
|
||||
|
||||
function print(obj) {
|
||||
return JSON.stringify(obj, null, 2);
|
||||
}
|
||||
|
||||
function pluck(arr, what) {
|
||||
return arr.map(function(ea) { return ea[what]; });
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
"test: detach removes emacs commands from command manager": function() {
|
||||
initEditor('');
|
||||
assert.ok(!!editor.commands.byName["keyboardQuit"], 'setup error: emacs commands not installed');
|
||||
editor.keyBinding.removeKeyboardHandler(editor.getKeyboardHandler());
|
||||
assert.ok(!editor.commands.byName["keyboardQuit"], 'emacs commands not removed');
|
||||
},
|
||||
|
||||
"test: keyboardQuit clears selection": function() {
|
||||
initEditor('foo');
|
||||
editor.selectAll();
|
||||
editor.execCommand('keyboardQuit');
|
||||
assert.ok(editor.selection.isEmpty(), 'selection non-empty');
|
||||
},
|
||||
|
||||
"test: exchangePointAndMark without mark set": function() {
|
||||
initEditor('foo');
|
||||
sel.setRange(Range.fromPoints({row: 0, column: 1}, {row: 0, column: 3}));
|
||||
editor.execCommand('exchangePointAndMark');
|
||||
assert.deepEqual({row: 0, column: 1}, editor.getCursorPosition(), print(editor.getCursorPosition()));
|
||||
},
|
||||
|
||||
"test: exchangePointAndMark with mark set": function() {
|
||||
initEditor('foo');
|
||||
editor.pushEmacsMark({row: 0, column: 1});
|
||||
editor.pushEmacsMark({row: 0, column: 2});
|
||||
editor.execCommand('exchangePointAndMark', {count: 4});
|
||||
assert.deepEqual({row: 0, column: 2}, editor.getCursorPosition(), print(editor.getCursorPosition()));
|
||||
assert.deepEqual([{row: 0, column: 1}, {row: 0, column: 0}], editor.session.$emacsMarkRing, print(editor.session.$emacsMarkRing));
|
||||
},
|
||||
|
||||
"test: exchangePointAndMark with selection": function() {
|
||||
initEditor('foo');
|
||||
editor.pushEmacsMark({row: 0, column: 1});
|
||||
editor.pushEmacsMark({row: 0, column: 2});
|
||||
sel.setRange(Range.fromPoints({row: 0, column: 0}, {row: 0, column: 1}), true);
|
||||
editor.execCommand('exchangePointAndMark');
|
||||
assert.deepEqual({row: 0, column: 1}, editor.getCursorPosition(), print(editor.getCursorPosition()));
|
||||
assert.deepEqual([{row: 0, column: 1}, {row: 0, column: 2}], editor.session.$emacsMarkRing, print(editor.session.$emacsMarkRing));
|
||||
},
|
||||
|
||||
"test: exchangePointAndMark with multi selection": function() {
|
||||
initEditor('foo\nhello world\n123');
|
||||
var ranges = [[{row: 0, column: 0}, {row: 0, column: 3}],
|
||||
[{row: 1, column: 0}, {row: 1, column: 5}],
|
||||
[{row: 1, column: 6}, {row: 1, column: 11}]]
|
||||
ranges.forEach(function(r) {
|
||||
sel.addRange(Range.fromPoints(r[0], r[1]));
|
||||
});
|
||||
assert.equal("foo\nhello\nworld", editor.getSelectedText());
|
||||
editor.execCommand('exchangePointAndMark');
|
||||
assert.equal("foo\nhello\nworld", editor.getSelectedText());
|
||||
assert.deepEqual(pluck(ranges, 0), pluck(sel.getAllRanges(), 'cursor'), "selections dir not inverted");
|
||||
},
|
||||
|
||||
"test: exchangePointAndMark with multi cursors": function() {
|
||||
initEditor('foo\nhello world\n123');
|
||||
var ranges = [[{row: 0, column: 0}, {row: 0, column: 3}],
|
||||
[{row: 1, column: 0}, {row: 1, column: 5}],
|
||||
[{row: 1, column: 6}, {row: 1, column: 11}]];
|
||||
// move cursors to the start of each range and set a mark to its end
|
||||
// without selecting anything
|
||||
ranges.forEach(function(r) {
|
||||
editor.pushEmacsMark(r[1]);
|
||||
sel.addRange(Range.fromPoints(r[0], r[0]));
|
||||
});
|
||||
assert.deepEqual(pluck(ranges, 0), pluck(sel.getAllRanges(), 'cursor'), print(sel.getAllRanges()));
|
||||
editor.execCommand('exchangePointAndMark');
|
||||
assert.deepEqual(pluck(ranges, 1), pluck(sel.getAllRanges(), 'cursor'), "not inverted: " + print(sel.getAllRanges()));
|
||||
},
|
||||
|
||||
"test: setMark with multi cursors": function() {
|
||||
initEditor('foo\nhello world\n123');
|
||||
var positions = [{row: 0, column: 0},
|
||||
{row: 1, column: 0},
|
||||
{row: 1, column: 6}];
|
||||
positions.forEach(function(p) { sel.addRange(Range.fromPoints(p,p)); });
|
||||
editor.execCommand('setMark');
|
||||
assert.deepEqual(positions, editor.session.$emacsMarkRing, print(editor.session.$emacsMarkRing));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
if (typeof module !== "undefined" && module === require.main) {
|
||||
require("asyncjs").test.testcase(module.exports).exec()
|
||||
}
|
||||
@@ -31,17 +31,27 @@
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var keyUtil = require("../lib/keys");
|
||||
var keyUtil = require("../lib/keys");
|
||||
var useragent = require("../lib/useragent");
|
||||
var KEY_MODS = keyUtil.KEY_MODS;
|
||||
|
||||
function HashHandler(config, platform) {
|
||||
this.platform = platform;
|
||||
this.platform = platform || (useragent.isMac ? "mac" : "win");
|
||||
this.commands = {};
|
||||
this.commmandKeyBinding = {};
|
||||
|
||||
this.commandKeyBinding = {};
|
||||
this.addCommands(config);
|
||||
};
|
||||
this.$singleCommand = true;
|
||||
}
|
||||
|
||||
function MultiHashHandler(config, platform) {
|
||||
HashHandler.call(this, config, platform);
|
||||
this.$singleCommand = false;
|
||||
}
|
||||
|
||||
MultiHashHandler.prototype = HashHandler.prototype;
|
||||
|
||||
(function() {
|
||||
|
||||
|
||||
this.addCommand = function(command) {
|
||||
if (this.commands[command.name])
|
||||
@@ -53,47 +63,92 @@ function HashHandler(config, platform) {
|
||||
this._buildKeyHash(command);
|
||||
};
|
||||
|
||||
this.removeCommand = function(command) {
|
||||
var name = (typeof command === 'string' ? command : command.name);
|
||||
this.removeCommand = function(command, keepCommand) {
|
||||
var name = command && (typeof command === 'string' ? command : command.name);
|
||||
command = this.commands[name];
|
||||
delete this.commands[name];
|
||||
if (!keepCommand)
|
||||
delete this.commands[name];
|
||||
|
||||
// exhaustive search is brute force but since removeCommand is
|
||||
// not a performance critical operation this should be OK
|
||||
var ckb = this.commmandKeyBinding;
|
||||
for (var hashId in ckb) {
|
||||
for (var key in ckb[hashId]) {
|
||||
if (ckb[hashId][key] == command)
|
||||
delete ckb[hashId][key];
|
||||
var ckb = this.commandKeyBinding;
|
||||
for (var keyId in ckb) {
|
||||
var cmdGroup = ckb[keyId];
|
||||
if (cmdGroup == command) {
|
||||
delete ckb[keyId];
|
||||
} else if (Array.isArray(cmdGroup)) {
|
||||
var i = cmdGroup.indexOf(command);
|
||||
if (i != -1) {
|
||||
cmdGroup.splice(i, 1);
|
||||
if (cmdGroup.length == 1)
|
||||
ckb[keyId] = cmdGroup[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.bindKey = function(key, command) {
|
||||
if(!key)
|
||||
this.bindKey = function(key, command, asDefault) {
|
||||
if (typeof key == "object")
|
||||
key = key[this.platform];
|
||||
if (!key)
|
||||
return;
|
||||
if (typeof command == "function") {
|
||||
this.addCommand({exec: command, bindKey: key, name: key});
|
||||
return;
|
||||
}
|
||||
|
||||
var ckb = this.commmandKeyBinding;
|
||||
if (typeof command == "function")
|
||||
return this.addCommand({exec: command, bindKey: key, name: command.name || key});
|
||||
|
||||
key.split("|").forEach(function(keyPart) {
|
||||
var binding = this.parseKeys(keyPart, command);
|
||||
var hashId = binding.hashId;
|
||||
(ckb[hashId] || (ckb[hashId] = {}))[binding.key] = command;
|
||||
var chain = "";
|
||||
if (keyPart.indexOf(" ") != -1) {
|
||||
var parts = keyPart.split(/\s+/);
|
||||
keyPart = parts.pop();
|
||||
parts.forEach(function(keyPart) {
|
||||
var binding = this.parseKeys(keyPart);
|
||||
var id = KEY_MODS[binding.hashId] + binding.key;
|
||||
chain += (chain ? " " : "") + id;
|
||||
this._addCommandToBinding(chain, "chainKeys");
|
||||
}, this);
|
||||
chain += " ";
|
||||
}
|
||||
var binding = this.parseKeys(keyPart);
|
||||
var id = KEY_MODS[binding.hashId] + binding.key;
|
||||
this._addCommandToBinding(chain + id, command, asDefault);
|
||||
}, this);
|
||||
};
|
||||
|
||||
this._addCommandToBinding = function(keyId, command, asDefault) {
|
||||
var ckb = this.commandKeyBinding, i;
|
||||
if (!command) {
|
||||
delete ckb[keyId];
|
||||
} else if (!ckb[keyId] || this.$singleCommand) {
|
||||
ckb[keyId] = command;
|
||||
} else {
|
||||
if (!Array.isArray(ckb[keyId])) {
|
||||
ckb[keyId] = [ckb[keyId]];
|
||||
} else if ((i = ckb[keyId].indexOf(command)) != -1) {
|
||||
ckb[keyId].splice(i, 1);
|
||||
}
|
||||
|
||||
if (asDefault || command.isDefault)
|
||||
ckb[keyId].unshift(command);
|
||||
else
|
||||
ckb[keyId].push(command);
|
||||
}
|
||||
};
|
||||
|
||||
this.addCommands = function(commands) {
|
||||
commands && Object.keys(commands).forEach(function(name) {
|
||||
var command = commands[name];
|
||||
if (!command)
|
||||
return;
|
||||
|
||||
if (typeof command === "string")
|
||||
return this.bindKey(command, name);
|
||||
|
||||
if (typeof command === "function")
|
||||
command = { exec: command };
|
||||
|
||||
if (typeof command !== "object")
|
||||
return;
|
||||
|
||||
if (!command.name)
|
||||
command.name = name;
|
||||
|
||||
@@ -114,16 +169,11 @@ function HashHandler(config, platform) {
|
||||
};
|
||||
|
||||
this._buildKeyHash = function(command) {
|
||||
var binding = command.bindKey;
|
||||
if (!binding)
|
||||
return;
|
||||
|
||||
var key = typeof binding == "string" ? binding: binding[this.platform];
|
||||
this.bindKey(key, command);
|
||||
this.bindKey(command.bindKey, command);
|
||||
};
|
||||
|
||||
// accepts keys in the form ctrl+Enter or ctrl-Enter
|
||||
// keys without modifiers or shift only
|
||||
|
||||
// accepts keys in the form ctrl+Enter or ctrl-Enter
|
||||
// keys without modifiers or shift only
|
||||
this.parseKeys = function(keys) {
|
||||
var parts = keys.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(x){return x});
|
||||
var key = parts.pop();
|
||||
@@ -139,25 +189,43 @@ function HashHandler(config, platform) {
|
||||
var hashId = 0;
|
||||
for (var i = parts.length; i--;) {
|
||||
var modifier = keyUtil.KEY_MODS[parts[i]];
|
||||
if (modifier == null)
|
||||
throw "invalid modifier " + parts[i] + " in " + keys;
|
||||
if (modifier == null) {
|
||||
if (typeof console != "undefined")
|
||||
console.error("invalid modifier " + parts[i] + " in " + keys);
|
||||
return false;
|
||||
}
|
||||
hashId |= modifier;
|
||||
}
|
||||
return {key: key, hashId: hashId};
|
||||
};
|
||||
|
||||
this.findKeyCommand = function findKeyCommand(hashId, keyString) {
|
||||
var ckbr = this.commmandKeyBinding;
|
||||
return ckbr[hashId] && ckbr[hashId][keyString];
|
||||
var key = KEY_MODS[hashId] + keyString;
|
||||
return this.commandKeyBinding[key];
|
||||
};
|
||||
|
||||
this.handleKeyboard = function(data, hashId, keyString, keyCode) {
|
||||
return {
|
||||
command: this.findKeyCommand(hashId, keyString)
|
||||
};
|
||||
var key = KEY_MODS[hashId] + keyString;
|
||||
var command = this.commandKeyBinding[key];
|
||||
if (data.$keyChain) {
|
||||
data.$keyChain += " " + key;
|
||||
command = this.commandKeyBinding[data.$keyChain] || command;
|
||||
}
|
||||
|
||||
if (command) {
|
||||
if (command == "chainKeys" || command[command.length - 1] == "chainKeys") {
|
||||
data.$keyChain = data.$keyChain || key;
|
||||
return {command: "null"};
|
||||
}
|
||||
}
|
||||
|
||||
if (data.$keyChain && keyCode > 0)
|
||||
data.$keyChain = "";
|
||||
return {command: command};
|
||||
};
|
||||
|
||||
}).call(HashHandler.prototype)
|
||||
}).call(HashHandler.prototype);
|
||||
|
||||
exports.HashHandler = HashHandler;
|
||||
exports.MultiHashHandler = MultiHashHandler;
|
||||
});
|
||||
|
||||
@@ -36,7 +36,7 @@ var event = require("../lib/event");
|
||||
|
||||
var KeyBinding = function(editor) {
|
||||
this.$editor = editor;
|
||||
this.$data = { };
|
||||
this.$data = {editor: editor};
|
||||
this.$handlers = [];
|
||||
this.setDefaultHandler(editor.commands);
|
||||
};
|
||||
@@ -46,15 +46,15 @@ var KeyBinding = function(editor) {
|
||||
this.removeKeyboardHandler(this.$defaultHandler);
|
||||
this.$defaultHandler = kb;
|
||||
this.addKeyboardHandler(kb, 0);
|
||||
this.$data = {editor: this.$editor};
|
||||
};
|
||||
|
||||
this.setKeyboardHandler = function(kb) {
|
||||
if (this.$handlers[this.$handlers.length - 1] == kb)
|
||||
var h = this.$handlers;
|
||||
if (h[h.length - 1] == kb)
|
||||
return;
|
||||
|
||||
while (this.$handlers[1])
|
||||
this.removeKeyboardHandler(this.$handlers[1]);
|
||||
while (h[h.length - 1] && h[h.length - 1] != this.$defaultHandler)
|
||||
this.removeKeyboardHandler(h[h.length - 1]);
|
||||
|
||||
this.addKeyboardHandler(kb, 1);
|
||||
};
|
||||
@@ -62,6 +62,8 @@ var KeyBinding = function(editor) {
|
||||
this.addKeyboardHandler = function(kb, pos) {
|
||||
if (!kb)
|
||||
return;
|
||||
if (typeof kb == "function" && !kb.handleKeyboard)
|
||||
kb.handleKeyboard = kb;
|
||||
var i = this.$handlers.indexOf(kb);
|
||||
if (i != -1)
|
||||
this.$handlers.splice(i, 1);
|
||||
@@ -87,33 +89,42 @@ var KeyBinding = function(editor) {
|
||||
this.getKeyboardHandler = function() {
|
||||
return this.$handlers[this.$handlers.length - 1];
|
||||
};
|
||||
|
||||
this.getStatusText = function() {
|
||||
var data = this.$data;
|
||||
var editor = data.editor;
|
||||
return this.$handlers.map(function(h) {
|
||||
return h.getStatusText && h.getStatusText(editor, data) || "";
|
||||
}).filter(Boolean).join(" ");
|
||||
};
|
||||
|
||||
this.$callKeyboardHandlers = function (hashId, keyString, keyCode, e) {
|
||||
this.$callKeyboardHandlers = function(hashId, keyString, keyCode, e) {
|
||||
var toExecute;
|
||||
var success = false;
|
||||
var commands = this.$editor.commands;
|
||||
|
||||
for (var i = this.$handlers.length; i--;) {
|
||||
toExecute = this.$handlers[i].handleKeyboard(
|
||||
this.$data, hashId, keyString, keyCode, e
|
||||
);
|
||||
if (toExecute && toExecute.command)
|
||||
if (!toExecute || !toExecute.command)
|
||||
continue;
|
||||
|
||||
// allow keyboardHandler to consume keys
|
||||
if (toExecute.command == "null") {
|
||||
success = true;
|
||||
} else {
|
||||
success = commands.exec(toExecute.command, this.$editor, toExecute.args, e);
|
||||
}
|
||||
// do not stop input events to not break repeating
|
||||
if (success && e && hashId != -1 &&
|
||||
toExecute.passEvent != true && toExecute.command.passEvent != true
|
||||
) {
|
||||
event.stopEvent(e);
|
||||
}
|
||||
if (success)
|
||||
break;
|
||||
}
|
||||
|
||||
if (!toExecute || !toExecute.command)
|
||||
return false;
|
||||
|
||||
var success = false;
|
||||
var commands = this.$editor.commands;
|
||||
|
||||
// allow keyboardHandler to consume keys
|
||||
if (toExecute.command != "null")
|
||||
success = commands.exec(toExecute.command, this.$editor, toExecute.args, e);
|
||||
else
|
||||
success = toExecute.passEvent != true;
|
||||
|
||||
// do not stop input events to not break repeating
|
||||
if (success && e && hashId != -1)
|
||||
event.stopEvent(e);
|
||||
|
||||
return success;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
if (typeof process !== "undefined") {
|
||||
require("amd-loader");
|
||||
}
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var EditSession = require("./../edit_session").EditSession,
|
||||
Editor = require("./../editor").Editor,
|
||||
MockRenderer = require("./../test/mockrenderer").MockRenderer,
|
||||
assert = require("./../test/assertions"),
|
||||
HashHandler = require('./hash_handler').HashHandler,
|
||||
keys = require('../lib/keys'),
|
||||
editor;
|
||||
|
||||
function initEditor(docString) {
|
||||
var doc = new EditSession(docString.split("\n"));
|
||||
editor = new Editor(new MockRenderer(), doc);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
|
||||
"test: adding a new keyboard handler does not remove the default handler": function() {
|
||||
initEditor('abc');
|
||||
var handler = new HashHandler({'del': 'f1'});
|
||||
editor.keyBinding.setKeyboardHandler(handler);
|
||||
editor.onCommandKey({}, 0, keys['f1']);
|
||||
assert.equal('bc', editor.getValue(), "binding of new handler");
|
||||
editor.onCommandKey({}, 0, keys['delete']);
|
||||
assert.equal('c', editor.getValue(), "bindings of the old handler should still work");
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
if (typeof module !== "undefined" && module === require.main) {
|
||||
require("asyncjs").test.testcase(module.exports).exec()
|
||||
}
|
||||
@@ -223,8 +223,7 @@ StateHandler.prototype = {
|
||||
* This is a useful matching function and therefore is defined here so that
|
||||
* users of KeyboardStateMapper can use it.
|
||||
*
|
||||
* @return boolean
|
||||
* If no command key (Command|Option|Shift|Ctrl) is pressed, it
|
||||
* @return {Boolean} If no command key (Command|Option|Shift|Ctrl) is pressed, it
|
||||
* returns true. If the only the Shift key is pressed + a character
|
||||
* true is returned as well. Otherwise, false is returned.
|
||||
* Summing up, the function returns true whenever the user typed
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var HashHandler = require("./hash_handler").HashHandler;
|
||||
exports.handler = new HashHandler();
|
||||
|
||||
[{
|
||||
bindKey: "Shift-Tab|Tab",
|
||||
command: "passKeysToBrowser"
|
||||
}, {
|
||||
bindKey: {win: "Ctrl-L", mac: "Cmd-L"},
|
||||
command: "passKeysToBrowser"
|
||||
}, {
|
||||
bindKey: {win: "Ctrl-G", mac: "Cmd-G"},
|
||||
command: "gotoline"
|
||||
}, {
|
||||
bindKey: {win: "Ctrl-T|Ctrl-Shift-T", mac: "Cmd-T|Cmd-Shift-T"},
|
||||
command: "passKeysToBrowser"
|
||||
}, {
|
||||
bindKey: {win: "Ctrl-G", mac: "Cmd-G"},
|
||||
command: "passKeysToBrowser"
|
||||
}, {
|
||||
bindKey: {win: "Ctrl-G", mac: "Cmd-G"},
|
||||
command: "passKeysToBrowser"
|
||||
}, {
|
||||
name: "golineup",
|
||||
bindKey: {win: null, mac: "Ctrl-P"},
|
||||
}, {
|
||||
name: "golinedown",
|
||||
bindKey: {win: null, mac: "Ctrl-N"},
|
||||
}, {
|
||||
name: "gotoleft",
|
||||
bindKey: {win: null, mac: "Ctrl-B"},
|
||||
}, {
|
||||
name: "gotoright",
|
||||
bindKey: {win: null, mac: "Ctrl-F"},
|
||||
}, {
|
||||
name: "gotolineend",
|
||||
bindKey: {win: null, mac: "Ctrl-E"},
|
||||
}, {
|
||||
name: "gotolinestart",
|
||||
bindKey: {win: null, mac: "Ctrl-A"},
|
||||
}
|
||||
].forEach(function(k) {
|
||||
var bindKey = k.bindKey;
|
||||
if (typeof bindKey == "object")
|
||||
bindKey = bindKey[exports.handler.platform];
|
||||
exports.handler.bindKey(bindKey, k.command);
|
||||
});
|
||||
exports.handler.$id = "ace/keyboard/textarea";
|
||||
|
||||
});
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
@@ -14,7 +14,7 @@
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
@@ -34,189 +34,253 @@ define(function(require, exports, module) {
|
||||
var event = require("../lib/event");
|
||||
var useragent = require("../lib/useragent");
|
||||
var dom = require("../lib/dom");
|
||||
var lang = require("../lib/lang");
|
||||
var BROKEN_SETDATA = useragent.isChrome < 18;
|
||||
var USE_IE_MIME_TYPE = useragent.isIE;
|
||||
|
||||
var TextInput = function(parentNode, host) {
|
||||
var text = dom.createElement("textarea");
|
||||
text.className = "ace_text-input";
|
||||
/*/ debug
|
||||
text.style.opacity = 1
|
||||
text.style.background = "rgba(0, 250, 0, 0.3)"
|
||||
text.style.outline = "rgba(0, 250, 0, 0.8) solid 1px"
|
||||
text.style.outlineOffset = "3px"
|
||||
/**/
|
||||
|
||||
if (useragent.isTouchPad)
|
||||
text.setAttribute("x-palm-disable-auto-cap", true);
|
||||
|
||||
text.wrap = "off";
|
||||
text.spellcheck = false;
|
||||
text.setAttribute("wrap", "off");
|
||||
text.setAttribute("autocorrect", "off");
|
||||
text.setAttribute("autocapitalize", "off");
|
||||
text.setAttribute("spellcheck", false);
|
||||
|
||||
text.style.top = "-2em";
|
||||
text.style.opacity = "0";
|
||||
if (useragent.isOldIE) text.style.top = "-100px";
|
||||
parentNode.insertBefore(text, parentNode.firstChild);
|
||||
|
||||
var PLACEHOLDER = useragent.isIE || useragent.isOpera ? "\x01\x01" : "\x00\x00";
|
||||
var PLACEHOLDER = "\x01\x01";
|
||||
|
||||
resetValue();
|
||||
|
||||
if (isFocused())
|
||||
host.onFocus();
|
||||
|
||||
// Somehow fixes problem with firing onpropertychange on first typed char
|
||||
if (useragent.isOldIE) {
|
||||
resetSelection();
|
||||
resetValue();
|
||||
setTimeout(resetSelection);
|
||||
}
|
||||
|
||||
var cut = false
|
||||
var copied = false;
|
||||
var pasted = false;
|
||||
|
||||
var inCompostion = false;
|
||||
|
||||
var resetTimeout = null;
|
||||
|
||||
var inComposition = false;
|
||||
var tempStyle = '';
|
||||
var isSelectionEmpty = true;
|
||||
|
||||
function resetValue() {
|
||||
text.value = PLACEHOLDER;
|
||||
//http://code.google.com/p/chromium/issues/detail?id=76516
|
||||
if (useragent.isWebKit && !resetTimeout)
|
||||
resetTimeout = setTimeout(function(){
|
||||
text.value = PLACEHOLDER;
|
||||
resetSelection();
|
||||
resetTimeout = null;
|
||||
});
|
||||
// FOCUS
|
||||
// ie9 throws error if document.activeElement is accessed too soon
|
||||
try { var isFocused = document.activeElement === text; } catch(e) {}
|
||||
|
||||
event.addListener(text, "blur", function(e) {
|
||||
host.onBlur(e);
|
||||
isFocused = false;
|
||||
});
|
||||
event.addListener(text, "focus", function(e) {
|
||||
isFocused = true;
|
||||
host.onFocus(e);
|
||||
resetSelection();
|
||||
});
|
||||
this.focus = function() {
|
||||
text.style.position = "fixed";
|
||||
text.style.top = "-10000000px";
|
||||
text.focus();
|
||||
setTimeout(function() {
|
||||
text.style.position = "";
|
||||
}, 0);
|
||||
};
|
||||
this.blur = function() { text.blur(); };
|
||||
this.isFocused = function() {
|
||||
return isFocused;
|
||||
};
|
||||
|
||||
// modifying selection of blured textarea can focus it (chrome mac/linux)
|
||||
var syncSelection = lang.delayedCall(function() {
|
||||
isFocused && resetSelection(isSelectionEmpty);
|
||||
});
|
||||
var syncValue = lang.delayedCall(function() {
|
||||
if (!inComposition) {
|
||||
text.value = PLACEHOLDER;
|
||||
isFocused && resetSelection();
|
||||
}
|
||||
});
|
||||
|
||||
function resetSelection(isEmpty) {
|
||||
var selectionStart = isEmpty ? 2 : 1;
|
||||
var selectionEnd = 2;
|
||||
if (inComposition)
|
||||
return;
|
||||
|
||||
// this prevents infinite recursion on safari 8
|
||||
// see https://github.com/ajaxorg/ace/issues/2114
|
||||
inComposition = true;
|
||||
|
||||
if (inputHandler) {
|
||||
selectionStart = 0;
|
||||
selectionEnd = isEmpty ? 0 : text.value.length - 1;
|
||||
} else {
|
||||
var selectionStart = isEmpty ? 2 : 1;
|
||||
var selectionEnd = 2;
|
||||
}
|
||||
// on firefox this throws if textarea is hidden
|
||||
try {
|
||||
if (text.setSelectionRange) {
|
||||
text.setSelectionRange(selectionStart, selectionEnd);
|
||||
}
|
||||
// IE8 does not support setSelectionRange
|
||||
else if (text.createTextRange) {
|
||||
var range = text.createTextRange();
|
||||
range.collapse(true);
|
||||
range.moveEnd('character', selectionEnd);
|
||||
range.moveStart('character', selectionStart);
|
||||
range.select();
|
||||
}
|
||||
text.setSelectionRange(selectionStart, selectionEnd);
|
||||
} catch(e){}
|
||||
|
||||
inComposition = false;
|
||||
}
|
||||
|
||||
function resetValue() {
|
||||
if (inComposition)
|
||||
return;
|
||||
text.value = PLACEHOLDER;
|
||||
//http://code.google.com/p/chromium/issues/detail?id=76516
|
||||
if (useragent.isWebKit)
|
||||
syncValue.schedule();
|
||||
}
|
||||
|
||||
useragent.isWebKit || host.addEventListener('changeSelection', function() {
|
||||
if (host.selection.isEmpty() != isSelectionEmpty) {
|
||||
isSelectionEmpty = !isSelectionEmpty;
|
||||
syncSelection.schedule();
|
||||
}
|
||||
});
|
||||
|
||||
resetValue();
|
||||
if (isFocused)
|
||||
host.onFocus();
|
||||
|
||||
|
||||
var isAllSelected = function(text) {
|
||||
return text.selectionStart === 0 && text.selectionEnd === text.value.length;
|
||||
};
|
||||
// IE8 does not support setSelectionRange
|
||||
if (!text.setSelectionRange && text.createTextRange) {
|
||||
text.setSelectionRange = function(selectionStart, selectionEnd) {
|
||||
var range = this.createTextRange();
|
||||
range.collapse(true);
|
||||
range.moveStart('character', selectionStart);
|
||||
range.moveEnd('character', selectionEnd);
|
||||
range.select();
|
||||
};
|
||||
isAllSelected = function(text) {
|
||||
try {
|
||||
var range = text.ownerDocument.selection.createRange();
|
||||
}catch(e) {}
|
||||
if (!range || range.parentElement() != text) return false;
|
||||
return range.text == text.value;
|
||||
}
|
||||
}
|
||||
if (useragent.isOldIE) {
|
||||
var inPropertyChange = false;
|
||||
var onPropertyChange = function(e){
|
||||
if (inPropertyChange)
|
||||
return;
|
||||
var data = text.value;
|
||||
if (inComposition || !data || data == PLACEHOLDER)
|
||||
return;
|
||||
// can happen either after delete or during insert operation
|
||||
if (e && data == PLACEHOLDER[0])
|
||||
return syncProperty.schedule();
|
||||
|
||||
sendText(data);
|
||||
// ie8 calls propertychange handlers synchronously!
|
||||
inPropertyChange = true;
|
||||
resetValue();
|
||||
inPropertyChange = false;
|
||||
};
|
||||
var syncProperty = lang.delayedCall(onPropertyChange);
|
||||
event.addListener(text, "propertychange", onPropertyChange);
|
||||
|
||||
var keytable = { 13:1, 27:1 };
|
||||
event.addListener(text, "keyup", function (e) {
|
||||
if (inComposition && (!text.value || keytable[e.keyCode]))
|
||||
setTimeout(onCompositionEnd, 0);
|
||||
if ((text.value.charCodeAt(0)||0) < 129) {
|
||||
return syncProperty.call();
|
||||
}
|
||||
inComposition ? onCompositionUpdate() : onCompositionStart();
|
||||
});
|
||||
// when user presses backspace after focusing the editor
|
||||
// propertychange isn't called for the next character
|
||||
event.addListener(text, "keydown", function (e) {
|
||||
syncProperty.schedule(50);
|
||||
});
|
||||
}
|
||||
|
||||
var onSelect = function(e) {
|
||||
if (cut) {
|
||||
cut = false;
|
||||
return;
|
||||
}
|
||||
if (copied) {
|
||||
copied = false;
|
||||
return;
|
||||
}
|
||||
if (text.selectionStart === 0 && text.selectionEnd === text.value.length) {
|
||||
} else if (isAllSelected(text)) {
|
||||
host.selectAll();
|
||||
resetSelection();
|
||||
} else if (inputHandler) {
|
||||
resetSelection(host.selection.isEmpty());
|
||||
}
|
||||
};
|
||||
|
||||
var onInput = function(e) {
|
||||
if (inCompostion)
|
||||
return;
|
||||
|
||||
var inputHandler = null;
|
||||
this.setInputHandler = function(cb) {inputHandler = cb};
|
||||
this.getInputHandler = function() {return inputHandler};
|
||||
var afterContextMenu = false;
|
||||
|
||||
var sendText = function(data) {
|
||||
if (inputHandler) {
|
||||
data = inputHandler(data);
|
||||
inputHandler = null;
|
||||
}
|
||||
if (pasted) {
|
||||
var data = text.value;
|
||||
resetValue();
|
||||
resetSelection();
|
||||
if (data)
|
||||
host.onPaste(data);
|
||||
pasted = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var data = text.value;
|
||||
if (data.substring(0, 2) == PLACEHOLDER)
|
||||
data = data.substr(2);
|
||||
else
|
||||
data = data.substr(1);
|
||||
|
||||
resetValue();
|
||||
if (data) {
|
||||
// can happen if undo in textarea isn't stopped
|
||||
if (data[data.length - 1] == PLACEHOLDER[0])
|
||||
} else if (data == PLACEHOLDER.charAt(0)) {
|
||||
if (afterContextMenu)
|
||||
host.execCommand("del", {source: "ace"});
|
||||
else // some versions of android do not fire keydown when pressing backspace
|
||||
host.execCommand("backspace", {source: "ace"});
|
||||
} else {
|
||||
if (data.substring(0, 2) == PLACEHOLDER)
|
||||
data = data.substr(2);
|
||||
else if (data.charAt(0) == PLACEHOLDER.charAt(0))
|
||||
data = data.substr(1);
|
||||
else if (data.charAt(data.length - 1) == PLACEHOLDER.charAt(0))
|
||||
data = data.slice(0, -1);
|
||||
if (data)
|
||||
host.onTextInput(data);
|
||||
} else
|
||||
host.onDelete();
|
||||
// can happen if undo in textarea isn't stopped
|
||||
if (data.charAt(data.length - 1) == PLACEHOLDER.charAt(0))
|
||||
data = data.slice(0, -1);
|
||||
|
||||
if (data)
|
||||
host.onTextInput(data);
|
||||
}
|
||||
if (afterContextMenu)
|
||||
afterContextMenu = false;
|
||||
};
|
||||
|
||||
var onCompositionStart = function(e) {
|
||||
inCompostion = true;
|
||||
host.onCompositionStart();
|
||||
setTimeout(onCompositionUpdate, 0);
|
||||
};
|
||||
|
||||
var onCompositionUpdate = function() {
|
||||
if (!inCompostion) return;
|
||||
host.onCompositionUpdate(text.value);
|
||||
};
|
||||
|
||||
var onCompositionEnd = function(e) {
|
||||
inCompostion = false;
|
||||
host.onCompositionEnd();
|
||||
};
|
||||
|
||||
var onCut = function(e) {
|
||||
var data = host.getCopyText();
|
||||
if (!data) {
|
||||
event.preventDefault(e);
|
||||
var onInput = function(e) {
|
||||
// console.log("onInput", inComposition)
|
||||
if (inComposition)
|
||||
return;
|
||||
}
|
||||
|
||||
var data = text.value;
|
||||
sendText(data);
|
||||
resetValue();
|
||||
};
|
||||
|
||||
var handleClipboardData = function(e, data) {
|
||||
var clipboardData = e.clipboardData || window.clipboardData;
|
||||
|
||||
if (clipboardData) {
|
||||
if (!clipboardData || BROKEN_SETDATA)
|
||||
return;
|
||||
// using "Text" doesn't work on old webkit but ie needs it
|
||||
// TODO are there other browsers that require "Text"?
|
||||
var mime = USE_IE_MIME_TYPE ? "Text" : "text/plain";
|
||||
if (data) {
|
||||
// Safari 5 has clipboardData object, but does not handle setData()
|
||||
var supported = clipboardData.setData("Text", data);
|
||||
if (supported) {
|
||||
host.onCut();
|
||||
event.preventDefault(e);
|
||||
}
|
||||
}
|
||||
|
||||
if (!supported) {
|
||||
cut = true;
|
||||
text.value = data;
|
||||
text.select();
|
||||
setTimeout(function(){
|
||||
cut = false;
|
||||
resetValue();
|
||||
resetSelection();
|
||||
host.onCut();
|
||||
});
|
||||
return clipboardData.setData(mime, data) !== false;
|
||||
} else {
|
||||
return clipboardData.getData(mime);
|
||||
}
|
||||
};
|
||||
|
||||
var onCopy = function(e) {
|
||||
var doCopy = function(e, isCut) {
|
||||
var data = host.getCopyText();
|
||||
if (!data) {
|
||||
if (!data)
|
||||
return event.preventDefault(e);
|
||||
|
||||
if (handleClipboardData(e, data)) {
|
||||
isCut ? host.onCut() : host.onCopy();
|
||||
event.preventDefault(e);
|
||||
return;
|
||||
}
|
||||
|
||||
var clipboardData = e.clipboardData || window.clipboardData;
|
||||
|
||||
if (clipboardData) {
|
||||
// Safari 5 has clipboardData object, but does not handle setData()
|
||||
var supported = clipboardData.setData("Text", data);
|
||||
if (supported) {
|
||||
host.onCopy();
|
||||
event.preventDefault(e);
|
||||
}
|
||||
}
|
||||
if (!supported) {
|
||||
} else {
|
||||
copied = true;
|
||||
text.value = data;
|
||||
text.select();
|
||||
@@ -224,20 +288,26 @@ var TextInput = function(parentNode, host) {
|
||||
copied = false;
|
||||
resetValue();
|
||||
resetSelection();
|
||||
host.onCopy();
|
||||
isCut ? host.onCut() : host.onCopy();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
var onCut = function(e) {
|
||||
doCopy(e, true);
|
||||
};
|
||||
|
||||
var onCopy = function(e) {
|
||||
doCopy(e, false);
|
||||
};
|
||||
|
||||
var onPaste = function(e) {
|
||||
var clipboardData = e.clipboardData || window.clipboardData;
|
||||
|
||||
if (clipboardData) {
|
||||
var data = clipboardData.getData("Text");
|
||||
var data = handleClipboardData(e);
|
||||
if (typeof data == "string") {
|
||||
if (data)
|
||||
host.onPaste(data);
|
||||
if (useragent.isIE)
|
||||
setTimeout(resetSelection);
|
||||
event.preventDefault(e);
|
||||
}
|
||||
else {
|
||||
@@ -261,7 +331,7 @@ var TextInput = function(parentNode, host) {
|
||||
if (!('oncut' in text) || !('oncopy' in text) || !('onpaste' in text)){
|
||||
event.addListener(parentNode, "keydown", function(e) {
|
||||
if ((useragent.isMac && !e.metaKey) || !e.ctrlKey)
|
||||
return;
|
||||
return;
|
||||
|
||||
switch (e.keyCode) {
|
||||
case 67:
|
||||
@@ -277,68 +347,128 @@ var TextInput = function(parentNode, host) {
|
||||
});
|
||||
}
|
||||
|
||||
if (useragent.isOldIE) {
|
||||
event.addListener(text, "propertychange", function(e){
|
||||
if (text.value != "" && text.value != PLACEHOLDER)
|
||||
onInput(e);
|
||||
});
|
||||
|
||||
var keytable = { 13:1, 27:1 };
|
||||
event.addListener(text, "keyup", function (e) {
|
||||
if (inCompostion && (!text.value || keytable[e.keyCode]))
|
||||
setTimeout(onCompositionEnd, 0);
|
||||
if ((text.value.charCodeAt(0)|0) < 129) {
|
||||
// COMPOSITION
|
||||
var onCompositionStart = function(e) {
|
||||
if (inComposition || !host.onCompositionStart || host.$readOnly)
|
||||
return;
|
||||
// console.log("onCompositionStart", inComposition)
|
||||
inComposition = {};
|
||||
host.onCompositionStart();
|
||||
setTimeout(onCompositionUpdate, 0);
|
||||
host.on("mousedown", onCompositionEnd);
|
||||
if (!host.selection.isEmpty()) {
|
||||
host.insert("");
|
||||
host.session.markUndoGroup();
|
||||
host.selection.clearSelection();
|
||||
}
|
||||
host.session.markUndoGroup();
|
||||
};
|
||||
|
||||
var onCompositionUpdate = function() {
|
||||
// console.log("onCompositionUpdate", inComposition && JSON.stringify(text.value))
|
||||
if (!inComposition || !host.onCompositionUpdate || host.$readOnly)
|
||||
return;
|
||||
var val = text.value.replace(/\x01/g, "");
|
||||
if (inComposition.lastValue === val) return;
|
||||
|
||||
host.onCompositionUpdate(val);
|
||||
if (inComposition.lastValue)
|
||||
host.undo();
|
||||
inComposition.lastValue = val;
|
||||
if (inComposition.lastValue) {
|
||||
var r = host.selection.getRange();
|
||||
host.insert(inComposition.lastValue);
|
||||
host.session.markUndoGroup();
|
||||
inComposition.range = host.selection.getRange();
|
||||
host.selection.setRange(r);
|
||||
host.selection.clearSelection();
|
||||
}
|
||||
};
|
||||
|
||||
var onCompositionEnd = function(e) {
|
||||
if (!host.onCompositionEnd || host.$readOnly) return;
|
||||
// console.log("onCompositionEnd", inComposition &&inComposition.lastValue)
|
||||
var c = inComposition;
|
||||
inComposition = false;
|
||||
var timer = setTimeout(function() {
|
||||
timer = null;
|
||||
var str = text.value.replace(/\x01/g, "");
|
||||
// console.log(str, c.lastValue)
|
||||
if (inComposition)
|
||||
return;
|
||||
else if (str == c.lastValue)
|
||||
resetValue();
|
||||
else if (!c.lastValue && str) {
|
||||
resetValue();
|
||||
sendText(str);
|
||||
}
|
||||
inCompostion ? onCompositionUpdate() : onCompositionStart();
|
||||
});
|
||||
}
|
||||
inputHandler = function compositionInputHandler(str) {
|
||||
// console.log("onCompositionEnd", str, c.lastValue)
|
||||
if (timer)
|
||||
clearTimeout(timer);
|
||||
str = str.replace(/\x01/g, "");
|
||||
if (str == c.lastValue)
|
||||
return "";
|
||||
if (c.lastValue && timer)
|
||||
host.undo();
|
||||
return str;
|
||||
};
|
||||
host.onCompositionEnd();
|
||||
host.removeListener("mousedown", onCompositionEnd);
|
||||
if (e.type == "compositionend" && c.range) {
|
||||
host.selection.setRange(c.range);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
var syncComposition = lang.delayedCall(onCompositionUpdate, 50);
|
||||
|
||||
event.addListener(text, "compositionstart", onCompositionStart);
|
||||
if (useragent.isGecko) {
|
||||
event.addListener(text, "text", onCompositionUpdate);
|
||||
}
|
||||
if (useragent.isWebKit) {
|
||||
event.addListener(text, "keyup", onCompositionUpdate);
|
||||
event.addListener(text, "text", function(){syncComposition.schedule()});
|
||||
} else {
|
||||
event.addListener(text, "keyup", function(){syncComposition.schedule()});
|
||||
event.addListener(text, "keydown", function(){syncComposition.schedule()});
|
||||
}
|
||||
event.addListener(text, "compositionend", onCompositionEnd);
|
||||
|
||||
event.addListener(text, "blur", function() {
|
||||
host.onBlur();
|
||||
});
|
||||
|
||||
event.addListener(text, "focus", function() {
|
||||
host.onFocus();
|
||||
resetSelection();
|
||||
});
|
||||
|
||||
this.focus = function() {
|
||||
text.focus();
|
||||
};
|
||||
|
||||
this.blur = function() {
|
||||
text.blur();
|
||||
};
|
||||
|
||||
function isFocused() {
|
||||
return document.activeElement === text;
|
||||
}
|
||||
this.isFocused = isFocused;
|
||||
|
||||
this.getElement = function() {
|
||||
return text;
|
||||
};
|
||||
|
||||
this.setReadOnly = function(readOnly) {
|
||||
text.readOnly = readOnly;
|
||||
};
|
||||
|
||||
this.onContextMenu = function(e) {
|
||||
afterContextMenu = true;
|
||||
resetSelection(host.selection.isEmpty());
|
||||
host._emit("nativecontextmenu", {target: host, domEvent: e});
|
||||
this.moveToMouse(e, true);
|
||||
};
|
||||
|
||||
this.moveToMouse = function(e, bringToFront) {
|
||||
if (!bringToFront && useragent.isOldIE)
|
||||
return;
|
||||
if (!tempStyle)
|
||||
tempStyle = text.style.cssText;
|
||||
text.style.cssText = (bringToFront ? "z-index:100000;" : "")
|
||||
+ "height:" + text.style.height + ";"
|
||||
+ (useragent.isIE ? "opacity:0.1;" : "");
|
||||
|
||||
text.style.cssText =
|
||||
"position:fixed; z-index:100000;" +
|
||||
(useragent.isIE ? "background:rgba(0, 0, 0, 0.03); opacity:0.1;" : "") + //"background:rgba(250, 0, 0, 0.3); opacity:1;" +
|
||||
"left:" + (e.clientX - 2) + "px; top:" + (e.clientY - 2) + "px;";
|
||||
|
||||
resetSelection(host.selection.isEmpty());
|
||||
var rect = host.container.getBoundingClientRect();
|
||||
var style = dom.computedStyle(host.container);
|
||||
var top = rect.top + (parseInt(style.borderTopWidth) || 0);
|
||||
var left = rect.left + (parseInt(rect.borderLeftWidth) || 0);
|
||||
var maxTop = rect.bottom - top - text.clientHeight -2;
|
||||
var move = function(e) {
|
||||
text.style.left = e.clientX - left - 2 + "px";
|
||||
text.style.top = Math.min(e.clientY - top - 2, maxTop) + "px";
|
||||
};
|
||||
move(e);
|
||||
|
||||
if (e.type != "mousedown")
|
||||
return;
|
||||
@@ -347,15 +477,15 @@ var TextInput = function(parentNode, host) {
|
||||
host.renderer.$keepTextAreaAtCursor = null;
|
||||
|
||||
// on windows context menu is opened after mouseup
|
||||
if (useragent.isWin)
|
||||
event.capture(host.container, function(e) {
|
||||
text.style.left = e.clientX - 2 + "px";
|
||||
text.style.top = e.clientY - 2 + "px";
|
||||
}, onContextMenuClose);
|
||||
if (useragent.isWin && !useragent.isOldIE)
|
||||
event.capture(host.container, move, onContextMenuClose);
|
||||
};
|
||||
|
||||
this.onContextMenuClose = onContextMenuClose;
|
||||
var closeTimeout;
|
||||
function onContextMenuClose() {
|
||||
setTimeout(function () {
|
||||
clearTimeout(closeTimeout)
|
||||
closeTimeout = setTimeout(function () {
|
||||
if (tempStyle) {
|
||||
text.style.cssText = tempStyle;
|
||||
tempStyle = '';
|
||||
@@ -364,16 +494,15 @@ var TextInput = function(parentNode, host) {
|
||||
host.renderer.$keepTextAreaAtCursor = true;
|
||||
host.renderer.$moveTextAreaToCursor();
|
||||
}
|
||||
}, 0);
|
||||
};
|
||||
this.onContextMenuClose = onContextMenuClose;
|
||||
}, useragent.isOldIE ? 200 : 0);
|
||||
}
|
||||
|
||||
// firefox fires contextmenu event after opening it
|
||||
if (!useragent.isGecko)
|
||||
event.addListener(text, "contextmenu", function(e) {
|
||||
host.textInput.onContextMenu(e);
|
||||
onContextMenuClose();
|
||||
});
|
||||
var onContextMenu = function(e) {
|
||||
host.textInput.onContextMenu(e);
|
||||
onContextMenuClose();
|
||||
};
|
||||
event.addListener(host.renderer.scroller, "contextmenu", onContextMenu);
|
||||
event.addListener(text, "contextmenu", onContextMenu);
|
||||
};
|
||||
|
||||
exports.TextInput = TextInput;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,561 +0,0 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
|
||||
"never use strict";
|
||||
|
||||
var util = require("./maps/util");
|
||||
var motions = require("./maps/motions");
|
||||
var operators = require("./maps/operators");
|
||||
var alias = require("./maps/aliases");
|
||||
var registers = require("./registers");
|
||||
|
||||
var NUMBER = 1;
|
||||
var OPERATOR = 2;
|
||||
var MOTION = 3;
|
||||
var ACTION = 4;
|
||||
var HMARGIN = 8; // Minimum amount of line separation between margins;
|
||||
|
||||
var repeat = function repeat(fn, count, args) {
|
||||
while (0 < count--)
|
||||
fn.apply(this, args);
|
||||
};
|
||||
|
||||
var ensureScrollMargin = function(editor) {
|
||||
var renderer = editor.renderer;
|
||||
var pos = renderer.$cursorLayer.getPixelPosition();
|
||||
|
||||
var top = pos.top;
|
||||
|
||||
var margin = HMARGIN * renderer.layerConfig.lineHeight;
|
||||
if (2 * margin > renderer.$size.scrollerHeight)
|
||||
margin = renderer.$size.scrollerHeight / 2;
|
||||
|
||||
if (renderer.scrollTop > top - margin) {
|
||||
renderer.session.setScrollTop(top - margin);
|
||||
}
|
||||
|
||||
if (renderer.scrollTop + renderer.$size.scrollerHeight < top + margin + renderer.lineHeight) {
|
||||
renderer.session.setScrollTop(top + margin + renderer.lineHeight - renderer.$size.scrollerHeight);
|
||||
}
|
||||
};
|
||||
|
||||
var actions = exports.actions = {
|
||||
"z": {
|
||||
param: true,
|
||||
fn: function(editor, range, count, param) {
|
||||
switch (param) {
|
||||
case "z":
|
||||
editor.renderer.alignCursor(null, 0.5);
|
||||
break;
|
||||
case "t":
|
||||
editor.renderer.alignCursor(null, 0);
|
||||
break;
|
||||
case "b":
|
||||
editor.renderer.alignCursor(null, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
"r": {
|
||||
param: true,
|
||||
fn: function(editor, range, count, param) {
|
||||
if (param && param.length) {
|
||||
repeat(function() { editor.insert(param); }, count || 1);
|
||||
editor.navigateLeft();
|
||||
}
|
||||
}
|
||||
},
|
||||
"R": {
|
||||
fn: function(editor, range, count, param) {
|
||||
util.insertMode(editor);
|
||||
editor.setOverwrite(true);
|
||||
}
|
||||
},
|
||||
"~": {
|
||||
fn: function(editor, range, count) {
|
||||
repeat(function() {
|
||||
var range = editor.selection.getRange();
|
||||
if (range.isEmpty())
|
||||
range.end.column++;
|
||||
var text = editor.session.getTextRange(range);
|
||||
var toggled = text.toUpperCase();
|
||||
if (toggled == text)
|
||||
editor.navigateRight();
|
||||
else
|
||||
editor.session.replace(range, toggled);
|
||||
}, count || 1);
|
||||
}
|
||||
},
|
||||
"*": {
|
||||
fn: function(editor, range, count, param) {
|
||||
editor.selection.selectWord();
|
||||
editor.findNext();
|
||||
ensureScrollMargin(editor);
|
||||
var r = editor.selection.getRange();
|
||||
editor.selection.setSelectionRange(r, true);
|
||||
}
|
||||
},
|
||||
"#": {
|
||||
fn: function(editor, range, count, param) {
|
||||
editor.selection.selectWord();
|
||||
editor.findPrevious();
|
||||
ensureScrollMargin(editor);
|
||||
var r = editor.selection.getRange();
|
||||
editor.selection.setSelectionRange(r, true);
|
||||
}
|
||||
},
|
||||
"n": {
|
||||
fn: function(editor, range, count, param) {
|
||||
var options = editor.getLastSearchOptions();
|
||||
options.backwards = false;
|
||||
|
||||
editor.selection.moveCursorRight();
|
||||
editor.selection.clearSelection();
|
||||
editor.findNext(options);
|
||||
|
||||
ensureScrollMargin(editor);
|
||||
var r = editor.selection.getRange();
|
||||
r.end.row = r.start.row;
|
||||
r.end.column = r.start.column;
|
||||
editor.selection.setSelectionRange(r, true);
|
||||
}
|
||||
},
|
||||
"N": {
|
||||
fn: function(editor, range, count, param) {
|
||||
var options = editor.getLastSearchOptions();
|
||||
options.backwards = true;
|
||||
|
||||
editor.findPrevious(options);
|
||||
ensureScrollMargin(editor);
|
||||
var r = editor.selection.getRange();
|
||||
r.end.row = r.start.row;
|
||||
r.end.column = r.start.column;
|
||||
editor.selection.setSelectionRange(r, true);
|
||||
}
|
||||
},
|
||||
"v": {
|
||||
fn: function(editor, range, count, param) {
|
||||
editor.selection.selectRight();
|
||||
util.visualMode(editor, false);
|
||||
},
|
||||
acceptsMotion: true
|
||||
},
|
||||
"V": {
|
||||
fn: function(editor, range, count, param) {
|
||||
//editor.selection.selectLine();
|
||||
//editor.selection.selectLeft();
|
||||
var row = editor.getCursorPosition().row;
|
||||
editor.selection.clearSelection();
|
||||
editor.selection.moveCursorTo(row, 0);
|
||||
editor.selection.selectLineEnd();
|
||||
editor.selection.visualLineStart = row;
|
||||
|
||||
util.visualMode(editor, true);
|
||||
},
|
||||
acceptsMotion: true
|
||||
},
|
||||
"Y": {
|
||||
fn: function(editor, range, count, param) {
|
||||
util.copyLine(editor);
|
||||
}
|
||||
},
|
||||
"p": {
|
||||
fn: function(editor, range, count, param) {
|
||||
var defaultReg = registers._default;
|
||||
|
||||
editor.setOverwrite(false);
|
||||
if (defaultReg.isLine) {
|
||||
var pos = editor.getCursorPosition();
|
||||
var lines = defaultReg.text.split("\n");
|
||||
editor.session.getDocument().insertLines(pos.row + 1, lines);
|
||||
editor.moveCursorTo(pos.row + 1, 0);
|
||||
}
|
||||
else {
|
||||
editor.navigateRight();
|
||||
editor.insert(defaultReg.text);
|
||||
editor.navigateLeft();
|
||||
}
|
||||
editor.setOverwrite(true);
|
||||
editor.selection.clearSelection();
|
||||
}
|
||||
},
|
||||
"P": {
|
||||
fn: function(editor, range, count, param) {
|
||||
var defaultReg = registers._default;
|
||||
editor.setOverwrite(false);
|
||||
|
||||
if (defaultReg.isLine) {
|
||||
var pos = editor.getCursorPosition();
|
||||
var lines = defaultReg.text.split("\n");
|
||||
editor.session.getDocument().insertLines(pos.row, lines);
|
||||
editor.moveCursorTo(pos.row, 0);
|
||||
}
|
||||
else {
|
||||
editor.insert(defaultReg.text);
|
||||
}
|
||||
editor.setOverwrite(true);
|
||||
editor.selection.clearSelection();
|
||||
}
|
||||
},
|
||||
"J": {
|
||||
fn: function(editor, range, count, param) {
|
||||
var session = editor.session;
|
||||
range = editor.getSelectionRange();
|
||||
var pos = {row: range.start.row, column: range.start.column};
|
||||
count = count || range.end.row - range.start.row;
|
||||
var maxRow = Math.min(pos.row + (count || 1), session.getLength() - 1);
|
||||
|
||||
range.start.column = session.getLine(pos.row).length;
|
||||
range.end.column = session.getLine(maxRow).length;
|
||||
range.end.row = maxRow;
|
||||
|
||||
var text = "";
|
||||
for (var i = pos.row; i < maxRow; i++) {
|
||||
var nextLine = session.getLine(i + 1);
|
||||
text += " " + /^\s*(.*)$/.exec(nextLine)[1] || "";
|
||||
}
|
||||
|
||||
session.replace(range, text);
|
||||
editor.moveCursorTo(pos.row, pos.column);
|
||||
}
|
||||
},
|
||||
"u": {
|
||||
fn: function(editor, range, count, param) {
|
||||
count = parseInt(count || 1, 10);
|
||||
for (var i = 0; i < count; i++) {
|
||||
editor.undo();
|
||||
}
|
||||
editor.selection.clearSelection();
|
||||
}
|
||||
},
|
||||
"ctrl-r": {
|
||||
fn: function(editor, range, count, param) {
|
||||
count = parseInt(count || 1, 10);
|
||||
for (var i = 0; i < count; i++) {
|
||||
editor.redo();
|
||||
}
|
||||
editor.selection.clearSelection();
|
||||
}
|
||||
},
|
||||
":": {
|
||||
fn: function(editor, range, count, param) {
|
||||
// not implemented
|
||||
}
|
||||
},
|
||||
"/": {
|
||||
fn: function(editor, range, count, param) {
|
||||
// not implemented
|
||||
}
|
||||
},
|
||||
"?": {
|
||||
fn: function(editor, range, count, param) {
|
||||
// not implemented
|
||||
}
|
||||
},
|
||||
".": {
|
||||
fn: function(editor, range, count, param) {
|
||||
util.onInsertReplaySequence = inputBuffer.lastInsertCommands;
|
||||
var previous = inputBuffer.previous;
|
||||
if (previous) // If there is a previous action
|
||||
inputBuffer.exec(editor, previous.action, previous.param);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var inputBuffer = exports.inputBuffer = {
|
||||
accepting: [NUMBER, OPERATOR, MOTION, ACTION],
|
||||
currentCmd: null,
|
||||
//currentMode: 0,
|
||||
currentCount: "",
|
||||
status: "",
|
||||
|
||||
// Types
|
||||
operator: null,
|
||||
motion: null,
|
||||
|
||||
lastInsertCommands: [],
|
||||
|
||||
push: function(editor, ch, keyId) {
|
||||
this.idle = false;
|
||||
var wObj = this.waitingForParam;
|
||||
if (wObj) {
|
||||
this.exec(editor, wObj, ch);
|
||||
}
|
||||
// If input is a number (that doesn't start with 0)
|
||||
else if (!(ch === "0" && !this.currentCount.length) &&
|
||||
(ch.match(/^\d+$/) && this.isAccepting(NUMBER))) {
|
||||
// Assuming that ch is always of type String, and not Number
|
||||
this.currentCount += ch;
|
||||
this.currentCmd = NUMBER;
|
||||
this.accepting = [NUMBER, OPERATOR, MOTION, ACTION];
|
||||
}
|
||||
else if (!this.operator && this.isAccepting(OPERATOR) && operators[ch]) {
|
||||
this.operator = {
|
||||
ch: ch,
|
||||
count: this.getCount()
|
||||
};
|
||||
this.currentCmd = OPERATOR;
|
||||
this.accepting = [NUMBER, MOTION, ACTION];
|
||||
this.exec(editor, { operator: this.operator });
|
||||
}
|
||||
else if (motions[ch] && this.isAccepting(MOTION)) {
|
||||
this.currentCmd = MOTION;
|
||||
|
||||
var ctx = {
|
||||
operator: this.operator,
|
||||
motion: {
|
||||
ch: ch,
|
||||
count: this.getCount()
|
||||
}
|
||||
};
|
||||
|
||||
if (motions[ch].param)
|
||||
this.waitForParam(ctx);
|
||||
else
|
||||
this.exec(editor, ctx);
|
||||
}
|
||||
else if (alias[ch] && this.isAccepting(MOTION)) {
|
||||
alias[ch].operator.count = this.getCount();
|
||||
this.exec(editor, alias[ch]);
|
||||
}
|
||||
else if (actions[ch] && this.isAccepting(ACTION)) {
|
||||
var actionObj = {
|
||||
action: {
|
||||
fn: actions[ch].fn,
|
||||
count: this.getCount()
|
||||
}
|
||||
};
|
||||
|
||||
if (actions[ch].param) {
|
||||
this.waitForParam(actionObj);
|
||||
}
|
||||
else {
|
||||
this.exec(editor, actionObj);
|
||||
}
|
||||
|
||||
if (actions[ch].acceptsMotion)
|
||||
this.idle = false;
|
||||
}
|
||||
else if (this.operator) {
|
||||
this.exec(editor, { operator: this.operator }, ch);
|
||||
}
|
||||
else {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
if (this.waitingForParam || this.motion || this.operator) {
|
||||
this.status += ch;
|
||||
} else if (this.currentCount) {
|
||||
this.status = this.currentCount;
|
||||
} else if (this.status) {
|
||||
this.status = "";
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
editor._emit("changeStatus");
|
||||
},
|
||||
|
||||
waitForParam: function(cmd) {
|
||||
this.waitingForParam = cmd;
|
||||
},
|
||||
|
||||
getCount: function() {
|
||||
var count = this.currentCount;
|
||||
this.currentCount = "";
|
||||
return count && parseInt(count, 10);
|
||||
},
|
||||
|
||||
exec: function(editor, action, param) {
|
||||
var m = action.motion;
|
||||
var o = action.operator;
|
||||
var a = action.action;
|
||||
|
||||
if (!param)
|
||||
param = action.param;
|
||||
|
||||
if (o) {
|
||||
this.previous = {
|
||||
action: action,
|
||||
param: param
|
||||
};
|
||||
}
|
||||
|
||||
if (o && !editor.selection.isEmpty()) {
|
||||
if (operators[o.ch].selFn) {
|
||||
operators[o.ch].selFn(editor, editor.getSelectionRange(), o.count, param);
|
||||
this.reset();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// There is an operator, but no motion or action. We try to pass the
|
||||
// current ch to the operator to see if it responds to it (an example
|
||||
// of this is the 'dd' operator).
|
||||
else if (!m && !a && o && param) {
|
||||
operators[o.ch].fn(editor, null, o.count, param);
|
||||
this.reset();
|
||||
}
|
||||
else if (m) {
|
||||
var run = function(fn) {
|
||||
if (fn && typeof fn === "function") { // There should always be a motion
|
||||
if (m.count && !motionObj.handlesCount)
|
||||
repeat(fn, m.count, [editor, null, m.count, param]);
|
||||
else
|
||||
fn(editor, null, m.count, param);
|
||||
}
|
||||
};
|
||||
|
||||
var motionObj = motions[m.ch];
|
||||
var selectable = motionObj.sel;
|
||||
|
||||
if (!o) {
|
||||
if ((util.onVisualMode || util.onVisualLineMode) && selectable)
|
||||
run(motionObj.sel);
|
||||
else
|
||||
run(motionObj.nav);
|
||||
}
|
||||
else if (selectable) {
|
||||
repeat(function() {
|
||||
run(motionObj.sel);
|
||||
operators[o.ch].fn(editor, editor.getSelectionRange(), o.count, param);
|
||||
}, o.count || 1);
|
||||
}
|
||||
this.reset();
|
||||
}
|
||||
else if (a) {
|
||||
a.fn(editor, editor.getSelectionRange(), a.count, param);
|
||||
this.reset();
|
||||
}
|
||||
handleCursorMove(editor);
|
||||
},
|
||||
|
||||
isAccepting: function(type) {
|
||||
return this.accepting.indexOf(type) !== -1;
|
||||
},
|
||||
|
||||
reset: function() {
|
||||
this.operator = null;
|
||||
this.motion = null;
|
||||
this.currentCount = "";
|
||||
this.status = "";
|
||||
this.accepting = [NUMBER, OPERATOR, MOTION, ACTION];
|
||||
this.idle = true;
|
||||
this.waitingForParam = null;
|
||||
}
|
||||
};
|
||||
|
||||
function setPreviousCommand(fn) {
|
||||
inputBuffer.previous = { action: { action: { fn: fn } } };
|
||||
}
|
||||
|
||||
exports.coreCommands = {
|
||||
start: {
|
||||
exec: function start(editor) {
|
||||
util.insertMode(editor);
|
||||
setPreviousCommand(start);
|
||||
}
|
||||
},
|
||||
startBeginning: {
|
||||
exec: function startBeginning(editor) {
|
||||
editor.navigateLineStart();
|
||||
util.insertMode(editor);
|
||||
setPreviousCommand(startBeginning);
|
||||
}
|
||||
},
|
||||
// Stop Insert mode as soon as possible. Works like typing <Esc> in
|
||||
// insert mode.
|
||||
stop: {
|
||||
exec: function stop(editor) {
|
||||
inputBuffer.reset();
|
||||
util.onVisualMode = false;
|
||||
util.onVisualLineMode = false;
|
||||
inputBuffer.lastInsertCommands = util.normalMode(editor);
|
||||
}
|
||||
},
|
||||
append: {
|
||||
exec: function append(editor) {
|
||||
var pos = editor.getCursorPosition();
|
||||
var lineLen = editor.session.getLine(pos.row).length;
|
||||
if (lineLen)
|
||||
editor.navigateRight();
|
||||
util.insertMode(editor);
|
||||
setPreviousCommand(append);
|
||||
}
|
||||
},
|
||||
appendEnd: {
|
||||
exec: function appendEnd(editor) {
|
||||
editor.navigateLineEnd();
|
||||
util.insertMode(editor);
|
||||
setPreviousCommand(appendEnd);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var handleCursorMove = exports.onCursorMove = function(editor, e) {
|
||||
if (util.currentMode === 'insert' || handleCursorMove.running)
|
||||
return;
|
||||
else if(!editor.selection.isEmpty()) {
|
||||
handleCursorMove.running = true;
|
||||
if (util.onVisualLineMode) {
|
||||
var originRow = editor.selection.visualLineStart;
|
||||
var cursorRow = editor.getCursorPosition().row;
|
||||
if(originRow <= cursorRow) {
|
||||
var endLine = editor.session.getLine(cursorRow);
|
||||
editor.selection.clearSelection();
|
||||
editor.selection.moveCursorTo(originRow, 0);
|
||||
editor.selection.selectTo(cursorRow, endLine.length);
|
||||
} else {
|
||||
var endLine = editor.session.getLine(originRow);
|
||||
editor.selection.clearSelection();
|
||||
editor.selection.moveCursorTo(originRow, endLine.length);
|
||||
editor.selection.selectTo(cursorRow, 0);
|
||||
}
|
||||
}
|
||||
handleCursorMove.running = false;
|
||||
return;
|
||||
}
|
||||
else {
|
||||
if (e && (util.onVisualLineMode || util.onVisualMode)) {
|
||||
editor.selection.clearSelection();
|
||||
util.normalMode(editor);
|
||||
}
|
||||
|
||||
handleCursorMove.running = true;
|
||||
var pos = editor.getCursorPosition();
|
||||
var lineLen = editor.session.getLine(pos.row).length;
|
||||
|
||||
if (lineLen && pos.column === lineLen)
|
||||
editor.navigateLeft();
|
||||
handleCursorMove.running = false;
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -1,593 +0,0 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
|
||||
define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var util = require("./util");
|
||||
|
||||
var keepScrollPosition = function(editor, fn) {
|
||||
var scrollTopRow = editor.renderer.getScrollTopRow();
|
||||
var initialRow = editor.getCursorPosition().row;
|
||||
var diff = initialRow - scrollTopRow;
|
||||
fn && fn.call(editor);
|
||||
editor.renderer.scrollToRow(editor.getCursorPosition().row - diff);
|
||||
};
|
||||
|
||||
function Motion(m) {
|
||||
if (typeof m == "function") {
|
||||
var getPos = m;
|
||||
m = this;
|
||||
} else {
|
||||
var getPos = m.getPos;
|
||||
}
|
||||
m.nav = function(editor, range, count, param) {
|
||||
var a = getPos(editor, range, count, param, false);
|
||||
if (!a)
|
||||
return;
|
||||
editor.clearSelection();
|
||||
editor.moveCursorTo(a.row, a.column);
|
||||
};
|
||||
m.sel = function(editor, range, count, param) {
|
||||
var a = getPos(editor, range, count, param, true);
|
||||
if (!a)
|
||||
return;
|
||||
editor.selection.selectTo(a.row, a.column);
|
||||
};
|
||||
return m;
|
||||
}
|
||||
|
||||
var nonWordRe = /[\s.\/\\()\"'-:,.;<>~!@#$%^&*|+=\[\]{}`~?]/;
|
||||
var wordSeparatorRe = /[.\/\\()\"'-:,.;<>~!@#$%^&*|+=\[\]{}`~?]/;
|
||||
var whiteRe = /\s/;
|
||||
var StringStream = function(editor, cursor) {
|
||||
var sel = editor.selection;
|
||||
this.range = sel.getRange();
|
||||
cursor = cursor || sel.selectionLead;
|
||||
this.row = cursor.row;
|
||||
this.col = cursor.column;
|
||||
var line = editor.session.getLine(this.row);
|
||||
var maxRow = editor.session.getLength();
|
||||
this.ch = line[this.col] || '\n';
|
||||
this.skippedLines = 0;
|
||||
|
||||
this.next = function() {
|
||||
this.ch = line[++this.col] || this.handleNewLine(1);
|
||||
//this.debug()
|
||||
return this.ch;
|
||||
};
|
||||
this.prev = function() {
|
||||
this.ch = line[--this.col] || this.handleNewLine(-1);
|
||||
//this.debug()
|
||||
return this.ch;
|
||||
};
|
||||
this.peek = function(dir) {
|
||||
var ch = line[this.col + dir];
|
||||
if (ch)
|
||||
return ch;
|
||||
if (dir == -1)
|
||||
return '\n';
|
||||
if (this.col == line.length - 1)
|
||||
return '\n';
|
||||
return editor.session.getLine(this.row + 1)[0] || '\n';
|
||||
};
|
||||
|
||||
this.handleNewLine = function(dir) {
|
||||
if (dir == 1){
|
||||
if (this.col == line.length)
|
||||
return '\n';
|
||||
if (this.row == maxRow - 1)
|
||||
return '';
|
||||
this.col = 0;
|
||||
this.row ++;
|
||||
line = editor.session.getLine(this.row);
|
||||
this.skippedLines++;
|
||||
return line[0] || '\n';
|
||||
}
|
||||
if (dir == -1) {
|
||||
if (this.row === 0)
|
||||
return '';
|
||||
this.row --;
|
||||
line = editor.session.getLine(this.row);
|
||||
this.col = line.length;
|
||||
this.skippedLines--;
|
||||
return '\n';
|
||||
}
|
||||
};
|
||||
this.debug = function() {
|
||||
console.log(line.substring(0, this.col)+'|'+this.ch+'\''+this.col+'\''+line.substr(this.col+1));
|
||||
};
|
||||
};
|
||||
|
||||
var Search = require("../../../search").Search;
|
||||
var search = new Search();
|
||||
|
||||
function find(editor, needle, dir) {
|
||||
search.$options.needle = needle;
|
||||
search.$options.backwards = dir == -1;
|
||||
return search.find(editor.session);
|
||||
}
|
||||
|
||||
var Range = require("../../../range").Range;
|
||||
|
||||
module.exports = {
|
||||
"w": new Motion(function(editor) {
|
||||
var str = new StringStream(editor);
|
||||
|
||||
if (str.ch && wordSeparatorRe.test(str.ch)) {
|
||||
while (str.ch && wordSeparatorRe.test(str.ch))
|
||||
str.next();
|
||||
} else {
|
||||
while (str.ch && !nonWordRe.test(str.ch))
|
||||
str.next();
|
||||
}
|
||||
while (str.ch && whiteRe.test(str.ch) && str.skippedLines < 2)
|
||||
str.next();
|
||||
|
||||
str.skippedLines == 2 && str.prev();
|
||||
return {column: str.col, row: str.row};
|
||||
}),
|
||||
"W": new Motion(function(editor) {
|
||||
var str = new StringStream(editor);
|
||||
while(str.ch && !(whiteRe.test(str.ch) && !whiteRe.test(str.peek(1))) && str.skippedLines < 2)
|
||||
str.next();
|
||||
if (str.skippedLines == 2)
|
||||
str.prev();
|
||||
else
|
||||
str.next();
|
||||
|
||||
return {column: str.col, row: str.row};
|
||||
}),
|
||||
"b": new Motion(function(editor) {
|
||||
var str = new StringStream(editor);
|
||||
|
||||
str.prev();
|
||||
while (str.ch && whiteRe.test(str.ch) && str.skippedLines > -2)
|
||||
str.prev();
|
||||
|
||||
if (str.ch && wordSeparatorRe.test(str.ch)) {
|
||||
while (str.ch && wordSeparatorRe.test(str.ch))
|
||||
str.prev();
|
||||
} else {
|
||||
while (str.ch && !nonWordRe.test(str.ch))
|
||||
str.prev();
|
||||
}
|
||||
str.ch && str.next();
|
||||
return {column: str.col, row: str.row};
|
||||
}),
|
||||
"B": new Motion(function(editor) {
|
||||
var str = new StringStream(editor);
|
||||
str.prev();
|
||||
while(str.ch && !(!whiteRe.test(str.ch) && whiteRe.test(str.peek(-1))) && str.skippedLines > -2)
|
||||
str.prev();
|
||||
|
||||
if (str.skippedLines == -2)
|
||||
str.next();
|
||||
|
||||
return {column: str.col, row: str.row};
|
||||
}),
|
||||
"e": new Motion(function(editor) {
|
||||
var str = new StringStream(editor);
|
||||
|
||||
str.next();
|
||||
while (str.ch && whiteRe.test(str.ch))
|
||||
str.next();
|
||||
|
||||
if (str.ch && wordSeparatorRe.test(str.ch)) {
|
||||
while (str.ch && wordSeparatorRe.test(str.ch))
|
||||
str.next();
|
||||
} else {
|
||||
while (str.ch && !nonWordRe.test(str.ch))
|
||||
str.next();
|
||||
}
|
||||
str.ch && str.prev();
|
||||
return {column: str.col, row: str.row};
|
||||
}),
|
||||
"E": new Motion(function(editor) {
|
||||
var str = new StringStream(editor);
|
||||
str.next();
|
||||
while(str.ch && !(!whiteRe.test(str.ch) && whiteRe.test(str.peek(1))))
|
||||
str.next();
|
||||
|
||||
return {column: str.col, row: str.row};
|
||||
}),
|
||||
|
||||
"l": {
|
||||
nav: function(editor) {
|
||||
editor.navigateRight();
|
||||
},
|
||||
sel: function(editor) {
|
||||
var pos = editor.getCursorPosition();
|
||||
var col = pos.column;
|
||||
var lineLen = editor.session.getLine(pos.row).length;
|
||||
|
||||
// Solving the behavior at the end of the line due to the
|
||||
// different 0 index-based colum positions in ACE.
|
||||
if (lineLen && col !== lineLen) //In selection mode you can select the newline
|
||||
editor.selection.selectRight();
|
||||
}
|
||||
},
|
||||
"h": {
|
||||
nav: function(editor) {
|
||||
var pos = editor.getCursorPosition();
|
||||
if (pos.column > 0)
|
||||
editor.navigateLeft();
|
||||
},
|
||||
sel: function(editor) {
|
||||
var pos = editor.getCursorPosition();
|
||||
if (pos.column > 0)
|
||||
editor.selection.selectLeft();
|
||||
}
|
||||
},
|
||||
"H": {
|
||||
nav: function(editor) {
|
||||
var row = editor.renderer.getScrollTopRow();
|
||||
editor.moveCursorTo(row);
|
||||
},
|
||||
sel: function(editor) {
|
||||
var row = editor.renderer.getScrollTopRow();
|
||||
editor.selection.selectTo(row);
|
||||
}
|
||||
},
|
||||
"M": {
|
||||
nav: function(editor) {
|
||||
var topRow = editor.renderer.getScrollTopRow();
|
||||
var bottomRow = editor.renderer.getScrollBottomRow();
|
||||
var row = topRow + ((bottomRow - topRow) / 2);
|
||||
editor.moveCursorTo(row);
|
||||
},
|
||||
sel: function(editor) {
|
||||
var topRow = editor.renderer.getScrollTopRow();
|
||||
var bottomRow = editor.renderer.getScrollBottomRow();
|
||||
var row = topRow + ((bottomRow - topRow) / 2);
|
||||
editor.selection.selectTo(row);
|
||||
}
|
||||
},
|
||||
"L": {
|
||||
nav: function(editor) {
|
||||
var row = editor.renderer.getScrollBottomRow();
|
||||
editor.moveCursorTo(row);
|
||||
},
|
||||
sel: function(editor) {
|
||||
var row = editor.renderer.getScrollBottomRow();
|
||||
editor.selection.selectTo(row);
|
||||
}
|
||||
},
|
||||
"k": {
|
||||
nav: function(editor) {
|
||||
editor.navigateUp();
|
||||
},
|
||||
sel: function(editor) {
|
||||
editor.selection.selectUp();
|
||||
}
|
||||
},
|
||||
"j": {
|
||||
nav: function(editor) {
|
||||
editor.navigateDown();
|
||||
},
|
||||
sel: function(editor) {
|
||||
editor.selection.selectDown();
|
||||
}
|
||||
},
|
||||
|
||||
"i": {
|
||||
param: true,
|
||||
sel: function(editor, range, count, param) {
|
||||
switch (param) {
|
||||
case "w":
|
||||
editor.selection.selectWord();
|
||||
break;
|
||||
case "W":
|
||||
editor.selection.selectAWord();
|
||||
break;
|
||||
case "(":
|
||||
case "{":
|
||||
case "[":
|
||||
var cursor = editor.getCursorPosition();
|
||||
var end = editor.session.$findClosingBracket(param, cursor, /paren/);
|
||||
if (!end)
|
||||
return;
|
||||
var start = editor.session.$findOpeningBracket(editor.session.$brackets[param], cursor, /paren/);
|
||||
if (!start)
|
||||
return;
|
||||
start.column ++;
|
||||
editor.selection.setSelectionRange(Range.fromPoints(start, end));
|
||||
break;
|
||||
case "'":
|
||||
case '"':
|
||||
case "/":
|
||||
var end = find(editor, param, 1);
|
||||
if (!end)
|
||||
return;
|
||||
var start = find(editor, param, -1);
|
||||
if (!start)
|
||||
return;
|
||||
editor.selection.setSelectionRange(Range.fromPoints(start.end, end.start));
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
"a": {
|
||||
param: true,
|
||||
sel: function(editor, range, count, param) {
|
||||
switch (param) {
|
||||
case "w":
|
||||
editor.selection.selectAWord();
|
||||
break;
|
||||
case "W":
|
||||
editor.selection.selectAWord();
|
||||
break;
|
||||
case "(":
|
||||
case "{":
|
||||
case "[":
|
||||
var cursor = editor.getCursorPosition();
|
||||
var end = editor.session.$findClosingBracket(param, cursor, /paren/);
|
||||
if (!end)
|
||||
return;
|
||||
var start = editor.session.$findOpeningBracket(editor.session.$brackets[param], cursor, /paren/);
|
||||
if (!start)
|
||||
return;
|
||||
end.column ++;
|
||||
editor.selection.setSelectionRange(Range.fromPoints(start, end));
|
||||
break;
|
||||
case "'":
|
||||
case "\"":
|
||||
case "/":
|
||||
var end = find(editor, param, 1);
|
||||
if (!end)
|
||||
return;
|
||||
var start = find(editor, param, -1);
|
||||
if (!start)
|
||||
return;
|
||||
end.column ++;
|
||||
editor.selection.setSelectionRange(Range.fromPoints(start.start, end.end));
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"f": new Motion({
|
||||
param: true,
|
||||
handlesCount: true,
|
||||
getPos: function(editor, range, count, param, isSel) {
|
||||
var cursor = editor.getCursorPosition();
|
||||
var column = util.getRightNthChar(editor, cursor, param, count || 1);
|
||||
|
||||
if (typeof column === "number") {
|
||||
cursor.column += column + (isSel ? 2 : 1);
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
}),
|
||||
"F": new Motion({
|
||||
param: true,
|
||||
handlesCount: true,
|
||||
getPos: function(editor, range, count, param, isSel) {
|
||||
var cursor = editor.getCursorPosition();
|
||||
var column = util.getLeftNthChar(editor, cursor, param, count || 1);
|
||||
|
||||
if (typeof column === "number") {
|
||||
cursor.column -= column + 1;
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
}),
|
||||
"t": new Motion({
|
||||
param: true,
|
||||
handlesCount: true,
|
||||
getPos: function(editor, range, count, param, isSel) {
|
||||
var cursor = editor.getCursorPosition();
|
||||
var column = util.getRightNthChar(editor, cursor, param, count || 1);
|
||||
|
||||
if (typeof column === "number") {
|
||||
cursor.column += column + (isSel ? 1 : 0);
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
}),
|
||||
"T": new Motion({
|
||||
param: true,
|
||||
handlesCount: true,
|
||||
getPos: function(editor, range, count, param, isSel) {
|
||||
var cursor = editor.getCursorPosition();
|
||||
var column = util.getLeftNthChar(editor, cursor, param, count || 1);
|
||||
|
||||
if (typeof column === "number") {
|
||||
cursor.column -= column;
|
||||
return cursor;
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
"^": {
|
||||
nav: function(editor) {
|
||||
editor.navigateLineStart();
|
||||
},
|
||||
sel: function(editor) {
|
||||
editor.selection.selectLineStart();
|
||||
}
|
||||
},
|
||||
"$": {
|
||||
nav: function(editor) {
|
||||
editor.navigateLineEnd();
|
||||
},
|
||||
sel: function(editor) {
|
||||
editor.selection.selectLineEnd();
|
||||
}
|
||||
},
|
||||
"0": new Motion(function(ed) {
|
||||
return {row: ed.selection.lead.row, column: 0};
|
||||
}),
|
||||
"G": {
|
||||
nav: function(editor, range, count, param) {
|
||||
if (!count && count !== 0) { // Stupid JS
|
||||
count = editor.session.getLength();
|
||||
}
|
||||
editor.gotoLine(count);
|
||||
},
|
||||
sel: function(editor, range, count, param) {
|
||||
if (!count && count !== 0) { // Stupid JS
|
||||
count = editor.session.getLength();
|
||||
}
|
||||
editor.selection.selectTo(count, 0);
|
||||
}
|
||||
},
|
||||
"g": {
|
||||
param: true,
|
||||
nav: function(editor, range, count, param) {
|
||||
switch(param) {
|
||||
case "m":
|
||||
console.log("Middle line");
|
||||
break;
|
||||
case "e":
|
||||
console.log("End of prev word");
|
||||
break;
|
||||
case "g":
|
||||
editor.gotoLine(count || 0);
|
||||
case "u":
|
||||
editor.gotoLine(count || 0);
|
||||
case "U":
|
||||
editor.gotoLine(count || 0);
|
||||
}
|
||||
},
|
||||
sel: function(editor, range, count, param) {
|
||||
switch(param) {
|
||||
case "m":
|
||||
console.log("Middle line");
|
||||
break;
|
||||
case "e":
|
||||
console.log("End of prev word");
|
||||
break;
|
||||
case "g":
|
||||
editor.selection.selectTo(count || 0, 0);
|
||||
}
|
||||
}
|
||||
},
|
||||
"o": {
|
||||
nav: function(editor, range, count, param) {
|
||||
count = count || 1;
|
||||
var content = "";
|
||||
while (0 < count--)
|
||||
content += "\n";
|
||||
|
||||
if (content.length) {
|
||||
editor.navigateLineEnd()
|
||||
editor.insert(content);
|
||||
util.insertMode(editor);
|
||||
}
|
||||
}
|
||||
},
|
||||
"O": {
|
||||
nav: function(editor, range, count, param) {
|
||||
var row = editor.getCursorPosition().row;
|
||||
count = count || 1;
|
||||
var content = "";
|
||||
while (0 < count--)
|
||||
content += "\n";
|
||||
|
||||
if (content.length) {
|
||||
if(row > 0) {
|
||||
editor.navigateUp();
|
||||
editor.navigateLineEnd()
|
||||
editor.insert(content);
|
||||
} else {
|
||||
editor.session.insert({row: 0, column: 0}, content);
|
||||
editor.navigateUp();
|
||||
}
|
||||
util.insertMode(editor);
|
||||
}
|
||||
}
|
||||
},
|
||||
"%": new Motion(function(editor){
|
||||
var brRe = /[\[\]{}()]/g;
|
||||
var cursor = editor.getCursorPosition();
|
||||
var ch = editor.session.getLine(cursor.row)[cursor.column];
|
||||
if (!brRe.test(ch)) {
|
||||
var range = find(editor, brRe);
|
||||
if (!range)
|
||||
return;
|
||||
cursor = range.start;
|
||||
}
|
||||
var match = editor.session.findMatchingBracket({
|
||||
row: cursor.row,
|
||||
column: cursor.column + 1
|
||||
});
|
||||
|
||||
return match;
|
||||
}),
|
||||
"{": new Motion(function(ed) {
|
||||
var session = ed.session;
|
||||
var row = session.selection.lead.row;
|
||||
while(row > 0 && !/\S/.test(session.getLine(row)))
|
||||
row--;
|
||||
while(/\S/.test(session.getLine(row)))
|
||||
row--;
|
||||
return {column: 0, row: row};
|
||||
}),
|
||||
"}": new Motion(function(ed) {
|
||||
var session = ed.session;
|
||||
var l = session.getLength();
|
||||
var row = session.selection.lead.row;
|
||||
while(row < l && !/\S/.test(session.getLine(row)))
|
||||
row++;
|
||||
while(/\S/.test(session.getLine(row)))
|
||||
row++;
|
||||
return {column: 0, row: row};
|
||||
}),
|
||||
"ctrl-d": {
|
||||
nav: function(editor, range, count, param) {
|
||||
editor.selection.clearSelection();
|
||||
keepScrollPosition(editor, editor.gotoPageDown);
|
||||
},
|
||||
sel: function(editor, range, count, param) {
|
||||
keepScrollPosition(editor, editor.selectPageDown);
|
||||
}
|
||||
},
|
||||
"ctrl-u": {
|
||||
nav: function(editor, range, count, param) {
|
||||
editor.selection.clearSelection();
|
||||
keepScrollPosition(editor, editor.gotoPageUp);
|
||||
|
||||
},
|
||||
sel: function(editor, range, count, param) {
|
||||
keepScrollPosition(editor, editor.selectPageUp);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
module.exports.backspace = module.exports.left = module.exports.h;
|
||||
module.exports.right = module.exports.l;
|
||||
module.exports.up = module.exports.k;
|
||||
module.exports.down = module.exports.j;
|
||||
module.exports.pagedown = module.exports["ctrl-d"];
|
||||
module.exports.pageup = module.exports["ctrl-u"];
|
||||
|
||||
});
|
||||
@@ -1,196 +0,0 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
|
||||
"never use strict";
|
||||
|
||||
var util = require("./util");
|
||||
var registers = require("../registers");
|
||||
|
||||
module.exports = {
|
||||
"d": {
|
||||
selFn: function(editor, range, count, param) {
|
||||
registers._default.text = editor.getCopyText();
|
||||
registers._default.isLine = util.onVisualLineMode;
|
||||
if(util.onVisualLineMode)
|
||||
editor.removeLines();
|
||||
else
|
||||
editor.session.remove(range);
|
||||
util.normalMode(editor);
|
||||
},
|
||||
fn: function(editor, range, count, param) {
|
||||
count = count || 1;
|
||||
switch (param) {
|
||||
case "d":
|
||||
registers._default.text = "";
|
||||
registers._default.isLine = true;
|
||||
for (var i = 0; i < count; i++) {
|
||||
editor.selection.selectLine();
|
||||
registers._default.text += editor.getCopyText();
|
||||
var selRange = editor.getSelectionRange();
|
||||
// check if end of the document was reached
|
||||
if (!selRange.isMultiLine()) {
|
||||
lastLineReached = true
|
||||
var row = selRange.start.row - 1;
|
||||
var col = editor.session.getLine(row).length
|
||||
selRange.setStart(row, col);
|
||||
editor.session.remove(selRange);
|
||||
editor.selection.clearSelection();
|
||||
break;
|
||||
}
|
||||
editor.session.remove(selRange);
|
||||
editor.selection.clearSelection();
|
||||
}
|
||||
registers._default.text = registers._default.text.replace(/\n$/, "");
|
||||
break;
|
||||
default:
|
||||
if (range) {
|
||||
editor.selection.setSelectionRange(range);
|
||||
registers._default.text = editor.getCopyText();
|
||||
registers._default.isLine = false;
|
||||
editor.session.remove(range);
|
||||
editor.selection.clearSelection();
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"c": {
|
||||
selFn: function(editor, range, count, param) {
|
||||
editor.session.remove(range);
|
||||
util.insertMode(editor);
|
||||
},
|
||||
fn: function(editor, range, count, param) {
|
||||
count = count || 1;
|
||||
switch (param) {
|
||||
case "c":
|
||||
for (var i = 0; i < count; i++) {
|
||||
editor.removeLines();
|
||||
util.insertMode(editor);
|
||||
}
|
||||
|
||||
break;
|
||||
default:
|
||||
if (range) {
|
||||
|
||||
// range.end.column ++;
|
||||
editor.session.remove(range);
|
||||
util.insertMode(editor);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"y": {
|
||||
selFn: function(editor, range, count, param) {
|
||||
registers._default.text = editor.getCopyText();
|
||||
registers._default.isLine = util.onVisualLineMode;
|
||||
editor.selection.clearSelection();
|
||||
util.normalMode(editor);
|
||||
},
|
||||
fn: function(editor, range, count, param) {
|
||||
count = count || 1;
|
||||
switch (param) {
|
||||
case "y":
|
||||
var pos = editor.getCursorPosition();
|
||||
editor.selection.selectLine();
|
||||
for (var i = 0; i < count - 1; i++) {
|
||||
editor.selection.moveCursorDown();
|
||||
}
|
||||
registers._default.text = editor.getCopyText().replace(/\n$/, "");
|
||||
editor.selection.clearSelection();
|
||||
registers._default.isLine = true;
|
||||
editor.moveCursorToPosition(pos);
|
||||
break;
|
||||
default:
|
||||
if (range) {
|
||||
var pos = editor.getCursorPosition();
|
||||
editor.selection.setSelectionRange(range);
|
||||
registers._default.text = editor.getCopyText();
|
||||
registers._default.isLine = false;
|
||||
editor.selection.clearSelection();
|
||||
editor.moveCursorTo(pos.row, pos.column);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
">": {
|
||||
selFn: function(editor, range, count, param) {
|
||||
count = count || 1;
|
||||
for (var i = 0; i < count; i++) {
|
||||
editor.indent();
|
||||
}
|
||||
util.normalMode(editor);
|
||||
},
|
||||
fn: function(editor, range, count, param) {
|
||||
count = parseInt(count || 1, 10);
|
||||
switch (param) {
|
||||
case ">":
|
||||
var pos = editor.getCursorPosition();
|
||||
editor.selection.selectLine();
|
||||
for (var i = 0; i < count - 1; i++) {
|
||||
editor.selection.moveCursorDown();
|
||||
}
|
||||
editor.indent();
|
||||
editor.selection.clearSelection();
|
||||
editor.moveCursorToPosition(pos);
|
||||
editor.navigateLineEnd();
|
||||
editor.navigateLineStart();
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
"<": {
|
||||
selFn: function(editor, range, count, param) {
|
||||
count = count || 1;
|
||||
for (var i = 0; i < count; i++) {
|
||||
editor.blockOutdent();
|
||||
}
|
||||
util.normalMode(editor);
|
||||
},
|
||||
fn: function(editor, range, count, param) {
|
||||
count = count || 1;
|
||||
switch (param) {
|
||||
case "<":
|
||||
var pos = editor.getCursorPosition();
|
||||
editor.selection.selectLine();
|
||||
for (var i = 0; i < count - 1; i++) {
|
||||
editor.selection.moveCursorDown();
|
||||
}
|
||||
editor.blockOutdent();
|
||||
editor.selection.clearSelection();
|
||||
editor.moveCursorToPosition(pos);
|
||||
editor.navigateLineEnd();
|
||||
editor.navigateLineStart();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
@@ -1,134 +0,0 @@
|
||||
define(function(require, exports, module) {
|
||||
var registers = require("../registers");
|
||||
|
||||
var dom = require("../../../lib/dom");
|
||||
dom.importCssString('.insert-mode .ace_cursor{\
|
||||
border-left: 2px solid #333333;\
|
||||
}\
|
||||
.ace_dark.insert-mode .ace_cursor{\
|
||||
border-left: 2px solid #eeeeee;\
|
||||
}\
|
||||
.normal-mode .ace_cursor{\
|
||||
border: 0!important;\
|
||||
background-color: red;\
|
||||
opacity: 0.5;\
|
||||
}', 'vimMode');
|
||||
|
||||
module.exports = {
|
||||
onVisualMode: false,
|
||||
onVisualLineMode: false,
|
||||
currentMode: 'normal',
|
||||
noMode: function(editor) {
|
||||
editor.unsetStyle('insert-mode');
|
||||
editor.unsetStyle('normal-mode');
|
||||
if (editor.commands.recording)
|
||||
editor.commands.toggleRecording(editor);
|
||||
editor.setOverwrite(false);
|
||||
},
|
||||
insertMode: function(editor) {
|
||||
this.currentMode = 'insert';
|
||||
// Switch editor to insert mode
|
||||
editor.setStyle('insert-mode');
|
||||
editor.unsetStyle('normal-mode');
|
||||
|
||||
editor.setOverwrite(false);
|
||||
editor.keyBinding.$data.buffer = "";
|
||||
editor.keyBinding.$data.state = "insertMode";
|
||||
this.onVisualMode = false;
|
||||
this.onVisualLineMode = false;
|
||||
if(this.onInsertReplaySequence) {
|
||||
// Ok, we're apparently replaying ("."), so let's do it
|
||||
editor.commands.macro = this.onInsertReplaySequence;
|
||||
editor.commands.replay(editor);
|
||||
this.onInsertReplaySequence = null;
|
||||
this.normalMode(editor);
|
||||
} else {
|
||||
editor._emit("changeStatus");
|
||||
// Record any movements, insertions in insert mode
|
||||
if(!editor.commands.recording)
|
||||
editor.commands.toggleRecording(editor);
|
||||
}
|
||||
},
|
||||
normalMode: function(editor) {
|
||||
// Switch editor to normal mode
|
||||
this.currentMode = 'normal';
|
||||
|
||||
editor.unsetStyle('insert-mode');
|
||||
editor.setStyle('normal-mode');
|
||||
editor.clearSelection();
|
||||
|
||||
var pos;
|
||||
if (!editor.getOverwrite()) {
|
||||
pos = editor.getCursorPosition();
|
||||
if (pos.column > 0)
|
||||
editor.navigateLeft();
|
||||
}
|
||||
|
||||
editor.setOverwrite(true);
|
||||
editor.keyBinding.$data.buffer = "";
|
||||
editor.keyBinding.$data.state = "start";
|
||||
this.onVisualMode = false;
|
||||
this.onVisualLineMode = false;
|
||||
editor._emit("changeStatus");
|
||||
// Save recorded keystrokes
|
||||
if (editor.commands.recording) {
|
||||
editor.commands.toggleRecording(editor);
|
||||
return editor.commands.macro;
|
||||
}
|
||||
else {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
visualMode: function(editor, lineMode) {
|
||||
if (
|
||||
(this.onVisualLineMode && lineMode)
|
||||
|| (this.onVisualMode && !lineMode)
|
||||
) {
|
||||
this.normalMode(editor);
|
||||
return;
|
||||
}
|
||||
|
||||
editor.setStyle('insert-mode');
|
||||
editor.unsetStyle('normal-mode');
|
||||
|
||||
editor._emit("changeStatus");
|
||||
if (lineMode) {
|
||||
this.onVisualLineMode = true;
|
||||
} else {
|
||||
this.onVisualMode = true;
|
||||
this.onVisualLineMode = false;
|
||||
}
|
||||
},
|
||||
getRightNthChar: function(editor, cursor, ch, n) {
|
||||
var line = editor.getSession().getLine(cursor.row);
|
||||
var matches = line.substr(cursor.column + 1).split(ch);
|
||||
|
||||
return n < matches.length ? matches.slice(0, n).join(ch).length : null;
|
||||
},
|
||||
getLeftNthChar: function(editor, cursor, ch, n) {
|
||||
var line = editor.getSession().getLine(cursor.row);
|
||||
var matches = line.substr(0, cursor.column).split(ch);
|
||||
|
||||
return n < matches.length ? matches.slice(-1 * n).join(ch).length : null;
|
||||
},
|
||||
toRealChar: function(ch) {
|
||||
if (ch.length === 1)
|
||||
return ch;
|
||||
|
||||
if (/^shift-./.test(ch))
|
||||
return ch[ch.length - 1].toUpperCase();
|
||||
else
|
||||
return "";
|
||||
},
|
||||
copyLine: function(editor) {
|
||||
var pos = editor.getCursorPosition();
|
||||
editor.selection.clearSelection();
|
||||
editor.moveCursorTo(pos.row, pos.column);
|
||||
editor.selection.selectLine();
|
||||
registers._default.isLine = true;
|
||||
registers._default.text = editor.getCopyText().replace(/\n$/, "");
|
||||
editor.selection.clearSelection();
|
||||
editor.moveCursorTo(pos.row, pos.column);
|
||||
}
|
||||
};
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -32,11 +32,15 @@ define(function(require, exports, module) {
|
||||
"use strict";
|
||||
|
||||
var dom = require("../lib/dom");
|
||||
var IE8;
|
||||
|
||||
var Cursor = function(parentEl) {
|
||||
this.element = dom.createElement("div");
|
||||
this.element.className = "ace_layer ace_cursor-layer";
|
||||
parentEl.appendChild(this.element);
|
||||
|
||||
if (IE8 === undefined)
|
||||
IE8 = "opacity" in this.element;
|
||||
|
||||
this.isVisible = false;
|
||||
this.isBlinking = true;
|
||||
@@ -46,9 +50,22 @@ var Cursor = function(parentEl) {
|
||||
this.cursors = [];
|
||||
this.cursor = this.addCursor();
|
||||
dom.addCssClass(this.element, "ace_hidden-cursors");
|
||||
this.$updateCursors = this.$updateVisibility.bind(this);
|
||||
};
|
||||
|
||||
(function() {
|
||||
|
||||
this.$updateVisibility = function(val) {
|
||||
var cursors = this.cursors;
|
||||
for (var i = cursors.length; i--; )
|
||||
cursors[i].style.visibility = val ? "" : "hidden";
|
||||
};
|
||||
this.$updateOpacity = function(val) {
|
||||
var cursors = this.cursors;
|
||||
for (var i = cursors.length; i--; )
|
||||
cursors[i].style.opacity = val ? "" : "0";
|
||||
};
|
||||
|
||||
|
||||
this.$padding = 0;
|
||||
this.setPadding = function(padding) {
|
||||
@@ -74,12 +91,13 @@ var Cursor = function(parentEl) {
|
||||
};
|
||||
|
||||
this.setSmoothBlinking = function(smoothBlinking) {
|
||||
if (smoothBlinking != this.smoothBlinking) {
|
||||
if (smoothBlinking != this.smoothBlinking && !IE8) {
|
||||
this.smoothBlinking = smoothBlinking;
|
||||
if (smoothBlinking)
|
||||
dom.addCssClass(this.element, "ace_smooth-blinking");
|
||||
else
|
||||
dom.removeCssClass(this.element, "ace_smooth-blinking");
|
||||
dom.setCssClass(this.element, "ace_smooth-blinking", smoothBlinking);
|
||||
this.$updateCursors(true);
|
||||
this.$updateCursors = (smoothBlinking
|
||||
? this.$updateOpacity
|
||||
: this.$updateVisibility).bind(this);
|
||||
this.restartTimer();
|
||||
}
|
||||
};
|
||||
@@ -113,35 +131,34 @@ var Cursor = function(parentEl) {
|
||||
};
|
||||
|
||||
this.restartTimer = function() {
|
||||
var update = this.$updateCursors;
|
||||
clearInterval(this.intervalId);
|
||||
clearTimeout(this.timeoutId);
|
||||
if (this.smoothBlinking)
|
||||
if (this.smoothBlinking) {
|
||||
dom.removeCssClass(this.element, "ace_smooth-blinking");
|
||||
for (var i = this.cursors.length; i--; )
|
||||
this.cursors[i].style.opacity = "";
|
||||
}
|
||||
|
||||
update(true);
|
||||
|
||||
if (!this.isBlinking || !this.blinkInterval || !this.isVisible)
|
||||
return;
|
||||
|
||||
if (this.smoothBlinking)
|
||||
if (this.smoothBlinking) {
|
||||
setTimeout(function(){
|
||||
dom.addCssClass(this.element, "ace_smooth-blinking");
|
||||
}.bind(this));
|
||||
|
||||
}
|
||||
|
||||
var blink = function(){
|
||||
this.timeoutId = setTimeout(function() {
|
||||
for (var i = this.cursors.length; i--; ) {
|
||||
this.cursors[i].style.opacity = 0;
|
||||
}
|
||||
}.bind(this), 0.6 * this.blinkInterval);
|
||||
update(false);
|
||||
}, 0.6 * this.blinkInterval);
|
||||
}.bind(this);
|
||||
|
||||
this.intervalId = setInterval(function() {
|
||||
for (var i = this.cursors.length; i--; ) {
|
||||
this.cursors[i].style.opacity = "";
|
||||
}
|
||||
update(true);
|
||||
blink();
|
||||
}.bind(this), this.blinkInterval);
|
||||
}, this.blinkInterval);
|
||||
|
||||
blink();
|
||||
};
|
||||
@@ -170,19 +187,23 @@ var Cursor = function(parentEl) {
|
||||
selections = [{cursor: null}];
|
||||
}
|
||||
|
||||
for (var i = selections.length; i--; ) {
|
||||
for (var i = 0, n = selections.length; i < n; i++) {
|
||||
var pixelPos = this.getPixelPosition(selections[i].cursor, true);
|
||||
if ((pixelPos.top > config.height + config.offset ||
|
||||
pixelPos.top < -config.offset) && i > 1) {
|
||||
pixelPos.top < 0) && i > 1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var style = (this.cursors[cursorIndex++] || this.addCursor()).style;
|
||||
|
||||
style.left = pixelPos.left + "px";
|
||||
style.top = pixelPos.top + "px";
|
||||
style.width = config.characterWidth + "px";
|
||||
style.height = config.lineHeight + "px";
|
||||
|
||||
if (!this.drawCursor) {
|
||||
style.left = pixelPos.left + "px";
|
||||
style.top = pixelPos.top + "px";
|
||||
style.width = config.characterWidth + "px";
|
||||
style.height = config.lineHeight + "px";
|
||||
} else {
|
||||
this.drawCursor(style, pixelPos, config, selections[i], this.session);
|
||||
}
|
||||
}
|
||||
while (this.cursors.length > cursorIndex)
|
||||
this.removeCursor();
|
||||
@@ -194,6 +215,8 @@ var Cursor = function(parentEl) {
|
||||
this.$pixelPos = pixelPos;
|
||||
this.restartTimer();
|
||||
};
|
||||
|
||||
this.drawCursor = null;
|
||||
|
||||
this.$setOverwrite = function(overwrite) {
|
||||
if (overwrite != this.overwrite) {
|
||||
|
||||
@@ -0,0 +1,176 @@
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Distributed under the BSD license:
|
||||
*
|
||||
* Copyright (c) 2010, Ajax.org B.V.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* * Neither the name of Ajax.org B.V. nor the
|
||||
* names of its contributors may be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
|
||||
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
define(function(require, exports, module) {
|
||||
|
||||
var oop = require("../lib/oop");
|
||||
var dom = require("../lib/dom");
|
||||
var lang = require("../lib/lang");
|
||||
var useragent = require("../lib/useragent");
|
||||
var EventEmitter = require("../lib/event_emitter").EventEmitter;
|
||||
|
||||
var CHAR_COUNT = 0;
|
||||
|
||||
var FontMetrics = exports.FontMetrics = function(parentEl, interval) {
|
||||
this.el = dom.createElement("div");
|
||||
this.$setMeasureNodeStyles(this.el.style, true);
|
||||
|
||||
this.$main = dom.createElement("div");
|
||||
this.$setMeasureNodeStyles(this.$main.style);
|
||||
|
||||
this.$measureNode = dom.createElement("div");
|
||||
this.$setMeasureNodeStyles(this.$measureNode.style);
|
||||
|
||||
|
||||
this.el.appendChild(this.$main);
|
||||
this.el.appendChild(this.$measureNode);
|
||||
parentEl.appendChild(this.el);
|
||||
|
||||
if (!CHAR_COUNT)
|
||||
this.$testFractionalRect();
|
||||
this.$measureNode.innerHTML = lang.stringRepeat("X", CHAR_COUNT);
|
||||
|
||||
this.$characterSize = {width: 0, height: 0};
|
||||
this.checkForSizeChanges();
|
||||
};
|
||||
|
||||
(function() {
|
||||
|
||||
oop.implement(this, EventEmitter);
|
||||
|
||||
this.$characterSize = {width: 0, height: 0};
|
||||
|
||||
this.$testFractionalRect = function() {
|
||||
var el = dom.createElement("div");
|
||||
this.$setMeasureNodeStyles(el.style);
|
||||
el.style.width = "0.2px";
|
||||
document.documentElement.appendChild(el);
|
||||
var w = el.getBoundingClientRect().width;
|
||||
if (w > 0 && w < 1)
|
||||
CHAR_COUNT = 50;
|
||||
else
|
||||
CHAR_COUNT = 100;
|
||||
el.parentNode.removeChild(el);
|
||||
};
|
||||
|
||||
this.$setMeasureNodeStyles = function(style, isRoot) {
|
||||
style.width = style.height = "auto";
|
||||
style.left = style.top = "0px";
|
||||
style.visibility = "hidden";
|
||||
style.position = "absolute";
|
||||
style.whiteSpace = "pre";
|
||||
|
||||
if (useragent.isIE < 8) {
|
||||
style["font-family"] = "inherit";
|
||||
} else {
|
||||
style.font = "inherit";
|
||||
}
|
||||
style.overflow = isRoot ? "hidden" : "visible";
|
||||
};
|
||||
|
||||
this.checkForSizeChanges = function() {
|
||||
var size = this.$measureSizes();
|
||||
if (size && (this.$characterSize.width !== size.width || this.$characterSize.height !== size.height)) {
|
||||
this.$measureNode.style.fontWeight = "bold";
|
||||
var boldSize = this.$measureSizes();
|
||||
this.$measureNode.style.fontWeight = "";
|
||||
this.$characterSize = size;
|
||||
this.charSizes = Object.create(null);
|
||||
this.allowBoldFonts = boldSize && boldSize.width === size.width && boldSize.height === size.height;
|
||||
this._emit("changeCharacterSize", {data: size});
|
||||
}
|
||||
};
|
||||
|
||||
this.$pollSizeChanges = function() {
|
||||
if (this.$pollSizeChangesTimer)
|
||||
return this.$pollSizeChangesTimer;
|
||||
var self = this;
|
||||
return this.$pollSizeChangesTimer = setInterval(function() {
|
||||
self.checkForSizeChanges();
|
||||
}, 500);
|
||||
};
|
||||
|
||||
this.setPolling = function(val) {
|
||||
if (val) {
|
||||
this.$pollSizeChanges();
|
||||
} else {
|
||||
if (this.$pollSizeChangesTimer)
|
||||
this.$pollSizeChangesTimer;
|
||||
}
|
||||
};
|
||||
|
||||
this.$measureSizes = function() {
|
||||
if (CHAR_COUNT === 50) {
|
||||
var rect = null;
|
||||
try {
|
||||
rect = this.$measureNode.getBoundingClientRect();
|
||||
} catch(e) {
|
||||
rect = {width: 0, height:0 };
|
||||
};
|
||||
var size = {
|
||||
height: rect.height,
|
||||
width: rect.width / CHAR_COUNT
|
||||
};
|
||||
} else {
|
||||
var size = {
|
||||
height: this.$measureNode.clientHeight,
|
||||
width: this.$measureNode.clientWidth / CHAR_COUNT
|
||||
};
|
||||
}
|
||||
// Size and width can be null if the editor is not visible or
|
||||
// detached from the document
|
||||
if (size.width === 0 || size.height === 0)
|
||||
return null;
|
||||
return size;
|
||||
};
|
||||
|
||||
this.$measureCharWidth = function(ch) {
|
||||
this.$main.innerHTML = lang.stringRepeat(ch, CHAR_COUNT);
|
||||
var rect = this.$main.getBoundingClientRect();
|
||||
return rect.width / CHAR_COUNT;
|
||||
};
|
||||
|
||||
this.getCharacterWidth = function(ch) {
|
||||
var w = this.charSizes[ch];
|
||||
if (w === undefined) {
|
||||
this.charSizes[ch] = this.$measureCharWidth(ch) / this.$characterSize.width;
|
||||
}
|
||||
return w;
|
||||
};
|
||||
|
||||
this.destroy = function() {
|
||||
clearInterval(this.$pollSizeChangesTimer);
|
||||
if (this.el && this.el.parentNode)
|
||||
this.el.parentNode.removeChild(this.el);
|
||||
};
|
||||
|
||||
}).call(FontMetrics.prototype);
|
||||
|
||||
});
|
||||
@@ -46,6 +46,8 @@ var Gutter = function(parentEl) {
|
||||
|
||||
this.$annotations = [];
|
||||
this.$updateAnnotations = this.$updateAnnotations.bind(this);
|
||||
|
||||
this.$cells = [];
|
||||
};
|
||||
|
||||
(function() {
|
||||
@@ -56,7 +58,8 @@ var Gutter = function(parentEl) {
|
||||
if (this.session)
|
||||
this.session.removeEventListener("change", this.$updateAnnotations);
|
||||
this.session = session;
|
||||
session.on("change", this.$updateAnnotations);
|
||||
if (session)
|
||||
session.on("change", this.$updateAnnotations);
|
||||
};
|
||||
|
||||
this.addGutterDecoration = function(row, className){
|
||||
@@ -73,8 +76,7 @@ var Gutter = function(parentEl) {
|
||||
|
||||
this.setAnnotations = function(annotations) {
|
||||
// iterate over sparse array
|
||||
this.$annotations = []
|
||||
var rowInfo, row;
|
||||
this.$annotations = [];
|
||||
for (var i = 0; i < annotations.length; i++) {
|
||||
var annotation = annotations[i];
|
||||
var row = annotation.row;
|
||||
@@ -110,76 +112,140 @@ var Gutter = function(parentEl) {
|
||||
} else if (delta.action == "removeText" || delta.action == "removeLines") {
|
||||
this.$annotations.splice(firstRow, len + 1, null);
|
||||
} else {
|
||||
var args = Array(len + 1);
|
||||
var args = new Array(len + 1);
|
||||
args.unshift(firstRow, 1);
|
||||
this.$annotations.splice.apply(this.$annotations, args);
|
||||
}
|
||||
};
|
||||
|
||||
this.update = function(config) {
|
||||
var emptyAnno = {className: ""};
|
||||
var html = [];
|
||||
var i = config.firstRow;
|
||||
var lastRow = config.lastRow;
|
||||
var fold = this.session.getNextFoldLine(i);
|
||||
var session = this.session;
|
||||
var firstRow = config.firstRow;
|
||||
var lastRow = Math.min(config.lastRow + config.gutterOffset, // needed to compensate for hor scollbar
|
||||
session.getLength() - 1);
|
||||
var fold = session.getNextFoldLine(firstRow);
|
||||
var foldStart = fold ? fold.start.row : Infinity;
|
||||
var foldWidgets = this.$showFoldWidgets && this.session.foldWidgets;
|
||||
var breakpoints = this.session.$breakpoints;
|
||||
var decorations = this.session.$decorations;
|
||||
var foldWidgets = this.$showFoldWidgets && session.foldWidgets;
|
||||
var breakpoints = session.$breakpoints;
|
||||
var decorations = session.$decorations;
|
||||
var firstLineNumber = session.$firstLineNumber;
|
||||
var lastLineNumber = 0;
|
||||
|
||||
var gutterRenderer = session.gutterRenderer || this.$renderer;
|
||||
|
||||
var cell = null;
|
||||
var index = -1;
|
||||
var row = firstRow;
|
||||
while (true) {
|
||||
if(i > foldStart) {
|
||||
i = fold.end.row + 1;
|
||||
fold = this.session.getNextFoldLine(i, fold);
|
||||
foldStart = fold ?fold.start.row :Infinity;
|
||||
if (row > foldStart) {
|
||||
row = fold.end.row + 1;
|
||||
fold = session.getNextFoldLine(row, fold);
|
||||
foldStart = fold ? fold.start.row : Infinity;
|
||||
}
|
||||
if(i > lastRow)
|
||||
if (row > lastRow) {
|
||||
while (this.$cells.length > index + 1) {
|
||||
cell = this.$cells.pop();
|
||||
this.element.removeChild(cell.element);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
var annotation = this.$annotations[i] || emptyAnno;
|
||||
html.push(
|
||||
"<div class='ace_gutter-cell ",
|
||||
breakpoints[i] || "", decorations[i] || "", annotation.className,
|
||||
"' style='height:", this.session.getRowLength(i) * config.lineHeight, "px;'>",
|
||||
lastLineNumber = i + 1
|
||||
);
|
||||
cell = this.$cells[++index];
|
||||
if (!cell) {
|
||||
cell = {element: null, textNode: null, foldWidget: null};
|
||||
cell.element = dom.createElement("div");
|
||||
cell.textNode = document.createTextNode('');
|
||||
cell.element.appendChild(cell.textNode);
|
||||
this.element.appendChild(cell.element);
|
||||
this.$cells[index] = cell;
|
||||
}
|
||||
|
||||
var className = "ace_gutter-cell ";
|
||||
if (breakpoints[row])
|
||||
className += breakpoints[row];
|
||||
if (decorations[row])
|
||||
className += decorations[row];
|
||||
if (this.$annotations[row])
|
||||
className += this.$annotations[row].className;
|
||||
if (cell.element.className != className)
|
||||
cell.element.className = className;
|
||||
|
||||
var height = session.getRowLength(row) * config.lineHeight + "px";
|
||||
if (height != cell.element.style.height)
|
||||
cell.element.style.height = height;
|
||||
|
||||
if (foldWidgets) {
|
||||
var c = foldWidgets[i];
|
||||
var c = foldWidgets[row];
|
||||
// check if cached value is invalidated and we need to recompute
|
||||
if (c == null)
|
||||
c = foldWidgets[i] = this.session.getFoldWidget(i);
|
||||
if (c)
|
||||
html.push(
|
||||
"<span class='ace_fold-widget ace_", c,
|
||||
c == "start" && i == foldStart && i < fold.end.row ? " ace_closed" : " ace_open",
|
||||
"' style='height:", config.lineHeight, "px",
|
||||
"'></span>"
|
||||
);
|
||||
c = foldWidgets[row] = session.getFoldWidget(row);
|
||||
}
|
||||
|
||||
html.push("</div>");
|
||||
if (c) {
|
||||
if (!cell.foldWidget) {
|
||||
cell.foldWidget = dom.createElement("span");
|
||||
cell.element.appendChild(cell.foldWidget);
|
||||
}
|
||||
var className = "ace_fold-widget ace_" + c;
|
||||
if (c == "start" && row == foldStart && row < fold.end.row)
|
||||
className += " ace_closed";
|
||||
else
|
||||
className += " ace_open";
|
||||
if (cell.foldWidget.className != className)
|
||||
cell.foldWidget.className = className;
|
||||
|
||||
i++;
|
||||
var height = config.lineHeight + "px";
|
||||
if (cell.foldWidget.style.height != height)
|
||||
cell.foldWidget.style.height = height;
|
||||
} else {
|
||||
if (cell.foldWidget) {
|
||||
cell.element.removeChild(cell.foldWidget);
|
||||
cell.foldWidget = null;
|
||||
}
|
||||
}
|
||||
|
||||
var text = lastLineNumber = gutterRenderer
|
||||
? gutterRenderer.getText(session, row)
|
||||
: row + firstLineNumber;
|
||||
if (text != cell.textNode.data)
|
||||
cell.textNode.data = text;
|
||||
|
||||
row++;
|
||||
}
|
||||
|
||||
this.element = dom.setInnerHtml(this.element, html.join(""));
|
||||
this.element.style.height = config.minHeight + "px";
|
||||
|
||||
if (this.$fixedWidth || session.$useWrapMode)
|
||||
lastLineNumber = session.getLength() + firstLineNumber;
|
||||
|
||||
var gutterWidth = gutterRenderer
|
||||
? gutterRenderer.getWidth(session, lastLineNumber, config)
|
||||
: lastLineNumber.toString().length * config.characterWidth;
|
||||
|
||||
if (this.session.$useWrapMode)
|
||||
lastLineNumber = this.session.getLength();
|
||||
|
||||
var gutterWidth = ("" + lastLineNumber).length * config.characterWidth;
|
||||
var padding = this.$padding || this.$computePadding();
|
||||
gutterWidth += padding.left + padding.right;
|
||||
if (gutterWidth !== this.gutterWidth) {
|
||||
if (gutterWidth !== this.gutterWidth && !isNaN(gutterWidth)) {
|
||||
this.gutterWidth = gutterWidth;
|
||||
this.element.style.width = Math.ceil(this.gutterWidth) + "px";
|
||||
this._emit("changeGutterWidth", gutterWidth);
|
||||
}
|
||||
};
|
||||
|
||||
this.$fixedWidth = false;
|
||||
|
||||
this.$showLineNumbers = true;
|
||||
this.$renderer = "";
|
||||
this.setShowLineNumbers = function(show) {
|
||||
this.$renderer = !show && {
|
||||
getWidth: function() {return ""},
|
||||
getText: function() {return ""}
|
||||
};
|
||||
};
|
||||
|
||||
this.getShowLineNumbers = function() {
|
||||
return this.$showLineNumbers;
|
||||
};
|
||||
|
||||
this.$showFoldWidgets = true;
|
||||
this.setShowFoldWidgets = function(show) {
|
||||
if (show)
|
||||
@@ -199,9 +265,9 @@ var Gutter = function(parentEl) {
|
||||
if (!this.element.firstChild)
|
||||
return {left: 0, right: 0};
|
||||
var style = dom.computedStyle(this.element.firstChild);
|
||||
this.$padding = {}
|
||||
this.$padding.left = parseInt(style.paddingLeft) + 1;
|
||||
this.$padding.right = parseInt(style.paddingRight);
|
||||
this.$padding = {};
|
||||
this.$padding.left = parseInt(style.paddingLeft) + 1 || 0;
|
||||
this.$padding.right = parseInt(style.paddingRight) || 0;
|
||||
return this.$padding;
|
||||
};
|
||||
|
||||
|
||||
@@ -82,6 +82,8 @@ var Marker = function(parentEl) {
|
||||
marker.renderer(html, range, left, top, config);
|
||||
} else if (marker.type == "fullLine") {
|
||||
this.drawFullLineMarker(html, range, marker.clazz, config);
|
||||
} else if (marker.type == "screenLine") {
|
||||
this.drawScreenLineMarker(html, range, marker.clazz, config);
|
||||
} else if (range.isMultiLine()) {
|
||||
if (marker.type == "text")
|
||||
this.drawTextMarker(html, range, marker.clazz, config);
|
||||
@@ -91,7 +93,7 @@ var Marker = function(parentEl) {
|
||||
this.drawSingleLineMarker(html, range, marker.clazz + " ace_start", config);
|
||||
}
|
||||
}
|
||||
this.element = dom.setInnerHtml(this.element, html.join(""));
|
||||
this.element.innerHTML = html.join("");
|
||||
};
|
||||
|
||||
this.$getTop = function(row, layerConfig) {
|
||||
@@ -99,7 +101,7 @@ var Marker = function(parentEl) {
|
||||
};
|
||||
|
||||
// Draws a marker, which spans a range of text on multiple lines
|
||||
this.drawTextMarker = function(stringBuilder, range, clazz, layerConfig) {
|
||||
this.drawTextMarker = function(stringBuilder, range, clazz, layerConfig, extraStyle) {
|
||||
// selection start
|
||||
var row = range.start.row;
|
||||
|
||||
@@ -107,35 +109,36 @@ var Marker = function(parentEl) {
|
||||
row, range.start.column,
|
||||
row, this.session.getScreenLastRowColumn(row)
|
||||
);
|
||||
this.drawSingleLineMarker(stringBuilder, lineRange, clazz + " ace_start", layerConfig, 1, "text");
|
||||
this.drawSingleLineMarker(stringBuilder, lineRange, clazz + " ace_start", layerConfig, 1, extraStyle);
|
||||
|
||||
// selection end
|
||||
row = range.end.row;
|
||||
lineRange = new Range(row, 0, row, range.end.column);
|
||||
this.drawSingleLineMarker(stringBuilder, lineRange, clazz, layerConfig, 0, "text");
|
||||
this.drawSingleLineMarker(stringBuilder, lineRange, clazz, layerConfig, 0, extraStyle);
|
||||
|
||||
for (row = range.start.row + 1; row < range.end.row; row++) {
|
||||
lineRange.start.row = row;
|
||||
lineRange.end.row = row;
|
||||
lineRange.end.column = this.session.getScreenLastRowColumn(row);
|
||||
this.drawSingleLineMarker(stringBuilder, lineRange, clazz, layerConfig, 1, "text");
|
||||
this.drawSingleLineMarker(stringBuilder, lineRange, clazz, layerConfig, 1, extraStyle);
|
||||
}
|
||||
};
|
||||
|
||||
// Draws a multi line marker, where lines span the full width
|
||||
this.drawMultiLineMarker = function(stringBuilder, range, clazz, config, type) {
|
||||
this.drawMultiLineMarker = function(stringBuilder, range, clazz, config, extraStyle) {
|
||||
// from selection start to the end of the line
|
||||
var padding = this.$padding;
|
||||
var height = config.lineHeight;
|
||||
var top = this.$getTop(range.start.row, config);
|
||||
var left = padding + range.start.column * config.characterWidth;
|
||||
extraStyle = extraStyle || "";
|
||||
|
||||
stringBuilder.push(
|
||||
"<div class='", clazz, " ace_start' style='",
|
||||
"height:", height, "px;",
|
||||
"right:0;",
|
||||
"top:", top, "px;",
|
||||
"left:", left, "px;'></div>"
|
||||
"left:", left, "px;", extraStyle, "'></div>"
|
||||
);
|
||||
|
||||
// from start of the last line to the selection end
|
||||
@@ -147,7 +150,7 @@ var Marker = function(parentEl) {
|
||||
"height:", height, "px;",
|
||||
"width:", width, "px;",
|
||||
"top:", top, "px;",
|
||||
"left:", padding, "px;'></div>"
|
||||
"left:", padding, "px;", extraStyle, "'></div>"
|
||||
);
|
||||
|
||||
// all the complete lines
|
||||
@@ -161,12 +164,12 @@ var Marker = function(parentEl) {
|
||||
"height:", height, "px;",
|
||||
"right:0;",
|
||||
"top:", top, "px;",
|
||||
"left:", padding, "px;'></div>"
|
||||
"left:", padding, "px;", extraStyle, "'></div>"
|
||||
);
|
||||
};
|
||||
|
||||
// Draws a marker which covers part or whole width of a single screen line
|
||||
this.drawSingleLineMarker = function(stringBuilder, range, clazz, config, extraLength) {
|
||||
this.drawSingleLineMarker = function(stringBuilder, range, clazz, config, extraLength, extraStyle) {
|
||||
var height = config.lineHeight;
|
||||
var width = (range.end.column + (extraLength || 0) - range.start.column) * config.characterWidth;
|
||||
|
||||
@@ -178,11 +181,11 @@ var Marker = function(parentEl) {
|
||||
"height:", height, "px;",
|
||||
"width:", width, "px;",
|
||||
"top:", top, "px;",
|
||||
"left:", left,"px;'></div>"
|
||||
"left:", left, "px;", extraStyle || "", "'></div>"
|
||||
);
|
||||
};
|
||||
|
||||
this.drawFullLineMarker = function(stringBuilder, range, clazz, config) {
|
||||
this.drawFullLineMarker = function(stringBuilder, range, clazz, config, extraStyle) {
|
||||
var top = this.$getTop(range.start.row, config);
|
||||
var height = config.lineHeight;
|
||||
if (range.start.row != range.end.row)
|
||||
@@ -192,9 +195,21 @@ var Marker = function(parentEl) {
|
||||
"<div class='", clazz, "' style='",
|
||||
"height:", height, "px;",
|
||||
"top:", top, "px;",
|
||||
"left:0;right:0;'></div>"
|
||||
"left:0;right:0;", extraStyle || "", "'></div>"
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
this.drawScreenLineMarker = function(stringBuilder, range, clazz, config, extraStyle) {
|
||||
var top = this.$getTop(range.start.row, config);
|
||||
var height = config.lineHeight;
|
||||
|
||||
stringBuilder.push(
|
||||
"<div class='", clazz, "' style='",
|
||||
"height:", height, "px;",
|
||||
"top:", top, "px;",
|
||||
"left:0;right:0;", extraStyle || "", "'></div>"
|
||||
);
|
||||
};
|
||||
|
||||
}).call(Marker.prototype);
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user