Compare commits

..

138 Commits

Author SHA1 Message Date
Dawa Ometto 6de0914788 Release 4.1.2 2017-08-07 17:54:00 +02:00
Dawa Ometto e4f702d1e2 Lock to newer gollum-lib to avoid falling back to unsafe nokogiri 2017-08-07 17:40:49 +02:00
Dawa Ometto 2f864c5e15 Stop support for ruby 2.0.0 because it is not compatible with a safe nokogiri. See https://github.com/gollum/gollum-lib/issues/278 2017-07-31 18:09:32 +02:00
Dawa Ometto 7139590798 Try fix travis 2017-07-31 18:01:37 +02:00
Dawa Ometto 0870655455 Release 4.1.1 2017-04-17 11:20:06 +02:00
Dawa Ometto ba24a7bb8c Update gemijione dependency. Closes #1227 2017-04-17 11:01:50 +02:00
Dawa Ometto f32d7465a2 Set bar_side for versioned pages. Closes #1226 2017-04-14 00:31:09 +02:00
Dawa Ometto e202698bf1 Merge pull request #1189 from nimag42/patch-1
Solve bug when folder contains non-ascii character
2017-04-09 11:49:54 +02:00
Dawa Ometto 11c2bf7dae Fix date. Closes #1211 2017-04-05 23:05:24 +02:00
Dawa Ometto 53cf0e1148 Merge pull request #1201 from adamniedzielski/skip-transliteration-tests-rugged
Skip tests for transliteration for adapters different than grit
2017-03-11 20:35:53 +01:00
Adam Niedzielski 2d1e49e3f2 Skip tests for transliteration for adapters different than grit 2017-03-11 19:28:31 +01:00
Dawa Ometto 199161f611 Merge pull request #1188 from adamniedzielski/pass-non-empty-author-details
Pass non-empty commit author details in transliteration test
2017-03-10 12:00:57 +01:00
Dawa Ometto ece5c775f1 Release 4.1.0 2017-03-09 17:34:37 +01:00
Dawa Ometto 3121c43b71 Merge pull request #1180 from bogado/issue#1179
Fix page list for files that have regexp special chars.
2017-03-09 16:22:40 +01:00
Bart Kamphorst 51e8caf369 Merge pull request #1191 from polyzen/patch-1
readme: Use --document in place of deprecated options
2017-02-16 16:05:35 +01:00
Daniel M. Capella e7e7937678 readme: Use --document in place of deprecated options 2017-02-02 14:26:32 -05:00
Jacquin Théo a0f5a60ea0 Solve bug when folder contain non-ascii character
When you create a file in a folder with non-ascii character, for exemple "Réseau", after creating the page, it throwed an "URI::InvalidURIError", given the fact that the url returned was "/Réseau/H%C3%A9y", only the part with the name of the file was correctly encoded.

So I propose to encode every part of the url to solve this issue
So I just
2017-01-29 00:39:39 +01:00
Adam Niedzielski af29c6e441 Pass non-empty commit author details in transliteration test
Empty name or email are not allowed by libgit2 and cause a test failure
when the test suite is run against rugged_adapter.
2017-01-27 15:15:50 +01:00
Dawa Ometto 00dfecb1c7 Merge pull request #1185 from connorshea/ruby-24
Test on Ruby 2.4
2017-01-20 23:30:24 +01:00
Dawa Ometto cc249bef15 Merge pull request #1182 from bjoernalbers/master
Add another video to README.
2017-01-19 15:14:11 +01:00
Connor Shea 9a2231804d Test on Ruby 2.4 2017-01-14 19:10:17 -05:00
Björn Albers 1edcf15bcd Add another video to README. 2017-01-10 18:26:56 +01:00
Victor Bogado 0190e08763 Fix page list for files that have regexp special chars.
The page list collection logic was using the filename without any
scaping to create a regexp. This not only breaks for some names it might
even be a security problem by introducing bad regular expression as
filenames.
2017-01-01 19:55:18 -08:00
Dawa Ometto d3cc5a69c4 Merge branch 'rc' 2016-08-07 21:36:51 +02:00
Dawa Ometto 07ca53a778 Merge branch 'rc' into master
Conflicts:
	bin/gollum
2016-08-07 21:36:13 +02:00
Dawa Ometto 6910b6d024 Merge pull request #1143 from astorije/patch-1
Fix compare page not accessible in no-edit mode. Resolves #1140.
2016-08-07 20:17:34 +02:00
Bart Kamphorst f02c934ad7 Merge pull request #1154 from maarten/#855-delete-files
Fixes #855 Adds file deletion fuctionality.
2016-08-07 19:46:55 +02:00
Maarten Engelen 236680aab9 Add file deletion functionality
Adds route for file deletion
Add styles and images for this
2016-08-07 17:18:36 +02:00
Dawa Ometto 6316f97c06 Update CONTRIBUTING.md 2016-08-07 12:50:48 +02:00
Dawa Ometto 14830af306 Update CONTRIBUTING.md 2016-08-07 12:50:18 +02:00
Dawa Ometto b620decde7 Add contributing guidelines 2016-08-07 12:48:07 +02:00
Bart Kamphorst b08ca620a0 Merge pull request #1148 from astorije/patch-2
Remove deprecated github-markdown markup
2016-07-11 21:44:02 +02:00
Jérémie Astori 66a2bb0393 Remove deprecated github-markdown markup
From [RubyGems](https://rubygems.org/gems/github-markdown/versions/0.6.9):

> THIS GEM IS NOT MAINTAINED AND NOT SUPPORTED.

Also, the GitHub repository, maintained by GitHub, was removed.

The link is now a redirect, it doesn't point to the GFM page anymore.
2016-07-11 12:19:43 -04:00
Jérémie Astori 0675844d97 Fix compare page not accessible in no-edit mode
Permission checking was spread across `post` action handlers instead of inside the `before` to normalize between `get` and `post` action handlers and be more explicit.
2016-06-27 13:50:47 -04:00
Dawa Ometto 148eda2990 Merge pull request #1136 from gollum/503-safari-live
Add Safari to livepreview-enabled browsers. Fixes #503
2016-06-16 13:28:46 +02:00
Dawa Ometto 330f7b4002 Add Safari to livepreview-enabled browsers. Fixes #503 2016-06-16 12:58:22 +02:00
Dawa Ometto def0fc8866 Merge pull request #1126 from svoop/emoji
Support for emoji
2016-06-09 16:43:52 +02:00
Sven Schwyn fa1bcf9608 Add support for emojione 2016-05-19 20:33:25 +02:00
Bart Kamphorst 5a5e56a47b Merge pull request #1111 from jhominal/master
Unconditionally add 'test-unit' dependency.
2016-02-24 02:28:24 +01:00
Jean Hominal e64f40eaa3 Unconditionally add 'test-unit' dependency.
Fixes #1110
2016-02-23 22:52:07 +01:00
Dawa Ometto 3f3d86ad4c Merge pull request #1091 from jhominal/master
Use `last_version` instead of `versions` when possible. Fixes #1087.
2016-02-21 22:51:40 +01:00
Jean Hominal 91833dd72e Use last_version instead of versions when possible. Fixes #1087.
Use Gollum::Page#last_version instead of Gollum::Page#versions in the
cases identified in #1087:
 * In Precious::App#show_page_or_file
 * In Precious::Views::Page#author
 * In Precious::Views::Page#date
2016-02-14 23:15:38 +01:00
Bart Kamphorst c049f7c11f Merge pull request #1106 from gollum/youtube_demo_vids
Adds links to youtube videos in README.
2016-01-14 17:01:28 +01:00
Bart Kamphorst 6bd2f3bff5 Drop 1.9.3 testing on Travis, add 2.3.0. 2016-01-10 15:28:23 +01:00
Bart Kamphorst 8c3c519679 Adds links to youtube videos in README. 2016-01-10 11:49:34 +01:00
Dawa Ometto 4e5002a31e Merge pull request #1098 from andrehjr/gh-1066-sanitize-basepath
Sanitize basepath options when adding with more than one / at the start Fixes #1066.
2015-12-30 18:39:30 +01:00
André Luis Leal Cardoso Junior 5a53c58b04 Sanitize basepath options when adding with more than one / at the start. Fixes #1066 2015-12-05 23:23:22 -02:00
Bart Kamphorst a61d1e3d10 Merge pull request #1094 from repotag/master
Update kramdown and useragent dependencies.
2015-11-30 11:26:51 +01:00
Bart Kamphorst 1a50f99189 Merge branch 'master' of https://github.com/gollum/gollum 2015-11-30 00:28:30 +01:00
Bart Kamphorst 6caacbf388 Update kramdown and useragent dependencies. 2015-11-30 00:28:01 +01:00
Dawa Ometto f876aa7d0f Merge pull request #1077 from mpnowacki/renaming
fixed file renaming issues with page_file_dir
2015-11-02 11:42:50 +01:00
Dawa Ometto 31e1281600 Merge pull request #1086 from ngyuki/fix-preview-sidebar
Fix position of sidebar in preview
2015-11-02 11:42:25 +01:00
ngyuki 90072a9332 Fix position of sidebar in preview 2015-11-02 12:45:30 +09:00
Dawa Ometto bacd2893b9 Merge pull request #1084 from SkyCrawl/master
Fixing the vague "running from source" description in README
2015-10-25 13:20:17 +01:00
SkyCrawl 7362fd859b Fixing the "running" section 2015-10-25 13:12:36 +01:00
mpnowacki 3811fb46b3 fixed file renaming issues with page_file_dir. This will only work with appropriate changes in gollum-lib 2015-10-04 11:34:25 +02:00
Dawa Ometto ab42c0c0df Release 4.0.1 2015-09-20 14:48:32 +02:00
Bart Kamphorst de5aed2f6a Merge pull request #1069 from repotag/master
Added security check.
2015-09-20 14:31:14 +02:00
Bart Kamphorst ce68a88293 Added security check. 2015-09-20 13:53:02 +02:00
Dawa Ometto 288f75929d Merge pull request #1065 from n-st/sysv-init-script
Added init script for Debian's SysV-style init system
2015-09-16 12:52:38 +02:00
Dawa Ometto 8528dd0c7f Merge pull request #1067 from SkyCrawl/master
Fixing the link to Windows support meta issue
2015-09-14 17:55:18 +02:00
SkyCrawl e0f35eceab Fixing the link to Windows support meta issue 2015-09-14 17:31:25 +02:00
Dawa Ometto 7e36517a79 Merge pull request #1054 from SkyCrawl/master
Config files restored + BIN enhancements + BIN/README sync
2015-09-14 12:40:22 +02:00
Dawa Ometto 69327766bb Merge pull request #1061 from rgroux/gollum-with-cas-sso
README add link to wiki about CAS SSO
2015-09-14 12:38:35 +02:00
Nils Steinger 9ecf8a61ba Added init script for Debian's SysV-style init system
Adapted from the OpenRC init script in contrib/openrc/init.d/gollum.
2015-09-14 05:03:06 +02:00
Richard Groux 2323506c82 Update README.md 2015-09-10 18:14:59 +02:00
Bart Kamphorst caa1a55e7a Merge pull request #1062 from mortonfox/patch-1
Fix github-markup link in readme.
2015-08-26 09:49:56 +02:00
Morton Fox 2ce44c157a Fix github-markup link 2015-08-25 23:48:53 -04:00
rgroux e17778190d README add link to wiki about CAS SSO 2015-08-25 17:06:36 +02:00
SkyCrawl d1d81a0043 Syncing BIN with README + fixes & enhancements 2015-08-23 14:44:18 +02:00
SkyCrawl ddb5ced3d5 Updating gemspec to reflect recent changes 2015-08-23 14:43:46 +02:00
Bart Kamphorst 9cebe655fd Merge pull request #1060 from mchill/custom-template
Fix custom.x path when page-file-dir is not set
2015-08-21 14:11:02 +02:00
Matt Hill 51b5a11a54 Fix custom.x path when page-file-dir is not set 2015-08-20 23:19:25 -04:00
Bart Kamphorst f3405851a7 Merge pull request #1055 from repotag/webrick_fix
Add webrick as development dependency. Fixes #1053.
2015-08-14 11:42:17 +02:00
Bart Kamphorst 5b6870d610 Add webrick as development dependency. Fixes #1053. 2015-08-14 11:06:17 +02:00
SkyCrawl c3dceaff5c Adding reference to the "Troubleshoot" wiki page 2015-08-12 22:26:59 +02:00
SkyCrawl 0a9b35c039 Restoring config files to repo root 2015-08-12 22:26:20 +02:00
Dawa Ometto dc32997f45 Merge pull request #1051 from SkyCrawl/master
Docs update
2015-08-09 20:30:21 +02:00
SkyCrawl 67c64c17d5 README makeover 2015-08-09 19:22:16 +02:00
Dawa Ometto 9cc02982be Merge pull request #973 from hsanson/add-plantuml-opt
Add option to configure PlantUML endpoint
2015-08-09 19:15:02 +02:00
SkyCrawl b4c70a62a9 Removing Home.md due to being a mere lorem ipsum 2015-08-08 15:21:30 +02:00
SkyCrawl c9d5921f4e Moving docs/sanitization.md to the wiki 2015-08-08 15:21:00 +02:00
SkyCrawl 0f47a4de5d Removing config.rb due to being pointless 2015-08-08 15:20:35 +02:00
Dawa Ometto 0ba5e4d3c7 Merge pull request #1047 from benubird/custom-template
Changed layout template, to handle custom.x with page-file-dir
2015-08-05 14:51:51 +02:00
Joshua Swanson 73e61dbcff Changed layout template, to correctly handle custom.js when --page-file-dir is set 2015-08-05 13:26:19 +01:00
Bart Kamphorst ba142e3b19 Merge pull request #1038 from rgroux/development
upgrade/update a number of dependencies.
2015-07-27 22:14:55 +02:00
GROUX Richard f651a401e8 deps(update): test with ruby 2.2.2
need to add `test-unit` gem when using ruby >= 2.2.0
2015-07-27 13:49:03 +02:00
GROUX Richard 3301252fb2 deps(update): twitter_cldr update to > 3.2
Changelog is
[there](https://github.com/twitter/twitter-cldr-rb/blob/master/History.txt)
2015-07-27 13:20:50 +02:00
GROUX Richard fe77e2b0d3 deps(update): upgrade the mocha version to 1.1
Changelog is
[there](https://github.com/freerange/mocha/blob/master/RELEASE.md)
2015-07-27 13:16:29 +02:00
GROUX Richard d586567c3b deps(update): upgrade the useragent version to 0.14 2015-07-27 13:15:50 +02:00
GROUX Richard 211178d39e deps(update): upgrade the kramdown version to 1.8
Changelog of kramdown is [there](http://kramdown.gettalong.org/news.html)
2015-07-27 13:12:48 +02:00
Dawa Ometto d8354faa49 Merge pull request #1035 from rgroux/search
Search default query to ''
2015-07-26 20:23:09 +02:00
GROUX Richard f9e5c05046 Fix search without querry
App crash when we try to get /search

Add an empty querry '' as default
2015-07-21 19:35:45 +02:00
Dawa Ometto 596afe9451 Merge pull request #1034 from mehulkar/patch-1
Fix a typo in the readme
2015-07-16 23:35:51 +02:00
Mehul Kar f7b5de986a Fix a typo in the readme 2015-07-16 13:57:11 -07:00
Bart Kamphorst ee9262472b Merge pull request #1032 from techwiz24/iss1030-ie-cache
Fix #1030: Disable AJAX Cach in Live Editor
2015-07-12 19:09:04 +02:00
Nathan Lowe 8a6a8db730 Fix #1030: Disable AJAX Cach in Live Editor
Internet Explorer caches all AJAX get requests and decides when to expire
them in the cache. This commit disables caching on the GET request to
/data/<PAGE NAME>, which fixes IE displaying old page data when using the
live editor.

See http://www.dashbay.com/2011/05/internet-explorer-caches-ajax/ and
https://stackoverflow.com/questions/4303829/how-to-prevent-a-jquery-ajax-request-from-caching-in-internet-explorer
2015-07-03 17:28:43 -04:00
Bart Kamphorst 3642370f11 Merge pull request #1014 from techwiz24/iss/1012-pages-view
Fix #1012: /Pages View should list folders first, then files, all alphabetically.
2015-05-21 02:15:57 +02:00
Nathan Lowe 2db2cfb81c Update test coverage to account for #1012
The existing 'files_folders' test only covered the /Mordor subdirectory,
which does not include a diverse enough set of pages and folders to account
for the new rendering logic.

This test uses the results from the root of the lotr.git example repository
as it exists at this point in time. The mock '@results' are not passed as a
sorted array in order to test the sorting logic.

The existing 'files_folders' test has been renamed to 'files_folders from subdir'
because the 'files_folders' function should be tested in a situation when
viewing /pages from a subdirectory to ensure proper functionality.
2015-05-20 18:58:24 -04:00
Nathan Lowe 279b028c5e Fix #1012: /Pages should render Folders First, then files, alphabetically 2015-05-20 18:58:10 -04:00
Dawa Ometto f268827a2e Merge pull request #995 from tfogo/html-cleanup
Remove extraneous li tags
2015-04-26 19:05:00 +02:00
Dawa Ometto d8bc065057 Merge pull request #1003 from akston/readme_typo
Fix typo in README
2015-04-26 19:04:26 +02:00
Conor Schaub 6301bbbb66 Fix typo in README 2015-04-25 15:32:30 -07:00
Dawa Ometto 3ad65eaef8 Update README.md 2015-04-11 11:53:59 +02:00
Dawa Ometto 3d1bc7949d Update README.md 2015-04-11 11:43:40 +02:00
Dawa Ometto 26e092fd20 Update HISTORY.md 2015-04-11 11:42:21 +02:00
Dawa Ometto be7ddec1b0 Update README.md 2015-04-11 11:40:43 +02:00
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
Tim Fogarty 93af289d3b Remove extraneous li tags 2015-04-06 02:40:02 -04: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
Horacio Sanson 1cceb7d4b4 Add option to configure PlantUML endpoint 2015-02-05 01:10:12 +09: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
930 changed files with 215557 additions and 68393 deletions
+6 -2
View File
@@ -1,8 +1,12 @@
rvm:
- 1.9.3
- 2.0.0
- 2.1.0
- 2.1.1
- 2.2.2
- 2.3.0
- 2.4.0
- jruby-9.1.8.0
jdk:
- oraclejdk8
before_install:
- sudo apt-get update
- sudo apt-get install libicu-dev
+91
View File
@@ -0,0 +1,91 @@
# Contributing to Gollum
Thanks for your interest in the gollum project!
## Submitting an Issue
Please note that the issue tracker is meant for:
1. Bug reports.
2. Feature requests.
If your have problems using or installing the software which stem from bugs in the software or a lack of documentation, we are always happy to help out! However, **for ordinary usage questions, please consider asking elsewhere**, for instance on [StackOverflow](http://stackoverflow.com/questions/tagged/gollum-wiki).
Gollum supports [custom macros](https://github.com/gollum/gollum/wiki#macros) for the creation of additional wiki markup tags. Please **do not** use this tracker to request macros specific to your situation. However, if you have or are working on a macro that you think may be useful to more users, you can share it as a GitHub [gist](https://gist.github.com) and link to it in the [wiki](https://github.com/gollum/gollum/wiki/Custom-macros).
Before submitting an issue, **please carefully look through the following places** to make sure your problem is not already addressed:
1. The issue tracker.
1. The [README](https://github.com/gollum/gollum/blob/master/README.md).
1. The project's [wiki](https://github.com/gollum/gollum/wiki).
Lastly, please **consider helping out** by opening a Pull Request!
## Opening a Pull Request
Pull Requests fixing bugs, implementing new features, or updating documentation and dependencies are all very welcome! If you would like to help out with the project, you can pick an open issue from the issue tracker. We're more than happy to help you get started! Here's how you can proceed:
1. Fork and clone Gollum.
2. Create a thoughtfully named topic branch to contain your changes.
3. If you haven't installed dependencies yet, navigate to your clone and execute:
```
[sudo] bundle install
```
4. Hack away.
5. Add your own tests and make sure they're all still passing.
6. If some of your changes deserve a mention on Gollum's home page, edit the README accordingly.
7. If necessary, rebase your commits into logical chunks, without errors.
8. Push the branch to your fork on GitHub.
9. Create a pull request for Gollum.
**Notes:**
* Do not change Gollum's version numbers, we will do that on our own.
### Running tests
1. Install [Bundler](http://bundler.io/).
2. Navigate to the cloned source of Gollum.
3. Install dependencies:
```
[sudo] bundle install
```
4. Run the tests:
```
bundle exec rake test
```
### Working with test repositories
An example of how to add a test file to the bare repository lotr.git.
```
mkdir tmp
cd tmp
git clone ../lotr.git/
git log
echo "test" > test.md
git add .
git commit -am "Add test"
git push ../lotr.git/ master
```
## Releasing the gem
Gollum uses [Semantic Versioning](http://semver.org/).
x.y.z
For z releases:
```
rake bump
rake release
```
For x.y releases:
```
# First update VERSION in lib/gollum.rb and then:
rake gemspec
rake release
```
+14
View File
@@ -1,3 +1,17 @@
# 4.1.2 /2017-08-07
* Lock to a newer version of gollum-lib to avoid installing an outdated and vulnerable dependency (nokogiri) on ruby 2.0. See https://github.com/gollum/gollum-lib/pull/279. Note: this breaks semantic versioning so those using outdated rubies will discover the problem on update.
# 4.1.0 /2017-03-09
* Added file deletion functionality to file view
* Various performance improvements
* Emoji support
# 4.0.0 /2015-04-11
* Now compatible with JRuby (via the [rjgit](https://github.com/repotag/rjgit) [adapter](https://github.com/repotag/gollum-lib_rjgit_adapter))
# 3.1.1 /2014-12-04
* Security fix for [remote code execution issue](https://github.com/gollum/gollum/issues/913). Please update!
-3
View File
@@ -1,3 +0,0 @@
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque vulputate tincidunt sollicitudin. Quisque sit amet leo sed nunc eleifend rhoncus in consectetur leo. Vivamus posuere semper convallis. Duis malesuada lacus sed erat lobortis tincidunt mattis ligula consectetur. Sed aliquam vulputate eros at euismod. Aenean egestas lorem ligula, quis faucibus turpis. Curabitur a eros in ipsum gravida ornare. Sed elementum enim vel mi scelerisque dapibus. Nulla id libero ligula, quis tempus sem. Morbi nec felis tortor, ac cursus risus. Mauris elementum tortor id lacus eleifend non lobortis tellus pharetra. Etiam posuere cursus vestibulum. $x \lt y$
Morbi tincidunt dolor vel massa dictum volutpat. Vestibulum quis nibh metus, id tincidunt nisl. Vivamus eget sem ac risus eleifend rhoncus at eu nisl. Nunc elit massa, vulputate ac gravida eget, condimentum quis justo. Integer scelerisque, libero vel consequat ultricies, eros libero sagittis libero, pellentesque bibendum quam massa ut orci. Maecenas sit amet urna eget quam aliquam posuere. Nulla facilisi. Duis imperdiet augue sit amet metus ornare et hendrerit dui feugiat. Aenean a lacus neque. Sed eu enim tincidunt dolor pharetra porttitor. Vestibulum ut felis dui, rutrum iaculis orci. Duis eu bibendum tortor. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse mollis sagittis purus sit amet sollicitudin. Phasellus vel interdum erat.
+127 -185
View File
@@ -1,4 +1,4 @@
gollum -- A wiki built on top of Git
gollum -- A git-based Wiki
====================================
[![Gem Version](https://badge.fury.io/rb/gollum.svg)](http://badge.fury.io/rb/gollum)
@@ -7,229 +7,171 @@ gollum -- A wiki built on top of Git
## DESCRIPTION
Gollum is a simple wiki system built on top of Git.
Gollum is a simple wiki system built on top of Git. A Gollum Wiki is simply a git repository (either bare or regular) of a specific nature:
* A Gollum repository's contents are human-editable, unless the repository is bare. Pages are unique text files which may be organized into directories any way you choose. Other content can also be included, for example images, PDFs and headers/footers for your pages.
* Gollum pages:
* May be written in a variety of [markups](#markups).
* Can be edited with your favourite system editor or IDE (changes will be visible after committing) or with the built-in web interface.
* Can be displayed in all versions (commits).
Gollum wikis are simply Git repositories that adhere to a specific format.
Gollum pages may be written in a variety of formats and can be edited in a
number of ways depending on your needs. You can edit your wiki locally:
Gollum can be launched either as a webserver (with the web interface) or in "console mode", where you can use a predefined API to query and manipulate the repository. For more information, see the [Running](#running) and [Configuration](#configuration) sections.
* With your favorite text editor or IDE (changes will be visible after committing).
* With the built-in web interface.
* With the Gollum Ruby API.
For more information on Gollum's capabilities and pitfalls:
Gollum follows the rules of [Semantic Versioning](http://semver.org/) and uses
[TomDoc](http://tomdoc.org/) for inline documentation.
1. [Syntax/capability overview for pages](https://github.com/gollum/gollum/wiki).
2. [Known limitations](https://github.com/gollum/gollum/wiki/Known-limitations).
3. [Troubleshoot guide](https://github.com/gollum/gollum/wiki/Troubleshoot-guide).
4. [Security overview](https://github.com/gollum/gollum/wiki/Security).
### Videos
* [Quick impression of gollum](https://www.youtube.com/watch?v=gj1qqK3Oku8)
* [Gollum overview and simple markdown tutorial (german with english subtitles)](https://www.youtube.com/watch?v=wfWgDRmcbU4)
* [Advanced features in action](https://www.youtube.com/watch?v=EauxgxsLDC4)
## SYSTEM REQUIREMENTS
- Python 2.5+ (2.7.3 recommended)
- Ruby 1.9.3+ (1.9.3 recommended)
- Unix like operating system (OS X, Ubuntu, Debian, and more)
- Will not work on Windows (because of [grit](https://github.com/github/grit))
| Operating System | Ruby | Adapters | Supported |
| ---------------- | -------------- | ------------------ | --------- |
| Unix/Linux-like | Ruby 1.9.3+ | all except [RJGit](https://github.com/repotag/rjgit) | yes |
| Unix/Linux-like | [JRuby](https://github.com/jruby/jruby) (1.9.3+ compatible) | [RJGit](https://github.com/repotag/rjgit) | yes |
| Windows | Ruby 1.9.3+ | all except [RJGit](https://github.com/repotag/rjgit) | no |
| Windows | [JRuby](https://github.com/jruby/jruby) (1.9.3+ compatible) | [RJGit](https://github.com/repotag/rjgit) | almost<sup>1</sup> |
## SECURITY
**Notes:**
Don't enable `--custom-css`, `--custom-js` or `--mathjax-config` unless you trust every user who has the ability to edit the wiki.
A better solution with more security is being tracked in [#665](https://github.com/gollum/gollum/issues/665).
1. There are still some bugs and this setup is not ready for production yet. You can track the progress at [Support Windows via JRuby - Meta Issue](https://github.com/gollum/gollum/issues/1044).
## INSTALLATION
The best way to install Gollum is with RubyGems:
Varies depending on operating system, package manager and Ruby installation. Generally, you should first install Ruby and then Gollum.
```bash
$ [sudo] gem install gollum
```
You may first need to install some additional [development packages](https://github.com/gollum/gollum/wiki/Installation) for your OS.
1. Ruby is best installed either via [RVM](https://rvm.io/) or a package manager of choice.
2. Gollum is best installed via RubyGems:
```
[sudo] gem install gollum
```
If you're installing from source, you can use [Bundler][bundler] to pick up all the
gems:
Installation examples for individual systems can be seen [here](https://github.com/gollum/gollum/wiki/Installation).
```bash
$ bundle install
```
**Notes:**
* Whichever Ruby implementation you're using, Gollum ships with the appropriate default git adapter. So the above installation procedure is common for both MRI and JRuby.
* If you're installing from source:
* Optionally uninstall any previous versions of Gollum:
```
[sudo] gem uninstall -aIx gollum
```
* Install [Bundler](http://bundler.io/).
* Navigate to the cloned source of Gollum.
* Install dependencies:
```
[sudo] bundle install
```
* Build:
```
rake build
```
* And install:
```
[sudo] gem install --no-document pkg/gollum*.gem
```
In order to use the various formats that Gollum supports, you will need to
separately install the necessary dependencies for each format. You only need
to install the dependencies for the formats that you plan to use.
### Markups
* [AsciiDoc](http://asciidoctor.org) -- `gem install asciidoctor`
* [Creole](http://wikicreole.org/) -- `gem install creole`
* [Markdown](http://daringfireball.net/projects/markdown/) -- `gem install redcarpet`
* [GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown) -- `gem install github-markdown`
* [Org](http://orgmode.org/) -- `gem install org-ruby`
* [Pod](http://search.cpan.org/dist/perl/pod/perlpod.pod) -- `Pod::Simple::HTML` comes with Perl >= 5.10. Lower versions should install Pod::Simple from CPAN.
Gollum presently ships with support for the following markups:
* [Markdown](http://daringfireball.net/projects/markdown/syntax)
* [RDoc](http://rdoc.sourceforge.net/)
* [ReStructuredText](http://docutils.sourceforge.net/rst.html) -- `easy_install docutils`
* [Textile](http://www.textism.com/tools/textile/) -- `gem install RedCloth`
* [MediaWiki](http://www.mediawiki.org/wiki/Help:Formatting) -- `gem install wikicloth`
[bundler]: http://gembundler.com/
## SYNTAX
Gollum supports a variety of formats and extensions (Markdown, MediaWiki, Textile, …).
On top of these formats Gollum lets you insert headers, footers, links, image, math and more.
Check out the [Gollum Wiki](https://github.com/gollum/gollum/wiki) for all of Gollum's formats and syntactic options.
Since all markups are rendered by the [github-markup](https://github.com/github/markup) gem, you can easily add support for other markups by additional installation:
* [AsciiDoc](http://asciidoctor.org/docs/asciidoc-syntax-quick-reference/) -- `[sudo] gem install asciidoctor`
* [Creole](http://www.wikicreole.org/wiki/CheatSheet) -- `[sudo] gem install creole`
* [MediaWiki](http://www.mediawiki.org/wiki/Help:Formatting) -- `[sudo] gem install wikicloth`
* [Org](http://orgmode.org/worg/dev/org-syntax.html) -- `[sudo] gem install org-ruby`
* [Pod](http://perldoc.perl.org/perlpod.html) -- requires Perl >= 5.10 (the `perl` command must be available on your command line)
* Lower versions should install `Pod::Simple` from CPAN.
* [ReStructuredText](http://docutils.sourceforge.net/docs/ref/rst/restructuredtext.html) -- requires python >= 2 (the `python2` command must be available on your command line)
* Note that Gollum will also need you to install `docutils` for your Python 2. Installation procedure can, again, vary depending on operating system and package manager.
* [Textile](http://redcloth.org/hobix.com/textile/quick.html) -- `[sudo] gem install RedCloth`
By default, Gollum ships with the `kramdown` gem to render Markdown. However, you can use any [Markdown renderer supported by github-markup](https://github.com/github/markup/blob/master/lib/github/markup/markdown.rb). The thing to remember is that the first installed renderer from the list will be used. So, for example, `redcarpet` will NOT be used if `github/markdown` is installed.
## RUNNING
To view and edit your Gollum repository locally via the built in web
interface, simply install the Gollum gem, navigate to your repository via the
command line, and run the executable:
Simply:
```bash
$ gollum
```
1. Navigate to your git repository (wiki) via the command line.
2. Run: `gollum`.
3. Open `http://localhost:4567` in your browser.
This will start up a web server running the Gollum frontend and you can view
and edit your wiki at http://localhost:4567. To get help on the command line
utility, you can run it like so:
This will start up a web server (WEBrick) running Gollum with a web interface, where you can view and edit your wiki.
```bash
$ gollum --help
```
### Running from source
This will show you the options you can pass as arguments to the `gollum` command:
1. `git clone https://github.com/gollum/gollum`
2. `cd gollum`
3. `[sudo] bundle install` (may not always be necessary).
4. `bundle exec bin/gollum`
* Like that, gollum assumes the target wiki (git repository) is the project repository itself. If it's not, execute `bundle exec bin/gollum <path-to-wiki>` instead.
5. Open `http://localhost:4567` in your browser.
```bash
Options:
--port [PORT] Bind port (default 4567).
--host [HOST] Hostname or IP address to listen on (default 0.0.0.0).
--version Display current version.
--config [CONFIG] Path to additional configuration file.
--adapter [ADAPTER] Git adapter to use in the backend. Defaults to grit.
--irb Start an irb process with gollum loaded for the current wiki.
--css Inject custom css. Uses custom.css from root repository.
--js Inject custom js. Uses custom.js from root repository.
--template-dir [PATH] Specify custom template directory.
--page-file-dir [PATH] Specify the sub directory for all page files (default: repository root).
--base-path [PATH] Specify the base path for the served pages (default: /) Example: --base-path wiki yields the home page accessible at http://localhost:4567/wiki/.
--gollum-path [PATH] Specify the path to the git repository to be served.
--ref [REF] Specify the repository ref to use (default: master).
--bare Specify that the repository is bare (only necessary when using the grit adapter).
--no-edit Restricts editing capability through frontend.
--no-live-preview Disables livepreview.
--live-preview Enables livepreview.
--allow-uploads [MODE] Allows file uploads. Modes: dir (default, store all uploads in the same directory), page (store each upload at the same location as the page).
--mathjax Enables mathjax for rendering mathematical equations. Uses the TeX-AMS-MML_HTMLorMML config with the autoload-all extension by default.
--mathjax-config [SOURCE] Inject custom mathjax config file. Uses mathjax.config.js from root repository by default.
--user-icons [SOURCE] Set the history user icons. Valid values: gravatar, identicon, none. Default: none.
--show-all Shows all files in file view. By default only valid pages are shown.
--collapse-tree Collapse file view tree. By default, expanded tree is shown.
--h1-title Sets page title to value of first h1.
```
### Rack
Note that the gollum server will not run on Windows because of [an issue](https://github.com/rtomayko/posix-spawn/issues/9) with posix-spawn (which is used by Grit).
Gollum can also be ran with any [rack-compatible web server](https://github.com/rack/rack#supported-web-servers). More on that [over here](https://github.com/gollum/gollum/wiki/Gollum-via-Rack).
### RACK
### Rack, with an authentication server
You can also run gollum with any rack-compatible server by placing this config.ru
file inside your wiki repository. This allows you to utilize any Rack middleware
like Rack::Auth, OmniAuth, etc.
Gollum can also be ran alongside a CAS (Central Authentication Service) SSO (single sign-on) server. With a bit of tweaking, this adds basic user-support to Gollum. To see an example and an explanation, navigate [over here](https://github.com/gollum/gollum/wiki/Gollum-via-Rack-and-CAS-SSO).
```ruby
#!/usr/bin/env ruby
require 'rubygems'
require 'gollum/app'
### Docker
gollum_path = File.expand_path(File.dirname(__FILE__)) # CHANGE THIS TO POINT TO YOUR OWN WIKI REPO
Precious::App.set(:gollum_path, gollum_path)
Precious::App.set(:default_markup, :markdown) # set your favorite markup language
Precious::App.set(:wiki_options, {:universal_toc => false})
run Precious::App
```
Gollum can also be ran via [Docker](https://www.docker.com/). More on that [over here](https://github.com/gollum/gollum/wiki/Gollum-via-Docker).
Your Rack middleware can pass author details to Gollum in a Hash in the session under the 'gollum.author' key.
### Service
## CONFIG FILE
Gollum can also be ran as a service. More on that [over here](https://github.com/gollum/gollum/wiki/Gollum-as-a-service).
Gollum optionally takes a `--config file`. See [config.rb](https://github.com/gollum/gollum/blob/master/config.rb) for an example.
## CONFIGURATION
## CUSTOM CSS/JS
Gollum comes with the following command line options:
The `--css` flag will inject `custom.css` from the root of your git repository into each page. `custom.css` must be commited to git or you will get a 302 redirect to the create page.
| Option | Arguments | Description |
| ----------------- | --------- | ----------- |
| --host | [HOST] | Specify the hostname or IP address to listen on. Default: `0.0.0.0`.<sup>1</sup> |
| --port | [PORT] | Specify the port to bind Gollum with. Default: `4567`. |
| --config | [FILE] | Specify path to Gollum's configuration file. |
| --ref | [REF] | Specify the git branch to serve. Default: `master`. |
| --adapter | [ADAPTER] | Launch Gollum using a specific git adapter. Default: `grit`.<sup>2</sup> |
| --bare | none | Tell Gollum that the git repository should be treated as bare. This is only necessary when using the default grit adapter. |
| --base-path | [PATH] | Specify the leading portion of all Gollum URLs (path info). Setting this to `/wiki` will make the wiki accessible under `http://localhost:4567/wiki/`. Default: `/`. |
| --page-file-dir | [PATH] | Specify the subdirectory for all pages. If set, Gollum will only serve pages from this directory and its subdirectories. Default: repository root. |
| --css | none | Tell Gollum to inject custom CSS into each page. Uses `custom.css` from repository root.<sup>3,5</sup> |
| --js | none | Tell Gollum to inject custom JS into each page. Uses `custom.js` from repository root.<sup>3,5</sup> |
| --emoji | none | Parse and interpret emoji tags (e.g. :heart:). |
| --no-edit | none | Disable the feature of editing pages. |
| --live-preview | none | Enable the live preview feature in page editor. |
| --no-live-preview | none | Disable the live preview feature in page editor. |
| --allow-uploads | [MODE] | Enable file uploads. If set to `dir`, Gollum will store all uploads in the `/uploads/` directory in repository root. If set to `page`, Gollum will store each upload at the currently edited page.<sup>4</sup> |
| --mathjax | none | Enables MathJax (renders mathematical equations). By default, uses the `TeX-AMS-MML_HTMLorMML` config with the `autoload-all` extension.<sup>5</sup> |
| --irb | none | Launch Gollum in "console mode", with a [predefined API](https://github.com/gollum/gollum-lib/). |
| --h1-title | none | Tell Gollum to use the first `<h1>` as page title. |
| --show-all | none | Tell Gollum to also show files in the file view. By default, only valid pages are shown. |
| --collapse-tree | none | Tell Gollum to collapse the file tree, when the file view is opened. By default, the tree is expanded. |
| --user-icons | [MODE] | Tell Gollum to use specific user icons for history view. Can be set to `gravatar`, `identicon` or `none`. Default: `none`. |
| --mathjax-config | [FILE] | Specify path to a custom MathJax configuration. If not specified, uses the `mathjax.config.js` file from repository root. |
| --template-dir | [PATH] | Specify custom mustache template directory. |
| --help | none | Display the list of options on the command line. |
| --version | none | Display the current version of Gollum. |
The `--js` flag will inject `custom.js` from the root of your git repository into each page. `custom.js` must be commited to git or you will get a 302 redirect to the create page.
**Notes:**
1. The `0.0.0.0` IP address allows remote access. Should you wish for Gollum to turn into a personal Wiki, use `127.0.0.1`.
2. Before using `--adapter`, you should probably read [this](https://github.com/gollum/gollum/wiki/Git-adapters) first.
3. When `--css` or `--js` is used, respective files must be committed to your git repository or you will get a 302 redirect to the create a page.
4. Files can be uploaded simply by dragging and dropping them onto the editor's text area (this is, however exclusive to the default editor, not the live preview editor).
5. Read the relevant [Security note](https://github.com/gollum/gollum/wiki/Security#custom-cssjs--mathjax-config) before using these.
## API DOCUMENTATION
### Config file
The [Gollum API](https://github.com/gollum/gollum-lib/) allows you to retrieve
raw or formatted wiki content from a Git repository, write new content to the
repository, and collect various meta data about the wiki as a whole.
When `--config` option is used, certain inner parts of Gollum can be customized. This is used throughout our wiki for certain user-level alterations, among which [customizing supported markups](https://github.com/gollum/gollum/wiki/Formats-and-extensions) will probably stand out.
## CONTRIBUTE
If you'd like to hack on Gollum, start by forking the repo on GitHub:
http://github.com/gollum/gollum
To get all of the dependencies, install the gem first. The best way to get
your changes merged back into core is as follows:
1. Clone down your fork
1. Create a thoughtfully named topic branch to contain your change
1. Hack away
1. Add tests and make sure everything still passes by running `rake`
1. If you are adding new functionality, document it in the README
1. Do not change the version number, I will do that on my end
1. If necessary, rebase your commits into logical chunks, without errors
1. Push the branch up to GitHub
1. Send a pull request to the gollum/gollum project.
## RELEASING
Gollum uses [Semantic Versioning](http://semver.org/).
x.y.z
For z releases:
```bash
$ rake bump
$ rake release
```
For x.y releases:
```bash
Update VERSION in lib/gollum.rb
$ rake gemspec
$ rake release
```
## BUILDING THE GEM FROM MASTER
```bash
$ gem uninstall -aIx gollum
$ git clone https://github.com/gollum/gollum.git
$ cd gollum
gollum$ rake build
gollum$ gem install --no-ri --no-rdoc pkg/gollum*.gem
```
## RUN THE TESTS
```bash
$ bundle install
$ bundle exec rake test
```
## WORK WITH TEST REPOS
An example of how to add a test file to the bare repository lotr.git.
```bash
$ mkdir tmp; cd tmp
$ git clone ../lotr.git/ .
Cloning into '.'...
done.
$ git log
$ echo "test" > test.md
$ git add . ; git commit -am "Add test"
$ git push ../lotr.git/ master
```
**All of the mentioned alterations work both for Gollum's config file (`config.rb`) and Rack's config file (`config.ru`).**
+169 -107
View File
@@ -2,128 +2,169 @@
$:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
help = <<HELP
Gollum is a multi-format Wiki Engine/API/Frontend.
Basic Command Line Usage:
gollum [OPTIONS] [PATH]
PATH The path to the Gollum repository (default .).
Options:
HELP
require 'optparse'
require 'rubygems'
require 'gollum'
require 'cgi'
exec = {}
options = { 'port' => 4567, 'bind' => '0.0.0.0' }
options = {
:port => 4567,
:bind => '0.0.0.0',
}
wiki_options = {
:live_preview => false,
:allow_uploads => false,
:allow_editing => true,
:live_preview => false,
:allow_uploads => false,
:allow_editing => true,
}
opts = OptionParser.new do |opts|
opts.banner = help
opts.on("--port [PORT]", "Bind port (default 4567).") do |port|
options['port'] = port.to_i
# define program name (although this defaults to the name of the file, just in case...)
opts.program_name = "gollum"
# set basic info for the "--help" command (options will be appended automatically from the below definitions)
opts.banner = '
Gollum is a multi-format Wiki Engine/API/Frontend.
Usage:
gollum [options] [git-repo]
Arguments:
[git-repo] Path to the git repository being served. If not specified, current working directory is used.
Notes:
Paths for all options are relative to <git-repo> unless absolute.
This message is only a basic description. For more information, please visit:
https://github.com/gollum/gollum
OPTIONS'
# define gollum options
opts.separator ""
opts.separator " Major:"
opts.on("-h", "--host [HOST]", "Specify the hostname or IP address to listen on. Default: '0.0.0.0'.") do |host|
options[:bind] = host
end
opts.on("--host [HOST]", "Hostname or IP address to listen on (default 0.0.0.0).") do |host|
options['bind'] = host
opts.on("-p", "--port [PORT]", "Specify the port to bind Gollum with. Default: '4567'.") do |port|
begin
# don't use "port.to_i" here... it doesn't raise errors which might result in a nice confusion later on
options[:port] = Integer(port)
rescue ArgumentError
puts "Error: '#{port}' is not a valid port number."
exit 1
end
end
opts.on("--version", "Display current version.") do
puts "Gollum " + Gollum::VERSION
exit 0
opts.on("-c", "--config [FILE]", "Specify path to the Gollum's configuration file.") do |file|
options[:config] = file
end
opts.on("--config [CONFIG]", "Path to additional configuration file") do |config|
options['config'] = config
end
opts.on("--adapter [ADAPTER]", "Git adapter to use in the backend. Defaults to grit.") do |adapter|
Gollum::GIT_ADAPTER = adapter
end
opts.on("--irb", "Start an irb process with gollum loaded for the current wiki.") do
options['irb'] = true
end
opts.on("--css", "Inject custom css. Uses custom.css from root repository") do
wiki_options[:css] = true
end
opts.on("--js", "Inject custom js. Uses custom.js from root repository") do
wiki_options[:js] = true
end
opts.on("--template-dir [PATH]", "Specify custom template directory") do |path|
wiki_options[:template_dir] = path
end
opts.on("--page-file-dir [PATH]", "Specify the sub directory for all page files (default: repository root).") do |path|
wiki_options[:page_file_dir] = path
end
opts.on("--base-path [PATH]", "Specify the base path for the served pages (default: /) Example: --base-path wiki yields the home page accessible at http://localhost:4567/wiki/.") do |path|
wiki_options[:base_path] = path
end
opts.on("--gollum-path [PATH]", "Specify the path to the git repository to be served.") do |path|
wiki_options[:gollum_path] = path
end
opts.on("--ref [REF]", "Specify the repository ref to use (default: master).") do |ref|
opts.on("-r", "--ref [REF]", "Specify the branch to serve. Default: 'master'.") do |ref|
wiki_options[:ref] = ref
end
opts.on("--bare", "Specify that the repository is bare (only necessary when using the grit adapter).") do
opts.on("-a", "--adapter [ADAPTER]", "Launch Gollum using a specific git adapter. Default: 'grit'.") do |adapter|
Gollum::GIT_ADAPTER = adapter
end
opts.on("--bare", "Declare '<git-repo>' to be bare. This is only necessary when using the grit adapter.") do
wiki_options[:repo_is_bare] = true
end
opts.on("-b", "--base-path [PATH]", "Specify the leading portion of all Gollum URLs (path info). Default: '/'.",
"Example: setting this to '/wiki' will make the wiki accessible under 'http://localhost:4567/wiki/'.") do |base_path|
# first trim a leading slash, if any
base_path.sub!(/^\/+/, '')
opts.on("--no-edit", "Restricts editing capability through frontend.") do
# make a backup of the option and sanitize it
base_path_original = base_path.dup
base_path = CGI.escape(base_path)
# then let the user know if we changed the URL
unless base_path_original == base_path
puts <<MSG
Warning: your base-path has been sanitized:
- original: '#{base_path_original}'
- sanitized: '#{base_path}'
MSG
end
# and finally, let others enjoy our hard work:
wiki_options[:base_path] = base_path
end
opts.on("--page-file-dir [PATH]", "Specify the subdirectory for all pages. Default: repository root.",
"Example: setting this to 'pages' will make Gollum serve only pages at '<git-repo>/pages/*'.") do |path|
wiki_options[:page_file_dir] = path
end
opts.on("--css", "Inject custom CSS into each page. The '<git-repo>/custom.css' file is used (must be committed).") do
wiki_options[:css] = true
end
opts.on("--js", "Inject custom JavaScript into each page. The '<git-repo>/custom.js' file is used (must be committed).") do
wiki_options[:js] = true
end
opts.on("--emoji", "Parse and interpret emoji tags (e.g. :heart:).") do
wiki_options[:emoji] = true
end
opts.on("--no-edit", "Disable the feature of editing pages.") do
wiki_options[:allow_editing] = false
end
opts.on("--no-live-preview", "Disables livepreview.") do
wiki_options[:live_preview] = false
end
opts.on("--live-preview", "Enables livepreview.") do
opts.on("--live-preview", "Enable the live preview feature in page editor.") do
wiki_options[:live_preview] = true
end
opts.on("--allow-uploads [MODE]", [:dir, :page], "Allows file uploads. Modes: dir (default, store all uploads in the same directory), page (store each upload at the same location as the page).") do |mode|
opts.on("--no-live-preview", "Disable the live preview feature in page editor.") do
wiki_options[:live_preview] = false
end
opts.on("--allow-uploads [MODE]", [:dir, :page], "Enable file uploads.",
"If set to 'dir', Gollum will store all uploads in the '<git-repo>/uploads/' directory.",
"If set to 'page', Gollum will store each upload at the currently edited page.") do |mode|
wiki_options[:allow_uploads] = true
wiki_options[:per_page_uploads] = true if mode == :page
end
opts.on("--mathjax", "Enables mathjax for rendering mathematical equations. Uses the TeX-AMS-MML_HTMLorMML config with the autoload-all extension by default.") do
opts.on("--mathjax", "Enable MathJax (renders mathematical equations).",
"By default, uses the 'TeX-AMS-MML_HTMLorMML' config with the 'autoload-all' extension.") do
wiki_options[:mathjax] = true
end
opts.on("--mathjax-config [SOURCE]", "Inject custom mathjax config file. Uses mathjax.config.js from root repository by default") do |source|
wiki_options[:mathjax_config] = source || 'mathjax.config.js'
opts.on("--irb", "Launch Gollum in 'console mode', with a predefined API.") do
options[:irb] = true
end
opts.on("--user-icons [SOURCE]", "Set the history user icons. Valid values: gravatar, identicon, none. Default: none.") do |source|
wiki_options[:user_icons] = source
end
opts.on("--show-all", "Shows all files in file view. By default only valid pages are shown.") do
wiki_options[:show_all] = true
end
opts.on("--collapse-tree", "Collapse file view tree. By default, expanded tree is shown.") do
wiki_options[:collapse_tree] = true
end
opts.on("--h1-title", "Sets page title to value of first h1") do
opts.separator ""
opts.separator " Minor:"
opts.on("--h1-title", "Use the first '<h1>' as page title.") do
wiki_options[:h1_title] = true
end
opts.on("--show-all", "Also show files in the file view. By default, only valid pages are shown.") do
wiki_options[:show_all] = true
end
opts.on("--collapse-tree", "Collapse the tree, when file view is opened. By default, the tree is expanded.") do
wiki_options[:collapse_tree] = true
end
opts.on("--user-icons [MODE]", [:gravatar, :identicon, :none], "Use specific user-icons for history view.",
"Can be set to 'gravatar', 'identicon' or 'none'. Default: 'none'.") do |mode|
wiki_options[:user_icons] = mode
end
opts.on("--mathjax-config [FILE]", "Specify path to a custom MathJax configuration.",
"If not specified, uses the '<git-repo>/mathjax.config.js' file.") do |file|
wiki_options[:mathjax_config] = file || 'mathjax.config.js'
end
opts.on("--plantuml-url [URL]", "Sets the PlantUML server endpoint.") do |url|
wiki_options[:plantuml_url] = url
end
opts.on("--template-dir [PATH]", "Specify custom mustache template directory.") do |path|
wiki_options[:template_dir] = path
end
opts.separator ""
opts.separator " Common:"
opts.on("--help", "Display this message.") do
puts opts
exit 0
end
opts.on("--version", "Display the current version of Gollum.") do
puts "Gollum " + Gollum::VERSION
exit 0
end
opts.separator ""
end
# Read command line options into `options` hash
@@ -136,11 +177,9 @@ rescue OptionParser::InvalidOption
end
# --gollum-path wins over ARGV[0]
gollum_path = wiki_options[:gollum_path] ?
wiki_options[:gollum_path] :
ARGV[0] || Dir.pwd
gollum_path = ARGV[0] || Dir.pwd
if options['irb']
if options[:irb]
require 'irb'
# http://jameskilton.com/2009/04/02/embedding-irb-into-your-ruby-application/
module IRB # :nodoc:
@@ -171,8 +210,17 @@ if options['irb']
if !wiki.exist? then
raise Gollum::InvalidGitRepositoryError
end
puts "Loaded Gollum wiki at #{File.expand_path(gollum_path).inspect}."
if wiki_options[:plantuml_url]
Gollum::Filter::PlantUML.configure do |config|
puts "Using #{wiki_options[:plantuml_url]} as PlantUML endpoint"
config.url = wiki_options[:plantuml_url]
end
end
puts
puts "Loaded Gollum wiki at:"
puts "#{File.expand_path(gollum_path).inspect}"
puts
puts "Example API calls:"
puts %( page = wiki.page('page-name'))
puts %( # => <Gollum::Page>)
puts
@@ -182,7 +230,9 @@ if options['irb']
puts %( page.formatted_data)
puts %( # => "<h1>My wiki page</h1>")
puts
puts "Check out the Gollum README for more."
puts "Full API documentation at:"
puts "https://github.com/gollum/gollum-lib"
puts
IRB.start_session(binding)
rescue Gollum::InvalidGitRepositoryError, Gollum::NoSuchPathError
puts "Invalid Gollum wiki at #{File.expand_path(gollum_path).inspect}"
@@ -194,30 +244,41 @@ else
Precious::App.set(:wiki_options, wiki_options)
Precious::App.settings.mustache[:templates] = wiki_options[:template_dir] if wiki_options[:template_dir]
if cfg = options['config']
if cfg = options[:config]
# If the path begins with a '/' it will be considered an absolute path,
# otherwise it will be relative to the CWD
cfg = File.join(Dir.getwd, cfg) unless cfg.slice(0) == File::SEPARATOR
require cfg
end
if wiki_options[:plantuml_url]
Gollum::Filter::PlantUML.configure do |config|
puts "Using #{wiki_options[:plantuml_url]} as PlantUML endpoint"
config.url = wiki_options[:plantuml_url]
end
end
base_path = wiki_options[:base_path]
if wiki_options[:base_path].nil?
if base_path.nil?
Precious::App.run!(options)
else
require 'rack'
class MapGollum
def initialize base_path
def initialize(base_path)
@mg = Rack::Builder.new do
map '/' do
run Proc.new { [302, { 'Location' => "/#{base_path}" }, []] }
end
map "/#{base_path}" do
run Precious::App
end
map '/' do
run Proc.new { [302, { 'Location' => "/#{base_path}" }, []] }
end
map '/*' do
run Proc.new { [302, { 'Location' => "/#{base_path}" }, []] }
end
end
end
@@ -225,7 +286,8 @@ else
@mg.call(env)
end
end
# Rack::Handler does not work with Ctrl + C. Use Rack::Server instead.
Rack::Server.new(:app => MapGollum.new(base_path), :Port => options['port'], :Host => options['bind']).start
Rack::Server.new(:app => MapGollum.new(base_path), :Port => options[:port], :Host => options[:bind]).start
end
end
+9 -26
View File
@@ -1,28 +1,11 @@
# Example gollum config
# gollum ../wiki --config config.rb
#
# or run from source with
#
# bundle exec bin/gollum ../wiki/ --config config.rb
# Remove const to avoid
# warning: already initialized constant FORMAT_NAMES
#
# only remove if it's defined.
# constant Gollum::Page::FORMAT_NAMES not defined (NameError)
Gollum::Page.send :remove_const, :FORMAT_NAMES if defined? Gollum::Page::FORMAT_NAMES
# limit to one format
Gollum::Page::FORMAT_NAMES = { :markdown => "Markdown" }
=begin
Valid formats are:
{ :markdown => "Markdown",
:textile => "Textile",
:rdoc => "RDoc",
:org => "Org-mode",
:creole => "Creole",
:rest => "reStructuredText",
:asciidoc => "AsciiDoc",
:mediawiki => "MediaWiki",
:pod => "Pod" }
This file can be used to (e.g.):
- alter certain inner parts of Gollum,
- extend it with your stuff.
It is especially useful for customizing supported formats/markups. For more information and examples:
- https://github.com/gollum/gollum#config-file
=end
# enter your Ruby code here ...
+12
View File
@@ -0,0 +1,12 @@
=begin
You should use this file, if you wish to:
- launch Gollum as a Rack app,
- alter certain startup behaviour of Gollum.
For more information and examples:
- https://github.com/gollum/gollum/wiki/Gollum-via-Rack
- https://github.com/gollum/gollum#config-file
=end
# enter your Ruby code here ...
+79
View File
@@ -0,0 +1,79 @@
#!/bin/sh
### BEGIN INIT INFO
# Provides: gollum
# Required-Start: $local_fs $remote_fs $network $syslog
# Required-Stop: $local_fs $remote_fs $network $syslog
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# X-Interactive: true
# Short-Description: Start/stop gollum wiki
### END INIT INFO
# Distributed under the terms of the MIT License
set -e
# Edit these settings to your liking:
GOLLUM_USER=gollum
GOLLUM_BASE=/var/lib/gollum/.git
GOLLUM_OPTS="--bare"
NAME=gollum
PID=/var/run/${NAME}.pid
EXEC=/usr/local/bin/gollum
LOG=/var/log/gollum.log
. /lib/lsb/init-functions
start ()
{
# Change log file to be owned by GOLLUM_USER
touch "${LOG}"
chown "${GOLLUM_USER}" "${LOG}"
log_daemon_msg "Starting Gollum"
start-stop-daemon --start \
--name "${NAME}" \
--user "${GOLLUM_USER}" \
--chuid "${GOLLUM_USER}" \
--pidfile "${PID}" \
--make-pidfile --background \
--startas /bin/sh -- -c "exec ${EXEC} $GOLLUM_OPTS \"$GOLLUM_BASE\" > \"${LOG}\" 2>&1"
log_end_msg $?
}
stop ()
{
log_daemon_msg "Stopping Gollum"
start-stop-daemon --stop \
--user "${GOLLUM_USER}" \
--signal INT \
--pidfile "${PID}" \
--retry 10
log_end_msg $?
}
status ()
{
status_of_proc -p $PID $EXEC $NAME
}
case $1 in
start)
start
;;
stop)
stop
;;
restart)
stop
start
;;
status)
status
;;
*)
log_success_msg "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
-33
View File
@@ -1,33 +0,0 @@
Sanitization Rules
==================
Gollum uses the [Sanitize](http://wonko.com/post/sanitize) gem for HTML
sanitization. Below you find the default allowed tags, attributes, and protocols, as well as directions to customize these settings.
# Default Settings
## ALLOWED TAGS
a, abbr, acronym, address, area, b, big, blockquote, br, button, caption, center, cite, code, col, colgroup, dd, del, dfn, dir, div, dl, dt, em, fieldset, font, form, h1, h2, h3, h4, h5, h6, hr, i, img, input, ins, kbd, label, legend, li, map, menu, ol, optgroup, option, p, pre, q, s, samp, select, small, span, strike, strong, sub, sup, table, tbody, td, textarea, tfoot, th, thead, tr, tt, u, ul, var
## ALLOWED ATTRIBUTES
a href, abbr, accept, accept-charset, accesskey, action, align, alt, axis, border, cellpadding, cellspacing, char, charoff, class, charset, checked, cite, clear, cols, colspan, color, compact, coords, datetime, dir, disabled, enctype, for, frame, headers, height, hreflang, hspace, id, img src, ismap, label, lang, longdesc, maxlength, media, method, multiple, name, nohref, noshade, nowrap, prompt, readonly, rel, rev, rows, rowspan, rules, scope, selected, shape, size, span, start, summary, tabindex, target, title, type, usemap, valign, value, vspace, width
## ALLOWED PROTOCOLS
* a href: http, https, mailto, ftp, irc, apt, :relative
* img src: http, https, :relative
* form action: http, https, :relative
# Customizing
To customize these settings, edit your `config.rb` file along the following lines (be sure to run gollum with the `--config` option):
```ruby
sanitizer = Gollum::Sanitization.new
sanitizer.protocols['a']['href'].concat ['ssh', 'vnc'] # Protocols
sanitizer.elements.concat ['customtag1', 'customtag2'] # Tags
sanitizer.attributes['a'].push 'target' # Attributes
Precious::App.set(:wiki_options, {:sanitization => sanitizer})
```
+572 -34
View File
@@ -5,8 +5,8 @@ Gem::Specification.new do |s|
s.required_ruby_version = '>= 1.9'
s.name = 'gollum'
s.version = '3.1.2'
s.date = '2015-01-23'
s.version = '4.1.2'
s.date = '2017-08-07'
s.rubyforge_project = 'gollum'
s.license = 'MIT'
@@ -24,20 +24,24 @@ Gem::Specification.new do |s|
s.rdoc_options = ['--charset=UTF-8']
s.extra_rdoc_files = %w[README.md LICENSE]
s.add_dependency 'gollum-lib', '~> 4.0', '>= 4.0.1'
s.add_dependency 'github-markdown', '~> 0.6.5'
s.add_dependency 'gollum-lib', '>= 4.2.7'
s.add_dependency 'kramdown', '~> 1.9.0'
s.add_dependency 'sinatra', '~> 1.4', '>= 1.4.4'
s.add_dependency 'mustache', ['>= 0.99.5', '< 1.0.0']
s.add_dependency 'useragent', '~> 0.10.0'
s.add_dependency 'useragent', '~> 0.16.2'
s.add_dependency 'gemojione', '~> 3.2'
s.add_development_dependency 'rack-test', '~> 0.6.2'
s.add_development_dependency 'shoulda', '~> 3.5.0'
s.add_development_dependency 'minitest-reporters', '~> 0.14.16'
s.add_development_dependency 'twitter_cldr', '~> 2.4.2'
s.add_development_dependency 'mocha', '~> 1.0.0'
s.add_development_dependency 'twitter_cldr', '~> 3.2.0'
s.add_development_dependency 'mocha', '~> 1.1.0'
s.add_development_dependency 'test-unit', '~> 3.1.0'
s.add_development_dependency 'webrick', '~> 1.3.1'
# = MANIFEST =
s.files = %w[
CONTRIBUTING.md
Gemfile
HISTORY.md
LICENSE
@@ -45,14 +49,14 @@ Gem::Specification.new do |s|
Rakefile
bin/gollum
config.rb
config.ru
contrib/openrc/conf.d/gollum
contrib/openrc/init.d/gollum
contrib/systemd/gollum@.service
docs/sanitization.md
contrib/sysv-debian/init.d/gollum
gollum.gemspec
lib/gollum.rb
lib/gollum/app.rb
lib/gollum/editing_auth.rb
lib/gollum/helpers.rb
lib/gollum/public/gollum/css/_styles.css
lib/gollum/public/gollum/css/dialog.css
@@ -71,6 +75,7 @@ Gem::Specification.new do |s|
lib/gollum/public/gollum/images/fileview/folder-horizontal.png
lib/gollum/public/gollum/images/fileview/toggle-small-expand.png
lib/gollum/public/gollum/images/fileview/toggle-small.png
lib/gollum/public/gollum/images/fileview/trashcan.png
lib/gollum/public/gollum/images/icon-sprite.png
lib/gollum/public/gollum/images/man_24.png
lib/gollum/public/gollum/images/para.png
@@ -99,16 +104,21 @@ Gem::Specification.new do |s|
lib/gollum/public/gollum/livepreview/images/lr_24.png
lib/gollum/public/gollum/livepreview/images/save_24.png
lib/gollum/public/gollum/livepreview/images/savecomment_24.png
lib/gollum/public/gollum/livepreview/index.html
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ace.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/anchor.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/anchor_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/autocomplete.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/autocomplete/popup.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/autocomplete/text_completer.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/autocomplete/util.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/background_tokenizer.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/background_tokenizer_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/commands/command_manager.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/commands/command_manager_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/commands/default_commands.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/commands/incremental_search_commands.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/commands/multi_select_commands.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/commands/occur_commands.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/config.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/config_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/css/codefolding-fold-button-states.png
@@ -127,28 +137,57 @@ Gem::Specification.new do |s|
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/editor_highlight_selected_word_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/editor_navigation_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/editor_text_edit_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/beautify.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/beautify/php_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/chromevox.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/elastic_tabstops_lite.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/emmet.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/error_marker.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/keybinding_menu.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/language_tools.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/linking.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/menu_tools/add_editor_menu_options.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/menu_tools/element_generator.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/menu_tools/generate_settings_menu.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/menu_tools/get_editor_keyboard_shortcuts.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/menu_tools/get_set_functions.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/menu_tools/overlay_page.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/menu_tools/settings_menu.css
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/modelist.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/old_ie.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/old_ie_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/searchbox.css
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/searchbox.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/settings_menu.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/spellcheck.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/split.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/static.css
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/static_highlight.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/static_highlight_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/statusbar.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/textarea.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/themelist.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/whitespace.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/ext/whitespace_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/incremental_search.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/incremental_search_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/emacs.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/emacs_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/hash_handler.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/keybinding.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/keybinding_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/state_handler.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/textarea.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/textinput.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/vim.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/vim/commands.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/vim/maps/aliases.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/vim/maps/motions.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/vim/maps/operators.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/vim/maps/util.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/vim/registers.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/keyboard/vim_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/layer/cursor.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/layer/font_metrics.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/layer/gutter.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/layer/marker.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/layer/text.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/layer/text_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/lib/browser_focus.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/lib/app_config.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/lib/dom.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/lib/es5-shim.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/lib/event.js
@@ -161,56 +200,158 @@ Gem::Specification.new do |s|
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/lib/oop.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/lib/regexp.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/lib/useragent.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/line_widgets.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/Readme.md
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/highlight_rules_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/package.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/test_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_asciidoc.txt
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_coffee.txt
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_curly.txt
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_html.txt
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_javascript.txt
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_livescript.txt
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_lucene.txt
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_markdown.txt
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_php.txt
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_ruby.txt
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/text_xml.txt
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_abap.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_abc.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_actionscript.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_ada.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_asciidoc.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_assembly_x86.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_autohotkey.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_batchfile.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_c9search.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_c_cpp.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_cirru.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_clojure.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_cobol.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_coffee.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_coldfusion.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_csharp.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_css.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_curly.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_d.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_dart.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_diff.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_dot.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_eiffel.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_ejs.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_elixir.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_elm.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_erlang.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_forth.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_ftl.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_gcode.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_gherkin.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_gitignore.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_glsl.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_golang.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_groovy.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_haml.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_handlebars.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_haskell.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_haxe.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_html.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_html_ruby.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_ini.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_io.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_jack.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_jade.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_java.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_javascript.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_json.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_jsoniq.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_jsp.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_jsx.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_julia.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_latex.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_less.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_liquid.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_lisp.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_livescript.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_logiql.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_lsl.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_lua.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_luapage.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_lucene.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_markdown.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_mask.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_matlab.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_mel.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_mushcode.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_mysql.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_nix.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_objectivec.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_ocaml.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_pascal.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_perl.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_pgsql.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_php.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_powershell.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_prolog.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_properties.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_protobuf.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_python.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_r.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_rdoc.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_rhtml.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_ruby.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_rust.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_sass.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_scad.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_scala.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_scheme.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_scss.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_sh.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_sjs.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_smarty.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_snippets.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_soy_template.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_space.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_sql.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_stylus.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_svg.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_tcl.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_tex.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_text.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_textile.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_toml.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_twig.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_typescript.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_vala.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_vbscript.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_velocity.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_verilog.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_vhdl.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_xml.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_xquery.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/_test/tokens_yaml.json
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/abap.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/abap_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/abc.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/abc_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/actionscript.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/actionscript_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ada.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ada_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/apache_conf.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/apache_conf_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/applescript.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/applescript_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/asciidoc.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/asciidoc_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/assembly_x86.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/assembly_x86_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/autohotkey.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/autohotkey_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/batchfile.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/batchfile_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/behaviour.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/behaviour/behaviour_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/behaviour/css.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/behaviour/cstyle.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/behaviour/html.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/behaviour/xml.js
@@ -219,8 +360,12 @@ Gem::Specification.new do |s|
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/c9search_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/c_cpp.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/c_cpp_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/cirru.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/cirru_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/clojure.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/clojure_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/cobol.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/cobol_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/coffee.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/coffee/coffee-script.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/coffee/helpers.js
@@ -231,7 +376,6 @@ Gem::Specification.new do |s|
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/coffee/rewriter.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/coffee/scope.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/coffee_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/coffee_highlight_rules_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/coffee_worker.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/coldfusion.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/coldfusion_highlight_rules.js
@@ -241,33 +385,63 @@ Gem::Specification.new do |s|
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/css.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/css/csslint.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/css_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/css_highlight_rules_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/css_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/css_worker.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/css_worker_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/curly.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/curly_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/d.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/d_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/dart.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/dart_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/diff.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/diff_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/django.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/doc_comment_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/dockerfile.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/dockerfile_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/dot.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/dot_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/eiffel.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/eiffel_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ejs.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/elixir.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/elixir_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/elm.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/elm_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/erlang.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/erlang_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/asciidoc.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/c9search.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/coffee.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/coffee_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/csharp.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/cstyle.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/cstyle_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/diff.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/fold_mode.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/html.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/html_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/ini.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/latex.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/lua.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/markdown.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/mixed.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/pythonic.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/pythonic_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/velocity.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/xml.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/folding/xml_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/forth.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/forth_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ftl.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ftl_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/gcode.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/gcode_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/gherkin.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/gherkin_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/gitignore.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/gitignore_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/glsl.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/glsl_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/golang.js
@@ -276,12 +450,26 @@ Gem::Specification.new do |s|
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/groovy_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/haml.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/haml_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/handlebars.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/handlebars_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/haskell.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/haskell_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/haxe.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/haxe_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/html.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/html/saxparser.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/html_completions.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/html_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/html_highlight_rules_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/html_ruby.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/html_ruby_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/html_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/html_worker.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ini.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ini_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/io.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/io_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/jack.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/jack_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/jade.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/jade_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/java.js
@@ -289,54 +477,89 @@ Gem::Specification.new do |s|
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/javascript.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/javascript/jshint.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/javascript_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/javascript_highlight_rules_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/javascript_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/javascript_worker.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/javascript_worker_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/js_regex_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/json.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/json/json_parse.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/json_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/json_worker.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/json_worker_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/jsoniq.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/jsp.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/jsp_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/jsx.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/jsx_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/julia.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/julia_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/latex.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/latex_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/less.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/less_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/liquid.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/liquid_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/liquid_highlight_rules_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lisp.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lisp_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/livescript.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/logiql.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/logiql_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/logiql_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lsl.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lsl_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lua.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lua/luaparse.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lua_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lua_worker.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/luapage.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/luapage_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lucene.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lucene_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/lucene_highlight_rules_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/makefile.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/makefile_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/markdown.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/markdown_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/markdown_highlight_rules_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/mask.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/mask_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/matching_brace_outdent.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/matching_parens_outdent.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/matlab.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/matlab_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/mel.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/mel_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/mushcode.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/mushcode_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/mysql.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/mysql_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/nix.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/nix_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/objectivec.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/objectivec_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ocaml.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ocaml_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/pascal.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/pascal_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/perl.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/perl_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/pgsql.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/pgsql_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/php.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/php/php.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/php_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/php_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/php_worker.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/plain_text.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/plain_text_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/powershell.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/powershell_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/praat.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/praat_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/prolog.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/prolog_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/properties.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/properties_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/protobuf.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/protobuf_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/python.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/python_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/python_test.js
@@ -348,15 +571,30 @@ Gem::Specification.new do |s|
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/rhtml_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ruby.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ruby_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ruby_highlight_rules_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/ruby_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/rust.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/rust_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/sass.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/sass_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/scad.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/scad_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/scala.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/scala_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/scheme.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/scheme_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/scss.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/scss_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/sh.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/sh_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/sjs.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/sjs_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/smarty.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/smarty_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/snippets.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/soy_template.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/soy_template_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/space.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/space_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/sql.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/sql_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/stylus.js
@@ -372,32 +610,50 @@ Gem::Specification.new do |s|
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/text_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/textile.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/textile_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/toml.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/toml_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/twig.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/twig_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/typescript.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/typescript_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/vala.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/vala_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/vbscript.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/vbscript_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/velocity.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/velocity_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/verilog.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/verilog_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/vhdl.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/vhdl_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xml.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xml/dom-parser.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xml/dom.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xml/sax.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xml_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xml_highlight_rules_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xml_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xml_util.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xml_worker.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xquery.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xquery/JSONParseTreeHandler.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xquery/Readme.md
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xquery/XQueryParser.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xquery/visitors/SyntaxHighlighter.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xquery_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xquery/jsoniq_lexer.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xquery/xqlint.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xquery/xquery_lexer.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/xquery_worker.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/yaml.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mode/yaml_highlight_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/model/editor.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mouse/default_gutter_handler.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mouse/default_handlers.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mouse/dragdrop.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mouse/dragdrop_handler.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mouse/fold_handler.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mouse/mouse_event.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mouse/mouse_handler.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mouse/mouse_handler_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/mouse/multi_select_handler.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/multi_select.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/multi_select_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/occur.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/occur_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/placeholder.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/placeholder_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/range.js
@@ -406,12 +662,282 @@ Gem::Specification.new do |s|
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/range_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/renderloop.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/requirejs/text.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/requirejs/text_build.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/scrollbar.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/search.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/search_highlight.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/search_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/selection.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/selection_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/_.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/_all_modes.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/_all_modes.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/abap.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/abap.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/abc.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/abc.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/actionscript.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/actionscript.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ada.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ada.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/all_modes.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/all_modes.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/apache.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/apache_conf.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/apache_conf.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/applescript.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/applescript.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/asciidoc.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/asciidoc.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/assembly_x86.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/assembly_x86.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/autohotkey.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/autohotkey.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/autoit.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/batchfile.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/batchfile.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/c.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/c9search.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/c9search.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/c_cpp.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/c_cpp.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/chef.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/cirru.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/cirru.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/clojure.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/clojure.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/cmake.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/cobol.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/cobol.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/coffee.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/coffee.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/coldfusion.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/coldfusion.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/cs.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/csharp.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/csharp.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/css.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/css.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/curly.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/curly.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/d.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/d.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/dart.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/dart.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/diff.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/diff.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/django.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/django.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/dockerfile.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/dockerfile.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/dot.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/dot.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/dummy.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/dummy_syntax.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/eiffel.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/eiffel.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ejs.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ejs.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/elixir.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/elixir.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/elm.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/elm.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/erlang.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/erlang.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/eruby.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/falcon.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/forth.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/forth.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ftl.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ftl.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/gcode.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/gcode.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/gherkin.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/gherkin.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/gitignore.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/gitignore.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/glsl.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/glsl.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/go.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/golang.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/golang.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/groovy.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/groovy.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/haml.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/haml.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/handlebars.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/handlebars.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/haskell.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/haskell.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/haxe.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/haxe.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/html.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/html.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/html_ruby.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/html_ruby.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/htmldjango.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/htmltornado.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ini.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ini.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/io.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/io.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/jack.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/jack.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/jade.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/jade.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/java.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/java.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/javascript-jquery.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/javascript.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/javascript.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/json.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/json.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/jsoniq.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/jsoniq.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/jsp.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/jsp.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/jsx.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/jsx.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/julia.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/julia.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/latex.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/latex.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ledger.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/less.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/less.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/liquid.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/liquid.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/lisp.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/lisp.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/livescript.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/livescript.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/logiql.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/logiql.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/lsl.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/lsl.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/lua.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/lua.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/luapage.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/luapage.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/lucene.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/lucene.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/makefile.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/makefile.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/mako.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/markdown.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/markdown.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/matlab.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/matlab.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/mel.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/mel.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/mushcode.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/mushcode.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/mushcode_high_rules.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/mushcode_high_rules.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/mysql.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/mysql.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/nix.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/nix.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/objc.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/objectivec.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/objectivec.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ocaml.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ocaml.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/pascal.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/pascal.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/perl.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/perl.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/pgsql.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/pgsql.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/php.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/php.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/plain_text.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/plain_text.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/powershell.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/powershell.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/praat.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/praat.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/prolog.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/prolog.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/properties.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/properties.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/protobuf.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/protobuf.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/python.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/python.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/r.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/r.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/rdoc.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/rdoc.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/rhtml.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/rhtml.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/rst.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ruby.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/ruby.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/rust.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/rust.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/sass.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/sass.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/scad.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/scad.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/scala.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/scala.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/scheme.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/scheme.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/scss.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/scss.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/sh.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/sh.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/sjs.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/sjs.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/smarty.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/smarty.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/snippets.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/snippets.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/soy_template.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/soy_template.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/space.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/space.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/sql.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/sql.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/stylus.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/stylus.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/svg.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/svg.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/tcl.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/tcl.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/tex.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/tex.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/text.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/text.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/textile.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/textile.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/tmsnippet.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/toml.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/toml.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/twig.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/twig.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/typescript.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/typescript.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/vala.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/vala.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/vbscript.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/vbscript.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/velocity.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/velocity.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/verilog.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/verilog.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/vhdl.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/vhdl.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/xml.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/xml.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/xquery.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/xquery.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/xslt.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/yaml.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets/yaml.snippets
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/snippets_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/split.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/test/all.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/test/all_browser.js
@@ -427,6 +953,8 @@ Gem::Specification.new do |s|
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/test/tests.html
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/ambiance.css
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/ambiance.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/chaos.css
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/chaos.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/chrome.css
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/chrome.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/clouds.css
@@ -447,8 +975,12 @@ Gem::Specification.new do |s|
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/github.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/idle_fingers.css
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/idle_fingers.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/katzenmilch.css
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/katzenmilch.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/kr_theme.css
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/kr_theme.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/kuroir.css
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/kuroir.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/merbivore.css
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/merbivore.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/merbivore_soft.css
@@ -463,6 +995,8 @@ Gem::Specification.new do |s|
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/solarized_dark.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/solarized_light.css
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/solarized_light.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/terminal.css
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/terminal.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/textmate.css
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/textmate.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/theme/tomorrow.css
@@ -485,6 +1019,8 @@ Gem::Specification.new do |s|
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/token_iterator_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/tokenizer.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/tokenizer_dev.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/tokenizer_test.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/tooltip.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/undomanager.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/unicode.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/virtual_renderer.js
@@ -492,7 +1028,7 @@ Gem::Specification.new do |s|
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/worker/mirror.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/worker/worker.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/worker/worker_client.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/worker/worker_sourcemint.js
lib/gollum/public/gollum/livepreview/js/ace/lib/ace/worker/worker_test.js
lib/gollum/public/gollum/livepreview/js/jquery.ba-throttle-debounce.min.js
lib/gollum/public/gollum/livepreview/js/livepreview.js
lib/gollum/public/gollum/livepreview/js/md_sundown.js
@@ -523,6 +1059,7 @@ Gem::Specification.new do |s|
lib/gollum/templates/history_authors/none.mustache
lib/gollum/templates/latest_changes.mustache
lib/gollum/templates/layout.mustache
lib/gollum/templates/livepreview.mustache
lib/gollum/templates/page.mustache
lib/gollum/templates/pages.mustache
lib/gollum/templates/search.mustache
@@ -538,6 +1075,7 @@ Gem::Specification.new do |s|
lib/gollum/views/history.rb
lib/gollum/views/latest_changes.rb
lib/gollum/views/layout.rb
lib/gollum/views/livepreview.rb
lib/gollum/views/page.rb
lib/gollum/views/pages.rb
lib/gollum/views/search.rb
+1 -1
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.1.2'
VERSION = '4.1.2'
def self.assets_path
::File.expand_path('gollum/public', ::File.dirname(__FILE__))
+56 -14
View File
@@ -13,8 +13,6 @@ require 'gollum/views/has_page'
require File.expand_path '../helpers', __FILE__
require 'gollum/editing_auth'
#required to upload bigger binary files
Gollum::set_git_timeout(120)
Gollum::set_git_max_filesize(190 * 10**6)
@@ -28,7 +26,7 @@ class String
self # Do not transliterate utf-8 url's unless using Grit
end
end
# _Header => header which causes errors
def to_url
return nil if self.nil?
@@ -51,7 +49,6 @@ module Precious
class App < Sinatra::Base
register Mustache::Sinatra
include Precious::Helpers
use Precious::EditingAuth
dir = File.dirname(File.expand_path(__FILE__))
@@ -62,6 +59,7 @@ module Precious
Browser.new('Internet Explorer', '10.0'),
Browser.new('Chrome', '7.0'),
Browser.new('Firefox', '4.0'),
Browser.new('Safari', '9.0')
]
def supported_useragent?(user_agent)
@@ -96,19 +94,20 @@ module Precious
end
before do
settings.wiki_options[:allow_editing] = settings.wiki_options.fetch(:allow_editing, true)
@allow_editing = settings.wiki_options[:allow_editing]
Precious::App.set(:mustache, {:templates => settings.wiki_options[:template_dir]}) if settings.wiki_options[:template_dir]
@base_url = url('/', false).chomp('/')
@page_dir = settings.wiki_options[:page_file_dir].to_s
# above will detect base_path when it's used with map in a config.ru
settings.wiki_options.merge!({ :base_path => @base_url })
@css = settings.wiki_options[:css]
@js = settings.wiki_options[:js]
@mathjax_config = settings.wiki_options[:mathjax_config]
settings.wiki_options[:allow_editing] = settings.wiki_options.fetch(:allow_editing, true)
@allow_editing = settings.wiki_options[:allow_editing]
end
get '/' do
page_dir = settings.wiki_options[:page_file_dir].to_s
redirect clean_url(::File.join(@base_url, page_dir, wiki_new.index_page))
redirect clean_url(::File.join(@base_url, @page_dir, wiki_new.index_page))
end
# path is set to name if path is nil.
@@ -131,6 +130,14 @@ module Precious
Gollum::Wiki.new(settings.gollum_path, settings.wiki_options)
end
get '/emoji/:name' do
begin
[200, {'Content-Type' => 'image/png'}, emoji(params['name'])]
rescue ArgumentError
not_found
end
end
get '/data/*' do
if page = wiki_page(params[:splat].first).page
page.raw_data
@@ -148,7 +155,7 @@ module Precious
@allow_uploads = wiki.allow_uploads
if page = wikip.page
if wiki.live_preview && page.format.to_s.include?('markdown') && supported_useragent?(request.user_agent)
live_preview_url = '/livepreview/index.html?page=' + encodeURIComponent(@name)
live_preview_url = '/livepreview/?page=' + encodeURIComponent(@name)
if @path
live_preview_url << '&path=' + encodeURIComponent(@path)
end
@@ -165,6 +172,8 @@ module Precious
end
post '/uploadFile' do
forbid unless @allow_editing
wiki = wiki_new
unless wiki.allow_uploads
@@ -177,8 +186,10 @@ module Precious
fullname = params[:file][:filename]
tempfile = params[:file][:tempfile]
end
halt 500 unless tempfile.is_a? Tempfile
dir = wiki.per_page_uploads ? params[:upload_dest] : 'uploads'
# Remove page file dir prefix from upload path if necessary -- committer handles this itself
dir = wiki.per_page_uploads ? params[:upload_dest] : ::File.join([wiki.page_file_dir, 'uploads'].compact)
ext = ::File.extname(fullname)
format = ext.split('.').last || 'txt'
filename = ::File.basename(fullname, ext)
@@ -211,7 +222,22 @@ module Precious
end
end
post '/deleteFile/*' do
forbid unless @allow_editing
wiki = wiki_new
filepath = params[:splat].first
unless filepath.nil?
commit = commit_message
commit[:message] = "Deleted #{filepath}"
wiki.delete_file(filepath, commit)
end
redirect to('/fileview')
end
post '/rename/*' do
forbid unless @allow_editing
wikip = wiki_page(params[:splat].first)
halt 500 if wikip.nil?
wiki = wikip.wiki
@@ -248,6 +274,8 @@ module Precious
end
post '/edit/*' do
forbid unless @allow_editing
path = '/' + clean_url(sanitize_empty_params(params[:path])).to_s
page_name = CGI.unescape(params[:page])
wiki = wiki_new
@@ -307,6 +335,8 @@ module Precious
end
post '/create' do
forbid unless @allow_editing
name = params[:page].to_url
path = sanitize_empty_params(params[:path]) || ''
format = params[:format].intern
@@ -318,7 +348,7 @@ module Precious
wiki.write_page(name, format, params[:content], commit_message, path)
page_dir = settings.wiki_options[:page_file_dir].to_s
redirect to("/#{clean_url(::File.join(page_dir, path, encodeURIComponent(name)))}")
redirect to("/#{clean_url(::File.join(encodeURIComponent(page_dir), encodeURIComponent(path), encodeURIComponent(name)))}")
rescue Gollum::DuplicatePageError => e
@message = "Duplicate page: #{e.message}"
mustache :error
@@ -326,6 +356,8 @@ module Precious
end
post '/revert/*/:sha1/:sha2' do
forbid unless @allow_editing
wikip = wiki_page(params[:splat].first)
@path = wikip.path
@name = wikip.name
@@ -349,6 +381,8 @@ module Precious
end
post '/preview' do
forbid unless @allow_editing
wiki = wiki_new
@name = params[:page] || "Preview"
@page = wiki.preview_page(@name, params[:content], params[:format])
@@ -357,10 +391,17 @@ module Precious
@mathjax = wiki.mathjax
@h1_title = wiki.h1_title
@editable = false
@bar_side = wiki.bar_side
@allow_uploads = wiki.allow_uploads
mustache :page
end
get '/livepreview/' do
wiki = wiki_new
@mathjax = wiki.mathjax
mustache :livepreview, { :layout => false }
end
get '/history/*' do
@page = wiki_page(params[:splat].first).page
@page_num = [params[:page].to_i, 1].max
@@ -378,7 +419,7 @@ module Precious
@versions = @wiki.latest_changes({:max_count => max_count})
mustache :latest_changes
end
post '/compare/*' do
@file = encodeURIComponent(params[:splat].first)
@versions = params[:versions] || []
@@ -423,6 +464,7 @@ module Precious
@name = name
@content = page.formatted_data
@version = version
@bar_side = wikip.wiki.bar_side
mustache :page
elsif file = wikip.wiki.file("#{file_path}", version, true)
show_file(file)
@@ -432,7 +474,7 @@ module Precious
end
get '/search' do
@query = params[:q]
@query = params[:q] || ''
wiki = wiki_new
# Sort wiki search results by count (desc) and then by name (asc)
@results = wiki.search(@query).sort { |a, b| (a[:count] <=> b[:count]).nonzero? || b[:name] <=> a[:name] }.reverse
@@ -488,7 +530,7 @@ module Precious
# Extensions and layout data
@editable = true
@page_exists = !page.versions.empty?
@page_exists = !page.last_version.nil?
@toc_content = wiki.universal_toc ? @page.toc_data : nil
@mathjax = wiki.mathjax
@h1_title = wiki.h1_title
-34
View File
@@ -1,34 +0,0 @@
module Precious
class EditingAuth < Sinatra::Base
def initialize(app)
@app = app
end
def call(env)
@env = env
# Blocks all potentially editable pages. Use EditingAuth::whitelist_pages to unblock pages.
unless (env["REQUEST_METHOD"] == "GET") || @app.settings.wiki_options[:allow_editing]
return block unless excluded_page?
end
@app.call(env)
end
def block
[403, {'Content-Type' => 'text/html', 'Content-Length' => '9'}, ['Forbidden']]
end
def excluded_page?
return false if env["REQUEST_PATH"].nil?
whitelist_pages.any? do |whitelisted_page|
env["REQUEST_PATH"].include? whitelisted_page
end
end
private
# List pages paths as str that you want to whitelist.
# Pages will be compared with env["REQUEST_PATH"] using String::include? method.
def whitelist_pages
return ["/compare/"]
end
end
end
+13
View File
@@ -1,6 +1,11 @@
# ~*~ encoding: utf-8 ~*~
require 'gemojione'
module Precious
module Helpers
EMOJI_PATHNAME = Pathname.new(Gemojione.images_path).freeze
# Extract the path string that Gollum::Wiki expects
def extract_path(file_path)
return nil if file_path.nil?
@@ -51,5 +56,13 @@ module Precious
return mustache :error
end
def emoji(name)
if emoji = Gemojione.index.find_by_name(name)
IO.read(EMOJI_PATHNAME.join("#{emoji['unicode']}.png"))
else
fail ArgumentError, "emoji `#{name}' not found"
end
end
end
end
+16 -1
View File
@@ -85,6 +85,22 @@ ol.tree
margin-right: 7px;
vertical-align: text-top;
}
li.file form
{
vertical-align:middle;
display: inline-block;
}
li.file form button
{
vertical-align:middle;
height: 20px;
padding-left: 36px;
padding-right: 10px;
border: 0px solid #000000;
background: url("../images/fileview/trashcan.png") no-repeat 16px center;
cursor: pointer;
}
}
li.file a[href *= '.pdf'] span.icon { background: url(../images/fileview/document.png) 0 0 no-repeat; }
li.file a[href *= '.html'] span.icon { background: url(../images/fileview/document.png) 0 0 no-repeat; }
li.file a[href $= '.css'] span.icon { background: url(../images/fileview/document.png) 0 0 no-repeat; }
@@ -125,4 +141,3 @@ ol.tree
}
li input:checked + ol > li { display: block; margin: 0 0 0.125em; /* 2px */}
li input:checked + ol > li:last-child { margin: 0 0 0.063em; /* 1px */ }
+6
View File
@@ -849,3 +849,9 @@ ul.actions {
.clearfloats {
clear: both;
}
.emoji {
width: 20px;
height: 20px;
vertical-align: -18%;
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

@@ -121,7 +121,7 @@
formData.append('file', file);
$.ajax({
url: '/uploadFile',
url: baseUrl + '/uploadFile',
data: formData,
cache: false,
contentType: false,
@@ -129,7 +129,7 @@
type: 'POST',
success: function(){
$editorBody.removeClass('uploading');
var text = '[[' + uploadDest + '/' + file.name + ']]';
var text = '[[/' + uploadDest + '/' + file.name + ']]';
var pos = editorBody.selectionStart || 0;
editorBody.value = editorBody.value.substring(0, pos) + text + editorBody.value.substring(pos);
editorBody.selectionStart = pos + text.length;
@@ -211,6 +211,11 @@ var MarkDownHelp = [
{
menuName: 'Escaping',
data: '<p>If you want to use a special Markdown character in your document (such as displaying literal asterisks), you can escape the character with the backslash (<code>\\</code>). Markdown will ignore the character directly after a backslash.'
},
{
menuName: 'Emoji',
data: '<p>See the <a href="http://emojione.com/demo/" target="_blank">EmojiOne demo</a> for all available emoji. To include one, wrap the emoji name in colons and use underscores instead of spaces (e.g. :heart: or :point_up:).'
}
]
}
@@ -46,13 +46,16 @@ var Editor = require("./editor").Editor;
var EditSession = require("./edit_session").EditSession;
var UndoManager = require("./undomanager").UndoManager;
var Renderer = require("./virtual_renderer").VirtualRenderer;
var MultiSelect = require("./multi_select").MultiSelect;
// The following require()s are for inclusion in the built ace file
require("./worker/worker_client");
require("./keyboard/hash_handler");
require("./placeholder");
require("./multi_select");
require("./mode/folding/fold_mode");
require("./theme/textmate");
require("./ext/error_marker");
exports.config = require("./config");
/**
@@ -71,34 +74,53 @@ exports.require = require;
exports.edit = function(el) {
if (typeof(el) == "string") {
var _id = el;
var el = document.getElementById(_id);
el = document.getElementById(_id);
if (!el)
throw "ace.edit can't find div #" + _id;
throw new Error("ace.edit can't find div #" + _id);
}
if (el.env && el.env.editor instanceof Editor)
if (el && el.env && el.env.editor instanceof Editor)
return el.env.editor;
var doc = exports.createEditSession(dom.getInnerText(el));
el.innerHTML = '';
var value = "";
if (el && /input|textarea/i.test(el.tagName)) {
var oldNode = el;
value = oldNode.value;
el = dom.createElement("pre");
oldNode.parentNode.replaceChild(el, oldNode);
} else {
value = dom.getInnerText(el);
el.innerHTML = '';
}
var doc = exports.createEditSession(value);
var editor = new Editor(new Renderer(el));
new MultiSelect(editor);
editor.setSession(doc);
var env = {
document: doc,
editor: editor,
onResize: editor.resize.bind(editor)
onResize: editor.resize.bind(editor, null)
};
if (oldNode) env.textarea = oldNode;
event.addListener(window, "resize", env.onResize);
el.env = editor.env = env;
editor.on("destroy", function() {
event.removeListener(window, "resize", env.onResize);
env.editor.container.env = null; // prevent memory leak on old ie
});
editor.container.env = editor.env = env;
return editor;
};
/**
* Creates a new [[EditSession]], and returns the associated [[Document]].
* @param {Document | String} text {:textParam}
* @param {TextMode} mode {:modeParam}
*
**/
exports.createEditSession = function(text, mode) {
var doc = new EditSession(text, doc);
var doc = new EditSession(text, mode);
doc.setUndoManager(new UndoManager());
return doc;
}
@@ -3,7 +3,7 @@
*
* Copyright (c) 2010, Ajax.org B.V.
* All rights reserved.
*
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
@@ -14,7 +14,7 @@
* * Neither the name of Ajax.org B.V. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -35,8 +35,8 @@ var oop = require("./lib/oop");
var EventEmitter = require("./lib/event_emitter").EventEmitter;
/**
*
* Defines the floating pointer in the document. Whenever text is inserted or deleted before the cursor, the position of the cursor is updated.
*
* Defines a floating pointer in the document. Whenever text is inserted or deleted before the cursor, the position of the anchor is updated.
*
* @class Anchor
**/
@@ -52,112 +52,112 @@ var EventEmitter = require("./lib/event_emitter").EventEmitter;
**/
var Anchor = exports.Anchor = function(doc, row, column) {
this.document = doc;
this.$onChange = this.onChange.bind(this);
this.attach(doc);
if (typeof column == "undefined")
this.setPosition(row.row, row.column);
else
this.setPosition(row, column);
this.$onChange = this.onChange.bind(this);
doc.on("change", this.$onChange);
};
(function() {
oop.implement(this, EventEmitter);
/**
* Returns an object identifying the `row` and `column` position of the current anchor.
* @returns {Object}
**/
this.getPosition = function() {
return this.$clipPositionToDocument(this.row, this.column);
};
/**
*
/**
*
* Returns the current document.
* @returns {Document}
**/
this.getDocument = function() {
return this.document;
};
/**
/**
* experimental: allows anchor to stick to the next on the left
*/
this.$insertRight = false;
/**
* Fires whenever the anchor position changes.
*
* Both of these objects have a `row` and `column` property corresponding to the position.
*
* Events that can trigger this function include [[Anchor.setPosition `setPosition()`]].
*
* @event change
* @param {Object} e An object containing information about the anchor position. It has two properties:
* - `old`: An object describing the old Anchor position
* - `value`: An object describing the new Anchor position
*
* @event change
* @param {Object} e An object containing information about the anchor position. It has two properties:
* - `old`: An object describing the old Anchor position
* - `value`: An object describing the new Anchor position
*
**/
this.onChange = function(e) {
var delta = e.data;
var range = delta.range;
if (range.start.row == range.end.row && range.start.row != this.row)
return;
if (range.start.row > this.row)
return;
if (range.start.row == this.row && range.start.column > this.column)
return;
var row = this.row;
var column = this.column;
var start = range.start;
var end = range.end;
if (delta.action === "insertText") {
if (range.start.row === row && range.start.column <= column) {
if (range.start.row === range.end.row) {
column += range.end.column - range.start.column;
if (start.row === row && start.column <= column) {
if (start.column === column && this.$insertRight) {
// do nothing
} else if (start.row === end.row) {
column += end.column - start.column;
} else {
column -= start.column;
row += end.row - start.row;
}
else {
column -= range.start.column;
row += range.end.row - range.start.row;
}
}
else if (range.start.row !== range.end.row && range.start.row < row) {
row += range.end.row - range.start.row;
} else if (start.row !== end.row && start.row < row) {
row += end.row - start.row;
}
} else if (delta.action === "insertLines") {
if (range.start.row <= row) {
row += range.end.row - range.start.row;
if (start.row === row && column === 0 && this.$insertRight) {
// do nothing
}
}
else if (delta.action == "removeText") {
if (range.start.row == row && range.start.column < column) {
if (range.end.column >= column)
column = range.start.column;
else if (start.row <= row) {
row += end.row - start.row;
}
} else if (delta.action === "removeText") {
if (start.row === row && start.column < column) {
if (end.column >= column)
column = start.column;
else
column = Math.max(0, column - (range.end.column - range.start.column));
} else if (range.start.row !== range.end.row && range.start.row < row) {
if (range.end.row == row) {
column = Math.max(0, column - range.end.column) + range.start.column;
}
row -= (range.end.row - range.start.row);
}
else if (range.end.row == row) {
row -= range.end.row - range.start.row;
column = Math.max(0, column - range.end.column) + range.start.column;
column = Math.max(0, column - (end.column - start.column));
} else if (start.row !== end.row && start.row < row) {
if (end.row === row)
column = Math.max(0, column - end.column) + start.column;
row -= (end.row - start.row);
} else if (end.row === row) {
row -= end.row - start.row;
column = Math.max(0, column - end.column) + start.column;
}
} else if (delta.action == "removeLines") {
if (range.start.row <= row) {
if (range.end.row <= row)
row -= range.end.row - range.start.row;
if (start.row <= row) {
if (end.row <= row)
row -= end.row - start.row;
else {
row = range.start.row;
row = start.row;
column = 0;
}
}
@@ -166,16 +166,13 @@ var Anchor = exports.Anchor = function(doc, row, column) {
this.setPosition(row, column, true);
};
/**
/**
* Sets the anchor position to the specified row and column. If `noClip` is `true`, the position is not clipped.
* @param {Number} row The row index to move the anchor to
* @param {Number} column The column index to move the anchor to
* @param {Boolean} noClip Identifies if you want the position to be clipped
*
*
*
**/
this.setPosition = function(row, column, noClip) {
var pos;
if (noClip) {
@@ -183,47 +180,47 @@ var Anchor = exports.Anchor = function(doc, row, column) {
row: row,
column: column
};
}
else {
} else {
pos = this.$clipPositionToDocument(row, column);
}
if (this.row == pos.row && this.column == pos.column)
return;
var old = {
row: this.row,
column: this.column
};
this.row = pos.row;
this.column = pos.column;
this._emit("change", {
this._signal("change", {
old: old,
value: pos
});
};
/**
* When called, the `'change'` event listener is removed.
* When called, the `"change"` event listener is removed.
*
**/
this.detach = function() {
this.document.removeEventListener("change", this.$onChange);
};
/**
this.attach = function(doc) {
this.document = doc || this.document;
this.document.on("change", this.$onChange);
};
/**
* Clips the anchor position to the specified row and column.
* @param {Number} row The row index to clip the anchor to
* @param {Number} column The column index to clip the anchor to
*
*
*
**/
this.$clipPositionToDocument = function(row, column) {
var pos = {};
if (row >= this.document.getLength()) {
pos.row = Math.max(0, this.document.getLength() - 1);
pos.column = this.document.getLine(pos.row).length;
@@ -236,13 +233,13 @@ var Anchor = exports.Anchor = function(doc, row, column) {
pos.row = row;
pos.column = Math.min(this.document.getLine(pos.row).length, Math.max(0, column));
}
if (column < 0)
pos.column = 0;
return pos;
};
}).call(Anchor.prototype);
});
@@ -57,6 +57,15 @@ module.exports = {
doc.insert({row: 1, column: 1}, "123");
assert.position(anchor.getPosition(), 1, 7);
},
"test insert text at anchor should not move anchor when insertRight is true": function() {
var doc = new Document("juhu\nkinners");
var anchor = new Anchor(doc, 1, 4);
anchor.$insertRight = true;
doc.insert({row: 1, column: 4}, "123");
assert.position(anchor.getPosition(), 1, 4);
},
"test insert lines before cursor should move anchor row": function() {
var doc = new Document("juhu\nkinners");
@@ -65,6 +74,32 @@ module.exports = {
doc.insertLines(1, ["123", "456"]);
assert.position(anchor.getPosition(), 3, 4);
},
"test insert lines at anchor position should move anchor down": function() {
var doc = new Document("juhu\nkinners");
var anchor = new Anchor(doc, 1, 0);
doc.insertLines(1, ["line"]);
assert.position(anchor.getPosition(), 2, 0);
},
"test insert lines at anchor position should not move anchor down when insertRight is true and column is 0": function() {
var doc = new Document("juhu\nkinners");
var anchor = new Anchor(doc, 1, 0);
anchor.$insertRight = true;
doc.insertLines(1, ["line"]);
assert.position(anchor.getPosition(), 1, 0);
},
"test insert lines at anchor row should move anchor down when column > 0": function() {
var doc = new Document("juhu\nkinners");
var anchor = new Anchor(doc, 1, 2);
anchor.$insertRight = true;
doc.insertLines(1, ["line"]);
assert.position(anchor.getPosition(), 2, 2);
},
"test insert new line before cursor should move anchor column": function() {
var doc = new Document("juhu\nkinners");
@@ -167,6 +202,17 @@ module.exports = {
});
doc.remove(new Range(2, 0, 2, 1));
},
"test insert/remove lines at the end of the document": function() {
var doc = new Document("juhu\nkinners\n123");
var anchor = new Anchor(doc, 2, 4);
doc.removeLines(0, 3);
assert.position(anchor.getPosition(), 0, 0);
doc.insertLines(0, ["a", "b", "c"]);
assert.position(anchor.getPosition(), 3, 0);
assert.equal(doc.getValue(), "a\nb\nc\n");
}
};
@@ -0,0 +1,497 @@
/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
* Copyright (c) 2012, Ajax.org B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Ajax.org B.V. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***** END LICENSE BLOCK ***** */
define(function(require, exports, module) {
"use strict";
var HashHandler = require("./keyboard/hash_handler").HashHandler;
var AcePopup = require("./autocomplete/popup").AcePopup;
var util = require("./autocomplete/util");
var event = require("./lib/event");
var lang = require("./lib/lang");
var dom = require("./lib/dom");
var snippetManager = require("./snippets").snippetManager;
var Autocomplete = function() {
this.autoInsert = false;
this.autoSelect = true;
this.exactMatch = false;
this.gatherCompletionsId = 0;
this.keyboardHandler = new HashHandler();
this.keyboardHandler.bindKeys(this.commands);
this.blurListener = this.blurListener.bind(this);
this.changeListener = this.changeListener.bind(this);
this.mousedownListener = this.mousedownListener.bind(this);
this.mousewheelListener = this.mousewheelListener.bind(this);
this.changeTimer = lang.delayedCall(function() {
this.updateCompletions(true);
}.bind(this));
this.tooltipTimer = lang.delayedCall(this.updateDocTooltip.bind(this), 50);
};
(function() {
this.$init = function() {
this.popup = new AcePopup(document.body || document.documentElement);
this.popup.on("click", function(e) {
this.insertMatch();
e.stop();
}.bind(this));
this.popup.focus = this.editor.focus.bind(this.editor);
this.popup.on("show", this.tooltipTimer.bind(null, null));
this.popup.on("select", this.tooltipTimer.bind(null, null));
this.popup.on("changeHoverMarker", this.tooltipTimer.bind(null, null));
return this.popup;
};
this.getPopup = function() {
return this.popup || this.$init();
};
this.openPopup = function(editor, prefix, keepPopupPosition) {
if (!this.popup)
this.$init();
this.popup.setData(this.completions.filtered);
editor.keyBinding.addKeyboardHandler(this.keyboardHandler);
var renderer = editor.renderer;
this.popup.setRow(this.autoSelect ? 0 : -1);
if (!keepPopupPosition) {
this.popup.setTheme(editor.getTheme());
this.popup.setFontSize(editor.getFontSize());
var lineHeight = renderer.layerConfig.lineHeight;
var pos = renderer.$cursorLayer.getPixelPosition(this.base, true);
pos.left -= this.popup.getTextLeftOffset();
var rect = editor.container.getBoundingClientRect();
pos.top += rect.top - renderer.layerConfig.offset;
pos.left += rect.left - editor.renderer.scrollLeft;
pos.left += renderer.$gutterLayer.gutterWidth;
this.popup.show(pos, lineHeight);
} else if (keepPopupPosition && !prefix) {
this.detach();
}
};
this.detach = function() {
this.editor.keyBinding.removeKeyboardHandler(this.keyboardHandler);
this.editor.off("changeSelection", this.changeListener);
this.editor.off("blur", this.blurListener);
this.editor.off("mousedown", this.mousedownListener);
this.editor.off("mousewheel", this.mousewheelListener);
this.changeTimer.cancel();
this.hideDocTooltip();
this.gatherCompletionsId += 1;
if (this.popup && this.popup.isOpen)
this.popup.hide();
if (this.base)
this.base.detach();
this.activated = false;
this.completions = this.base = null;
};
this.changeListener = function(e) {
var cursor = this.editor.selection.lead;
if (cursor.row != this.base.row || cursor.column < this.base.column) {
this.detach();
}
if (this.activated)
this.changeTimer.schedule();
else
this.detach();
};
this.blurListener = function(e) {
// we have to check if activeElement is a child of popup because
// on IE preventDefault doesn't stop scrollbar from being focussed
var el = document.activeElement;
var text = this.editor.textInput.getElement()
if (el != text && el.parentNode != this.popup.container
&& el != this.tooltipNode && e.relatedTarget != this.tooltipNode
&& e.relatedTarget != text
) {
this.detach();
}
};
this.mousedownListener = function(e) {
this.detach();
};
this.mousewheelListener = function(e) {
this.detach();
};
this.goTo = function(where) {
var row = this.popup.getRow();
var max = this.popup.session.getLength() - 1;
switch(where) {
case "up": row = row <= 0 ? max : row - 1; break;
case "down": row = row >= max ? -1 : row + 1; break;
case "start": row = 0; break;
case "end": row = max; break;
}
this.popup.setRow(row);
};
this.insertMatch = function(data) {
if (!data)
data = this.popup.getData(this.popup.getRow());
if (!data)
return false;
if (data.completer && data.completer.insertMatch) {
data.completer.insertMatch(this.editor, data);
} else {
if (this.completions.filterText) {
var ranges = this.editor.selection.getAllRanges();
for (var i = 0, range; range = ranges[i]; i++) {
range.start.column -= this.completions.filterText.length;
this.editor.session.remove(range);
}
}
if (data.snippet)
snippetManager.insertSnippet(this.editor, data.snippet);
else
this.editor.execCommand("insertstring", data.value || data);
}
this.detach();
};
this.commands = {
"Up": function(editor) { editor.completer.goTo("up"); },
"Down": function(editor) { editor.completer.goTo("down"); },
"Ctrl-Up|Ctrl-Home": function(editor) { editor.completer.goTo("start"); },
"Ctrl-Down|Ctrl-End": function(editor) { editor.completer.goTo("end"); },
"Esc": function(editor) { editor.completer.detach(); },
"Space": function(editor) { editor.completer.detach(); editor.insert(" ");},
"Return": function(editor) { return editor.completer.insertMatch(); },
"Shift-Return": function(editor) { editor.completer.insertMatch(true); },
"Tab": function(editor) {
var result = editor.completer.insertMatch();
if (!result && !editor.tabstopManager)
editor.completer.goTo("down");
else
return result;
},
"PageUp": function(editor) { editor.completer.popup.gotoPageUp(); },
"PageDown": function(editor) { editor.completer.popup.gotoPageDown(); }
};
this.gatherCompletions = function(editor, callback) {
var session = editor.getSession();
var pos = editor.getCursorPosition();
var line = session.getLine(pos.row);
var prefix = util.retrievePrecedingIdentifier(line, pos.column);
this.base = session.doc.createAnchor(pos.row, pos.column - prefix.length);
this.base.$insertRight = true;
var matches = [];
var total = editor.completers.length;
editor.completers.forEach(function(completer, i) {
completer.getCompletions(editor, session, pos, prefix, function(err, results) {
if (!err)
matches = matches.concat(results);
// Fetch prefix again, because they may have changed by now
var pos = editor.getCursorPosition();
var line = session.getLine(pos.row);
callback(null, {
prefix: util.retrievePrecedingIdentifier(line, pos.column, results[0] && results[0].identifierRegex),
matches: matches,
finished: (--total === 0)
});
});
});
return true;
};
this.showPopup = function(editor) {
if (this.editor)
this.detach();
this.activated = true;
this.editor = editor;
if (editor.completer != this) {
if (editor.completer)
editor.completer.detach();
editor.completer = this;
}
editor.on("changeSelection", this.changeListener);
editor.on("blur", this.blurListener);
editor.on("mousedown", this.mousedownListener);
editor.on("mousewheel", this.mousewheelListener);
this.updateCompletions();
};
this.updateCompletions = function(keepPopupPosition) {
if (keepPopupPosition && this.base && this.completions) {
var pos = this.editor.getCursorPosition();
var prefix = this.editor.session.getTextRange({start: this.base, end: pos});
if (prefix == this.completions.filterText)
return;
this.completions.setFilter(prefix);
if (!this.completions.filtered.length)
return this.detach();
if (this.completions.filtered.length == 1
&& this.completions.filtered[0].value == prefix
&& !this.completions.filtered[0].snippet)
return this.detach();
this.openPopup(this.editor, prefix, keepPopupPosition);
return;
}
// Save current gatherCompletions session, session is close when a match is insert
var _id = this.gatherCompletionsId;
this.gatherCompletions(this.editor, function(err, results) {
// Only detach if result gathering is finished
var detachIfFinished = function() {
if (!results.finished) return;
return this.detach();
}.bind(this);
var prefix = results.prefix;
var matches = results && results.matches;
if (!matches || !matches.length)
return detachIfFinished();
// Wrong prefix or wrong session -> ignore
if (prefix.indexOf(results.prefix) !== 0 || _id != this.gatherCompletionsId)
return;
this.completions = new FilteredList(matches);
if (this.exactMatch)
this.completions.exactMatch = true;
this.completions.setFilter(prefix);
var filtered = this.completions.filtered;
// No results
if (!filtered.length)
return detachIfFinished();
// One result equals to the prefix
if (filtered.length == 1 && filtered[0].value == prefix && !filtered[0].snippet)
return detachIfFinished();
// Autoinsert if one result
if (this.autoInsert && filtered.length == 1 && results.finished)
return this.insertMatch(filtered[0]);
this.openPopup(this.editor, prefix, keepPopupPosition);
}.bind(this));
};
this.cancelContextMenu = function() {
this.editor.$mouseHandler.cancelContextMenu();
};
this.updateDocTooltip = function() {
var popup = this.popup;
var all = popup.data;
var selected = all && (all[popup.getHoveredRow()] || all[popup.getRow()]);
var doc = null;
if (!selected || !this.editor || !this.popup.isOpen)
return this.hideDocTooltip();
this.editor.completers.some(function(completer) {
if (completer.getDocTooltip)
doc = completer.getDocTooltip(selected);
return doc;
});
if (!doc)
doc = selected;
if (typeof doc == "string")
doc = {docText: doc}
if (!doc || !(doc.docHTML || doc.docText))
return this.hideDocTooltip();
this.showDocTooltip(doc);
};
this.showDocTooltip = function(item) {
if (!this.tooltipNode) {
this.tooltipNode = dom.createElement("div");
this.tooltipNode.className = "ace_tooltip ace_doc-tooltip";
this.tooltipNode.style.margin = 0;
this.tooltipNode.style.pointerEvents = "auto";
this.tooltipNode.tabIndex = -1;
this.tooltipNode.onblur = this.blurListener.bind(this);
}
var tooltipNode = this.tooltipNode;
if (item.docHTML) {
tooltipNode.innerHTML = item.docHTML;
} else if (item.docText) {
tooltipNode.textContent = item.docText;
}
if (!tooltipNode.parentNode)
document.body.appendChild(tooltipNode);
var popup = this.popup;
var rect = popup.container.getBoundingClientRect();
tooltipNode.style.top = popup.container.style.top;
tooltipNode.style.bottom = popup.container.style.bottom;
if (window.innerWidth - rect.right < 320) {
tooltipNode.style.right = window.innerWidth - rect.left + "px";
tooltipNode.style.left = "";
} else {
tooltipNode.style.left = (rect.right + 1) + "px";
tooltipNode.style.right = "";
}
tooltipNode.style.display = "block";
};
this.hideDocTooltip = function() {
this.tooltipTimer.cancel();
if (!this.tooltipNode) return;
var el = this.tooltipNode;
if (!this.editor.isFocused() && document.activeElement == el)
this.editor.focus();
this.tooltipNode = null;
if (el.parentNode)
el.parentNode.removeChild(el);
};
}).call(Autocomplete.prototype);
Autocomplete.startCommand = {
name: "startAutocomplete",
exec: function(editor) {
if (!editor.completer)
editor.completer = new Autocomplete();
editor.completer.autoInsert = false;
editor.completer.autoSelect = true;
editor.completer.showPopup(editor);
// prevent ctrl-space opening context menu on firefox on mac
editor.completer.cancelContextMenu();
},
bindKey: "Ctrl-Space|Ctrl-Shift-Space|Alt-Space"
};
var FilteredList = function(array, filterText, mutateData) {
this.all = array;
this.filtered = array;
this.filterText = filterText || "";
this.exactMatch = false;
};
(function(){
this.setFilter = function(str) {
if (str.length > this.filterText && str.lastIndexOf(this.filterText, 0) === 0)
var matches = this.filtered;
else
var matches = this.all;
this.filterText = str;
matches = this.filterCompletions(matches, this.filterText);
matches = matches.sort(function(a, b) {
return b.exactMatch - a.exactMatch || b.score - a.score;
});
// make unique
var prev = null;
matches = matches.filter(function(item){
var caption = item.snippet || item.caption || item.value;
if (caption === prev) return false;
prev = caption;
return true;
});
this.filtered = matches;
};
this.filterCompletions = function(items, needle) {
var results = [];
var upper = needle.toUpperCase();
var lower = needle.toLowerCase();
loop: for (var i = 0, item; item = items[i]; i++) {
var caption = item.value || item.caption || item.snippet;
if (!caption) continue;
var lastIndex = -1;
var matchMask = 0;
var penalty = 0;
var index, distance;
if (this.exactMatch) {
if (needle !== caption.substr(0, needle.length))
continue loop;
}else{
// caption char iteration is faster in Chrome but slower in Firefox, so lets use indexOf
for (var j = 0; j < needle.length; j++) {
// TODO add penalty on case mismatch
var i1 = caption.indexOf(lower[j], lastIndex + 1);
var i2 = caption.indexOf(upper[j], lastIndex + 1);
index = (i1 >= 0) ? ((i2 < 0 || i1 < i2) ? i1 : i2) : i2;
if (index < 0)
continue loop;
distance = index - lastIndex - 1;
if (distance > 0) {
// first char mismatch should be more sensitive
if (lastIndex === -1)
penalty += 10;
penalty += distance;
}
matchMask = matchMask | (1 << index);
lastIndex = index;
}
}
item.matchMask = matchMask;
item.exactMatch = penalty ? 0 : 1;
item.score = (item.score || 0) - penalty;
results.push(item);
}
return results;
};
}).call(FilteredList.prototype);
exports.Autocomplete = Autocomplete;
exports.FilteredList = FilteredList;
});
@@ -0,0 +1,341 @@
/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
* Copyright (c) 2012, Ajax.org B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Ajax.org B.V. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***** END LICENSE BLOCK ***** */
define(function(require, exports, module) {
"use strict";
var EditSession = require("../edit_session").EditSession;
var Renderer = require("../virtual_renderer").VirtualRenderer;
var Editor = require("../editor").Editor;
var Range = require("../range").Range;
var event = require("../lib/event");
var lang = require("../lib/lang");
var dom = require("../lib/dom");
var $singleLineEditor = function(el) {
var renderer = new Renderer(el);
renderer.$maxLines = 4;
var editor = new Editor(renderer);
editor.setHighlightActiveLine(false);
editor.setShowPrintMargin(false);
editor.renderer.setShowGutter(false);
editor.renderer.setHighlightGutterLine(false);
editor.$mouseHandler.$focusWaitTimout = 0;
editor.$highlightTagPending = true;
return editor;
};
var AcePopup = function(parentNode) {
var el = dom.createElement("div");
var popup = new $singleLineEditor(el);
if (parentNode)
parentNode.appendChild(el);
el.style.display = "none";
popup.renderer.content.style.cursor = "default";
popup.renderer.setStyle("ace_autocomplete");
popup.setOption("displayIndentGuides", false);
popup.setOption("dragDelay", 150);
var noop = function(){};
popup.focus = noop;
popup.$isFocused = true;
popup.renderer.$cursorLayer.restartTimer = noop;
popup.renderer.$cursorLayer.element.style.opacity = 0;
popup.renderer.$maxLines = 8;
popup.renderer.$keepTextAreaAtCursor = false;
popup.setHighlightActiveLine(false);
// set default highlight color
popup.session.highlight("");
popup.session.$searchHighlight.clazz = "ace_highlight-marker";
popup.on("mousedown", function(e) {
var pos = e.getDocumentPosition();
popup.selection.moveToPosition(pos);
selectionMarker.start.row = selectionMarker.end.row = pos.row;
e.stop();
});
var lastMouseEvent;
var hoverMarker = new Range(-1,0,-1,Infinity);
var selectionMarker = new Range(-1,0,-1,Infinity);
selectionMarker.id = popup.session.addMarker(selectionMarker, "ace_active-line", "fullLine");
popup.setSelectOnHover = function(val) {
if (!val) {
hoverMarker.id = popup.session.addMarker(hoverMarker, "ace_line-hover", "fullLine");
} else if (hoverMarker.id) {
popup.session.removeMarker(hoverMarker.id);
hoverMarker.id = null;
}
};
popup.setSelectOnHover(false);
popup.on("mousemove", function(e) {
if (!lastMouseEvent) {
lastMouseEvent = e;
return;
}
if (lastMouseEvent.x == e.x && lastMouseEvent.y == e.y) {
return;
}
lastMouseEvent = e;
lastMouseEvent.scrollTop = popup.renderer.scrollTop;
var row = lastMouseEvent.getDocumentPosition().row;
if (hoverMarker.start.row != row) {
if (!hoverMarker.id)
popup.setRow(row);
setHoverMarker(row);
}
});
popup.renderer.on("beforeRender", function() {
if (lastMouseEvent && hoverMarker.start.row != -1) {
lastMouseEvent.$pos = null;
var row = lastMouseEvent.getDocumentPosition().row;
if (!hoverMarker.id)
popup.setRow(row);
setHoverMarker(row, true);
}
});
popup.renderer.on("afterRender", function() {
var row = popup.getRow();
var t = popup.renderer.$textLayer;
var selected = t.element.childNodes[row - t.config.firstRow];
if (selected == t.selectedNode)
return;
if (t.selectedNode)
dom.removeCssClass(t.selectedNode, "ace_selected");
t.selectedNode = selected;
if (selected)
dom.addCssClass(selected, "ace_selected");
});
var hideHoverMarker = function() { setHoverMarker(-1) };
var setHoverMarker = function(row, suppressRedraw) {
if (row !== hoverMarker.start.row) {
hoverMarker.start.row = hoverMarker.end.row = row;
if (!suppressRedraw)
popup.session._emit("changeBackMarker");
popup._emit("changeHoverMarker");
}
};
popup.getHoveredRow = function() {
return hoverMarker.start.row;
};
event.addListener(popup.container, "mouseout", hideHoverMarker);
popup.on("hide", hideHoverMarker);
popup.on("changeSelection", hideHoverMarker);
popup.session.doc.getLength = function() {
return popup.data.length;
};
popup.session.doc.getLine = function(i) {
var data = popup.data[i];
if (typeof data == "string")
return data;
return (data && data.value) || "";
};
var bgTokenizer = popup.session.bgTokenizer;
bgTokenizer.$tokenizeRow = function(row) {
var data = popup.data[row];
var tokens = [];
if (!data)
return tokens;
if (typeof data == "string")
data = {value: data};
if (!data.caption)
data.caption = data.value || data.name;
var last = -1;
var flag, c;
for (var i = 0; i < data.caption.length; i++) {
c = data.caption[i];
flag = data.matchMask & (1 << i) ? 1 : 0;
if (last !== flag) {
tokens.push({type: data.className || "" + ( flag ? "completion-highlight" : ""), value: c});
last = flag;
} else {
tokens[tokens.length - 1].value += c;
}
}
if (data.meta) {
var maxW = popup.renderer.$size.scrollerWidth / popup.renderer.layerConfig.characterWidth;
if (data.meta.length + data.caption.length < maxW - 2)
tokens.push({type: "rightAlignedText", value: data.meta});
}
return tokens;
};
bgTokenizer.$updateOnChange = noop;
bgTokenizer.start = noop;
popup.session.$computeWidth = function() {
return this.screenWidth = 0;
};
popup.$blockScrolling = Infinity;
// public
popup.isOpen = false;
popup.isTopdown = false;
popup.data = [];
popup.setData = function(list) {
popup.data = list || [];
popup.setValue(lang.stringRepeat("\n", list.length), -1);
popup.setRow(0);
};
popup.getData = function(row) {
return popup.data[row];
};
popup.getRow = function() {
return selectionMarker.start.row;
};
popup.setRow = function(line) {
line = Math.max(-1, Math.min(this.data.length, line));
if (selectionMarker.start.row != line) {
popup.selection.clearSelection();
selectionMarker.start.row = selectionMarker.end.row = line || 0;
popup.session._emit("changeBackMarker");
popup.moveCursorTo(line || 0, 0);
if (popup.isOpen)
popup._signal("select");
}
};
popup.on("changeSelection", function() {
if (popup.isOpen)
popup.setRow(popup.selection.lead.row);
popup.renderer.scrollCursorIntoView();
});
popup.hide = function() {
this.container.style.display = "none";
this._signal("hide");
popup.isOpen = false;
};
popup.show = function(pos, lineHeight, topdownOnly) {
var el = this.container;
var screenHeight = window.innerHeight;
var screenWidth = window.innerWidth;
var renderer = this.renderer;
// var maxLines = Math.min(renderer.$maxLines, this.session.getLength());
var maxH = renderer.$maxLines * lineHeight * 1.4;
var top = pos.top + this.$borderSize;
if (top + maxH > screenHeight - lineHeight && !topdownOnly) {
el.style.top = "";
el.style.bottom = screenHeight - top + "px";
popup.isTopdown = false;
} else {
top += lineHeight;
el.style.top = top + "px";
el.style.bottom = "";
popup.isTopdown = true;
}
el.style.display = "";
this.renderer.$textLayer.checkForSizeChanges();
var left = pos.left;
if (left + el.offsetWidth > screenWidth)
left = screenWidth - el.offsetWidth;
el.style.left = left + "px";
this._signal("show");
lastMouseEvent = null;
popup.isOpen = true;
};
popup.getTextLeftOffset = function() {
return this.$borderSize + this.renderer.$padding + this.$imageSize;
};
popup.$imageSize = 0;
popup.$borderSize = 1;
return popup;
};
dom.importCssString("\
.ace_editor.ace_autocomplete .ace_marker-layer .ace_active-line {\
background-color: #CAD6FA;\
z-index: 1;\
}\
.ace_editor.ace_autocomplete .ace_line-hover {\
border: 1px solid #abbffe;\
margin-top: -1px;\
background: rgba(233,233,253,0.4);\
}\
.ace_editor.ace_autocomplete .ace_line-hover {\
position: absolute;\
z-index: 2;\
}\
.ace_editor.ace_autocomplete .ace_scroller {\
background: none;\
border: none;\
box-shadow: none;\
}\
.ace_rightAlignedText {\
color: gray;\
display: inline-block;\
position: absolute;\
right: 4px;\
text-align: right;\
z-index: -1;\
}\
.ace_editor.ace_autocomplete .ace_completion-highlight{\
color: #000;\
text-shadow: 0 0 0.01em;\
}\
.ace_editor.ace_autocomplete {\
width: 280px;\
z-index: 200000;\
background: #fbfbfb;\
color: #444;\
border: 1px lightgray solid;\
position: fixed;\
box-shadow: 2px 3px 5px rgba(0,0,0,.2);\
line-height: 1.4;\
}");
exports.AcePopup = AcePopup;
});
@@ -0,0 +1,78 @@
/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
* Copyright (c) 2012, Ajax.org B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Ajax.org B.V. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***** END LICENSE BLOCK ***** */
define(function(require, exports, module) {
var Range = require("../range").Range;
var splitRegex = /[^a-zA-Z_0-9\$\-\u00C0-\u1FFF\u2C00-\uD7FF\w]+/;
function getWordIndex(doc, pos) {
var textBefore = doc.getTextRange(Range.fromPoints({row: 0, column:0}, pos));
return textBefore.split(splitRegex).length - 1;
}
/**
* Does a distance analysis of the word `prefix` at position `pos` in `doc`.
* @return Map
*/
function wordDistance(doc, pos) {
var prefixPos = getWordIndex(doc, pos);
var words = doc.getValue().split(splitRegex);
var wordScores = Object.create(null);
var currentWord = words[prefixPos];
words.forEach(function(word, idx) {
if (!word || word === currentWord) return;
var distance = Math.abs(prefixPos - idx);
var score = words.length - distance;
if (wordScores[word]) {
wordScores[word] = Math.max(score, wordScores[word]);
} else {
wordScores[word] = score;
}
});
return wordScores;
}
exports.getCompletions = function(editor, session, pos, prefix, callback) {
var wordScore = wordDistance(session, pos, prefix);
var wordList = Object.keys(wordScore);
callback(null, wordList.map(function(word) {
return {
caption: word,
value: word,
score: wordScore[word],
meta: "local"
};
}));
};
});
@@ -0,0 +1,74 @@
/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
* Copyright (c) 2012, Ajax.org B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Ajax.org B.V. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***** END LICENSE BLOCK ***** */
define(function(require, exports, module) {
"use strict";
exports.parForEach = function(array, fn, callback) {
var completed = 0;
var arLength = array.length;
if (arLength === 0)
callback();
for (var i = 0; i < arLength; i++) {
fn(array[i], function(result, err) {
completed++;
if (completed === arLength)
callback(result, err);
});
}
};
var ID_REGEX = /[a-zA-Z_0-9\$\-\u00A2-\uFFFF]/;
exports.retrievePrecedingIdentifier = function(text, pos, regex) {
regex = regex || ID_REGEX;
var buf = [];
for (var i = pos-1; i >= 0; i--) {
if (regex.test(text[i]))
buf.push(text[i]);
else
break;
}
return buf.reverse().join("");
};
exports.retrieveFollowingIdentifier = function(text, pos, regex) {
regex = regex || ID_REGEX;
var buf = [];
for (var i = pos; i < text.length; i++) {
if (regex.test(text[i]))
buf.push(text[i]);
else
break;
}
return buf;
};
});
@@ -34,12 +34,8 @@ define(function(require, exports, module) {
var oop = require("./lib/oop");
var EventEmitter = require("./lib/event_emitter").EventEmitter;
// tokenizing lines longer than this makes editor very slow
var MAX_LINE_LENGTH = 5000;
/**
*
*
* Tokenizes the current [[Document `Document`]] in the background, and caches the tokenized rows for future use.
*
* If a certain row is changed, everything below that row is re-tokenized.
@@ -52,8 +48,6 @@ var MAX_LINE_LENGTH = 5000;
* @param {Tokenizer} tokenizer The tokenizer to use
* @param {Editor} editor The editor to associate with
*
*
*
* @constructor
**/
@@ -70,29 +64,36 @@ var BackgroundTokenizer = function(tokenizer, editor) {
if (!self.running) { return; }
var workerStart = new Date();
var startLine = self.currentLine;
var currentLine = self.currentLine;
var endLine = -1;
var doc = self.doc;
var processedLines = 0;
while (self.lines[currentLine])
currentLine++;
var startLine = currentLine;
var len = doc.getLength();
while (self.currentLine < len) {
self.$tokenizeRow(self.currentLine);
while (self.lines[self.currentLine])
self.currentLine++;
var processedLines = 0;
self.running = false;
while (currentLine < len) {
self.$tokenizeRow(currentLine);
endLine = currentLine;
do {
currentLine++;
} while (self.lines[currentLine]);
// only check every 5 lines
processedLines ++;
if ((processedLines % 5 == 0) && (new Date() - workerStart) > 20) {
self.fireUpdateEvent(startLine, self.currentLine-1);
if ((processedLines % 5 === 0) && (new Date() - workerStart) > 20) {
self.running = setTimeout(self.$worker, 20);
return;
break;
}
}
self.running = false;
self.fireUpdateEvent(startLine, len - 1);
self.currentLine = currentLine;
if (startLine <= endLine)
self.fireUpdateEvent(startLine, endLine);
};
};
@@ -144,7 +145,7 @@ var BackgroundTokenizer = function(tokenizer, editor) {
first: firstRow,
last: lastRow
};
this._emit("update", {data: data});
this._signal("update", {data: data});
};
/**
@@ -164,6 +165,11 @@ var BackgroundTokenizer = function(tokenizer, editor) {
// pretty long delay to prevent the tokenizer from interfering with the user
this.running = setTimeout(this.$worker, 700);
};
this.scheduleStart = function() {
if (!this.running)
this.running = setTimeout(this.$worker, 700);
}
this.$updateOnChange = function(delta) {
var range = delta.range;
@@ -185,8 +191,6 @@ var BackgroundTokenizer = function(tokenizer, editor) {
this.currentLine = Math.min(startRow, this.currentLine, this.doc.getLength());
this.stop();
// pretty long delay to prevent the tokenizer from interfering with the user
this.running = setTimeout(this.$worker, 700);
};
/**
@@ -226,17 +230,9 @@ var BackgroundTokenizer = function(tokenizer, editor) {
var line = this.doc.getLine(row);
var state = this.states[row - 1];
if (line.length > MAX_LINE_LENGTH) {
var overflow = {value: line.substr(MAX_LINE_LENGTH), type: "text"};
line = line.slice(0, MAX_LINE_LENGTH);
}
var data = this.tokenizer.getLineTokens(line, state);
if (overflow) {
data.tokens.push(overflow);
data.state = "start";
}
var data = this.tokenizer.getLineTokens(line, state, row);
if (this.states[row] !== data.state) {
if (this.states[row] + "" !== data.state + "") {
this.states[row] = data.state;
this.lines[row + 1] = null;
if (this.currentLine > row + 1)
@@ -62,19 +62,19 @@ module.exports = {
doc.setMode("./mode/javascript")
forceTokenize(doc)
testStates(doc, ["comment", "start", "start"])
testStates(doc, ["comment_regex_allowed", "start", "no_regex"])
doc.remove(new Range(0,2,1,2))
testStates(doc, [null, "start"])
testStates(doc, [null, "no_regex"])
forceTokenize(doc)
testStates(doc, ["comment", "comment"])
testStates(doc, ["comment_regex_allowed", "comment_regex_allowed"])
doc.insert({row:0, column:2}, "\n*/")
testStates(doc, [undefined, undefined, "comment"])
testStates(doc, [undefined, undefined, "comment_regex_allowed"])
forceTokenize(doc)
testStates(doc, ["comment", "start", "start"])
testStates(doc, ["comment_regex_allowed", "start", "no_regex"])
}
};
@@ -2,45 +2,44 @@ define(function(require, exports, module) {
"use strict";
var oop = require("../lib/oop");
var HashHandler = require("../keyboard/hash_handler").HashHandler;
var MultiHashHandler = require("../keyboard/hash_handler").MultiHashHandler;
var EventEmitter = require("../lib/event_emitter").EventEmitter;
/**
* @class CommandManager
*
*
**/
/**
* new CommandManager(platform, commands)
* @param {String} platform Identifier for the platform; must be either `'mac'` or `'win'`
* @param {String} platform Identifier for the platform; must be either `"mac"` or `"win"`
* @param {Array} commands A list of commands
*
*
*
*
**/
var CommandManager = function(platform, commands) {
this.platform = platform;
this.commands = this.byName = {};
this.commmandKeyBinding = {};
this.addCommands(commands);
MultiHashHandler.call(this, commands, platform);
this.byName = this.commands;
this.setDefaultHandler("exec", function(e) {
return e.command.exec(e.editor, e.args || {});
});
};
oop.inherits(CommandManager, HashHandler);
oop.inherits(CommandManager, MultiHashHandler);
(function() {
oop.implement(this, EventEmitter);
this.exec = function(command, editor, args) {
if (typeof command === 'string')
if (Array.isArray(command)) {
for (var i = command.length; i--; ) {
if (this.exec(command[i], editor, args)) return true;
}
return false;
}
if (typeof command === "string")
command = this.commands[command];
if (!command)
@@ -49,13 +48,11 @@ oop.inherits(CommandManager, HashHandler);
if (editor && editor.$readOnly && !command.readOnly)
return false;
var retvalue = this._emit("exec", {
editor: editor,
command: command,
args: args
});
var e = {editor: editor, command: command, args: args};
e.returnValue = this._emit("exec", e);
this._signal("afterExec", e);
return retvalue === false ? false : true;
return e.returnValue === false ? false : true;
};
this.toggleRecording = function(editor) {
@@ -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