codemirror-rails 2.24 → 2.32

Sign up to get free protection for your applications and to get access to all the features.
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