Compare commits

..

107 Commits

Author SHA1 Message Date
Dawa Ometto 5781ac6bbd Release 4.0.0 2015-04-11 11:32:03 +02:00
Dawa Ometto a51bc4427a Merge pull request #994 from repotag/kramdown
Move to Kramdown and support JRuby
2015-04-11 11:07:34 +02:00
Dawa Ometto 80854584bb Make test regex more lenient 2015-04-11 01:29:08 +02:00
Dawa Ometto a27b882493 Test on jruby 2015-04-06 00:57:18 +02:00
Dawa Ometto ba6f957692 Move to Kramdown 2015-04-06 00:28:31 +02:00
Dawa Ometto ec0e1bf26f Merge pull request #992 from kirat-singh/fix-upload-file-url
prepend baseUrl to /uploadFile
2015-04-05 23:34:07 +02:00
Kirat Singh e4df298bb4 prepend baseUrl to /uploadFile 2015-04-05 14:22:41 -04:00
Dawa Ometto 804d21e31d Release 3.1.3 2015-04-04 11:45:17 +02:00
Dawa Ometto 6d33687866 Merge pull request #990 from repotag/issue940
Block on no-edit without using middleware
2015-04-03 17:32:20 +02:00
Dawa Ometto 21bb1efb46 Block on no-edit without using middleware 2015-04-03 17:22:51 +02:00
Dawa Ometto 9d0986f1ca Merge pull request #964 from simonzack/mathjax_livepreview
Enable mathjax in live preview
2015-04-02 13:13:25 +02:00
Bart Kamphorst fd483f397f Merge pull request #988 from andrewarrow/fix_template_dir
adding template dir logic to app.rb vs. just the bin
2015-03-30 13:27:54 +02:00
Andrew Arrow 09364850ee adding template dir logic to app.rb vs. just the bin 2015-03-27 15:13:55 -07:00
Dawa Ometto 4d3a8bad4c Merge pull request #985 from uraimo/patch-1
Fixed two broken links in README.md
2015-03-13 14:40:19 +01:00
Umberto Raimondi cc39b0aa41 Fixed two broken links in README.md
The textile site was shut down a few years ago, better refer to wiki for format details.
2015-03-13 14:28:04 +01:00
Dawa Ometto 6fc3b4be75 Merge pull request #970 from repotag/issue969
Add root slash to drag-and-drop upload path. Resolves #969.
2015-01-30 22:13:01 +01:00
Dawa Ometto af6d690fc8 Add root slash to drag-and-drop upload path. Resolves #969. 2015-01-30 22:06:31 +01:00
Dawa Ometto eea5152444 Update README.md 2015-01-30 18:34:18 +01:00
Dawa Ometto 1ca38b625b Update README.md 2015-01-30 18:33:52 +01:00
Sunny Ripert f964407c47 Merge pull request #965 from simonzack/ace_hook
added an initAce hook to allow configuration of the live editor
2015-01-28 17:00:43 +01:00
simonzack 3be2e76ec0 enable $ inline expressions so the example markdown doc works, $ can be escaped using \$ so shouldn't be much of an issue 2015-01-28 19:06:24 +11:00
simonzack f9a9b90ed7 enable mathjax in live preview 2015-01-28 18:00:35 +11:00
Dawa Ometto 1f2917ac22 Merge pull request #959 from simonzack/ace_update
Updated ace so markdown highlights better.
2015-01-27 17:51:47 +01:00
simonzack fe1a8569f6 updated ace 2015-01-27 22:46:30 +11:00
simonzack 33e8d4f328 added an initAce hook to allow configuration of the live editor 2015-01-27 21:16:05 +11:00
Dawa Ometto e183260d0a Merge pull request #956 from repotag/issue_955
Resolves #955.
2015-01-26 23:16:50 +01:00
Dawa Ometto b4023942b4 Remove page file dir from upload path 2015-01-26 23:10:28 +01:00
Dawa Ometto c10c24d90d Merge pull request #953 from simonzack/master
Resolves #952.
2015-01-25 18:45:17 +01:00
simonzack 915f63cac4 pixel tweaks so the left scrollbar is selectable and the viewport takes the whole page 2015-01-26 04:15:14 +11:00
Dawa Ometto 5c30ff4d3e Release 3.1.2 2015-01-23 00:27:04 +01:00
Dawa Ometto 3269f02ba7 Merge pull request #950 from repotag/fix_allow_editing
Set the allow_editing wiki option to true by default.
2015-01-23 00:26:22 +01:00
Dawa Ometto e5c2e3b3eb Set the allow_editing wiki option to true by default. 2015-01-23 00:14:13 +01:00
Dawa Ometto 77f4aee0af Fix test related to gollum-lib TOC update 2015-01-23 00:00:01 +01:00
Dawa Ometto 868cbdfc7b Merge pull request #936 from techwiz24/iss922-sort-pages
Sort `/pages` alphabetically. Resolves #922.
2015-01-13 10:18:51 +01:00
Nathan Lowe a650c0eab8 Sort /pages alphabetically
Previously, the 'All Pages' view was not sorted alphabetically. We need
to sort on the lowercase version of the page names so that lowercase
names do not end up at the bottom of the list and are instead mixed in
alphabetically, as they should be.

Patches test/test_latest_changes_view.rb to include changes needed to
test alphabetical sorting.
2015-01-12 19:04:07 -05:00
Dawa Ometto a7dc8d8c6f Merge pull request #929 from repotag/830_pagename_encodings
Allow utf-8 page names when not using grit. Resolves #830 (when using rugged adapter).
2015-01-04 13:07:27 +01:00
Dawa Ometto 355db16d2c Allow utf-8 page names when not using grit. 2015-01-04 12:20:25 +01:00
Dawa Ometto c54ce41eb7 Merge pull request #928 from LEW21/compare-escape-redirect
Encode the page title in the URL of a diff page
2015-01-03 19:58:56 +01:00
Janusz Lewandowski afb7d4c9d1 Encode the page title in the URL of a diff page. 2015-01-03 17:56:51 +01:00
Dawa Ometto b55cdde9da Merge pull request #924 from rtrvrtg/patch-1
Fix wiki_options errors for hosted Gollum instances.
2014-12-29 12:31:39 +01:00
Geoffrey Roberts 93cbc6c770 Fix intermittent wiki_options errors
Changed to resolve intermittent errors where I'd see the following in my logs:

```text
ERROR -- : app error: undefined method `wiki_options' for Precious::App:Class (NoMethodError)
ERROR -- : /home/gollum/production/releases/20141229-172128/vendor/bundle/ruby/2.0.0/gems/gollum-3.1.1.1anchor4/lib/gollum/editing_auth.rb:10:in `call'
ERROR -- : /home/gollum/production/releases/20141229-172128/vendor/bundle/ruby/2.0.0/gems/rack-protection-1.5.3/lib/rack/protection/xss_header.rb:18:in `call'
...
```

I suspect this is the case for Sinatra applications that host instances of Gollum using its `map` feature, as this reflects the production setup we're using it in. Calling @app.settings.wiki_options uses the app instance that gets passed into Precious::EditingAuth, thereby ensuring that we're getting a Gollum instance all the time.
2014-12-29 17:28:47 +11:00
Dawa Ometto 3b1b4a0a96 Update README.md 2014-12-22 20:28:50 +01:00
Dawa Ometto 10b45cb54d Add --bare command line option (see #811). 2014-12-22 20:20:57 +01:00
Dawa Ometto 9d289e571b Describe important security update. 2014-12-18 16:44:23 +01:00
Dawa Ometto cce871c30e Allow running tests with alternative adapter 2014-12-08 01:44:01 +01:00
Dawa Ometto c2b605a90f Release 3.1.1 2014-12-04 14:06:57 +01:00
Dawa Ometto 09149592b5 Set allow_editing in Precious::App to true by default. Closes #911 2014-12-04 13:53:58 +01:00
Dawa Ometto e7410e551b Bring rake dependency up to date. 2014-11-28 14:13:40 +01:00
Dawa Ometto 9d77bc4192 Release 3.1.0 2014-11-28 14:06:36 +01:00
Dawa Ometto 537fa0c423 Merge branch 'rc' 2014-11-28 13:38:33 +01:00
Dawa Ometto c78dbc8bc1 Merge branch 'master' into rc
Conflicts:
	README.md
	lib/gollum/app.rb
2014-11-28 13:36:21 +01:00
Dawa Ometto 249eed5c2c Update HISTORY.md 2014-11-28 13:26:47 +01:00
Dawa Ometto 55eb3b24f7 Merge pull request #904 from ut7/latest_changes_test_fix_dependency
Update test to avoide being dependent on git configuration
2014-11-28 12:39:12 +01:00
Étienne Charignon 508c255d0f Update test to avoide being dependent on git configuration
The previous test implementation was dependent on the git configuration:
	renames = copies

It could then pass on a computer with that config but not passe on
a different developpeur computer who could have a different git configuration.

That new implementation is testing the same behaviour but at a lower
level and is not dependent on the git configuration anymore.
2014-11-28 12:12:23 +01:00
Dawa Ometto 9f8a90c0ac Merge pull request #875 from ut7/latest-changes
Add a "latest changes" button and page
2014-11-27 19:22:42 +01:00
Dawa Ometto 861dc935cb Configure git on Travis 2014-11-27 19:12:54 +01:00
Dawa Ometto 96ef8cacea Merge pull request #899 from repotag/gollum-lib-112
Fix tests, see https://github.com/gollum/gollum-lib/issues/112
2014-11-27 14:11:01 +01:00
Dawa Ometto 5f3ecc8713 Merge pull request #901 from repotag/prepare-release
Prepare new release.
2014-11-27 13:58:24 +01:00
Dawa Ometto 8b8ef0eb46 Prepare new release. 2014-11-27 13:51:02 +01:00
Dawa Ometto 2dbea54c84 Merge pull request #900 from repotag/adapter-flag
Implement git adapter CLI flag.
2014-11-26 00:47:39 +01:00
Dawa Ometto a776d9fb6f Implement git adapter CLI flag. 2014-11-26 00:33:03 +01:00
Dawa Ometto 57b7bbff5a Update README.md
Updated command line options.
2014-11-25 23:43:42 +01:00
Dawa Ometto a832b0ed54 Fix tests, see https://github.com/gollum/gollum-lib/issues/112 2014-11-25 19:54:31 +01:00
Dawa Ometto 3ec75b84ae Merge pull request #898 from bambycha/editable
Disable editing from UI
2014-11-22 12:15:24 +01:00
Roman Bambycha b932763080 add function to disable editing, resolves #879 2014-11-17 19:46:46 +02:00
Étienne Charignon 374f8f2f69 First attempt at a global latest changes overview.
- uses a wiki_options entry named :latest_changes_count instead of a
  constant
- lists modified pages with links
2014-11-11 22:43:34 +01:00
Dawa Ometto 226c253d5a Merge pull request #895 from repotag/rc_fix_sidebar
Fix sidebar, header, and footer, and add regression test.
2014-11-10 22:54:05 +01:00
Dawa Ometto c78a9f7950 Fix sidebar, header, and footer, and add regression test. 2014-11-10 22:39:42 +01:00
Bart Kamphorst 7264510ab2 Merge pull request #893 from tuftedocelot/add-systemd
Add systemd service file to contrib and move openrc files to contrib directory as well.
2014-11-06 22:03:18 +01:00
Dawa Ometto 3458ec6511 Merge pull request #894 from repotag/892-view-file-revisions
Route /filename/[commit-sha] will display specific revision of a file. Resolves #892.
2014-11-06 21:57:25 +01:00
Dawa Ometto 2c80db6678 Route /filename/[commit-sha] will display specific revision of a file. Resolves #892. 2014-11-06 21:31:23 +01:00
tuftedocelot 439aa6e4f5 Prepare contrib directory
Move openrc files to contrib/ in conjunction with systemd service.
Update gemspec to bundle contrib/ into the gem.
2014-11-06 14:20:33 -06:00
tuftedocelot adf544dca9 Add systemd service file to contrib
Supersedes #887 to add a systemd unit file which will run as a user and
whose gollum path and options can be changed as needed.
2014-11-06 10:33:18 -06:00
Bart Kamphorst 0965269ee9 Merge pull request #890 from repotag/ignore_footer_header_sidebar_if_empty
Do not render footers, headers and sidebars if (after formatting) they are empty. Resolves #398 .
2014-11-05 09:10:28 +01:00
Dawa Ometto 19d370c4e9 Merge pull request #891 from repotag/704-showall-files
Resolves #704. Implements the fix proposed by @bdillahu in https://github.com/gollum/gollum/pull/767 and modifies tests accordingly.
2014-11-05 02:48:03 +01:00
Dawa Ometto cee0f8b652 Change pages display to handle non-wiki file types with --show_all enabled 2014-11-05 02:30:07 +01:00
Bart Kamphorst 37664d3487 Do not render footers, headers and sidebars if (after formatting) they are empty. Resolves #398 . 2014-11-05 02:15:35 +01:00
Dawa Ometto 5a78015d81 Merge pull request #889 from repotag/888-h1-logic
Page header now uses h1-title logic. Resolves #888.
2014-11-04 23:34:42 +01:00
Dawa Ometto 7250962ba3 Page header now uses h1-title logic. 2014-11-04 23:23:46 +01:00
Bart Kamphorst 442120bfdb Merge pull request #884 from tuftedocelot/issue600-docs
add detail to inline help
2014-10-28 20:25:52 +01:00
tuftedocelot de624d1e54 add detail to inline help 2014-10-27 07:12:43 -05:00
Dawa Ometto 523029cc45 Merge pull request #876 from LEW21/delete-commit-rc
Fix the commit information for page deletion. Solves #807.
2014-10-11 14:21:50 +02:00
Janusz Lewandowski 934affe419 Fix the commit information for page deletion.
Now they will be authored by the gollum.author.
2014-10-11 13:15:23 +02:00
Bart Kamphorst 55b9af1589 Merge pull request #869 from lucas-clemente/rc
allow uploading files by drag and drop (if uploading files is enabled).
2014-10-05 08:22:40 -04:00
Lucas Clemente 16dd7e46ef allow uploading files by drag and drop 2014-10-05 14:15:48 +02:00
Dawa Ometto 851c77d8f2 Merge pull request #847 from hardywu/master
add --mathjax-config to inject root-repo/mathjax.config.js. Closes #842.
2014-10-05 12:41:03 +02:00
Hardy ee55b74898 add --mathjax [CONFIG] to inject root-repo/[CONFIG] file,
which is similar to the behavior of --css and --js.
CONFIG is "mathjax.config.js" by default.
2014-10-04 23:09:10 -04:00
Dawa Ometto 13fc1e5c66 Merge pull request #872 from Mogztter/issue-870
Resolves #870.
2014-10-04 13:53:03 +02:00
Dawa Ometto 87112c2942 Merge pull request #873 from Mogztter/issue-871
Fix css selector for Asciidoctor h1 html ouptput
2014-10-04 13:47:26 +02:00
Guillaume Grossetie 2664fdca30 Fix css selector for Asciidoctor h1 html ouptput 2014-10-04 12:45:35 +02:00
Guillaume Grossetie e05f523145 Horizontal scroll when table is too wide 2014-10-04 12:37:21 +02:00
Geoffrey Roberts 1148d29439 Made the Gollum theme responsive.
Not a particularly comprehensive change in style, just one that removes all the fixed sizing for browsers below 940px in width.

Closes #831.
2014-10-02 00:50:29 +02:00
Sunny Ripert 7ba52978d1 Merge pull request #864 from Mogztter/asciidoc-headers
Adds headers to the AsciiDoc editor
2014-09-26 15:48:47 +02:00
Sunny Ripert 138a9fee43 Merge branch 'master' into rc 2014-09-24 10:58:10 +02:00
Sunny Ripert 1d522eaf0d Merge pull request #862 from Mogztter/asciidoctor-1.5.0-syntax
Use Asciidoctor 1.5.0 new syntax backtick
2014-09-22 13:51:40 +02:00
Sunny Ripert b5c5da64c9 Merge pull request #859 from Mogztter/patch-1
Update installation procedure for AsciiDoc
2014-09-22 13:49:05 +02:00
Guillaume Grossetie 8466425836 Adds headers to the AsciiDoc editor 2014-09-18 20:03:35 +02:00
Guillaume Grossetie b774ee5cd0 Use Asciidoctor 1.5.0 new syntax backtick
Asciidoctor 1.5.0 is now using backtick for inline code.
Replaces ASCIIDoc by AsciiDoc to respect the case.
2014-09-18 19:50:54 +02:00
Guillaume Grossetie 7e2e0e926b Update README.md
GitHub Markup is now using asciidoctor
2014-09-17 19:09:28 +02:00
Dawa Ometto 30f673c63c Update README.md with installation instructions 2014-09-13 12:29:39 +02:00
Bart Kamphorst bb5be728e3 Merge pull request #853 from joscarsson/patch-1
Update --allow-uploads help text in README
2014-09-09 17:19:12 +02:00
Jonas Oscarsson 0be5d1c657 Update --allow-uploads help text in README 2014-09-09 16:57:33 +02:00
Sunny Ripert d143e6be06 Merge pull request #841 from marcusps/mathjax-cdn-fix
Fix MathJax CDN URL
2014-07-24 13:52:33 +02:00
Marcus P S e65a78a5f5 Fix MathJax CDN URL
According to the [MathJax Blog](http://www.mathjax.org/changes-to-the-mathjax-cdn/), MathJax will retire the `rackcdn` CDN address at the end of this month. Updated the script to used the new CDN URL, and also made it so that the URL is HTTP/HTTPS agnostic, following the [MathJax docs](http://docs.mathjax.org/en/latest/start.html#secure-access-to-the-cdn).
2014-07-23 11:46:22 -04:00
Dawa Ometto 4f443a3c62 Merge pull request #805 from MrJaeger/respect-dollar-signs
Allow for $ characters on front-end editor
2014-06-01 17:54:52 +02:00
bootstraponline ff82ddea97 Add font awesome link 2014-04-05 14:38:34 -04:00
Andrew Jaeger 08dd36e1b0 In the javascript editor, '$' characters were getting stomped on when
trying to remove backreferences after doing transformations on text
(Bolding, Italicizing, etc.).  This can be fixed by first escaping the
string to be transformed and then unescaping it afterwards.
2014-02-08 14:18:36 -08:00
948 changed files with 216114 additions and 68170 deletions
+1
View File
@@ -3,6 +3,7 @@ rvm:
- 2.0.0
- 2.1.0
- 2.1.1
- jruby-19mode
before_install:
- sudo apt-get update
- sudo apt-get install libicu-dev
+1 -1
View File
@@ -1,4 +1,4 @@
source 'https://rubygems.org'
gemspec
gem 'rake', '~> 10.2.2'
gem 'rake', '~> 10.4'
+18
View File
@@ -1,3 +1,21 @@
# 3.1.1 /2014-12-04
* Security fix for [remote code execution issue](https://github.com/gollum/gollum/issues/913). Please update!
# 3.1 / 2014-11-28
* New features
* Drag-and-drop uploading in the editor [@lucas-clemente](https://github.com/lucas-clemente)
* Latest changes view [@etienneCharignon](https://github.com/etienneCharignon) (#707)
* Option `--no-edit` to disable editing from the web interface [@bambycha](https://github.com/bambycha) (#879)
* Option `--mathjax-config` to specify custom mathjax configuration [@hardywu](https://github.com/hardywu) (#842)
* Major enhancements
* Made the Gollum theme responsive [@rtrvrtg](https://github.com/rtrvrtg) (#831)
* Depends on new [gollum-lib](https://github.com/gollum/gollum-lib) `4.0.0`
* Allows specifiying [git adapter](https://github.com/gollum/gollum/wiki/Git-adapters) with `--adapter` [@bartkamphorst](https://github.com/bartkamphorst), [@dometto](https://github.com/dometto)
* Numerous bugfixes
* **NB**: please pass `--h1-title` if you do not want page titles to default to the page's filepath. See [here](https://github.com/gollum/gollum/wiki/Page-titles).
# 2.4.11 / 2013-01-08
* Numerous security issues have been fixed. Please update to `2.4.11`
+20 -16
View File
@@ -29,7 +29,7 @@ Gollum follows the rules of [Semantic Versioning](http://semver.org/) and uses
## SECURITY
Don't enable `--custom-css` or `--custom-js` unless you trust every user who has the ability to edit the wiki.
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).
## INSTALLATION
@@ -39,6 +39,7 @@ The best way to install Gollum is with RubyGems:
```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.
If you're installing from source, you can use [Bundler][bundler] to pick up all the
gems:
@@ -51,15 +52,15 @@ 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.
* [ASCIIDoc](http://www.methods.co.nz/asciidoc/) -- `brew install asciidoc` on mac or `apt-get install -y asciidoc` on Ubuntu
* [Creole](http://wikicreole.org/) -- `gem install creole`
* [AsciiDoc](http://asciidoctor.org) -- `gem install asciidoctor`
* [Creole](http://www.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.
* [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`
* [Textile](http://en.wikipedia.org/wiki/Textile_(markup_language)) -- `gem install RedCloth`
* [MediaWiki](http://www.mediawiki.org/wiki/Help:Formatting) -- `gem install wikicloth`
[bundler]: http://gembundler.com/
@@ -98,32 +99,35 @@ 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
--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
--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.
--gollum-path [PATH] Specify the gollum path.
--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 Allows file uploads.
--mathjax Enables mathjax.
--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
--h1-title Sets page title to value of first h1.
```
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).
### RACK
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.
You can also run gollum with any rack-compatible server by placing configuring a config.ru
file. This allows you to utilize any Rack middleware like Rack::Auth, OmniAuth, etc. See below for an example of a `config.ru`. You can define all the [options available on the command line](#running) by configuring the app's `:wiki_options` hash. See [here](https://github.com/gollum/gollum/wiki/Using-Gollum-with-Rack) for the names of the options corresponding to the command line switches.
```ruby
#!/usr/bin/env ruby
+21 -4
View File
@@ -22,6 +22,7 @@ options = { 'port' => 4567, 'bind' => '0.0.0.0' }
wiki_options = {
:live_preview => false,
:allow_uploads => false,
:allow_editing => true,
}
opts = OptionParser.new do |opts|
@@ -44,6 +45,10 @@ opts = OptionParser.new do |opts|
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
@@ -64,11 +69,11 @@ opts = OptionParser.new do |opts|
wiki_options[:page_file_dir] = path
end
opts.on("--base-path [PATH]", "Specify the base path.") do |path|
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 gollum path.") do |path|
opts.on("--gollum-path [PATH]", "Specify the path to the git repository to be served.") do |path|
wiki_options[:gollum_path] = path
end
@@ -76,6 +81,14 @@ opts = OptionParser.new do |opts|
wiki_options[:ref] = ref
end
opts.on("--bare", "Specify that the repository is bare (only necessary when using the grit adapter).") do
wiki_options[:repo_is_bare] = true
end
opts.on("--no-edit", "Restricts editing capability through frontend.") do
wiki_options[:allow_editing] = false
end
opts.on("--no-live-preview", "Disables livepreview.") do
wiki_options[:live_preview] = false
end
@@ -89,10 +102,14 @@ opts = OptionParser.new do |opts|
wiki_options[:per_page_uploads] = true if mode == :page
end
opts.on("--mathjax", "Enables mathjax.") do
opts.on("--mathjax", "Enables mathjax for rendering mathematical equations. Uses the TeX-AMS-MML_HTMLorMML config with the autoload-all extension by default.") 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'
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
@@ -211,4 +228,4 @@ else
# 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
end
end
end
+12
View File
@@ -0,0 +1,12 @@
[Unit]
Description=Gollum wiki server
After=network.target
[Service]
Type=simple
User=%i
ExecStart=/usr/bin/gollum
Restart=on-abort
[Install]
WantedBy=multi-user.target
+567 -31
View File
@@ -5,8 +5,8 @@ Gem::Specification.new do |s|
s.required_ruby_version = '>= 1.9'
s.name = 'gollum'
s.version = '3.0.0'
s.date = '2014-04-05'
s.version = '4.0.0'
s.date = '2015-04-11'
s.rubyforge_project = 'gollum'
s.license = 'MIT'
@@ -24,8 +24,8 @@ Gem::Specification.new do |s|
s.rdoc_options = ['--charset=UTF-8']
s.extra_rdoc_files = %w[README.md LICENSE]
s.add_dependency 'gollum-lib', '~> 3.0'
s.add_dependency 'github-markdown', '~> 0.6.5'
s.add_dependency 'gollum-lib', '~> 4.0', '>= 4.0.1'
s.add_dependency 'kramdown', '~> 1.6.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'
@@ -45,6 +45,9 @@ Gem::Specification.new do |s|
Rakefile
bin/gollum
config.rb
contrib/openrc/conf.d/gollum
contrib/openrc/init.d/gollum
contrib/systemd/gollum@.service
docs/sanitization.md
gollum.gemspec
lib/gollum.rb
@@ -95,16 +98,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
@@ -123,28 +131,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
@@ -157,56 +194,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
@@ -215,8 +354,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
@@ -227,7 +370,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
@@ -237,33 +379,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
@@ -272,12 +444,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
@@ -285,54 +471,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
@@ -344,15 +565,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
@@ -368,32 +604,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
@@ -402,12 +656,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
@@ -423,6 +947,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
@@ -443,8 +969,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
@@ -459,6 +989,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
@@ -481,6 +1013,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
@@ -488,7 +1022,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
@@ -517,7 +1051,9 @@ Gem::Specification.new do |s|
lib/gollum/templates/history_authors/gravatar.mustache
lib/gollum/templates/history_authors/identicon.mustache
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
@@ -531,15 +1067,15 @@ Gem::Specification.new do |s|
lib/gollum/views/file_view.rb
lib/gollum/views/has_page.rb
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
licenses/css_tree_menu_thecssninja/license.txt
licenses/licenses.txt
licenses/unity_asset_pool/COPYRIGHT
openrc/conf.d/gollum
openrc/init.d/gollum
]
# = MANIFEST =
+1 -1
View File
@@ -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.0.0'
VERSION = '4.0.0'
def self.assets_path
::File.expand_path('gollum/public', ::File.dirname(__FILE__))
+64 -17
View File
@@ -20,6 +20,13 @@ Gollum::set_git_max_filesize(190 * 10**6)
# Fix to_url
class String
alias :upstream_to_url :to_url
if defined?(Gollum::GIT_ADAPTER) && Gollum::GIT_ADAPTER != 'grit'
def to_ascii
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?
@@ -42,7 +49,7 @@ module Precious
class App < Sinatra::Base
register Mustache::Sinatra
include Precious::Helpers
dir = File.dirname(File.expand_path(__FILE__))
# Detect unsupported browsers.
@@ -86,11 +93,16 @@ module Precious
end
before do
settings.wiki_options[:allow_editing] = settings.wiki_options.fetch(:allow_editing, true)
@allow_editing = settings.wiki_options[:allow_editing]
forbid unless @allow_editing || request.request_method == "GET"
Precious::App.set(:mustache, {:templates => settings.wiki_options[:template_dir]}) if settings.wiki_options[:template_dir]
@base_url = url('/', false).chomp('/')
# 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]
end
get '/' do
@@ -105,7 +117,6 @@ module Precious
# name, path, version
def wiki_page(name, path = nil, version = nil, exact = true)
wiki = wiki_new
path = name if path.nil?
name = extract_name(name) || wiki.index_page
path = extract_path(path)
@@ -126,14 +137,17 @@ module Precious
end
get '/edit/*' do
forbid unless @allow_editing
wikip = wiki_page(params[:splat].first)
@name = wikip.name
@path = wikip.path
@upload_dest = find_upload_dest(@path)
wiki = wikip.wiki
@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
@@ -163,7 +177,8 @@ module Precious
tempfile = params[:file][:tempfile]
end
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].match(/^(#{wiki.page_file_dir}\/+)?(.*)/)[2] : 'uploads'
ext = ::File.extname(fullname)
format = ext.split('.').last || 'txt'
filename = ::File.basename(fullname, ext)
@@ -251,21 +266,27 @@ module Precious
end
get '/delete/*' do
forbid unless @allow_editing
wikip = wiki_page(params[:splat].first)
name = wikip.name
wiki = wikip.wiki
page = wikip.page
unless page.nil?
wiki.delete_page(page, { :message => "Destroyed #{name} (#{page.format})" })
commit = commit_message
commit[:message] = "Destroyed #{name} (#{page.format})"
wiki.delete_page(page, commit)
end
redirect to('/')
end
get '/create/*' do
forbid unless @allow_editing
wikip = wiki_page(params[:splat].first.gsub('+', '-'))
@name = wikip.name.to_url
@path = wikip.path
@allow_uploads = wikip.wiki.allow_uploads
@upload_dest = find_upload_dest(@path)
page_dir = settings.wiki_options[:page_file_dir].to_s
unless page_dir.empty?
@@ -297,7 +318,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, name))}")
redirect to("/#{clean_url(::File.join(page_dir, path, encodeURIComponent(name)))}")
rescue Gollum::DuplicatePageError => e
@message = "Duplicate page: #{e.message}"
mustache :error
@@ -340,6 +361,12 @@ module Precious
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
@@ -351,8 +378,15 @@ module Precious
end
end
get '/latest_changes' do
@wiki = wiki_new
max_count = settings.wiki_options.fetch(:latest_changes_count, 10)
@versions = @wiki.latest_changes({:max_count => max_count})
mustache :latest_changes
end
post '/compare/*' do
@file = params[:splat].first
@file = encodeURIComponent(params[:splat].first)
@versions = params[:versions] || []
if @versions.size < 2
redirect to("/history/#{@file}")
@@ -396,6 +430,8 @@ module Precious
@content = page.formatted_data
@version = version
mustache :page
elsif file = wikip.wiki.file("#{file_path}", version, true)
show_file(file)
else
halt 404
end
@@ -421,6 +457,7 @@ module Precious
wiki = Gollum::Wiki.new(settings.gollum_path, wiki_options)
@results = wiki.pages
@results += wiki.files if settings.wiki_options[:show_all]
@results = @results.sort_by { |p| p.name.downcase } # Sort Results alphabetically, fixes 922
@ref = wiki.ref
mustache :pages
end
@@ -453,10 +490,7 @@ module Precious
@page = page
@name = name
@content = page.formatted_data
@upload_dest = settings.wiki_options[:allow_uploads] ?
(settings.wiki_options[:per_page_uploads] ?
"#{path}/#{@name}".sub(/^\/\//, '') : 'uploads'
) : ''
@upload_dest = find_upload_dest(path)
# Extensions and layout data
@editable = true
@@ -469,18 +503,24 @@ module Precious
mustache :page
elsif file = wiki.file(fullpath, wiki.ref, true)
if file.on_disk?
send_file file.on_disk_path, :disposition => 'inline'
else
content_type file.mime_type
file.raw_data
end
show_file(file)
else
not_found unless @allow_editing
page_path = [path, name].compact.join('/')
redirect to("/create/#{clean_url(encodeURIComponent(page_path))}")
end
end
def show_file(file)
return unless file
if file.on_disk?
send_file file.on_disk_path, :disposition => 'inline'
else
content_type file.mime_type
file.raw_data
end
end
def update_wiki_page(wiki, page, content, commit, name = nil, format = nil)
return if !page ||
((!content || page.raw_data == content) && page.format == format)
@@ -505,5 +545,12 @@ module Precious
commit_message.merge! author_parameters unless author_parameters.nil?
commit_message
end
def find_upload_dest(path)
settings.wiki_options[:allow_uploads] ?
(settings.wiki_options[:per_page_uploads] ?
"#{path}/#{@name}".sub(/^\/\//, '') : 'uploads'
) : ''
end
end
end
+12
View File
@@ -39,5 +39,17 @@ module Precious
url.gsub('%2F', '/').gsub(/^\/+/, '').gsub('//', '/')
end
def forbid(msg = "Forbidden. This wiki is set to no-edit mode.")
@message = msg
status 403
halt mustache :error
end
def not_found(msg = nil)
@message = msg || "The requested page does not exist."
status 404
return mustache :error
end
end
end
+117 -15
View File
@@ -4,8 +4,11 @@
display: block;
overflow: visible;
position: absolute;
top: 50%;
left: 50%;
top: 0;
left: 0;
z-index: 999999;
width: 100%;
height: 100%;
}
#gollum-dialog-dialog.active {
@@ -13,24 +16,117 @@
}
#gollum-dialog-dialog-inner {
margin: 0 0 0 -225px;
position: relative;
width: 450px;
border: 7px solid #999;
border: 7px solid rgba(0, 0, 0, 0.3);
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
margin: 0px;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
width: 100%;
height: 100%;
}
#gollum-dialog-dialog-bg {
background-color: #fff;
overflow: hidden;
padding: 1em;
background: -webkit-gradient(linear, left top, left bottom, from(#f7f7f7), to(#ffffff));
background: -moz-linear-gradient(top, #f7f7f7, #ffffff);
height: 100%;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}
@media all and (min-width: 480px) {
#gollum-dialog-dialog {
display: block;
overflow: visible;
position: absolute;
position: fixed;
top: 0;
left: 0;
z-index: 999999;
width: auto;
height: auto;
}
#gollum-dialog-dialog.active {
display: block;
}
#gollum-dialog-dialog-inner {
margin: auto;
position: fixed;
width: auto;
height: auto;
min-width: 280px;
min-height: 380px;
max-width: 450px;
max-height: 450px;
top: 10px;
right: 10px;
bottom: 10px;
left: 10px;
border: 7px solid #999;
border: 7px solid rgba(0, 0, 0, 0.3);
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
}
#gollum-dialog-dialog-bg {
background-color: #fff;
overflow: hidden;
padding: 1em;
background: -webkit-gradient(linear, left top, left bottom, from(#f7f7f7), to(#ffffff));
background: -moz-linear-gradient(top, #f7f7f7, #ffffff);
height: 100%;
box-sizing: border-box;
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
}
}
@media all and (min-width: 940px) {
#gollum-dialog-dialog {
position: absolute;
top: 50%;
left: 50%;
width: auto;
height: auto;
}
#gollum-dialog-dialog-inner {
margin: 0 0 0 -225px;
position: relative;
width: 450px;
height: auto;
top: auto;
right: auto;
bottom: auto;
left: auto;
border: 7px solid #999;
border: 7px solid rgba(0, 0, 0, 0.3);
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
}
#gollum-dialog-dialog-bg {
height: auto;
box-sizing: content-box;
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box;
}
}
#gollum-dialog-dialog-inner h4 {
@@ -52,10 +148,16 @@
display: block;
border: 0;
margin: 0;
overflow: hidden;
padding: 0;
}
#gollum-dialog-dialog-body fieldset:after {
content: ".";
display: block;
clear: both;
visibility: hidden;
}
#gollum-dialog-dialog-body fieldset .field {
margin: 0 0 1.5em 0;
padding: 0;
+218 -60
View File
@@ -19,15 +19,15 @@ a {
}
#gollum-editor {
border: 1px solid #e4e4e4;
background: #f9f9f9;
margin: 1em 0 5em;
overflow: hidden;
padding: 1em 1em 0.4em;
margin: 0 0 5em;
padding: 0em 1em 0.4em;
}
border-radius: 1em;
-moz-border-radius: 1em;
-webkit-border-radius: 1em;
#gollum-editor:after {
content: ".";
display: block;
visibility: hidden;
clear: both;
}
.ff #gollum-editor,
@@ -35,6 +35,18 @@ a {
padding-bottom: 1em;
}
@media all and (min-width: 940px) {
#gollum-editor {
border: 1px solid #e4e4e4;
background: #f9f9f9;
margin: 1em 0 5em;
border-radius: 1em;
-moz-border-radius: 1em;
-webkit-border-radius: 1em;
}
}
#gollum-editor form fieldset {
border: 0;
margin: 0;
@@ -45,7 +57,13 @@ a {
#gollum-editor .singleline {
display: block;
margin: 0 0 0.7em 0;
overflow: hidden;
}
#gollum-editor .singleline:after {
content: ".";
display: block;
visibility: hidden;
clear: both;
}
#gollum-editor .singleline input {
@@ -94,7 +112,6 @@ a {
/* @control function-bar */
#gollum-editor #gollum-editor-function-bar {
border-bottom: 1px solid #ddd;
overflow: hidden;
padding: 0;
}
@@ -109,9 +126,21 @@ a {
#gollum-editor #gollum-editor-function-bar.active #gollum-editor-function-buttons {
display: block;
float: left;
overflow: hidden;
padding: 0 0 1.1em 0;
margin: 0;
padding: 0;
}
@media all and (min-width: 940px) {
#gollum-editor #gollum-editor-function-bar {
overflow: hidden;
}
#gollum-editor #gollum-editor-function-bar.active #gollum-editor-function-buttons {
overflow: hidden;
margin: 0;
padding: 0 0 1.1em 0;
float: left;
}
}
#gollum-editor #gollum-editor-function-bar a.function-button {
@@ -120,12 +149,12 @@ a {
color: #333;
display: block;
float: left;
height: 25px;
height: 32px;
overflow: hidden;
margin: 0.2em 0.5em 0 0;
margin: 1px 1px 0 0;
/* text-indent: -5000px; */
text-shadow: 0 1px 0 #fff;
width: 25px;
width: 32px;
border-radius: 0.3em;
-moz-border-radius: 0.3em;
@@ -150,41 +179,84 @@ a {
background-image: url(../images/icon-sprite.png);
background-repeat: no-repeat;
display: block;
height: 25px;
height: 32px;
overflow: hidden;
text-indent: -5000px;
width: 25px;
width: 32px;
}
a#function-bold span { background-position: 0 0; }
a#function-italic span { background-position: -27px 0; }
a#function-underline span { background-position: -54px 0; }
a#function-code span { background-position: -82px 0; }
a#function-ul span { background-position: -109px 0; }
a#function-ol span { background-position: -136px 0; }
a#function-blockquote span { background-position: -163px 0; }
a#function-hr span { background-position: -190px 0; }
a#function-h1 span { background-position: -217px 0; }
a#function-h2 span { background-position: -244px 0; }
a#function-h3 span { background-position: -271px 0; }
a#function-link span { background-position: -298px 0; }
a#function-image span { background-position: -324px 0; }
a#function-help span { background-position: -405px 0; }
a#function-bold span { background-position: 3px 3px; }
a#function-italic span { background-position: -24px 3px; }
a#function-underline span { background-position: -51px 3px; }
a#function-code span { background-position: -79px 3px; }
a#function-ul span { background-position: -106px 3px; }
a#function-ol span { background-position: -133px 3px; }
a#function-blockquote span { background-position: -160px 3px; }
a#function-hr span { background-position: -187px 3px; }
a#function-h1 span { background-position: -214px 3px; }
a#function-h2 span { background-position: -241px 3px; }
a#function-h3 span { background-position: -268px 3px; }
a#function-link span { background-position: -295px 3px; }
a#function-image span { background-position: -321px 3px; }
a#function-help span { background-position: -402px 3px; }
a#function-bold:hover span { background-position: 0 -28px; }
a#function-italic:hover span { background-position: -27px -28px; }
a#function-underline:hover span { background-position: -54px -28px; }
a#function-code:hover span { background-position: -82px -28px; }
a#function-ul:hover span { background-position: -109px -28px; }
a#function-ol:hover span { background-position: -136px -28px; }
a#function-blockquote:hover span { background-position: -163px -28px; }
a#function-hr:hover span { background-position: -190px -28px; }
a#function-h1:hover span { background-position: -217px -28px; }
a#function-h2:hover span { background-position: -244px -28px; }
a#function-h3:hover span { background-position: -271px -28px; }
a#function-link:hover span { background-position: -298px -28px; }
a#function-image:hover span { background-position: -324px -28px; }
a#function-help:hover span { background-position: -405px -28px; }
a#function-bold:hover span { background-position: 3px -25px; }
a#function-italic:hover span { background-position: -24px -25px; }
a#function-underline:hover span { background-position: -51px -25px; }
a#function-code:hover span { background-position: -79px -25px; }
a#function-ul:hover span { background-position: -106px -25px; }
a#function-ol:hover span { background-position: -133px -25px; }
a#function-blockquote:hover span { background-position: -160px -25px; }
a#function-hr:hover span { background-position: -187px -25px; }
a#function-h1:hover span { background-position: -214px -25px; }
a#function-h2:hover span { background-position: -241px -25px; }
a#function-h3:hover span { background-position: -268px -25px; }
a#function-link:hover span { background-position: -295px -25px; }
a#function-image:hover span { background-position: -321px -25px; }
a#function-help:hover span { background-position: -402px -25px; }
@media all and (min-width: 940px) {
#gollum-editor #gollum-editor-function-bar a.function-button {
height: 25px;
width: 25px;
margin: 0.2em 0.5em 0 0;
}
#gollum-editor #gollum-editor-function-bar a span {
width: 25px;
height: 25px;
}
a#function-bold span { background-position: 0 0; }
a#function-italic span { background-position: -27px 0; }
a#function-underline span { background-position: -54px 0; }
a#function-code span { background-position: -82px 0; }
a#function-ul span { background-position: -109px 0; }
a#function-ol span { background-position: -136px 0; }
a#function-blockquote span { background-position: -163px 0; }
a#function-hr span { background-position: -190px 0; }
a#function-h1 span { background-position: -217px 0; }
a#function-h2 span { background-position: -244px 0; }
a#function-h3 span { background-position: -271px 0; }
a#function-link span { background-position: -298px 0; }
a#function-image span { background-position: -324px 0; }
a#function-help span { background-position: -405px 0; }
a#function-bold:hover span { background-position: 0 -28px; }
a#function-italic:hover span { background-position: -27px -28px; }
a#function-underline:hover span { background-position: -54px -28px; }
a#function-code:hover span { background-position: -82px -28px; }
a#function-ul:hover span { background-position: -109px -28px; }
a#function-ol:hover span { background-position: -136px -28px; }
a#function-blockquote:hover span { background-position: -163px -28px; }
a#function-hr:hover span { background-position: -190px -28px; }
a#function-h1:hover span { background-position: -217px -28px; }
a#function-h2:hover span { background-position: -244px -28px; }
a#function-h3:hover span { background-position: -271px -28px; }
a#function-link:hover span { background-position: -298px -28px; }
a#function-image:hover span { background-position: -324px -28px; }
a#function-help:hover span { background-position: -405px -28px; }
}
#gollum-editor #gollum-editor-function-bar a.disabled {
@@ -192,14 +264,19 @@ a#function-help:hover span { background-position: -405px -28px; }
}
#gollum-editor #gollum-editor-function-bar span.function-divider {
display: block;
float: left;
width: 0.5em;
display: none;
}
#gollum-editor #gollum-editor-function-bar #gollum-editor-format-selector {
overflow: hidden;
padding: .2em 0 .5em 0;
padding: 0.2em 0 0.5em 0;
clear: both;
}
#gollum-editor #gollum-editor-function-bar #gollum-editor-format-selector:after {
content: ".";
display: block;
clear: both;
visibility: hidden;
}
#gollum-editor #gollum-editor-function-bar
@@ -208,13 +285,12 @@ a#function-help:hover span { background-position: -405px -28px; }
border: 1px solid #ddd;
color: #333;
float: right;
font-size: 1em;
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
font-weight: bold;
line-height: 1.6em;
padding: 0.3em 0.4em;
display: inline-block;
border-radius: 0.5em;
-moz-border-radius: 0.5em;
@@ -224,11 +300,11 @@ a#function-help:hover span { background-position: -405px -28px; }
#gollum-editor #gollum-editor-function-bar
#gollum-editor-format-selector label {
color: #999;
float: right;
font-size: 1em;
font-weight: bold;
line-height: 1.6em;
padding: .3em 0.5em 0 0;
display: inline-block;
}
#gollum-editor #gollum-editor-function-bar
@@ -236,6 +312,56 @@ a#function-help:hover span { background-position: -405px -28px; }
content: ':';
}
@media all and (min-width: 940px) {
#gollum-editor #gollum-editor-function-bar span.function-divider {
display: block;
width: 0.5em;
}
#gollum-editor #gollum-editor-function-bar span.function-divider {
float: left;
}
#gollum-editor #gollum-editor-function-bar
#gollum-editor-format-selector {
clear: none;
text-align: right;
}
#gollum-editor #gollum-editor-function-bar
#gollum-editor-format-selector select {
}
#gollum-editor #gollum-editor-function-bar
#gollum-editor-format-selector label {
}
}
/* @section uploads */
#gollum-editor-body.dragging {
box-shadow: 0 0 10px #AAE000;
}
#gollum-editor-body.uploading {
opacity: 0.5;
}
#gollum-editor-body + div {
display: none;
font-size: 1.5em;
}
#gollum-editor-body + div > i {
font-size: 1em;
}
#gollum-editor-body.uploading + div {
display: block;
}
/* @section form-fields */
@@ -245,12 +371,18 @@ a#function-help:hover span { background-position: -405px -28px; }
font-size: 1em;
font-family: Consolas, "Liberation Mono", Courier, monospace;
line-height: 1.4em;
margin: 1em 0 0.4em;
margin: 0 0 0.4em;
padding: 0.5em;
width: 98%;
height: 20em;
}
@media all and (min-width: 940px) {
#gollum-editor textarea {
margin: 1em 0 0.4em;
}
}
#gollum-editor input#gollum-editor-submit {
background-color: #f7f7f7;
border: 1px solid #d4d4d4;
@@ -445,6 +577,7 @@ a#function-help:hover span { background-position: -405px -28px; }
/* @section help */
#gollum-editor-help {
clear: both;
margin: 0;
overflow: hidden;
padding: 0;
@@ -455,13 +588,13 @@ a#function-help:hover span { background-position: -405px -28px; }
#gollum-editor-help-parent,
#gollum-editor-help-list {
display: block;
float: left;
height: 17em;
list-style-type: none;
overflow: auto;
margin: 0;
padding: 1em 0;
width: 18%;
float: left;
width: 50%;
box-sizing: border-box;
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
}
#gollum-editor-help-parent {
@@ -491,6 +624,7 @@ a#function-help:hover span { background-position: -405px -28px; }
width: auto;
padding: 0.2em 1em;
text-shadow: 0 -1px 0 #fff;
font-size: 0.8em;
}
#gollum-editor-help-parent li a:hover,
@@ -518,6 +652,7 @@ a#function-help:hover span { background-position: -405px -28px; }
overflow: auto;
height: 17em;
padding: 1em;
clear: both;
}
#gollum-editor-help-content {
@@ -532,6 +667,29 @@ a#function-help:hover span { background-position: -405px -28px; }
padding: 0;
}
@media all and (min-width: 940px) {
#gollum-editor-help {
clear: none;
}
#gollum-editor-help-parent,
#gollum-editor-help-list {
height: 17em;
width: 18%;
overflow: auto;
padding: 1em 0;
}
#gollum-editor-help-parent li a,
#gollum-editor-help-list li a {
font-size: 1em;
}
#gollum-editor-help-wrapper {
clear: none;
}
}
/* IE */
.ie #gollum-editor .singleline input {
padding-top: 0.25em;
+139 -22
View File
@@ -21,9 +21,15 @@ body, html {
#wiki-wrapper {
margin: 0 auto;
overflow: visible;
width: 920px;
padding-left:20px;
padding-right:20px;
width: 100%;
}
@media all and (min-width: 940px) {
#wiki-wrapper {
max-width: 920px;
padding-left:20px;
padding-right:20px;
}
}
a:link {
@@ -39,22 +45,41 @@ a:hover, a:visited {
/* @section head */
#head {
border-bottom: 1px solid #ddd;
margin: 4em 0 1.5em;
padding-bottom: 0.3em;
margin: 1em 0 0;
padding: 0;
overflow: hidden;
}
#head h1 {
font-size: 2.5em;
font-size: 1.5em;
float: left;
line-height: normal;
margin: 0;
padding: 2px 0 0 0;
padding: 0 0 0 0.667em;
}
#head ul.actions {
float: right;
clear: both;
margin: 0 1em;
}
@media all and (min-width: 940px) {
#head {
border-bottom: 1px solid #ddd;
padding-bottom: 0.3em;
margin: 4em 0 1.5em;
}
#head h1 {
font-size: 2.5em;
padding: 2px 0 0 0;
}
#head ul.actions {
clear: none;
float: right;
margin: 0;
}
}
/* @section content */
@@ -89,6 +114,12 @@ a:hover, a:visited {
width: 100%;
}
#wiki-body table {
display: block;
overflow: auto;
border: 0;
}
.has-sidebar #wiki-body {
width: 68%;
}
@@ -244,6 +275,16 @@ a:hover, a:visited {
font-weight: bold;
}
#footer .actions {
margin-left: 1em;
}
@media all and (min-width: 940px) {
#footer .actions {
margin: 0;
}
}
/* @section history */
.history h1 {
@@ -257,7 +298,7 @@ a:hover, a:visited {
}
#wiki-history {
margin-top: 2em;
margin: 2em 1em 0 1em;
}
#wiki-history fieldset {
@@ -283,7 +324,8 @@ a:hover, a:visited {
}
#wiki-history table tr td {
border: 1px solid #c0dce9;
border-top: 1px solid #c0dce9;
border-bottom: 1px solid #c0dce9;
font-size: 1em;
line-height: 1.6em;
margin: 0;
@@ -291,8 +333,8 @@ a:hover, a:visited {
}
#wiki-history table tr td.checkbox {
width: 4em;
padding: 0.3em;
width: auto;
padding: 0 0.2em 0 0;
}
#wiki-history table tr td.checkbox input {
@@ -364,6 +406,25 @@ a:hover, a:visited {
margin: 0 0.6em 0 0;
}
@media all and (min-width: 940px) {
#wiki-history {
margin: 2em 0 0 0;
}
#wiki-history table tr td {
border: 1px solid #c0dce9;
font-size: 1em;
line-height: 1.6em;
margin: 0;
padding: 0.3em 0.7em;
}
#wiki-history table tr td.checkbox {
width: 4em;
padding: 0.3em;
}
}
/* @section edit */
.edit h1 {
@@ -378,6 +439,7 @@ a:hover, a:visited {
/* @section search */
.results h1 {
color: #999;
font-weight: normal;
@@ -390,6 +452,8 @@ a:hover, a:visited {
.results #results {
border-bottom: 1px solid #ccc;
margin-left: 1em;
margin-right: 1em;
margin-bottom: 2em;
padding-bottom: 2em;
}
@@ -400,12 +464,33 @@ a:hover, a:visited {
}
.results #results ul li {
font-size: 1.2em;
line-height: 1.6em;
list-style-position: outside;
list-style: none;
padding: 0.2em 0;
}
.results #results ul li a {
word-wrap: break-word;
}
@media all and (min-width: 640px) {
.results #results ul li {
font-size: 1.2em;
}
}
@media all and (min-width: 940px) {
.results #results {
margin-left: 0;
margin-right: 0;
}
.results #results ul li {
list-style: disc;
list-style-position: outside;
line-height: 1.6em;
}
}
.results #results ul li span.count {
color: #999;
}
@@ -522,8 +607,8 @@ ul.actions {
ul.actions li {
float: left;
font-size: 0.9em;
margin-left: 0.6em;
margin-bottom: 0.6em;
margin-left: 1px;
margin-bottom: 1px;
}
.minibutton a {
@@ -533,7 +618,7 @@ ul.actions {
display: block;
font-weight: bold;
margin: 0;
padding: 0.4em 1em;
padding: 0.6em 1em;
height: 1.4em;
text-shadow: 0 1px 0 #fff;
@@ -547,6 +632,18 @@ ul.actions {
-webkit-border-radius: 3px;
}
@media all and (min-width: 940px) {
ul.actions li {
margin-left: 0.6em;
margin-bottom: 0.6em;
}
.minibutton a {
padding: 0.4em 1em;
height: 1.4em;
}
}
#search-submit {
background-color: #f7f7f7;
border: 1px solid #d4d4d4;
@@ -627,7 +724,7 @@ ul.actions {
/* @control searchbar */
#head #searchbar {
float: right;
padding: 0;
padding: 2px 0 0 0;
overflow: hidden;
}
@@ -684,7 +781,7 @@ ul.actions {
height: inherit;
overflow: hidden;
text-indent: -5000px;
width: 28px;
width: 32px;
}
.ff #head #searchbar #searchbar-fauxtext #search-submit span,
@@ -697,11 +794,31 @@ ul.actions {
padding: 0;
}
@media all and (min-width: 940px) {
#head #searchbar {
padding: 0;
}
#head #searchbar #searchbar-fauxtext #search-submit span {
width: 28px;
}
#head #searchbar #searchbar-fauxtext #search-submit:hover span {
background-position: -431px -28px;
}
}
/* @section pages */
#pages {
font-size: 1.2em;
margin-bottom: 20px;
margin: 0 1em 20px 1em;
}
@media all and (min-width: 940px) {
#pages {
margin: 0 0 20px 0;
}
}
#pages ul {
+6
View File
@@ -1,5 +1,11 @@
/* IE7-specific styles */
.ie #wiki-wrapper {
width: 920px;
padding-left:20px;
padding-right:20px;
}
.ie #head #searchbar #searchbar-fauxtext input#search-query {
border: 0;
float: left;
+56 -2
View File
@@ -2,6 +2,10 @@
Gollum v3 Template
*/
/*!
* Font Awesome 4.0.3 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
*/
@font-face {
font-family: 'FontAwesome';
src: url('../fonts/fontawesome-webfont.eot?v=4.0.3');
@@ -23,6 +27,51 @@
content: "\f0c1";
}
.fa-spinner:before {
content: "\f110";
}
.fa-spin {
-webkit-animation: spin 2s infinite linear;
-moz-animation: spin 2s infinite linear;
-o-animation: spin 2s infinite linear;
animation: spin 2s infinite linear;
}
@-moz-keyframes spin {
0% {
-moz-transform: rotate(0deg);
}
100% {
-moz-transform: rotate(359deg);
}
}
@-webkit-keyframes spin {
0% {
-webkit-transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
}
}
@-o-keyframes spin {
0% {
-o-transform: rotate(0deg);
}
100% {
-o-transform: rotate(359deg);
}
}
@keyframes spin {
0% {
-webkit-transform: rotate(0deg);
transform: rotate(0deg);
}
100% {
-webkit-transform: rotate(359deg);
transform: rotate(359deg);
}
}
/* margin & padding reset*/
* {
margin: 0;
@@ -48,7 +97,6 @@ body {
font: 13.34px Helvetica, arial, freesans, clean, sans-serif;
font-size: small;
line-height: 1.4;
min-width: 980px;
}
img {
@@ -82,13 +130,19 @@ a:active, a:hover {
}
.markdown-body {
padding: 30px;
padding: 1em;
font-size: 15px;
line-height: 1.7;
overflow: hidden;
word-wrap: break-word;
}
@media all and (min-width: 940px) {
.markdown-body {
padding: 30px;
}
}
.markdown-body > *:first-child {
margin-top: 0 !important;
}
@@ -98,6 +98,52 @@
$('#gollum-editor-help').removeClass('jaws');
}
} // EditorHas.functionBar
if ( EditorHas.dragDropUpload() ) {
var $editorBody = $('#gollum-editor-body');
var editorBody = $('#gollum-editor-body')[0];
editorBody.ondragover = function(e) {
$editorBody.addClass('dragging');
return false;
};
editorBody.ondragleave = function() {
$editorBody.removeClass('dragging');
return false;
};
editorBody.ondrop = function(e) {
debug("dropped file");
e.preventDefault();
$editorBody.removeClass('dragging').addClass('uploading');
var file = e.dataTransfer.files[0],
formData = new FormData();
formData.append('upload_dest', uploadDest);
formData.append('file', file);
$.ajax({
url: baseUrl + '/uploadFile',
data: formData,
cache: false,
contentType: false,
processData: false,
type: 'POST',
success: function(){
$editorBody.removeClass('uploading');
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;
editorBody.selectionEnd = pos + text.length;
},
error: function(r, textStatus) {
alert('Error uploading file: ' + textStatus);
$editorBody.removeClass('uploading');
}
});
return false;
};
} // EditorHas.dragDropUpload
} // EditorHas.baseEditorMarkup
};
@@ -444,6 +490,17 @@
*/
titleDisplayed: function() {
return ( ActiveOptions.NewFile );
},
/**
* EditorHas.dragDropUpload
* True if the editor is supports drag and drop file uploads, false otherwise.
*
* @return boolean
*/
dragDropUpload: function() {
return $('#gollum-editor.uploads-allowed').length;
}
};
@@ -546,9 +603,12 @@
typeof definitionObject.replace == 'string' ) {
debug('Running replacement - using ' + definitionObject.replace);
var rt = definitionObject.replace;
repText = escape( repText );
repText = repText.replace( searchExp, rt );
// remove backreferences
repText = repText.replace( /\$[\d]/g, '' );
repText = unescape( repText );
if ( repText === '' ) {
debug('Search string is empty');
@@ -1,11 +1,11 @@
/**
* ASCIIDoc Language Definition
* AsciiDoc Language Definition
*
*/
(function($) {
var ASCIIDoc = {
var AsciiDoc = {
'function-bold' : {
search: /(^[\n]+)([\n\s]*)/g,
@@ -19,7 +19,7 @@ var ASCIIDoc = {
'function-code' : {
search: /(^[\n]+)([\n\s]*)/g,
replace: "+$1+$2"
replace: "`$1`$2"
},
'function-ul' : {
@@ -37,6 +37,21 @@ var ASCIIDoc = {
replace: "----\n$1$2\n----\n"
},
'function-h1' : {
search: /(.+)([\n]?)/g,
replace: "= $1$2"
},
'function-h2' : {
search: /(.+)([\n]?)/g,
replace: "== $1$2"
},
'function-h3' : {
search: /(.+)([\n]?)/g,
replace: "=== $1$2"
},
'function-link' : {
exec: function( txt, selText, $field ) {
var results = null;
@@ -102,20 +117,20 @@ var ASCIIDoc = {
};
$.GollumEditor.defineLanguage('asciidoc', ASCIIDoc);
$.GollumEditor.defineLanguage('asciidoc', AsciiDoc);
var ASCIIDocHelp = [
var AsciiDocHelp = [
{
menuName: 'Text Formatting',
content: [
{
menuName: 'Headers',
data: '<p>ASCIIDoc headers can be written in two ways: with differing underlines or with different indentation using <code>=</code> (equals sign). ASCIIDoc supports headings 1-4. The editor will automatically use the <code>=</code> notation. To create a level one header, prefix your line with one <code>=</code>. Level two headers are created with <code>==</code> and so on.</p>'
data: '<p>AsciiDoc headers can be written in two ways: with differing underlines or with different indentation using <code>=</code> (equals sign). AsciiDoc supports headings 1-4. The editor will automatically use the <code>=</code> notation. To create a level one header, prefix your line with one <code>=</code>. Level two headers are created with <code>==</code> and so on.</p>'
},
{
menuName: 'Bold / Italic',
data: '<p>To display text as <strong>bold</strong>, wrap the text in <code>*</code> (asterisks). To display text as <em>italic</em>, wrap the text in <code>_</code> (underscores). To create <code>monospace</code> text, wrap the text in <code>+</code> (plus signs).'
data: '<p>To display text as <strong>bold</strong>, wrap the text in <code>*</code> (asterisks). To display text as <em>italic</em>, wrap the text in <code>_</code> (underscores). To create <code>monospace</code> text, wrap the text in <code>`</code> (backtick).'
},
{
menuName: 'Scripts',
@@ -123,7 +138,7 @@ var ASCIIDocHelp = [
},
{
menuName: 'Special Characters',
data: '<p>ASCIIDoc will automatically convert textual representations of commonly-used special characters. For example, <code>(R)</code> becomes &reg;, <code>(C)</code> becomes &copy; and <code>(TM)</code> becomes &trade;.</p>'
data: '<p>AsciiDoc will automatically convert textual representations of commonly-used special characters. For example, <code>(R)</code> becomes &reg;, <code>(C)</code> becomes &copy; and <code>(TM)</code> becomes &trade;.</p>'
}
]
},
@@ -132,7 +147,7 @@ var ASCIIDocHelp = [
content: [
{
menuName: 'Paragraphs',
data: '<p>ASCIIDoc allows paragraphs to have optional titles or icons to denote special sections. To make a normal paragraph, simply add a line between blocks and a new paragraph will start. If you want to title your paragraphs, adda line prefixed by <code>.</code> (full stop). An example paragraph with optional title is displayed below:<br><br><code>.Optional Title<br><br>This is my paragraph. It is two sentences long.</code></p>'
data: '<p>AsciiDoc allows paragraphs to have optional titles or icons to denote special sections. To make a normal paragraph, simply add a line between blocks and a new paragraph will start. If you want to title your paragraphs, adda line prefixed by <code>.</code> (full stop). An example paragraph with optional title is displayed below:<br><br><code>.Optional Title<br><br>This is my paragraph. It is two sentences long.</code></p>'
},
{
menuName: 'Source Blocks',
@@ -157,12 +172,12 @@ var ASCIIDocHelp = [
},
{
menuName: 'Images',
data: '<p>Images in ASCIIDoc work much like hyperlinks, but image URLs are prefixed with <code>image:</code>. For example, to link to an image at <code>images/icons/home.png</code>, write <code>image:images/icons/home.png</code>. Alt text can be added by appending the text to the URI in <code>[</code> (brackets).</p>'
data: '<p>Images in AsciiDoc work much like hyperlinks, but image URLs are prefixed with <code>image:</code>. For example, to link to an image at <code>images/icons/home.png</code>, write <code>image:images/icons/home.png</code>. Alt text can be added by appending the text to the URI in <code>[</code> (brackets).</p>'
}
]
}
];
$.GollumEditor.defineHelp('asciidoc', ASCIIDocHelp);
$.GollumEditor.defineHelp('asciidoc', AsciiDocHelp);
})(jQuery);
@@ -13,6 +13,12 @@
markupCreated: false,
markup: '',
currentAspect: function(){
if (window.innerWidth < 480) return "small-mobile";
if ($('#gollum-dialog-dialog').css('position') == 'fixed') return "large-mobile";
return "desktop";
},
attachEvents: function( evtOK ) {
$('#gollum-dialog-action-ok').click(function( e ) {
Dialog.eventOK( e, evtOK );
@@ -185,6 +191,8 @@
}
});
}
$(window).unbind('resize', Dialog.resize);
}
},
@@ -268,14 +276,28 @@
});
}
}
$(window).bind('resize', Dialog.resize);
}
},
resize: function(){
Dialog.position();
},
position: function() {
var dialogHeight = $('#gollum-dialog-dialog-inner').height();
$('#gollum-dialog-dialog-inner')
.css('height', dialogHeight + 'px')
.css('margin-top', -1 * parseInt( dialogHeight / 2 ));
if (Dialog.currentAspect() == "small-mobile") {
$('#gollum-dialog-dialog-inner').css('height', '100%').css('margin-top', 'auto');
}
else if (Dialog.currentAspect() == "large-mobile") {
$('#gollum-dialog-dialog-inner').css('height', 'auto').css('margin-top', 'auto');
}
else if (Dialog.currentAspect() == "desktop") {
var dialogHeight = $('#gollum-dialog-dialog-inner').height();
$('#gollum-dialog-dialog-inner')
.css('height', dialogHeight + 'px')
.css('margin-top', -1 * parseInt( dialogHeight / 2 ));
}
}
};
@@ -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) {
@@ -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"
}];
});
@@ -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;
});
@@ -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
@@ -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});
}
}
});
@@ -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
};
});
};
});
@@ -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;
};
});
@@ -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();
};
});
@@ -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;
};
});
@@ -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
}
});
});
@@ -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'>/**&#160;this&#160;is&#160;a&#160;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>&#160;<span class='ace_entity ace_name ace_function'>hello</span>&#160;<span class='ace_paren ace_lparen'>(</span><span class='ace_variable ace_parameter'>a</span><span class='ace_punctuation ace_operator'>,&#160;</span><span class='ace_variable ace_parameter'>b</span><span class='ace_punctuation ace_operator'>,&#160;</span><span class='ace_variable ace_parameter'>c</span><span class='ace_paren ace_rparen'>)</span>&#160;<span class='ace_paren ace_lparen'>{</span></div><div class='ace_line'><span class='ace_gutter ace_gutter-cell' unselectable='on'>5</span>&#160;&#160;&#160;&#160;<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>&#160;<span class='ace_keyword ace_operator'>*</span>&#160;<span class='ace_identifier'>b</span>&#160;<span class='ace_keyword ace_operator'>+</span>&#160;<span class='ace_identifier'>c</span>&#160;<span class='ace_keyword ace_operator'>+</span>&#160;<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