codemirror-rails 2.24 → 2.32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. data/.gitignore +1 -0
  2. data/Rakefile +19 -0
  3. data/codemirror-rails.gemspec +6 -1
  4. data/lib/codemirror/rails/version.rb +2 -2
  5. data/test/dummy/README.rdoc +261 -0
  6. data/test/dummy/Rakefile +7 -0
  7. data/test/dummy/app/assets/javascripts/application.js +15 -0
  8. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  9. data/test/dummy/app/controllers/application_controller.rb +3 -0
  10. data/test/dummy/app/helpers/application_helper.rb +2 -0
  11. data/test/dummy/app/mailers/.gitkeep +0 -0
  12. data/test/dummy/app/models/.gitkeep +0 -0
  13. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  14. data/test/dummy/config.ru +4 -0
  15. data/test/dummy/config/application.rb +56 -0
  16. data/test/dummy/config/boot.rb +10 -0
  17. data/test/dummy/config/environment.rb +5 -0
  18. data/test/dummy/config/environments/development.rb +37 -0
  19. data/test/dummy/config/environments/production.rb +67 -0
  20. data/test/dummy/config/environments/test.rb +37 -0
  21. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  22. data/test/dummy/config/initializers/inflections.rb +15 -0
  23. data/test/dummy/config/initializers/mime_types.rb +5 -0
  24. data/test/dummy/config/initializers/secret_token.rb +7 -0
  25. data/test/dummy/config/initializers/session_store.rb +8 -0
  26. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  27. data/test/dummy/config/locales/en.yml +5 -0
  28. data/test/dummy/config/routes.rb +58 -0
  29. data/test/dummy/lib/assets/.gitkeep +0 -0
  30. data/test/dummy/log/.gitkeep +0 -0
  31. data/test/dummy/public/404.html +26 -0
  32. data/test/dummy/public/422.html +26 -0
  33. data/test/dummy/public/500.html +25 -0
  34. data/test/dummy/public/favicon.ico +0 -0
  35. data/test/dummy/script/rails +6 -0
  36. data/test/integration/codemirror_rails_integration_test.rb +13 -0
  37. data/test/test_helper.rb +13 -0
  38. data/vendor/assets/javascripts/codemirror.js +381 -197
  39. data/vendor/assets/javascripts/codemirror/keymaps/emacs.js +1 -1
  40. data/vendor/assets/javascripts/codemirror/keymaps/vim.js +649 -94
  41. data/vendor/assets/javascripts/codemirror/modes/clike.js +53 -7
  42. data/vendor/assets/javascripts/codemirror/modes/coffeescript.js +12 -7
  43. data/vendor/assets/javascripts/codemirror/modes/diff.js +24 -5
  44. data/vendor/assets/javascripts/codemirror/modes/ecl.js +2 -2
  45. data/vendor/assets/javascripts/codemirror/modes/erlang.js +463 -0
  46. data/vendor/assets/javascripts/codemirror/modes/gfm.js +38 -2
  47. data/vendor/assets/javascripts/codemirror/modes/go.js +1 -1
  48. data/vendor/assets/javascripts/codemirror/modes/groovy.js +1 -1
  49. data/vendor/assets/javascripts/codemirror/modes/haxe.js +432 -0
  50. data/vendor/assets/javascripts/codemirror/modes/javascript.js +1 -1
  51. data/vendor/assets/javascripts/codemirror/modes/less.js +93 -93
  52. data/vendor/assets/javascripts/codemirror/modes/markdown.js +29 -6
  53. data/vendor/assets/javascripts/codemirror/modes/mysql.js +6 -8
  54. data/vendor/assets/javascripts/codemirror/modes/ocaml.js +114 -0
  55. data/vendor/assets/javascripts/codemirror/modes/pascal.js +1 -1
  56. data/vendor/assets/javascripts/codemirror/modes/pig.js +2 -2
  57. data/vendor/assets/javascripts/codemirror/modes/plsql.js +2 -2
  58. data/vendor/assets/javascripts/codemirror/modes/python.js +13 -16
  59. data/vendor/assets/javascripts/codemirror/modes/ruby.js +3 -8
  60. data/vendor/assets/javascripts/codemirror/modes/scheme.js +74 -46
  61. data/vendor/assets/javascripts/codemirror/modes/shell.js +22 -7
  62. data/vendor/assets/javascripts/codemirror/modes/stex.js +7 -5
  63. data/vendor/assets/javascripts/codemirror/modes/tiddlywiki.js +14 -14
  64. data/vendor/assets/javascripts/codemirror/modes/vb.js +260 -0
  65. data/vendor/assets/javascripts/codemirror/modes/verilog.js +1 -1
  66. data/vendor/assets/javascripts/codemirror/modes/xml.js +2 -1
  67. data/vendor/assets/javascripts/codemirror/modes/xquery.js +3 -3
  68. data/vendor/assets/javascripts/codemirror/utils/closetag.js +24 -34
  69. data/vendor/assets/javascripts/codemirror/utils/dialog.js +5 -1
  70. data/vendor/assets/javascripts/codemirror/utils/foldcode.js +10 -5
  71. data/vendor/assets/javascripts/codemirror/utils/formatting.js +8 -3
  72. data/vendor/assets/javascripts/codemirror/utils/loadmode.js +2 -1
  73. data/vendor/assets/javascripts/codemirror/utils/match-highlighter.js +1 -1
  74. data/vendor/assets/javascripts/codemirror/utils/multiplex.js +81 -0
  75. data/vendor/assets/javascripts/codemirror/utils/overlay.js +2 -1
  76. data/vendor/assets/javascripts/codemirror/utils/pig-hint.js +123 -0
  77. data/vendor/assets/javascripts/codemirror/utils/search.js +16 -12
  78. data/vendor/assets/javascripts/codemirror/utils/searchcursor.js +1 -1
  79. data/vendor/assets/javascripts/codemirror/utils/simple-hint.js +4 -0
  80. data/vendor/assets/javascripts/codemirror/utils/xml-hint.js +137 -0
  81. data/vendor/assets/stylesheets/codemirror.css +59 -4
  82. data/vendor/assets/stylesheets/codemirror/modes/tiddlywiki.css +14 -21
  83. data/vendor/assets/stylesheets/codemirror/themes/ambiance.css +1 -2
  84. data/vendor/assets/stylesheets/codemirror/themes/erlang-dark.css +21 -0
  85. data/vendor/assets/stylesheets/codemirror/themes/lesser-dark.css +2 -3
  86. data/vendor/assets/stylesheets/codemirror/themes/night.css +1 -1
  87. data/vendor/assets/stylesheets/codemirror/themes/vibrant-ink.css +27 -0
  88. data/vendor/assets/stylesheets/codemirror/utils/dialog.css +4 -0
  89. metadata +98 -5
  90. data/vendor/assets/javascripts/codemirror/modes/rpm-spec.css +0 -5
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ gemfile = File.expand_path('../../../../Gemfile', __FILE__)
3
+
4
+ if File.exist?(gemfile)
5
+ ENV['BUNDLE_GEMFILE'] = gemfile
6
+ require 'bundler'
7
+ Bundler.setup
8
+ end
9
+
10
+ $:.unshift File.expand_path('../../../../lib', __FILE__)
@@ -0,0 +1,5 @@
1
+ # Load the rails application
2
+ require File.expand_path('../application', __FILE__)
3
+
4
+ # Initialize the rails application
5
+ Dummy::Application.initialize!
@@ -0,0 +1,37 @@
1
+ Dummy::Application.configure do
2
+ # Settings specified here will take precedence over those in config/application.rb
3
+
4
+ # In the development environment your application's code is reloaded on
5
+ # every request. This slows down response time but is perfect for development
6
+ # since you don't have to restart the web server when you make code changes.
7
+ config.cache_classes = false
8
+
9
+ # Log error messages when you accidentally call methods on nil.
10
+ config.whiny_nils = true
11
+
12
+ # Show full error reports and disable caching
13
+ config.consider_all_requests_local = true
14
+ config.action_controller.perform_caching = false
15
+
16
+ # Don't care if the mailer can't send
17
+ config.action_mailer.raise_delivery_errors = false
18
+
19
+ # Print deprecation notices to the Rails logger
20
+ config.active_support.deprecation = :log
21
+
22
+ # Only use best-standards-support built into browsers
23
+ config.action_dispatch.best_standards_support = :builtin
24
+
25
+ # Raise exception on mass assignment protection for Active Record models
26
+ config.active_record.mass_assignment_sanitizer = :strict
27
+
28
+ # Log the query plan for queries taking more than this (works
29
+ # with SQLite, MySQL, and PostgreSQL)
30
+ config.active_record.auto_explain_threshold_in_seconds = 0.5
31
+
32
+ # Do not compress assets
33
+ config.assets.compress = false
34
+
35
+ # Expands the lines which load the assets
36
+ config.assets.debug = true
37
+ end
@@ -0,0 +1,67 @@
1
+ Dummy::Application.configure do
2
+ # Settings specified here will take precedence over those in config/application.rb
3
+
4
+ # Code is not reloaded between requests
5
+ config.cache_classes = true
6
+
7
+ # Full error reports are disabled and caching is turned on
8
+ config.consider_all_requests_local = false
9
+ config.action_controller.perform_caching = true
10
+
11
+ # Disable Rails's static asset server (Apache or nginx will already do this)
12
+ config.serve_static_assets = false
13
+
14
+ # Compress JavaScripts and CSS
15
+ config.assets.compress = true
16
+
17
+ # Don't fallback to assets pipeline if a precompiled asset is missed
18
+ config.assets.compile = false
19
+
20
+ # Generate digests for assets URLs
21
+ config.assets.digest = true
22
+
23
+ # Defaults to Rails.root.join("public/assets")
24
+ # config.assets.manifest = YOUR_PATH
25
+
26
+ # Specifies the header that your server uses for sending files
27
+ # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for apache
28
+ # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx
29
+
30
+ # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
31
+ # config.force_ssl = true
32
+
33
+ # See everything in the log (default is :info)
34
+ # config.log_level = :debug
35
+
36
+ # Prepend all log lines with the following tags
37
+ # config.log_tags = [ :subdomain, :uuid ]
38
+
39
+ # Use a different logger for distributed setups
40
+ # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new)
41
+
42
+ # Use a different cache store in production
43
+ # config.cache_store = :mem_cache_store
44
+
45
+ # Enable serving of images, stylesheets, and JavaScripts from an asset server
46
+ # config.action_controller.asset_host = "http://assets.example.com"
47
+
48
+ # Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
49
+ # config.assets.precompile += %w( search.js )
50
+
51
+ # Disable delivery errors, bad email addresses will be ignored
52
+ # config.action_mailer.raise_delivery_errors = false
53
+
54
+ # Enable threaded mode
55
+ # config.threadsafe!
56
+
57
+ # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
58
+ # the I18n.default_locale when a translation can not be found)
59
+ config.i18n.fallbacks = true
60
+
61
+ # Send deprecation notices to registered listeners
62
+ config.active_support.deprecation = :notify
63
+
64
+ # Log the query plan for queries taking more than this (works
65
+ # with SQLite, MySQL, and PostgreSQL)
66
+ # config.active_record.auto_explain_threshold_in_seconds = 0.5
67
+ end
@@ -0,0 +1,37 @@
1
+ Dummy::Application.configure do
2
+ # Settings specified here will take precedence over those in config/application.rb
3
+
4
+ # The test environment is used exclusively to run your application's
5
+ # test suite. You never need to work with it otherwise. Remember that
6
+ # your test database is "scratch space" for the test suite and is wiped
7
+ # and recreated between test runs. Don't rely on the data there!
8
+ config.cache_classes = true
9
+
10
+ # Configure static asset server for tests with Cache-Control for performance
11
+ config.serve_static_assets = true
12
+ config.static_cache_control = "public, max-age=3600"
13
+
14
+ # Log error messages when you accidentally call methods on nil
15
+ config.whiny_nils = true
16
+
17
+ # Show full error reports and disable caching
18
+ config.consider_all_requests_local = true
19
+ config.action_controller.perform_caching = false
20
+
21
+ # Raise exceptions instead of rendering exception templates
22
+ config.action_dispatch.show_exceptions = false
23
+
24
+ # Disable request forgery protection in test environment
25
+ config.action_controller.allow_forgery_protection = false
26
+
27
+ # Tell Action Mailer not to deliver emails to the real world.
28
+ # The :test delivery method accumulates sent emails in the
29
+ # ActionMailer::Base.deliveries array.
30
+ config.action_mailer.delivery_method = :test
31
+
32
+ # Raise exception on mass assignment protection for Active Record models
33
+ config.active_record.mass_assignment_sanitizer = :strict
34
+
35
+ # Print deprecation notices to the stderr
36
+ config.active_support.deprecation = :stderr
37
+ end
@@ -0,0 +1,7 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4
+ # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5
+
6
+ # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7
+ # Rails.backtrace_cleaner.remove_silencers!
@@ -0,0 +1,15 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Add new inflection rules using the following format
4
+ # (all these examples are active by default):
5
+ # ActiveSupport::Inflector.inflections do |inflect|
6
+ # inflect.plural /^(ox)$/i, '\1en'
7
+ # inflect.singular /^(ox)en/i, '\1'
8
+ # inflect.irregular 'person', 'people'
9
+ # inflect.uncountable %w( fish sheep )
10
+ # end
11
+ #
12
+ # These inflection rules are supported but not enabled by default:
13
+ # ActiveSupport::Inflector.inflections do |inflect|
14
+ # inflect.acronym 'RESTful'
15
+ # end
@@ -0,0 +1,5 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Add new mime types for use in respond_to blocks:
4
+ # Mime::Type.register "text/richtext", :rtf
5
+ # Mime::Type.register_alias "text/html", :iphone
@@ -0,0 +1,7 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ # Your secret key for verifying the integrity of signed cookies.
4
+ # If you change this key, all old signed cookies will become invalid!
5
+ # Make sure the secret is at least 30 characters and all random,
6
+ # no regular words or you'll be exposed to dictionary attacks.
7
+ Dummy::Application.config.secret_token = '17f0c3cb560a96dbd11a2322311215f4457303a24e9e09a49a3d538129decaa98eadf5a27f4e100940452796238c3e54bd131229ca341ea5379f894f09540fa8'
@@ -0,0 +1,8 @@
1
+ # Be sure to restart your server when you modify this file.
2
+
3
+ Dummy::Application.config.session_store :cookie_store, key: '_dummy_session'
4
+
5
+ # Use the database for sessions instead of the cookie-based default,
6
+ # which shouldn't be used to store highly confidential information
7
+ # (create the session table with "rails generate session_migration")
8
+ # Dummy::Application.config.session_store :active_record_store
@@ -0,0 +1,14 @@
1
+ # Be sure to restart your server when you modify this file.
2
+ #
3
+ # This file contains settings for ActionController::ParamsWrapper which
4
+ # is enabled by default.
5
+
6
+ # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7
+ ActiveSupport.on_load(:action_controller) do
8
+ wrap_parameters format: [:json]
9
+ end
10
+
11
+ # Disable root element in JSON by default.
12
+ ActiveSupport.on_load(:active_record) do
13
+ self.include_root_in_json = false
14
+ end
@@ -0,0 +1,5 @@
1
+ # Sample localization file for English. Add more files in this directory for other locales.
2
+ # See https://github.com/svenfuchs/rails-i18n/tree/master/rails%2Flocale for starting points.
3
+
4
+ en:
5
+ hello: "Hello world"
@@ -0,0 +1,58 @@
1
+ Dummy::Application.routes.draw do
2
+ # The priority is based upon order of creation:
3
+ # first created -> highest priority.
4
+
5
+ # Sample of regular route:
6
+ # match 'products/:id' => 'catalog#view'
7
+ # Keep in mind you can assign values other than :controller and :action
8
+
9
+ # Sample of named route:
10
+ # match 'products/:id/purchase' => 'catalog#purchase', :as => :purchase
11
+ # This route can be invoked with purchase_url(:id => product.id)
12
+
13
+ # Sample resource route (maps HTTP verbs to controller actions automatically):
14
+ # resources :products
15
+
16
+ # Sample resource route with options:
17
+ # resources :products do
18
+ # member do
19
+ # get 'short'
20
+ # post 'toggle'
21
+ # end
22
+ #
23
+ # collection do
24
+ # get 'sold'
25
+ # end
26
+ # end
27
+
28
+ # Sample resource route with sub-resources:
29
+ # resources :products do
30
+ # resources :comments, :sales
31
+ # resource :seller
32
+ # end
33
+
34
+ # Sample resource route with more complex sub-resources
35
+ # resources :products do
36
+ # resources :comments
37
+ # resources :sales do
38
+ # get 'recent', :on => :collection
39
+ # end
40
+ # end
41
+
42
+ # Sample resource route within a namespace:
43
+ # namespace :admin do
44
+ # # Directs /admin/products/* to Admin::ProductsController
45
+ # # (app/controllers/admin/products_controller.rb)
46
+ # resources :products
47
+ # end
48
+
49
+ # You can have the root of your site routed with "root"
50
+ # just remember to delete public/index.html.
51
+ # root :to => 'welcome#index'
52
+
53
+ # See how all your routes lay out with "rake routes"
54
+
55
+ # This is a legacy wild controller route that's not recommended for RESTful applications.
56
+ # Note: This route will make all actions in every controller accessible via GET requests.
57
+ # match ':controller(/:action(/:id))(.:format)'
58
+ end
File without changes
File without changes
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The page you were looking for doesn't exist (404)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/404.html -->
21
+ <div class="dialog">
22
+ <h1>The page you were looking for doesn't exist.</h1>
23
+ <p>You may have mistyped the address or the page may have moved.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,26 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>The change you wanted was rejected (422)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/422.html -->
21
+ <div class="dialog">
22
+ <h1>The change you wanted was rejected.</h1>
23
+ <p>Maybe you tried to change something you didn't have access to.</p>
24
+ </div>
25
+ </body>
26
+ </html>
@@ -0,0 +1,25 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>We're sorry, but something went wrong (500)</title>
5
+ <style type="text/css">
6
+ body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
7
+ div.dialog {
8
+ width: 25em;
9
+ padding: 0 4em;
10
+ margin: 4em auto 0 auto;
11
+ border: 1px solid #ccc;
12
+ border-right-color: #999;
13
+ border-bottom-color: #999;
14
+ }
15
+ h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
16
+ </style>
17
+ </head>
18
+
19
+ <body>
20
+ <!-- This file lives in public/500.html -->
21
+ <div class="dialog">
22
+ <h1>We're sorry, but something went wrong.</h1>
23
+ </div>
24
+ </body>
25
+ </html>
File without changes
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
3
+
4
+ APP_PATH = File.expand_path('../../config/application', __FILE__)
5
+ require File.expand_path('../../config/boot', __FILE__)
6
+ require 'rails/commands'
@@ -0,0 +1,13 @@
1
+ require 'test_helper'
2
+
3
+ describe 'codemiror-rails integration' do
4
+ it 'provides codemirror.js on the asset pipeline' do
5
+ visit '/assets/codemirror.js'
6
+ page.text.must_include 'var CodeMirror'
7
+ end
8
+
9
+ it 'provides codemirror css on the asset pipeline' do
10
+ visit '/assets/codemirror.css'
11
+ page.text.must_include '.CodeMirror'
12
+ end
13
+ end
@@ -0,0 +1,13 @@
1
+ ENV['RAILS_ENV'] = 'test'
2
+
3
+ require File.expand_path('../dummy/config/environment.rb', __FILE__)
4
+ require 'rails/test_help'
5
+ require 'minitest/autorun'
6
+ require 'capybara/rails'
7
+
8
+ Rails.backtrace_cleaner.remove_silencers!
9
+
10
+ class IntegrationTest < MiniTest::Spec
11
+ include Capybara::DSL
12
+ register_spec_type(/integration$/, self)
13
+ end
@@ -1,4 +1,4 @@
1
- // CodeMirror version 2.24
1
+ // CodeMirror version 2.32
2
2
  //
3
3
  // All functions that need access to the editor's state live inside
4
4
  // the CodeMirror function. Below that, at the bottom of the file,
@@ -23,14 +23,19 @@ var CodeMirror = (function() {
23
23
  '<div style="overflow: hidden; position: relative; width: 3px; height: 0px;">' + // Wraps and hides input textarea
24
24
  '<textarea style="position: absolute; padding: 0; width: 1px; height: 1em" wrap="off" ' +
25
25
  'autocorrect="off" autocapitalize="off"></textarea></div>' +
26
+ '<div class="CodeMirror-scrollbar">' + // The vertical scrollbar. Horizontal scrolling is handled by the scroller itself.
27
+ '<div class="CodeMirror-scrollbar-inner">' + // The empty scrollbar content, used solely for managing the scrollbar thumb.
28
+ '</div></div>' + // This must be before the scroll area because it's float-right.
26
29
  '<div class="CodeMirror-scroll" tabindex="-1">' +
27
30
  '<div style="position: relative">' + // Set to the height of the text, causes scrolling
28
31
  '<div style="position: relative">' + // Moved around its parent to cover visible view
29
32
  '<div class="CodeMirror-gutter"><div class="CodeMirror-gutter-text"></div></div>' +
30
33
  // Provides positioning relative to (visible) text origin
31
34
  '<div class="CodeMirror-lines"><div style="position: relative; z-index: 0">' +
32
- '<div style="position: absolute; width: 100%; height: 0; overflow: hidden; visibility: hidden;"></div>' +
35
+ // Used to measure text size
36
+ '<div style="position: absolute; width: 100%; height: 0px; overflow: hidden; visibility: hidden;"></div>' +
33
37
  '<pre class="CodeMirror-cursor">&#160;</pre>' + // Absolutely positioned blinky cursor
38
+ '<pre class="CodeMirror-cursor" style="visibility: hidden">&#160;</pre>' + // Used to force a width
34
39
  '<div style="position: relative; z-index: -1"></div><div></div>' + // DIVs containing the selection and the actual code
35
40
  '</div></div></div></div></div>';
36
41
  if (place.appendChild) place.appendChild(wrapper); else place(wrapper);
@@ -39,12 +44,13 @@ var CodeMirror = (function() {
39
44
  scroller = wrapper.lastChild, code = scroller.firstChild,
40
45
  mover = code.firstChild, gutter = mover.firstChild, gutterText = gutter.firstChild,
41
46
  lineSpace = gutter.nextSibling.firstChild, measure = lineSpace.firstChild,
42
- cursor = measure.nextSibling, selectionDiv = cursor.nextSibling,
43
- lineDiv = selectionDiv.nextSibling;
44
- themeChanged();
47
+ cursor = measure.nextSibling, widthForcer = cursor.nextSibling,
48
+ selectionDiv = widthForcer.nextSibling, lineDiv = selectionDiv.nextSibling,
49
+ scrollbar = inputDiv.nextSibling, scrollbarInner = scrollbar.firstChild;
50
+ themeChanged(); keyMapChanged();
45
51
  // Needed to hide big blue blinking cursor on Mobile Safari
46
52
  if (ios) input.style.width = "0px";
47
- if (!webkit) lineSpace.draggable = true;
53
+ if (!webkit) scroller.draggable = true;
48
54
  lineSpace.style.outline = "none";
49
55
  if (options.tabindex != null) input.tabIndex = options.tabindex;
50
56
  if (options.autofocus) focusInput();
@@ -52,6 +58,17 @@ var CodeMirror = (function() {
52
58
  // Needed to handle Tab key in KHTML
53
59
  if (khtml) inputDiv.style.height = "1px", inputDiv.style.position = "absolute";
54
60
 
61
+ // Check for OS X >= 10.7. If so, we need to force a width on the scrollbar, and
62
+ // make it overlap the content. (But we only do this if the scrollbar doesn't already
63
+ // have a natural width. If the mouse is plugged in or the user sets the system pref
64
+ // to always show scrollbars, the scrollbar shouldn't overlap.)
65
+ if (mac_geLion) {
66
+ scrollbar.className += (overlapScrollbars() ? " cm-sb-overlap" : " cm-sb-nonoverlap");
67
+ } else if (ie_lt8) {
68
+ // Need to set a minimum width to see the scrollbar on IE7 (but must not set it on IE8).
69
+ scrollbar.className += " cm-sb-ie7";
70
+ }
71
+
55
72
  // Check for problem with IE innerHTML not working when we have a
56
73
  // P (or similar) parent node.
57
74
  try { stringWidth("x"); }
@@ -75,7 +92,7 @@ var CodeMirror = (function() {
75
92
  var sel = {from: {line: 0, ch: 0}, to: {line: 0, ch: 0}, inverted: false};
76
93
  // Selection-related flags. shiftSelecting obviously tracks
77
94
  // whether the user is holding shift.
78
- var shiftSelecting, lastClick, lastDoubleClick, lastScrollPos = 0, draggingText,
95
+ var shiftSelecting, lastClick, lastDoubleClick, lastScrollTop = 0, lastScrollLeft = 0, draggingText,
79
96
  overwrite = false, suppressEdits = false;
80
97
  // Variables used by startOperation/endOperation to track what
81
98
  // happened during the operation.
@@ -88,7 +105,7 @@ var CodeMirror = (function() {
88
105
  var bracketHighlighted;
89
106
  // Tracks the maximum line length so that the horizontal scrollbar
90
107
  // can be kept static when scrolling.
91
- var maxLine = "", maxWidth;
108
+ var maxLine = "", updateMaxLine = false, maxLineChanged = true;
92
109
  var tabCache = {};
93
110
 
94
111
  // Initialize the content.
@@ -103,12 +120,11 @@ var CodeMirror = (function() {
103
120
  // which point we can't mess with it anymore. Context menu is
104
121
  // handled in onMouseDown for Gecko.
105
122
  if (!gecko) connect(scroller, "contextmenu", onContextMenu);
106
- connect(scroller, "scroll", function() {
107
- lastScrollPos = scroller.scrollTop;
108
- updateDisplay([]);
109
- if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
110
- if (options.onScroll) options.onScroll(instance);
111
- });
123
+ connect(scroller, "scroll", onScroll);
124
+ connect(scrollbar, "scroll", onScroll);
125
+ connect(scrollbar, "mousedown", function() {if (focused) setTimeout(focusInput, 0);});
126
+ connect(scroller, "mousewheel", onMouseWheel);
127
+ connect(scroller, "DOMMouseScroll", onMouseWheel);
112
128
  connect(window, "resize", function() {updateDisplay(true);});
113
129
  connect(input, "keyup", operation(onKeyUp));
114
130
  connect(input, "input", fastPoll);
@@ -118,7 +134,7 @@ var CodeMirror = (function() {
118
134
  connect(input, "blur", onBlur);
119
135
 
120
136
  if (options.dragDrop) {
121
- connect(lineSpace, "dragstart", onDragStart);
137
+ connect(scroller, "dragstart", onDragStart);
122
138
  function drag_(e) {
123
139
  if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return;
124
140
  e_stop(e);
@@ -166,6 +182,7 @@ var CodeMirror = (function() {
166
182
  else if (option == "theme") themeChanged();
167
183
  else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
168
184
  else if (option == "tabSize") updateDisplay(true);
185
+ else if (option == "keyMap") keyMapChanged();
169
186
  if (option == "lineNumbers" || option == "gutter" || option == "firstLineNumber" || option == "theme") {
170
187
  gutterChanged();
171
188
  updateDisplay(true);
@@ -184,6 +201,15 @@ var CodeMirror = (function() {
184
201
  indentSelection: operation(indentSelected),
185
202
  historySize: function() {return {undo: history.done.length, redo: history.undone.length};},
186
203
  clearHistory: function() {history = new History();},
204
+ setHistory: function(histData) {
205
+ history = new History();
206
+ history.done = histData.done;
207
+ history.undone = histData.undone;
208
+ },
209
+ getHistory: function() {
210
+ history.time = 0;
211
+ return {done: history.done.concat([]), undone: history.undone.concat([])};
212
+ },
187
213
  matchBrackets: operation(function(){matchBrackets(true);}),
188
214
  getTokenAt: operation(function(pos) {
189
215
  pos = clipPos(pos);
@@ -275,7 +301,7 @@ var CodeMirror = (function() {
275
301
  if (isLine(line)) replaceRange("", {line: line, ch: 0}, clipPos({line: line+1, ch: 0}));
276
302
  }),
277
303
  replaceRange: operation(replaceRange),
278
- getRange: function(from, to) {return getRange(clipPos(from), clipPos(to));},
304
+ getRange: function(from, to, lineSep) {return getRange(clipPos(from), clipPos(to), lineSep);},
279
305
 
280
306
  triggerOnKeyDown: operation(onKeyDown),
281
307
  execCommand: function(cmd) {return commands[cmd](instance);},
@@ -313,16 +339,28 @@ var CodeMirror = (function() {
313
339
  },
314
340
  scrollTo: function(x, y) {
315
341
  if (x != null) scroller.scrollLeft = x;
316
- if (y != null) scroller.scrollTop = y;
342
+ if (y != null) scrollbar.scrollTop = y;
317
343
  updateDisplay([]);
318
344
  },
345
+ getScrollInfo: function() {
346
+ return {x: scroller.scrollLeft, y: scrollbar.scrollTop,
347
+ height: scrollbar.scrollHeight, width: scroller.scrollWidth};
348
+ },
349
+ setSize: function(width, height) {
350
+ function interpret(val) {
351
+ val = String(val);
352
+ return /^\d+$/.test(val) ? val + "px" : val;
353
+ }
354
+ if (width != null) wrapper.style.width = interpret(width);
355
+ if (height != null) scroller.style.height = interpret(height);
356
+ },
319
357
 
320
358
  operation: function(f){return operation(f)();},
321
359
  compoundChange: function(f){return compoundChange(f);},
322
360
  refresh: function(){
323
- updateDisplay(true);
324
- if (scroller.scrollHeight > lastScrollPos)
325
- scroller.scrollTop = lastScrollPos;
361
+ updateDisplay(true, null, lastScrollTop);
362
+ if (scrollbar.scrollHeight > lastScrollTop)
363
+ scrollbar.scrollTop = lastScrollTop;
326
364
  },
327
365
  getInputField: function(){return input;},
328
366
  getWrapperElement: function(){return wrapper;},
@@ -343,10 +381,24 @@ var CodeMirror = (function() {
343
381
  splitLines(code), top, top);
344
382
  updateInput = true;
345
383
  }
346
- function getValue() {
384
+ function getValue(lineSep) {
347
385
  var text = [];
348
386
  doc.iter(0, doc.size, function(line) { text.push(line.text); });
349
- return text.join("\n");
387
+ return text.join(lineSep || "\n");
388
+ }
389
+
390
+ function onScroll(e) {
391
+ if (scroller.scrollTop) {
392
+ scrollbar.scrollTop += scroller.scrollTop;
393
+ scroller.scrollTop = 0;
394
+ }
395
+ if (lastScrollTop != scrollbar.scrollTop || lastScrollLeft != scroller.scrollLeft) {
396
+ lastScrollTop = scrollbar.scrollTop;
397
+ lastScrollLeft = scroller.scrollLeft;
398
+ updateDisplay([]);
399
+ if (options.fixedGutter) gutter.style.left = scroller.scrollLeft + "px";
400
+ if (options.onScroll) options.onScroll(instance);
401
+ }
350
402
  }
351
403
 
352
404
  function onMouseDown(e) {
@@ -367,10 +419,12 @@ var CodeMirror = (function() {
367
419
 
368
420
  switch (e_button(e)) {
369
421
  case 3:
370
- if (gecko && !mac) onContextMenu(e);
422
+ if (gecko) onContextMenu(e);
371
423
  return;
372
424
  case 2:
373
425
  if (start) setCursor(start.line, start.ch, true);
426
+ setTimeout(focusInput, 20);
427
+ e_preventDefault(e);
374
428
  return;
375
429
  }
376
430
  // For button 1, if it was clicked inside the editor
@@ -380,24 +434,27 @@ var CodeMirror = (function() {
380
434
 
381
435
  if (!focused) onFocus();
382
436
 
383
- var now = +new Date;
437
+ var now = +new Date, type = "single";
384
438
  if (lastDoubleClick && lastDoubleClick.time > now - 400 && posEq(lastDoubleClick.pos, start)) {
439
+ type = "triple";
385
440
  e_preventDefault(e);
386
441
  setTimeout(focusInput, 20);
387
- return selectLine(start.line);
442
+ selectLine(start.line);
388
443
  } else if (lastClick && lastClick.time > now - 400 && posEq(lastClick.pos, start)) {
444
+ type = "double";
389
445
  lastDoubleClick = {time: now, pos: start};
390
446
  e_preventDefault(e);
391
- return selectWordAt(start);
447
+ var word = findWordAt(start);
448
+ setSelectionUser(word.from, word.to);
392
449
  } else { lastClick = {time: now, pos: start}; }
393
450
 
394
451
  var last = start, going;
395
452
  if (options.dragDrop && dragAndDrop && !options.readOnly && !posEq(sel.from, sel.to) &&
396
- !posLess(start, sel.from) && !posLess(sel.to, start)) {
453
+ !posLess(start, sel.from) && !posLess(sel.to, start) && type == "single") {
397
454
  // Let the drag handler handle this.
398
- if (webkit) lineSpace.draggable = true;
455
+ if (webkit) scroller.draggable = true;
399
456
  function dragEnd(e2) {
400
- if (webkit) lineSpace.draggable = false;
457
+ if (webkit) scroller.draggable = false;
401
458
  draggingText = false;
402
459
  up(); drop();
403
460
  if (Math.abs(e.clientX - e2.clientX) + Math.abs(e.clientY - e2.clientY) < 10) {
@@ -410,18 +467,33 @@ var CodeMirror = (function() {
410
467
  var drop = connect(scroller, "drop", operation(dragEnd), true);
411
468
  draggingText = true;
412
469
  // IE's approach to draggable
413
- if (lineSpace.dragDrop) lineSpace.dragDrop();
470
+ if (scroller.dragDrop) scroller.dragDrop();
414
471
  return;
415
472
  }
416
473
  e_preventDefault(e);
417
- setCursor(start.line, start.ch, true);
474
+ if (type == "single") setCursor(start.line, start.ch, true);
475
+
476
+ var startstart = sel.from, startend = sel.to;
477
+
478
+ function doSelect(cur) {
479
+ if (type == "single") {
480
+ setSelectionUser(start, cur);
481
+ } else if (type == "double") {
482
+ var word = findWordAt(cur);
483
+ if (posLess(cur, startstart)) setSelectionUser(word.from, startend);
484
+ else setSelectionUser(startstart, word.to);
485
+ } else if (type == "triple") {
486
+ if (posLess(cur, startstart)) setSelectionUser(startend, clipPos({line: cur.line, ch: 0}));
487
+ else setSelectionUser(startstart, clipPos({line: cur.line + 1, ch: 0}));
488
+ }
489
+ }
418
490
 
419
491
  function extend(e) {
420
492
  var cur = posFromMouse(e, true);
421
493
  if (cur && !posEq(cur, last)) {
422
494
  if (!focused) onFocus();
423
495
  last = cur;
424
- setSelectionUser(start, cur);
496
+ doSelect(cur);
425
497
  updateInput = false;
426
498
  var visible = visibleLines();
427
499
  if (cur.line >= visible.to || cur.line < visible.from)
@@ -432,7 +504,7 @@ var CodeMirror = (function() {
432
504
  function done(e) {
433
505
  clearTimeout(going);
434
506
  var cur = posFromMouse(e);
435
- if (cur) setSelectionUser(start, cur);
507
+ if (cur) doSelect(cur);
436
508
  e_preventDefault(e);
437
509
  focusInput();
438
510
  updateInput = true;
@@ -449,11 +521,7 @@ var CodeMirror = (function() {
449
521
  function onDoubleClick(e) {
450
522
  for (var n = e_target(e); n != wrapper; n = n.parentNode)
451
523
  if (n.parentNode == gutterText) return e_preventDefault(e);
452
- var start = posFromMouse(e);
453
- if (!start) return;
454
- lastDoubleClick = {time: +new Date, pos: start};
455
524
  e_preventDefault(e);
456
- selectWordAt(start);
457
525
  }
458
526
  function onDrop(e) {
459
527
  if (options.onDragEvent && options.onDragEvent(instance, addStop(e))) return;
@@ -477,8 +545,9 @@ var CodeMirror = (function() {
477
545
  }
478
546
  var n = files.length, text = Array(n), read = 0;
479
547
  for (var i = 0; i < n; ++i) loadFile(files[i], i);
480
- }
481
- else {
548
+ } else {
549
+ // Don't do a replace if the drop happened inside of the selected text.
550
+ if (draggingText && !(posLess(pos, sel.from) || posLess(sel.to, pos))) return;
482
551
  try {
483
552
  var text = e.dataTransfer.getData("Text");
484
553
  if (text) {
@@ -499,7 +568,7 @@ var CodeMirror = (function() {
499
568
  e.dataTransfer.setData("Text", txt);
500
569
 
501
570
  // Use dummy image instead of default browsers image.
502
- if (gecko || chrome) {
571
+ if (gecko || chrome || opera) {
503
572
  var img = document.createElement('img');
504
573
  img.scr = 'data:image/gif;base64,R0lGODdhAgACAIAAAAAAAP///ywAAAAAAgACAAACAoRRADs='; //1x1 image
505
574
  e.dataTransfer.setDragImage(img, 0, 0);
@@ -556,6 +625,7 @@ var CodeMirror = (function() {
556
625
  if (stopped) handled = false;
557
626
  if (handled) {
558
627
  e_preventDefault(e);
628
+ restartBlink();
559
629
  if (ie) { e.oldKeyCode = e.keyCode; e.keyCode = 0; }
560
630
  }
561
631
  return handled;
@@ -563,7 +633,10 @@ var CodeMirror = (function() {
563
633
  function handleCharBinding(e, ch) {
564
634
  var handled = lookupKey("'" + ch + "'", options.extraKeys,
565
635
  options.keyMap, function(b) { return doHandleBinding(b, true); });
566
- if (handled) e_preventDefault(e);
636
+ if (handled) {
637
+ e_preventDefault(e);
638
+ restartBlink();
639
+ }
567
640
  return handled;
568
641
  }
569
642
 
@@ -578,7 +651,7 @@ var CodeMirror = (function() {
578
651
  setShift(code == 16 || e_prop(e, "shiftKey"));
579
652
  // First give onKeyEvent option a chance to handle this.
580
653
  var handled = handleKeyBinding(e);
581
- if (window.opera) {
654
+ if (opera) {
582
655
  lastStoppedKey = handled ? code : null;
583
656
  // Opera has no cut event... we try to at least catch the key combo
584
657
  if (!handled && code == 88 && e_prop(e, mac ? "metaKey" : "ctrlKey"))
@@ -589,8 +662,8 @@ var CodeMirror = (function() {
589
662
  if (pollingFast) readInput();
590
663
  if (options.onKeyEvent && options.onKeyEvent(instance, addStop(e))) return;
591
664
  var keyCode = e_prop(e, "keyCode"), charCode = e_prop(e, "charCode");
592
- if (window.opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
593
- if (((window.opera && !e.which) || khtml) && handleKeyBinding(e)) return;
665
+ if (opera && keyCode == lastStoppedKey) {lastStoppedKey = null; e_preventDefault(e); return;}
666
+ if (((opera && (!e.which || e.which < 10)) || khtml) && handleKeyBinding(e)) return;
594
667
  var ch = String.fromCharCode(charCode == null ? keyCode : charCode);
595
668
  if (options.electricChars && mode.electricChars && options.smartIndent && !options.readOnly) {
596
669
  if (mode.electricChars.indexOf(ch) > -1)
@@ -609,8 +682,8 @@ var CodeMirror = (function() {
609
682
  if (!focused) {
610
683
  if (options.onFocus) options.onFocus(instance);
611
684
  focused = true;
612
- if (wrapper.className.search(/\bCodeMirror-focused\b/) == -1)
613
- wrapper.className += " CodeMirror-focused";
685
+ if (scroller.className.search(/\bCodeMirror-focused\b/) == -1)
686
+ scroller.className += " CodeMirror-focused";
614
687
  if (!leaveInputAlone) resetInput(true);
615
688
  }
616
689
  slowPoll();
@@ -624,12 +697,48 @@ var CodeMirror = (function() {
624
697
  operation(function(){
625
698
  if (bracketHighlighted) { bracketHighlighted(); bracketHighlighted = null; }
626
699
  })();
627
- wrapper.className = wrapper.className.replace(" CodeMirror-focused", "");
700
+ scroller.className = scroller.className.replace(" CodeMirror-focused", "");
628
701
  }
629
702
  clearInterval(blinker);
630
703
  setTimeout(function() {if (!focused) shiftSelecting = null;}, 150);
631
704
  }
632
705
 
706
+ function chopDelta(delta) {
707
+ // Make sure we always scroll a little bit for any nonzero delta.
708
+ if (delta > 0.0 && delta < 1.0) return 1;
709
+ else if (delta > -1.0 && delta < 0.0) return -1;
710
+ else return Math.round(delta);
711
+ }
712
+
713
+ function onMouseWheel(e) {
714
+ var deltaX = 0, deltaY = 0;
715
+ if (e.type == "DOMMouseScroll") { // Firefox
716
+ var delta = -e.detail * 8.0;
717
+ if (e.axis == e.HORIZONTAL_AXIS) deltaX = delta;
718
+ else if (e.axis == e.VERTICAL_AXIS) deltaY = delta;
719
+ } else if (e.wheelDeltaX !== undefined && e.wheelDeltaY !== undefined) { // WebKit
720
+ deltaX = e.wheelDeltaX / 3.0;
721
+ deltaY = e.wheelDeltaY / 3.0;
722
+ } else if (e.wheelDelta !== undefined) { // IE or Opera
723
+ deltaY = e.wheelDelta / 3.0;
724
+ }
725
+
726
+ var scrolled = false;
727
+ deltaX = chopDelta(deltaX);
728
+ deltaY = chopDelta(deltaY);
729
+ if ((deltaX > 0 && scroller.scrollLeft > 0) ||
730
+ (deltaX < 0 && scroller.scrollLeft + scroller.clientWidth < scroller.scrollWidth)) {
731
+ scroller.scrollLeft -= deltaX;
732
+ scrolled = true;
733
+ }
734
+ if ((deltaY > 0 && scrollbar.scrollTop > 0) ||
735
+ (deltaY < 0 && scrollbar.scrollTop + scrollbar.clientHeight < scrollbar.scrollHeight)) {
736
+ scrollbar.scrollTop -= deltaY;
737
+ scrolled = true;
738
+ }
739
+ if (scrolled) e_stop(e);
740
+ }
741
+
633
742
  // Replace the range from from to to by the strings in newText.
634
743
  // Afterwards, set the selection to selFrom, selTo.
635
744
  function updateLines(from, to, newText, selFrom, selTo) {
@@ -650,8 +759,8 @@ var CodeMirror = (function() {
650
759
  var replaced = [], end = change.start + change.added;
651
760
  doc.iter(change.start, end, function(line) { replaced.push(line.text); });
652
761
  out.push({start: change.start, added: change.old.length, old: replaced});
653
- var pos = clipPos({line: change.start + change.old.length - 1,
654
- ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])});
762
+ var pos = {line: change.start + change.old.length - 1,
763
+ ch: editEnd(replaced[replaced.length-1], change.old[change.old.length-1])};
655
764
  updateLinesNoUndo({line: change.start, ch: 0}, {line: end - 1, ch: getLine(end-1).text.length}, change.old, pos, pos);
656
765
  }
657
766
  updateInput = true;
@@ -665,7 +774,7 @@ var CodeMirror = (function() {
665
774
  var recomputeMaxLength = false, maxLineLength = maxLine.length;
666
775
  if (!options.lineWrapping)
667
776
  doc.iter(from.line, to.line + 1, function(line) {
668
- if (line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
777
+ if (!line.hidden && line.text.length == maxLineLength) {recomputeMaxLength = true; return true;}
669
778
  });
670
779
  if (from.line != to.line || newText.length > 1) gutterDirty = true;
671
780
 
@@ -721,20 +830,12 @@ var CodeMirror = (function() {
721
830
  } else {
722
831
  doc.iter(from.line, from.line + newText.length, function(line) {
723
832
  var l = line.text;
724
- if (l.length > maxLineLength) {
725
- maxLine = l; maxLineLength = l.length; maxWidth = null;
833
+ if (!line.hidden && l.length > maxLineLength) {
834
+ maxLine = l; maxLineLength = l.length; maxLineChanged = true;
726
835
  recomputeMaxLength = false;
727
836
  }
728
837
  });
729
- if (recomputeMaxLength) {
730
- maxLineLength = 0; maxLine = ""; maxWidth = null;
731
- doc.iter(0, doc.size, function(line) {
732
- var l = line.text;
733
- if (l.length > maxLineLength) {
734
- maxLineLength = l.length; maxLine = l;
735
- }
736
- });
737
- }
838
+ if (recomputeMaxLength) updateMaxLine = true;
738
839
  }
739
840
 
740
841
  // Add these lines to the work array, so that they will be
@@ -760,11 +861,55 @@ var CodeMirror = (function() {
760
861
 
761
862
  // Update the selection
762
863
  function updateLine(n) {return n <= Math.min(to.line, to.line + lendiff) ? n : n + lendiff;}
763
- setSelection(selFrom, selTo, updateLine(sel.from.line), updateLine(sel.to.line));
864
+ setSelection(clipPos(selFrom), clipPos(selTo),
865
+ updateLine(sel.from.line), updateLine(sel.to.line));
866
+ }
867
+
868
+ function needsScrollbar() {
869
+ var realHeight = doc.height * textHeight() + 2 * paddingTop();
870
+ return realHeight - 1 > scroller.offsetHeight ? realHeight : false;
871
+ }
872
+
873
+ function updateVerticalScroll(scrollTop) {
874
+ var scrollHeight = needsScrollbar();
875
+ scrollbar.style.display = scrollHeight ? "block" : "none";
876
+ if (scrollHeight) {
877
+ scrollbarInner.style.height = scrollHeight + "px";
878
+ scrollbar.style.height = scroller.offsetHeight + "px";
879
+ if (scrollTop != null) scrollbar.scrollTop = scrollTop;
880
+ }
881
+ // Position the mover div to align with the current virtual scroll position
882
+ mover.style.top = (displayOffset * textHeight() - scrollbar.scrollTop) + "px";
883
+ }
884
+
885
+ // On Mac OS X Lion and up, detect whether the mouse is plugged in by measuring
886
+ // the width of a div with a scrollbar in it. If the width is <= 1, then
887
+ // the mouse isn't plugged in and scrollbars should overlap the content.
888
+ function overlapScrollbars() {
889
+ var tmpSb = document.createElement('div'),
890
+ tmpSbInner = document.createElement('div');
891
+ tmpSb.className = "CodeMirror-scrollbar";
892
+ tmpSb.style.cssText = "position: absolute; left: -9999px; height: 100px;";
893
+ tmpSbInner.className = "CodeMirror-scrollbar-inner";
894
+ tmpSbInner.style.height = "200px";
895
+ tmpSb.appendChild(tmpSbInner);
896
+
897
+ document.body.appendChild(tmpSb);
898
+ var result = (tmpSb.offsetWidth <= 1);
899
+ document.body.removeChild(tmpSb);
900
+ return result;
901
+ }
764
902
 
765
- // Make sure the scroll-size div has the correct height.
766
- if (scroller.clientHeight)
767
- code.style.height = (doc.height * textHeight() + 2 * paddingTop()) + "px";
903
+ function computeMaxLength() {
904
+ var maxLineLength = 0;
905
+ maxLine = ""; maxLineChanged = true;
906
+ doc.iter(0, doc.size, function(line) {
907
+ var l = line.text;
908
+ if (!line.hidden && l.length > maxLineLength) {
909
+ maxLineLength = l.length; maxLine = l;
910
+ }
911
+ });
912
+ updateMaxLine = false;
768
913
  }
769
914
 
770
915
  function replaceRange(code, from, to) {
@@ -800,16 +945,16 @@ var CodeMirror = (function() {
800
945
  updateLines(from, to, code, newSel.from, newSel.to);
801
946
  }
802
947
 
803
- function getRange(from, to) {
948
+ function getRange(from, to, lineSep) {
804
949
  var l1 = from.line, l2 = to.line;
805
950
  if (l1 == l2) return getLine(l1).text.slice(from.ch, to.ch);
806
951
  var code = [getLine(l1).text.slice(from.ch)];
807
952
  doc.iter(l1 + 1, l2, function(line) { code.push(line.text); });
808
953
  code.push(getLine(l2).text.slice(0, to.ch));
809
- return code.join("\n");
954
+ return code.join(lineSep || "\n");
810
955
  }
811
- function getSelection() {
812
- return getRange(sel.from, sel.to);
956
+ function getSelection(lineSep) {
957
+ return getRange(sel.from, sel.to, lineSep);
813
958
  }
814
959
 
815
960
  var pollingFast = false; // Ensures slowPoll doesn't cancel fastPoll
@@ -853,7 +998,8 @@ var CodeMirror = (function() {
853
998
  else if (overwrite && posEq(sel.from, sel.to))
854
999
  sel.to = {line: sel.to.line, ch: Math.min(getLine(sel.to.line).text.length, sel.to.ch + (text.length - same))};
855
1000
  replaceSelection(text.slice(same), "end");
856
- prevInput = text;
1001
+ if (text.length > 1000) { input.value = prevInput = ""; }
1002
+ else prevInput = text;
857
1003
  return true;
858
1004
  }
859
1005
  function resetInput(user) {
@@ -869,44 +1015,50 @@ var CodeMirror = (function() {
869
1015
  }
870
1016
 
871
1017
  function scrollEditorIntoView() {
872
- if (!cursor.getBoundingClientRect) return;
873
1018
  var rect = cursor.getBoundingClientRect();
874
1019
  // IE returns bogus coordinates when the instance sits inside of an iframe and the cursor is hidden
875
1020
  if (ie && rect.top == rect.bottom) return;
876
1021
  var winH = window.innerHeight || Math.max(document.body.offsetHeight, document.documentElement.offsetHeight);
877
- if (rect.top < 0 || rect.bottom > winH) cursor.scrollIntoView();
1022
+ if (rect.top < 0 || rect.bottom > winH) scrollCursorIntoView();
878
1023
  }
879
1024
  function scrollCursorIntoView() {
1025
+ var coords = calculateCursorCoords();
1026
+ return scrollIntoView(coords.x, coords.y, coords.x, coords.yBot);
1027
+ }
1028
+ function calculateCursorCoords() {
880
1029
  var cursor = localCoords(sel.inverted ? sel.from : sel.to);
881
1030
  var x = options.lineWrapping ? Math.min(cursor.x, lineSpace.offsetWidth) : cursor.x;
882
- return scrollIntoView(x, cursor.y, x, cursor.yBot);
1031
+ return {x: x, y: cursor.y, yBot: cursor.yBot};
883
1032
  }
884
1033
  function scrollIntoView(x1, y1, x2, y2) {
1034
+ var scrollPos = calculateScrollPos(x1, y1, x2, y2), scrolled = false;
1035
+ if (scrollPos.scrollLeft != null) {scroller.scrollLeft = scrollPos.scrollLeft; scrolled = true;}
1036
+ if (scrollPos.scrollTop != null) {scrollbar.scrollTop = scrollPos.scrollTop; scrolled = true;}
1037
+ if (scrolled && options.onScroll) options.onScroll(instance);
1038
+ }
1039
+ function calculateScrollPos(x1, y1, x2, y2) {
885
1040
  var pl = paddingLeft(), pt = paddingTop();
886
1041
  y1 += pt; y2 += pt; x1 += pl; x2 += pl;
887
- var screen = scroller.clientHeight, screentop = scroller.scrollTop, scrolled = false, result = true;
888
- if (y1 < screentop) {scroller.scrollTop = Math.max(0, y1); scrolled = true;}
889
- else if (y2 > screentop + screen) {scroller.scrollTop = y2 - screen; scrolled = true;}
1042
+ var screen = scroller.clientHeight, screentop = scrollbar.scrollTop, result = {};
1043
+ var docBottom = scroller.scrollHeight;
1044
+ var atTop = y1 < pt + 10, atBottom = y2 + pt > docBottom - 10;;
1045
+ if (y1 < screentop) result.scrollTop = atTop ? 0 : Math.max(0, y1);
1046
+ else if (y2 > screentop + screen) result.scrollTop = (atBottom ? docBottom : y2) - screen;
890
1047
 
891
1048
  var screenw = scroller.clientWidth, screenleft = scroller.scrollLeft;
892
1049
  var gutterw = options.fixedGutter ? gutter.clientWidth : 0;
893
1050
  var atLeft = x1 < gutterw + pl + 10;
894
1051
  if (x1 < screenleft + gutterw || atLeft) {
895
1052
  if (atLeft) x1 = 0;
896
- scroller.scrollLeft = Math.max(0, x1 - 10 - gutterw);
897
- scrolled = true;
898
- }
899
- else if (x2 > screenw + screenleft - 3) {
900
- scroller.scrollLeft = x2 + 10 - screenw;
901
- scrolled = true;
902
- if (x2 > code.clientWidth) result = false;
1053
+ result.scrollLeft = Math.max(0, x1 - 10 - gutterw);
1054
+ } else if (x2 > screenw + screenleft - 3) {
1055
+ result.scrollLeft = x2 + 10 - screenw;
903
1056
  }
904
- if (scrolled && options.onScroll) options.onScroll(instance);
905
1057
  return result;
906
1058
  }
907
1059
 
908
- function visibleLines() {
909
- var lh = textHeight(), top = scroller.scrollTop - paddingTop();
1060
+ function visibleLines(scrollTop) {
1061
+ var lh = textHeight(), top = (scrollTop != null ? scrollTop : scrollbar.scrollTop) - paddingTop();
910
1062
  var fromHeight = Math.max(0, Math.floor(top / lh));
911
1063
  var toHeight = Math.ceil((top + scroller.clientHeight) / lh);
912
1064
  return {from: lineAtHeight(doc, fromHeight),
@@ -915,15 +1067,20 @@ var CodeMirror = (function() {
915
1067
  // Uses a set of changes plus the current scroll position to
916
1068
  // determine which DOM updates have to be made, and makes the
917
1069
  // updates.
918
- function updateDisplay(changes, suppressCallback) {
1070
+ function updateDisplay(changes, suppressCallback, scrollTop) {
919
1071
  if (!scroller.clientWidth) {
920
1072
  showingFrom = showingTo = displayOffset = 0;
921
1073
  return;
922
1074
  }
923
1075
  // Compute the new visible window
924
- var visible = visibleLines();
1076
+ // If scrollTop is specified, use that to determine which lines
1077
+ // to render instead of the current scrollbar position.
1078
+ var visible = visibleLines(scrollTop);
925
1079
  // Bail out if the visible area is already rendered and nothing changed.
926
- if (changes !== true && changes.length == 0 && visible.from > showingFrom && visible.to < showingTo) return;
1080
+ if (changes !== true && changes.length == 0 && visible.from > showingFrom && visible.to < showingTo) {
1081
+ updateVerticalScroll(scrollTop);
1082
+ return;
1083
+ }
927
1084
  var from = Math.max(visible.from - 100, 0), to = Math.min(doc.size, visible.to + 100);
928
1085
  if (showingFrom < from && from - showingFrom < 20) from = showingFrom;
929
1086
  if (showingTo > to && showingTo - to < 20) to = Math.min(doc.size, showingTo);
@@ -941,7 +1098,10 @@ var CodeMirror = (function() {
941
1098
  if (range.from >= range.to) intact.splice(i--, 1);
942
1099
  else intactLines += range.to - range.from;
943
1100
  }
944
- if (intactLines == to - from && from == showingFrom && to == showingTo) return;
1101
+ if (intactLines == to - from && from == showingFrom && to == showingTo) {
1102
+ updateVerticalScroll(scrollTop);
1103
+ return;
1104
+ }
945
1105
  intact.sort(function(a, b) {return a.domStart - b.domStart;});
946
1106
 
947
1107
  var th = textHeight(), gutterDisplay = gutter.style.display;
@@ -949,17 +1109,12 @@ var CodeMirror = (function() {
949
1109
  patchDisplay(from, to, intact);
950
1110
  lineDiv.style.display = gutter.style.display = "";
951
1111
 
952
- // Position the mover div to align with the lines it's supposed
953
- // to be showing (which will cover the visible display)
954
1112
  var different = from != showingFrom || to != showingTo || lastSizeC != scroller.clientHeight + th;
955
1113
  // This is just a bogus formula that detects when the editor is
956
1114
  // resized or the font size changes.
957
1115
  if (different) lastSizeC = scroller.clientHeight + th;
958
1116
  showingFrom = from; showingTo = to;
959
1117
  displayOffset = heightAtLine(doc, from);
960
- mover.style.top = (displayOffset * th) + "px";
961
- if (scroller.clientHeight)
962
- code.style.height = (doc.height * th + 2 * paddingTop()) + "px";
963
1118
 
964
1119
  // Since this is all rather error prone, it is honoured with the
965
1120
  // only assertion in the whole file.
@@ -968,7 +1123,6 @@ var CodeMirror = (function() {
968
1123
  " nodes=" + lineDiv.childNodes.length);
969
1124
 
970
1125
  function checkHeights() {
971
- maxWidth = scroller.clientWidth;
972
1126
  var curNode = lineDiv.firstChild, heightChanged = false;
973
1127
  doc.iter(showingFrom, showingTo, function(line) {
974
1128
  if (!line.hidden) {
@@ -980,22 +1134,17 @@ var CodeMirror = (function() {
980
1134
  }
981
1135
  curNode = curNode.nextSibling;
982
1136
  });
983
- if (heightChanged)
984
- code.style.height = (doc.height * th + 2 * paddingTop()) + "px";
985
1137
  return heightChanged;
986
1138
  }
987
1139
 
988
1140
  if (options.lineWrapping) {
989
1141
  checkHeights();
990
- } else {
991
- if (maxWidth == null) maxWidth = stringWidth(maxLine);
992
- if (maxWidth > scroller.clientWidth) {
993
- lineSpace.style.width = maxWidth + "px";
994
- // Needed to prevent odd wrapping/hiding of widgets placed in here.
995
- code.style.width = "";
996
- code.style.width = scroller.scrollWidth + "px";
997
- } else {
998
- lineSpace.style.width = code.style.width = "";
1142
+ var scrollHeight = needsScrollbar();
1143
+ var shouldHaveScrollbar = scrollHeight ? "block" : "none";
1144
+ if (scrollbar.style.display != shouldHaveScrollbar) {
1145
+ scrollbar.style.display = shouldHaveScrollbar;
1146
+ if (scrollHeight) scrollbarInner.style.height = scrollHeight + "px";
1147
+ checkHeights();
999
1148
  }
1000
1149
  }
1001
1150
 
@@ -1004,6 +1153,7 @@ var CodeMirror = (function() {
1004
1153
  // If the gutter grew in size, re-check heights. If those changed, re-draw gutter.
1005
1154
  updateGutter() && options.lineWrapping && checkHeights() && updateGutter();
1006
1155
  }
1156
+ updateVerticalScroll(scrollTop);
1007
1157
  updateSelection();
1008
1158
  if (!suppressCallback && options.onUpdate) options.onUpdate(instance);
1009
1159
  return true;
@@ -1083,7 +1233,7 @@ var CodeMirror = (function() {
1083
1233
  html.push("<pre></pre>");
1084
1234
  } else {
1085
1235
  var marker = line.gutterMarker;
1086
- var text = options.lineNumbers ? i + options.firstLineNumber : null;
1236
+ var text = options.lineNumbers ? options.lineNumberFormatter(i + options.firstLineNumber) : null;
1087
1237
  if (marker && marker.text)
1088
1238
  text = marker.text.replace("%N%", text != null ? text : "");
1089
1239
  else if (text == null)
@@ -1098,9 +1248,9 @@ var CodeMirror = (function() {
1098
1248
  gutter.style.display = "none";
1099
1249
  gutterText.innerHTML = html.join("");
1100
1250
  // Make sure scrolling doesn't cause number gutter size to pop
1101
- if (normalNode != null) {
1251
+ if (normalNode != null && options.lineNumbers) {
1102
1252
  var node = gutterText.childNodes[normalNode - showingFrom];
1103
- var minwidth = String(doc.size).length, val = eltText(node), pad = "";
1253
+ var minwidth = String(doc.size).length, val = eltText(node.firstChild), pad = "";
1104
1254
  while (val.length + pad.length < minwidth) pad += "\u00a0";
1105
1255
  if (pad) node.insertBefore(document.createTextNode(pad), node.firstChild);
1106
1256
  }
@@ -1282,17 +1432,19 @@ var CodeMirror = (function() {
1282
1432
  if (unit == "page") dist = Math.min(scroller.clientHeight, window.innerHeight || document.documentElement.clientHeight);
1283
1433
  else if (unit == "line") dist = textHeight();
1284
1434
  var target = coordsChar(pos.x, pos.y + dist * dir + 2);
1285
- if (unit == "page") scroller.scrollTop += localCoords(target, true).y - pos.y;
1435
+ if (unit == "page") scrollbar.scrollTop += localCoords(target, true).y - pos.y;
1286
1436
  setCursor(target.line, target.ch, true);
1287
1437
  goalColumn = pos.x;
1288
1438
  }
1289
1439
 
1290
- function selectWordAt(pos) {
1440
+ function findWordAt(pos) {
1291
1441
  var line = getLine(pos.line).text;
1292
1442
  var start = pos.ch, end = pos.ch;
1293
- while (start > 0 && isWordChar(line.charAt(start - 1))) --start;
1294
- while (end < line.length && isWordChar(line.charAt(end))) ++end;
1295
- setSelectionUser({line: pos.line, ch: start}, {line: pos.line, ch: end});
1443
+ var check = isWordChar(line.charAt(start < line.length ? start : start - 1)) ?
1444
+ isWordChar : function(ch) {return !isWordChar(ch);};
1445
+ while (start > 0 && check(line.charAt(start - 1))) --start;
1446
+ while (end < line.length && check(line.charAt(end))) ++end;
1447
+ return {from: {line: pos.line, ch: start}, to: {line: pos.line, ch: end}};
1296
1448
  }
1297
1449
  function selectLine(line) {
1298
1450
  setSelectionUser({line: line, ch: 0}, clipPos({line: line + 1, ch: 0}));
@@ -1312,26 +1464,23 @@ var CodeMirror = (function() {
1312
1464
 
1313
1465
  var line = getLine(n), curSpace = line.indentation(options.tabSize),
1314
1466
  curSpaceString = line.text.match(/^\s*/)[0], indentation;
1467
+ if (how == "smart") {
1468
+ indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text);
1469
+ if (indentation == Pass) how = "prev";
1470
+ }
1315
1471
  if (how == "prev") {
1316
1472
  if (n) indentation = getLine(n-1).indentation(options.tabSize);
1317
1473
  else indentation = 0;
1318
1474
  }
1319
- else if (how == "smart") indentation = mode.indent(state, line.text.slice(curSpaceString.length), line.text);
1320
1475
  else if (how == "add") indentation = curSpace + options.indentUnit;
1321
1476
  else if (how == "subtract") indentation = curSpace - options.indentUnit;
1322
1477
  indentation = Math.max(0, indentation);
1323
1478
  var diff = indentation - curSpace;
1324
1479
 
1325
- if (!diff) {
1326
- if (sel.from.line != n && sel.to.line != n) return;
1327
- var indentString = curSpaceString;
1328
- }
1329
- else {
1330
- var indentString = "", pos = 0;
1331
- if (options.indentWithTabs)
1332
- for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";}
1333
- while (pos < indentation) {++pos; indentString += " ";}
1334
- }
1480
+ var indentString = "", pos = 0;
1481
+ if (options.indentWithTabs)
1482
+ for (var i = Math.floor(indentation / options.tabSize); i; --i) {pos += options.tabSize; indentString += "\t";}
1483
+ while (pos < indentation) {++pos; indentString += " ";}
1335
1484
 
1336
1485
  replaceRange(indentString, {line: n, ch: 0}, {line: n, ch: curSpaceString.length});
1337
1486
  }
@@ -1358,9 +1507,10 @@ var CodeMirror = (function() {
1358
1507
  if (guess != 1) updateLineHeight(line, guess);
1359
1508
  });
1360
1509
  lineSpace.style.width = code.style.width = "";
1510
+ widthForcer.style.left = "";
1361
1511
  } else {
1362
1512
  wrapper.className = wrapper.className.replace(" CodeMirror-wrap", "");
1363
- maxWidth = null; maxLine = "";
1513
+ maxLine = ""; maxLineChanged = true;
1364
1514
  doc.iter(0, doc.size, function(line) {
1365
1515
  if (line.height != 1 && !line.hidden) updateLineHeight(line, 1);
1366
1516
  if (line.text.length > maxLine.length) maxLine = line.text;
@@ -1378,6 +1528,11 @@ var CodeMirror = (function() {
1378
1528
  scroller.className = scroller.className.replace(/\s*cm-s-\S+/g, "") +
1379
1529
  options.theme.replace(/(^|\s)\s*/g, " cm-s-");
1380
1530
  }
1531
+ function keyMapChanged() {
1532
+ var style = keyMap[options.keyMap].style;
1533
+ wrapper.className = wrapper.className.replace(/\s*cm-keymap-\S+/g, "") +
1534
+ (style ? " cm-keymap-" + style : "");
1535
+ }
1381
1536
 
1382
1537
  function TextMarker() { this.set = []; }
1383
1538
  TextMarker.prototype.clear = operation(function() {
@@ -1485,6 +1640,14 @@ var CodeMirror = (function() {
1485
1640
  return changeLine(handle, function(line, no) {
1486
1641
  if (line.hidden != hidden) {
1487
1642
  line.hidden = hidden;
1643
+ if (!options.lineWrapping) {
1644
+ var l = line.text;
1645
+ if (hidden && l.length == maxLine.length) {
1646
+ updateMaxLine = true;
1647
+ } else if (!hidden && l.length > maxLine.length) {
1648
+ maxLine = l; updateMaxLine = false;
1649
+ }
1650
+ }
1488
1651
  updateLineHeight(line, hidden ? 0 : 1);
1489
1652
  var fline = sel.from.line, tline = sel.to.line;
1490
1653
  if (hidden && (fline == no || tline == no)) {
@@ -1505,8 +1668,7 @@ var CodeMirror = (function() {
1505
1668
  var n = line;
1506
1669
  line = getLine(line);
1507
1670
  if (!line) return null;
1508
- }
1509
- else {
1671
+ } else {
1510
1672
  var n = lineNo(line);
1511
1673
  if (n == null) return null;
1512
1674
  }
@@ -1655,8 +1817,8 @@ var CodeMirror = (function() {
1655
1817
  return coordsChar(x - offL.left, y - offL.top);
1656
1818
  }
1657
1819
  function onContextMenu(e) {
1658
- var pos = posFromMouse(e), scrollPos = scroller.scrollTop;
1659
- if (!pos || window.opera) return; // Opera is difficult.
1820
+ var pos = posFromMouse(e), scrollPos = scrollbar.scrollTop;
1821
+ if (!pos || opera) return; // Opera is difficult.
1660
1822
  if (posEq(sel.from, sel.to) || posLess(pos, sel.from) || !posLess(pos, sel.to))
1661
1823
  operation(setCursor)(pos.line, pos.ch);
1662
1824
 
@@ -1671,10 +1833,10 @@ var CodeMirror = (function() {
1671
1833
  selectInput(input);
1672
1834
  function rehide() {
1673
1835
  var newVal = splitLines(input.value).join("\n");
1674
- if (newVal != val) operation(replaceSelection)(newVal, "end");
1836
+ if (newVal != val && !options.readOnly) operation(replaceSelection)(newVal, "end");
1675
1837
  inputDiv.style.position = "relative";
1676
1838
  input.style.cssText = oldCSS;
1677
- if (ie_lt9) scroller.scrollTop = scrollPos;
1839
+ if (ie_lt9) scrollbar.scrollTop = scrollPos;
1678
1840
  leaveInputAlone = false;
1679
1841
  resetInput(true);
1680
1842
  slowPoll();
@@ -1716,7 +1878,7 @@ var CodeMirror = (function() {
1716
1878
  var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur;
1717
1879
  for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) {
1718
1880
  var text = st[i];
1719
- if (st[i+1] != null && st[i+1] != style) {pos += d * text.length; continue;}
1881
+ if (st[i+1] != style) {pos += d * text.length; continue;}
1720
1882
  for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) {
1721
1883
  if (pos >= from && pos < to && re.test(cur = text.charAt(j))) {
1722
1884
  var match = matching[cur];
@@ -1836,14 +1998,24 @@ var CodeMirror = (function() {
1836
1998
  changes = []; selectionChanged = false; callbacks = [];
1837
1999
  }
1838
2000
  function endOperation() {
1839
- var reScroll = false, updated;
1840
- if (selectionChanged) reScroll = !scrollCursorIntoView();
1841
- if (changes.length) updated = updateDisplay(changes, true);
2001
+ if (updateMaxLine) computeMaxLength();
2002
+ if (maxLineChanged && !options.lineWrapping) {
2003
+ var cursorWidth = widthForcer.offsetWidth, left = stringWidth(maxLine);
2004
+ widthForcer.style.left = left + "px";
2005
+ lineSpace.style.minWidth = (left + cursorWidth) + "px";
2006
+ maxLineChanged = false;
2007
+ }
2008
+ var newScrollPos, updated;
2009
+ if (selectionChanged) {
2010
+ var coords = calculateCursorCoords();
2011
+ newScrollPos = calculateScrollPos(coords.x, coords.y, coords.x, coords.yBot);
2012
+ }
2013
+ if (changes.length) updated = updateDisplay(changes, true, (newScrollPos ? newScrollPos.scrollTop : null));
1842
2014
  else {
1843
2015
  if (selectionChanged) updateSelection();
1844
2016
  if (gutterDirty) updateGutter();
1845
2017
  }
1846
- if (reScroll) scrollCursorIntoView();
2018
+ if (newScrollPos) scrollCursorIntoView();
1847
2019
  if (selectionChanged) {scrollEditorIntoView(); restartBlink();}
1848
2020
 
1849
2021
  if (focused && !leaveInputAlone &&
@@ -1855,11 +2027,11 @@ var CodeMirror = (function() {
1855
2027
  if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;}
1856
2028
  if (posEq(sel.from, sel.to)) matchBrackets(false);
1857
2029
  }), 20);
1858
- var tc = textChanged, cbs = callbacks; // these can be reset by callbacks
1859
- if (selectionChanged && options.onCursorActivity)
2030
+ var sc = selectionChanged, cbs = callbacks; // these can be reset by callbacks
2031
+ if (textChanged && options.onChange && instance)
2032
+ options.onChange(instance, textChanged);
2033
+ if (sc && options.onCursorActivity)
1860
2034
  options.onCursorActivity(instance);
1861
- if (tc && options.onChange && instance)
1862
- options.onChange(instance, tc);
1863
2035
  for (var i = 0; i < cbs.length; ++i) cbs[i](instance);
1864
2036
  if (updated && options.onUpdate) options.onUpdate(instance);
1865
2037
  }
@@ -1919,7 +2091,8 @@ var CodeMirror = (function() {
1919
2091
  pollInterval: 100,
1920
2092
  undoDepth: 40,
1921
2093
  tabindex: null,
1922
- autofocus: null
2094
+ autofocus: null,
2095
+ lineNumberFormatter: function(integer) { return integer; }
1923
2096
  };
1924
2097
 
1925
2098
  var ios = /AppleWebKit/.test(navigator.userAgent) && /Mobile\/\w+/.test(navigator.userAgent);
@@ -2008,6 +2181,10 @@ var CodeMirror = (function() {
2008
2181
  indentMore: function(cm) {cm.indentSelection("add");},
2009
2182
  indentLess: function(cm) {cm.indentSelection("subtract");},
2010
2183
  insertTab: function(cm) {cm.replaceSelection("\t", "end");},
2184
+ defaultTab: function(cm) {
2185
+ if (cm.somethingSelected()) cm.indentSelection("add");
2186
+ else cm.replaceSelection("\t", "end");
2187
+ },
2011
2188
  transposeChars: function(cm) {
2012
2189
  var cur = cm.getCursor(), line = cm.getLine(cur.line);
2013
2190
  if (cur.ch > 0 && cur.ch < line.length - 1)
@@ -2025,7 +2202,7 @@ var CodeMirror = (function() {
2025
2202
  keyMap.basic = {
2026
2203
  "Left": "goCharLeft", "Right": "goCharRight", "Up": "goLineUp", "Down": "goLineDown",
2027
2204
  "End": "goLineEnd", "Home": "goLineStartSmart", "PageUp": "goPageUp", "PageDown": "goPageDown",
2028
- "Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "insertTab", "Shift-Tab": "indentAuto",
2205
+ "Delete": "delCharRight", "Backspace": "delCharLeft", "Tab": "defaultTab", "Shift-Tab": "indentAuto",
2029
2206
  "Enter": "newlineAndIndent", "Insert": "toggleOverwrite"
2030
2207
  };
2031
2208
  // Note that the save and find-related commands aren't defined by
@@ -2193,8 +2370,7 @@ var CodeMirror = (function() {
2193
2370
  if (consume !== false) this.pos += pattern.length;
2194
2371
  return true;
2195
2372
  }
2196
- }
2197
- else {
2373
+ } else {
2198
2374
  var match = this.string.slice(this.pos).match(pattern);
2199
2375
  if (match && consume !== false) this.pos += match[0].length;
2200
2376
  return match;
@@ -2361,11 +2537,20 @@ var CodeMirror = (function() {
2361
2537
  fixMarkEnds: function(other) {
2362
2538
  var mk = this.marked, omk = other.marked;
2363
2539
  if (!mk) return;
2364
- for (var i = 0; i < mk.length; ++i) {
2540
+ outer: for (var i = 0; i < mk.length; ++i) {
2365
2541
  var mark = mk[i], close = mark.to == null;
2366
2542
  if (close && omk) {
2367
- for (var j = 0; j < omk.length; ++j)
2368
- if (omk[j].sameSet(mark)) {close = false; break;}
2543
+ for (var j = 0; j < omk.length; ++j) {
2544
+ var om = omk[j];
2545
+ if (!om.sameSet(mark) || om.from != null) continue
2546
+ if (mark.from == this.text.length && om.to == 0) {
2547
+ omk.splice(j, 1);
2548
+ mk.splice(i--, 1);
2549
+ continue outer;
2550
+ } else {
2551
+ close = false; break;
2552
+ }
2553
+ }
2369
2554
  }
2370
2555
  if (close) mark.to = this.text.length;
2371
2556
  }
@@ -2472,15 +2657,22 @@ var CodeMirror = (function() {
2472
2657
  if (wrapWBR) html.push("<wbr>");
2473
2658
  }
2474
2659
  html.push(open);
2475
- span_(text.slice(wrapAt - outPos), style);
2660
+ var cut = wrapAt - outPos;
2661
+ span_(opera ? text.slice(cut, cut + 1) : text.slice(cut), style);
2476
2662
  html.push("</span>");
2663
+ if (opera) span_(text.slice(cut + 1), style);
2477
2664
  wrapAt--;
2478
2665
  outPos += l;
2479
2666
  } else {
2480
2667
  outPos += l;
2481
2668
  span_(text, style);
2482
2669
  // Output empty wrapper when at end of line
2483
- if (outPos == wrapAt && outPos == len) html.push(open + "</span>");
2670
+ // (Gecko and IE8+ do strange wrapping when adding a space
2671
+ // to the end of the line. Other browsers don't react well
2672
+ // to zero-width spaces. So we do hideous browser sniffing
2673
+ // to determine which to use.)
2674
+ if (outPos == wrapAt && outPos == len)
2675
+ html.push(open + (gecko || (ie && !ie_lt8) ? "&#x200b;" : " ") + "</span>");
2484
2676
  // Stop outputting HTML when gone sufficiently far beyond measure
2485
2677
  else if (outPos > wrapAt + 10 && /\s/.test(text)) span = function(){};
2486
2678
  }
@@ -2515,7 +2707,8 @@ var CodeMirror = (function() {
2515
2707
  }
2516
2708
  nextChange = markpos < marked.length ? marked[markpos].from : Infinity;
2517
2709
  for (var i = 0; i < marks.length; ++i) {
2518
- var to = marks[i].to || Infinity;
2710
+ var to = marks[i].to;
2711
+ if (to == null) to = Infinity;
2519
2712
  if (to == pos) marks.splice(i--, 1);
2520
2713
  else nextChange = Math.min(to, nextChange);
2521
2714
  }
@@ -2553,8 +2746,7 @@ var CodeMirror = (function() {
2553
2746
  if (state == 0) {
2554
2747
  if (end > from) dest.push(part.slice(from - pos, Math.min(part.length, to - pos)), source[i+1]);
2555
2748
  if (end >= from) state = 1;
2556
- }
2557
- else if (state == 1) {
2749
+ } else if (state == 1) {
2558
2750
  if (end > to) dest.push(part.slice(0, to - pos), source[i+1]);
2559
2751
  else dest.push(part, source[i+1]);
2560
2752
  }
@@ -2589,11 +2781,7 @@ var CodeMirror = (function() {
2589
2781
  },
2590
2782
  insertHeight: function(at, lines, height) {
2591
2783
  this.height += height;
2592
- // The trick below is apparently too advanced for IE, which
2593
- // occasionally corrupts this.lines (duplicating elements) when
2594
- // it is used.
2595
- if (ie) this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
2596
- else this.lines.splice.apply(this.lines, [at, 0].concat(lines));
2784
+ this.lines = this.lines.slice(0, at).concat(lines).concat(this.lines.slice(at));
2597
2785
  for (var i = 0, e = lines.length; i < e; ++i) lines[i].parent = this;
2598
2786
  },
2599
2787
  iterN: function(at, n, op) {
@@ -2814,10 +3002,14 @@ var CodeMirror = (function() {
2814
3002
 
2815
3003
  function e_target(e) {return e.target || e.srcElement;}
2816
3004
  function e_button(e) {
2817
- if (e.which) return e.which;
2818
- else if (e.button & 1) return 1;
2819
- else if (e.button & 2) return 3;
2820
- else if (e.button & 4) return 2;
3005
+ var b = e.which;
3006
+ if (b == null) {
3007
+ if (e.button & 1) b = 1;
3008
+ else if (e.button & 2) b = 3;
3009
+ else if (e.button & 4) b = 2;
3010
+ }
3011
+ if (mac && e.ctrlKey && b == 1) b = 3;
3012
+ return b;
2821
3013
  }
2822
3014
 
2823
3015
  // Allow 3rd-party code to override event properties by adding an override
@@ -2833,8 +3025,7 @@ var CodeMirror = (function() {
2833
3025
  if (typeof node.addEventListener == "function") {
2834
3026
  node.addEventListener(type, handler, false);
2835
3027
  if (disconnect) return function() {node.removeEventListener(type, handler, false);};
2836
- }
2837
- else {
3028
+ } else {
2838
3029
  var wrapHandler = function(event) {handler(event || window.event);};
2839
3030
  node.attachEvent("on" + type, wrapHandler);
2840
3031
  if (disconnect) return function() {node.detachEvent("on" + type, wrapHandler);};
@@ -2849,12 +3040,15 @@ var CodeMirror = (function() {
2849
3040
 
2850
3041
  var gecko = /gecko\/\d{7}/i.test(navigator.userAgent);
2851
3042
  var ie = /MSIE \d/.test(navigator.userAgent);
3043
+ var ie_lt8 = /MSIE [1-7]\b/.test(navigator.userAgent);
2852
3044
  var ie_lt9 = /MSIE [1-8]\b/.test(navigator.userAgent);
2853
3045
  var quirksMode = ie && document.documentMode == 5;
2854
3046
  var webkit = /WebKit\//.test(navigator.userAgent);
2855
3047
  var chrome = /Chrome\//.test(navigator.userAgent);
3048
+ var opera = /Opera\//.test(navigator.userAgent);
2856
3049
  var safari = /Apple Computer/.test(navigator.vendor);
2857
3050
  var khtml = /KHTML\//.test(navigator.userAgent);
3051
+ var mac_geLion = /Mac OS X 10\D([7-9]|\d\d)\D/.test(navigator.userAgent);
2858
3052
 
2859
3053
  // Detect drag-and-drop
2860
3054
  var dragAndDrop = function() {
@@ -2904,26 +3098,7 @@ var CodeMirror = (function() {
2904
3098
  return window.getComputedStyle(elt, null);
2905
3099
  }
2906
3100
 
2907
- // Find the position of an element by following the offsetParent chain.
2908
- // If screen==true, it returns screen (rather than page) coordinates.
2909
3101
  function eltOffset(node, screen) {
2910
- var bod = node.ownerDocument.body;
2911
- var x = 0, y = 0, skipBody = false;
2912
- for (var n = node; n; n = n.offsetParent) {
2913
- var ol = n.offsetLeft, ot = n.offsetTop;
2914
- // Firefox reports weird inverted offsets when the body has a border.
2915
- if (n == bod) { x += Math.abs(ol); y += Math.abs(ot); }
2916
- else { x += ol, y += ot; }
2917
- if (screen && computedStyle(n).position == "fixed")
2918
- skipBody = true;
2919
- }
2920
- var e = screen && !skipBody ? null : bod;
2921
- for (var n = node.parentNode; n != e; n = n.parentNode)
2922
- if (n.scrollLeft != null) { x -= n.scrollLeft; y -= n.scrollTop;}
2923
- return {left: x, top: y};
2924
- }
2925
- // Use the faster and saner getBoundingClientRect method when possible.
2926
- if (document.documentElement.getBoundingClientRect != null) eltOffset = function(node, screen) {
2927
3102
  // Take the parts of bounding client rect that we are interested in so we are able to edit if need be,
2928
3103
  // since the returned value cannot be changed externally (they are kept in sync as the element moves within the page)
2929
3104
  try { var box = node.getBoundingClientRect(); box = { top: box.top, left: box.left }; }
@@ -2939,7 +3114,7 @@ var CodeMirror = (function() {
2939
3114
  }
2940
3115
  }
2941
3116
  return box;
2942
- };
3117
+ }
2943
3118
 
2944
3119
  // Get a node's text content.
2945
3120
  function eltText(node) {
@@ -2964,18 +3139,19 @@ var CodeMirror = (function() {
2964
3139
  }
2965
3140
  // Recent (late 2011) Opera betas insert bogus newlines at the start
2966
3141
  // of the textContent, so we strip those.
2967
- if (htmlEscape("a") == "\na")
3142
+ if (htmlEscape("a") == "\na") {
2968
3143
  htmlEscape = function(str) {
2969
3144
  escapeElement.textContent = str;
2970
3145
  return escapeElement.innerHTML.slice(1);
2971
3146
  };
2972
3147
  // Some IEs don't preserve tabs through innerHTML
2973
- else if (htmlEscape("\t") != "\t")
3148
+ } else if (htmlEscape("\t") != "\t") {
2974
3149
  htmlEscape = function(str) {
2975
3150
  escapeElement.innerHTML = "";
2976
3151
  escapeElement.appendChild(document.createTextNode(str));
2977
3152
  return escapeElement.innerHTML;
2978
3153
  };
3154
+ }
2979
3155
  CodeMirror.htmlEscape = htmlEscape;
2980
3156
 
2981
3157
  // Used to position the cursor after an undo/redo by finding the
@@ -3001,14 +3177,22 @@ var CodeMirror = (function() {
3001
3177
  // See if "".split is the broken IE version, if so, provide an
3002
3178
  // alternative way to split lines.
3003
3179
  var splitLines = "\n\nb".split(/\n/).length != 3 ? function(string) {
3004
- var pos = 0, nl, result = [];
3005
- while ((nl = string.indexOf("\n", pos)) > -1) {
3006
- result.push(string.slice(pos, string.charAt(nl-1) == "\r" ? nl - 1 : nl));
3007
- pos = nl + 1;
3180
+ var pos = 0, result = [], l = string.length;
3181
+ while (pos <= l) {
3182
+ var nl = string.indexOf("\n", pos);
3183
+ if (nl == -1) nl = string.length;
3184
+ var line = string.slice(pos, string.charAt(nl - 1) == "\r" ? nl - 1 : nl);
3185
+ var rt = line.indexOf("\r");
3186
+ if (rt != -1) {
3187
+ result.push(line.slice(0, rt));
3188
+ pos += rt + 1;
3189
+ } else {
3190
+ result.push(line);
3191
+ pos = nl + 1;
3192
+ }
3008
3193
  }
3009
- result.push(string.slice(pos));
3010
3194
  return result;
3011
- } : function(string){return string.split(/\r?\n/);};
3195
+ } : function(string){return string.split(/\r\n?|\n/);};
3012
3196
  CodeMirror.splitLines = splitLines;
3013
3197
 
3014
3198
  var hasSelection = window.getSelection ? function(te) {
@@ -3029,10 +3213,10 @@ var CodeMirror = (function() {
3029
3213
  var keyNames = {3: "Enter", 8: "Backspace", 9: "Tab", 13: "Enter", 16: "Shift", 17: "Ctrl", 18: "Alt",
3030
3214
  19: "Pause", 20: "CapsLock", 27: "Esc", 32: "Space", 33: "PageUp", 34: "PageDown", 35: "End",
3031
3215
  36: "Home", 37: "Left", 38: "Up", 39: "Right", 40: "Down", 44: "PrintScrn", 45: "Insert",
3032
- 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 127: "Delete", 186: ";", 187: "=", 188: ",",
3033
- 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\", 221: "]", 222: "'", 63276: "PageUp",
3034
- 63277: "PageDown", 63275: "End", 63273: "Home", 63234: "Left", 63232: "Up", 63235: "Right",
3035
- 63233: "Down", 63302: "Insert", 63272: "Delete"};
3216
+ 46: "Delete", 59: ";", 91: "Mod", 92: "Mod", 93: "Mod", 109: "-", 107: "=", 127: "Delete",
3217
+ 186: ";", 187: "=", 188: ",", 189: "-", 190: ".", 191: "/", 192: "`", 219: "[", 220: "\\",
3218
+ 221: "]", 222: "'", 63276: "PageUp", 63277: "PageDown", 63275: "End", 63273: "Home",
3219
+ 63234: "Left", 63232: "Up", 63235: "Right", 63233: "Down", 63302: "Insert", 63272: "Delete"};
3036
3220
  CodeMirror.keyNames = keyNames;
3037
3221
  (function() {
3038
3222
  // Number keys