web-console 2.1.3 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.markdown +45 -0
  3. data/README.markdown +26 -6
  4. data/Rakefile +37 -0
  5. data/lib/web_console/extensions.rb +2 -1
  6. data/lib/web_console/integration/cruby.rb +2 -1
  7. data/lib/web_console/integration/rubinius.rb +2 -1
  8. data/lib/web_console/railtie.rb +19 -1
  9. data/lib/web_console/session.rb +4 -3
  10. data/lib/web_console/templates/_inner_console_markup.html.erb +8 -3
  11. data/lib/web_console/templates/console.js.erb +169 -46
  12. data/lib/web_console/templates/error_page.js.erb +2 -15
  13. data/lib/web_console/templates/main.js.erb +1 -24
  14. data/lib/web_console/templates/style.css.erb +22 -9
  15. data/lib/web_console/tracer.rb +11 -0
  16. data/lib/web_console/version.rb +1 -1
  17. metadata +5 -155
  18. data/test/dummy/README.rdoc +0 -28
  19. data/test/dummy/Rakefile +0 -6
  20. data/test/dummy/app/assets/javascripts/application.js +0 -13
  21. data/test/dummy/app/assets/stylesheets/application.css +0 -13
  22. data/test/dummy/app/controllers/application_controller.rb +0 -5
  23. data/test/dummy/app/controllers/controller_helper_test_controller.rb +0 -7
  24. data/test/dummy/app/controllers/exception_test_controller.rb +0 -15
  25. data/test/dummy/app/controllers/helper_error_controller.rb +0 -4
  26. data/test/dummy/app/controllers/helper_test_controller.rb +0 -5
  27. data/test/dummy/app/controllers/tests_controller.rb +0 -16
  28. data/test/dummy/app/helpers/application_helper.rb +0 -2
  29. data/test/dummy/app/views/controller_helper_test/index.html.erb +0 -1
  30. data/test/dummy/app/views/exception_test/xhr.html.erb +0 -1
  31. data/test/dummy/app/views/helper_error/index.html.erb +0 -2
  32. data/test/dummy/app/views/helper_test/index.html.erb +0 -220
  33. data/test/dummy/app/views/layouts/application.html.erb +0 -16
  34. data/test/dummy/bin/bundle +0 -3
  35. data/test/dummy/bin/rails +0 -4
  36. data/test/dummy/bin/rake +0 -4
  37. data/test/dummy/config.ru +0 -4
  38. data/test/dummy/config/application.rb +0 -19
  39. data/test/dummy/config/boot.rb +0 -5
  40. data/test/dummy/config/database.yml +0 -25
  41. data/test/dummy/config/environment.rb +0 -5
  42. data/test/dummy/config/environments/development.rb +0 -29
  43. data/test/dummy/config/environments/production.rb +0 -80
  44. data/test/dummy/config/environments/test.rb +0 -34
  45. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  46. data/test/dummy/config/initializers/filter_parameter_logging.rb +0 -4
  47. data/test/dummy/config/initializers/inflections.rb +0 -16
  48. data/test/dummy/config/initializers/mime_types.rb +0 -5
  49. data/test/dummy/config/initializers/secret_token.rb +0 -12
  50. data/test/dummy/config/initializers/session_store.rb +0 -3
  51. data/test/dummy/config/initializers/wrap_parameters.rb +0 -14
  52. data/test/dummy/config/locales/en.yml +0 -23
  53. data/test/dummy/config/routes.rb +0 -15
  54. data/test/dummy/db/schema.rb +0 -16
  55. data/test/dummy/db/test.sqlite3 +0 -0
  56. data/test/dummy/log/test.log +0 -808
  57. data/test/dummy/public/404.html +0 -58
  58. data/test/dummy/public/422.html +0 -58
  59. data/test/dummy/public/500.html +0 -57
  60. data/test/dummy/public/favicon.ico +0 -0
  61. data/test/dummy/tmp/cache/assets/test/sprockets/0c9b99b1b975b36a5b686845ae729db3 +0 -0
  62. data/test/dummy/tmp/cache/assets/test/sprockets/12e9f58adcf819cf65fd68b6859a438f +0 -0
  63. data/test/dummy/tmp/cache/assets/test/sprockets/1fd6abead3df36a4e4a2cb042a551b39 +0 -0
  64. data/test/dummy/tmp/cache/assets/test/sprockets/33b5eb81b5aaaa1e6dd5dcb6517792b4 +0 -0
  65. data/test/dummy/tmp/cache/assets/test/sprockets/3e460b2e021085f88b5597b38d194932 +0 -0
  66. data/test/dummy/tmp/cache/assets/test/sprockets/52f04b2ea6e461f4661b6d0be393fa94 +0 -0
  67. data/test/dummy/tmp/cache/assets/test/sprockets/688c344c2919f882338f06c5daaea209 +0 -0
  68. data/test/dummy/tmp/cache/assets/test/sprockets/7f079298e19bbef40a5d6a37dc03033a +0 -0
  69. data/test/dummy/tmp/cache/assets/test/sprockets/91aa709f89e465f58056fabe5f75eef4 +0 -0
  70. data/test/dummy/tmp/cache/assets/test/sprockets/95f4e4e5217aec9f71b2c1a65a9a9231 +0 -0
  71. data/test/dummy/tmp/cache/assets/test/sprockets/9c41be9b6625756fbb6d05b7665dd911 +0 -0
  72. data/test/dummy/tmp/cache/assets/test/sprockets/a95f64d41fcf8244b8f5b84a55f07822 +0 -0
  73. data/test/dummy/tmp/cache/assets/test/sprockets/c5e6bf1efd5929bee63e28ad7c9f6f83 +0 -0
  74. data/test/dummy/tmp/cache/assets/test/sprockets/d863259e9eeca657a1624135cfe24446 +0 -0
  75. data/test/dummy/tmp/cache/assets/test/sprockets/e1d2fc4601b6423ec8a42415ee05dd14 +0 -0
  76. data/test/dummy/tmp/cache/assets/test/sprockets/e82616536a759c2e87d703adc77d65da +0 -0
  77. data/test/support/scenarios/bad_custom_error_scenario.rb +0 -17
  78. data/test/support/scenarios/basic_nested_scenario.rb +0 -15
  79. data/test/support/scenarios/custom_error_scenario.rb +0 -11
  80. data/test/support/scenarios/eval_nested_scenario.rb +0 -15
  81. data/test/support/scenarios/flat_scenario.rb +0 -9
  82. data/test/support/scenarios/reraised_scenario.rb +0 -21
  83. data/test/test_helper.rb +0 -78
  84. data/test/web_console/evaluator_test.rb +0 -73
  85. data/test/web_console/extensions_test.rb +0 -28
  86. data/test/web_console/helper_test.rb +0 -76
  87. data/test/web_console/integration_test.rb +0 -47
  88. data/test/web_console/middleware_test.rb +0 -116
  89. data/test/web_console/railtie_test.rb +0 -99
  90. data/test/web_console/request_test.rb +0 -82
  91. data/test/web_console/session_test.rb +0 -59
  92. data/test/web_console/whiny_request_test.rb +0 -33
  93. data/test/web_console/whitelist_test.rb +0 -43
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5cf0cbd5310d3c232a6dc3f3ffac02155928afaf
4
- data.tar.gz: df79c6d5af175c9987a6b4e5361b6ad4bbb99761
3
+ metadata.gz: bde2832a1ef7a0ace010d61011afb4c74a116a93
4
+ data.tar.gz: c0e5de96dab453f1a3e233d969bf5ada72ba848a
5
5
  SHA512:
6
- metadata.gz: 4cb26884dfa66837135c89f632aaed9a2643d816a8955a3967005f5884c95f35f65597569b82431bc8027f3de35125e7282f6a8803fd329d2356538c44436816
7
- data.tar.gz: 51ee3fcdfa8f4a37f2607d48a9c4e1019c706937d074c74946e25f7d064136be67a7b492bafc23170b4963303f465d844942fc5c28ba2f024ece111b17a82648
6
+ metadata.gz: eb74d0994835ccc223df24cb3a5ee1e034ca2456580f99962724864994438f04a14c67aa05f9425263f1c36773623969abdc7bca5a2b255535e90449b6394ac3
7
+ data.tar.gz: 31f90bb5c32ed53a95f43cb1a0f25cf89765847c92010255a9df5c0235423203711be37e09eda9af4f435616772d247502b682a2ef367f5409cbbbcc23cca963
@@ -0,0 +1,45 @@
1
+ # CHANGELOG
2
+
3
+ ## master (unreleased)
4
+
5
+ ## 2.2.0
6
+
7
+ * [#140](https://github.com/rails/web-console/pull/140) Add the ability to close the console on each page ([@sh19910711])
8
+ * [#135](https://github.com/rails/web-console/pull/135) Run the console only in development mode and raise warning in tests ([@frenesim])
9
+
10
+ ## 2.1.3
11
+
12
+ * Fix remote code execution vulnerability in Web Console. CVE-2015-3224.
13
+ * [#123](https://github.com/rails/web-console/pull/123) Replace deprecated `alias_method_chain` with `alias_method` ([@jonatack])
14
+
15
+ ## 2.1.2
16
+
17
+ * [#115](https://github.com/rails/web-console/pull/115) Show proper binding when raising an error in a template ([@gsamokovarov])
18
+ * [#114](https://github.com/rails/web-console/pull/114) Fix templates non rendering, because of missing template suffix ([@gsamokovarov])
19
+
20
+ ## 2.1.1
21
+
22
+ * [#112](https://github.com/rails/web-console/pull/112) Always allow application/x-www-form-urlencoded content type ([@gsamokovarov])
23
+
24
+ ## 2.1.0
25
+
26
+ * [#109](https://github.com/rails/web-console/pull/109) Revamp unavailable session response message ([@gsamokovarov])
27
+ * [#107](https://github.com/rails/web-console/pull/107) Fix pasting regression for all browsers ([@parterburn])
28
+ * [#105](https://github.com/rails/web-console/pull/105) Lock scroll bottom on console window resize ([@noahpatterson])
29
+ * [#104](https://github.com/rails/web-console/pull/104) Always whitelist localhost and inform users why no console is displayed ([@gsamokovarov])
30
+ * [#100](https://github.com/rails/web-console/pull/100) Accept text/plain as acceptable content type for Puma ([@gsamokovarov])
31
+ * [#98](https://github.com/rails/web-console/pull/98) Add arbitrary big z-index to the console ([@bglbruno])
32
+ * [#88](https://github.com/rails/web-console/pull/88) Spelling fixes ([@jeffnv])
33
+ * [#86](https://github.com/rails/web-console/pull/86) Disable autofocus when initializing the console ([@ryandao])
34
+ * [#84](https://github.com/rails/web-console/pull/84) Allow Rails 5 as dependency in gemspec ([@jonatack])
35
+ * [#69](https://github.com/rails/web-console/pull/69) Introduce middleware for request dispatch and console rendering ([@gsamokovarov])
36
+
37
+ [@jonatack]: https://github.com/jonatack
38
+ [@ryandao]: https://github.com/ryandao
39
+ [@jeffnv]: https://github.com/jeffnv
40
+ [@gsamokovarov]: https://github.com/gsamokovarov
41
+ [@bglbruno]: https://github.com/bglbruno
42
+ [@noahpatterson]: https://github.com/noahpatterson
43
+ [@parterburn]: https://github.com/parterburn
44
+ [@sh19910711]: https://github.com/sh19910711
45
+ [@frenesim]: https://github.com/frenesim
@@ -1,6 +1,10 @@
1
1
  <p align=right>
2
2
  Documentation for:
3
3
  <a href=https://github.com/rails/web-console/tree/v2.0.0>v2.0.0</a>
4
+ <a href=https://github.com/rails/web-console/tree/v2.1.0>v2.1.0</a>
5
+ <a href=https://github.com/rails/web-console/tree/v2.1.1>v2.1.1</a>
6
+ <a href=https://github.com/rails/web-console/tree/v2.1.2>v2.1.2</a>
7
+ <a href=https://github.com/rails/web-console/tree/v2.1.3>v2.1.3</a>
4
8
  </p>
5
9
 
6
10
  # Web Console [![Build Status](https://travis-ci.org/rails/web-console.svg?branch=master)](https://travis-ci.org/rails/web-console)
@@ -119,7 +123,7 @@ end
119
123
  If you want to whitelist the whole private network, you can do:
120
124
 
121
125
  ```ruby
122
- class Application < Rails::Application
126
+ Rails.application.configure do
123
127
  config.web_console.whitelisted_ips = '192.168.0.0/16'
124
128
  end
125
129
  ```
@@ -138,19 +142,19 @@ messages like the following is printed in the server logs:
138
142
  If you don't wanna see this message anymore, set this option to `false`:
139
143
 
140
144
  ```ruby
141
- class Application < Rails::Application
145
+ Rails.application.configure do
142
146
  config.web_console.whiny_requests = false
143
147
  end
144
148
  ```
145
149
 
146
- ### config.web_console.templates_path
150
+ ### config.web_console.template_paths
147
151
 
148
152
  If you wanna style the console yourself, you can place `style.css` at a
149
- directory pointed by `config.web_console.templates_path`:
153
+ directory pointed by `config.web_console.template_paths`:
150
154
 
151
155
  ```ruby
152
- class Application < Rails::Application
153
- config.web_console.templates_path = 'app/views/web_console'
156
+ Rails.application.configure do
157
+ config.web_console.template_paths = 'app/views/web_console'
154
158
  end
155
159
  ```
156
160
 
@@ -181,6 +185,22 @@ server requests only out of one process.
181
185
  The interactive console executes Ruby code. Invoking `instance_variables` and
182
186
  `local_variables` will give you what you want.
183
187
 
188
+ ### Why does console only appear on error pages but not when I call it?
189
+
190
+ This can be happening if you are using `Rack::Deflater`. Be sure that
191
+ `WebConsole::Middleware` is used after `Rack::Deflater`. The easiest way to do
192
+ this is to insert `Rack::Deflater` as early as possible
193
+
194
+ ```ruby
195
+ Rails.application.configure do
196
+ config.middleware.insert(0, Rack::Deflater)
197
+ end
198
+ ```
199
+
200
+ ### Why I'm getting an undefined method `web_console`?
201
+
202
+ Make sure you configuration lives in `config/environments/development.rb`.
203
+
184
204
  ## Credits
185
205
 
186
206
  * Shoutout to [Charlie Somerville] for [better_errors] and [this] code.
data/Rakefile CHANGED
@@ -6,6 +6,8 @@ end
6
6
 
7
7
  require 'socket'
8
8
  require 'rake/testtask'
9
+ require 'tmpdir'
10
+ require 'securerandom'
9
11
 
10
12
  EXPANDED_CWD = File.expand_path(File.dirname(__FILE__))
11
13
 
@@ -16,6 +18,41 @@ Rake::TestTask.new(:test) do |t|
16
18
  t.verbose = false
17
19
  end
18
20
 
21
+ namespace :test do
22
+ desc "Run tests for templates"
23
+ task :templates => "templates:all"
24
+
25
+ namespace :templates do
26
+ task :all => [:daemonize, :npm, :rackup, :mocha, :kill]
27
+ task :serve => [:npm, :rackup]
28
+
29
+ work_dir = Pathname(__FILE__).dirname.join("test/templates")
30
+ pid_file = Pathname(Dir.tmpdir).join("web_console.#{SecureRandom.uuid}.pid")
31
+ server_port = 29292
32
+ rackup_opts = "-p #{server_port}"
33
+
34
+ task :daemonize do
35
+ rackup_opts += " -D -P #{pid_file}"
36
+ end
37
+
38
+ task :npm do
39
+ Dir.chdir(work_dir) { system "npm install --silent" }
40
+ end
41
+
42
+ task :rackup do
43
+ Dir.chdir(work_dir) { system "bundle exec rackup #{rackup_opts}" }
44
+ end
45
+
46
+ task :mocha do
47
+ Dir.chdir(work_dir) { system "$(npm bin)/mocha-phantomjs http://localhost:#{server_port}/html/spec_runner.html" }
48
+ end
49
+
50
+ task :kill do
51
+ system "kill #{File.read pid_file}"
52
+ end
53
+ end
54
+ end
55
+
19
56
  Bundler::GemHelper.install_tasks
20
57
 
21
58
  task default: :test
@@ -15,5 +15,6 @@ ActionDispatch::DebugExceptions.class_eval do
15
15
  end
16
16
  end
17
17
 
18
- alias_method_chain :render_exception, :web_console
18
+ alias_method :render_exception_without_web_console, :render_exception
19
+ alias_method :render_exception, :render_exception_with_web_console
19
20
  end
@@ -34,6 +34,7 @@ class Exception
34
34
  set_backtrace_without_binding_of_caller(*args)
35
35
  end
36
36
 
37
- alias_method_chain :set_backtrace, :binding_of_caller
37
+ alias_method :set_backtrace_without_binding_of_caller, :set_backtrace
38
+ alias_method :set_backtrace, :set_backtrace_with_binding_of_caller
38
39
  end
39
40
  end
@@ -62,5 +62,6 @@ end
62
62
  raise_exception_without_current_bindings(exc)
63
63
  end
64
64
 
65
- alias_method_chain :raise_exception, :current_bindings
65
+ alias_method :raise_exception_without_current_bindings, :raise_exception
66
+ alias_method :raise_exception, :raise_exception_with_current_bindings
66
67
  end
@@ -17,11 +17,29 @@ module WebConsole
17
17
  end
18
18
  end
19
19
 
20
+ initializer 'web_console.development_only' do
21
+ unless (config.web_console.development_only == false) || Rails.env.development?
22
+ abort <<-END.strip_heredoc
23
+ Web Console is activated in the #{Rails.env} environment, which is
24
+ usually a mistake. To ensure it's only activated in development
25
+ mode, move it to the development group of your Gemfile:
26
+
27
+ gem 'web-console', group: :development
28
+
29
+ If you still want to run it the #{Rails.env} environment (and know
30
+ what you are doing), put this in your Rails application
31
+ configuration:
32
+
33
+ config.web_console.development_only = false
34
+ END
35
+ end
36
+ end
37
+
20
38
  initializer 'web_console.insert_middleware' do |app|
21
39
  app.middleware.insert_before ActionDispatch::DebugExceptions, Middleware
22
40
  end
23
41
 
24
- initializer 'web_console.templates_path' do
42
+ initializer 'web_console.template_paths' do
25
43
  if template_paths = config.web_console.template_paths
26
44
  Template.template_paths.unshift(*Array(template_paths))
27
45
  end
@@ -9,7 +9,8 @@ module WebConsole
9
9
  # error pages only, as currently, this is the only client that needs to do
10
10
  # that.
11
11
  class Session
12
- INMEMORY_STORAGE = {}
12
+ cattr_reader :inmemory_storage
13
+ @@inmemory_storage = {}
13
14
 
14
15
  class << self
15
16
  # Finds a persisted session in memory by its id.
@@ -17,7 +18,7 @@ module WebConsole
17
18
  # Returns a persisted session if found in memory.
18
19
  # Raises NotFound error unless found in memory.
19
20
  def find(id)
20
- INMEMORY_STORAGE[id]
21
+ inmemory_storage[id]
21
22
  end
22
23
 
23
24
  # Create a Session from an exception.
@@ -59,7 +60,7 @@ module WebConsole
59
60
  private
60
61
 
61
62
  def store_into_memory
62
- INMEMORY_STORAGE[id] = self
63
+ inmemory_storage[id] = self
63
64
  end
64
65
  end
65
66
  end
@@ -1,3 +1,8 @@
1
- <div id='resizer'></div>
2
- <div class='console-inner'></div>
3
- <input id='clipboard' type='text'>
1
+ <div class='resizer layer'></div>
2
+ <div class='console-outer layer'>
3
+ <div class='console-actions'>
4
+ <div class='close-button button' title='close'>x</div>
5
+ </div>
6
+ <div class='console-inner'></div>
7
+ </div>
8
+ <input class='clipboard' type='text'>
@@ -1,40 +1,3 @@
1
- // DOM helpers
2
- function hasClass(el, className) {
3
- var regex = new RegExp('(?:^|\\s)' + className + '(?!\\S)', 'g');
4
- return el.className.match(regex);
5
- }
6
-
7
- function addClass(el, className) {
8
- el.className += " " + className;
9
- }
10
-
11
- function removeClass(el, className) {
12
- var regex = new RegExp('(?:^|\\s)' + className + '(?!\\S)', 'g');
13
- el.className = el.className.replace(regex, '');
14
- }
15
-
16
- function removeAllChildren(el) {
17
- while (el.firstChild) {
18
- el.removeChild(el.firstChild);
19
- }
20
- }
21
-
22
- function escapeHTML(html) {
23
- return html
24
- .replace(/&/g, '&amp;')
25
- .replace(/</g, '&lt;')
26
- .replace(/>/g, '&gt;')
27
- .replace(/"/g, '&quot;')
28
- .replace(/'/g, '&#x27;')
29
- .replace(/`/g, '&#x60;');
30
- }
31
-
32
- // Add CSS styles dynamically. This probably doesnt work for IE <8.
33
- var style = document.createElement('style');
34
- style.type = 'text/css';
35
- style.innerHTML = <%= render_inlined_string 'style.css' %>;
36
- document.getElementsByTagName('head')[0].appendChild(style);
37
-
38
1
  /**
39
2
  * Constructor for command storage.
40
3
  * It uses localStorage if available. Otherwise fallback to normal JS array.
@@ -84,6 +47,10 @@ function CommandStorage() {
84
47
  // HTML strings for dynamic elements.
85
48
  var consoleInnerHtml = <%= render_inlined_string '_inner_console_markup.html' %>;
86
49
  var promptBoxHtml = <%= render_inlined_string '_prompt_box_markup.html' %>;
50
+ // CSS
51
+ var consoleStyleCss = <%= render_inlined_string 'style.css' %>;
52
+ // Insert a style element with the unique ID
53
+ var styleElementId = 'sr02459pvbvrmhco';
87
54
 
88
55
  // REPLConsole Constructor
89
56
  function REPLConsole(config) {
@@ -122,17 +89,25 @@ REPLConsole.prototype.install = function(container) {
122
89
  // Render the console.
123
90
  container.innerHTML = consoleInnerHtml;
124
91
 
92
+ var consoleOuter = findChild(container, 'console-outer');
93
+ var consoleActions = findChild(consoleOuter, 'console-actions');
94
+
95
+ addClass(container, 'console');
96
+ addClass(container.getElementsByClassName('layer'), 'pos-absolute border-box');
97
+ addClass(container.getElementsByClassName('button'), 'border-box');
98
+ addClass(consoleActions, 'pos-fixed pos-right');
99
+
125
100
  // Make the console resizable.
126
- document.getElementById('resizer').addEventListener('mousedown', function(ev) {
101
+ function resizeContainer(ev) {
127
102
  var startY = ev.clientY;
128
103
  var startHeight = parseInt(document.defaultView.getComputedStyle(container).height, 10);
129
- var consoleInner = document.getElementsByClassName('console-inner')[0];
130
- var innerScrollTopStart = consoleInner.scrollTop;
131
- var innerClientHeightStart = consoleInner.clientHeight;
104
+ var scrollTopStart = consoleOuter.scrollTop;
105
+ var clientHeightStart = consoleOuter.clientHeight;
132
106
 
133
107
  var doDrag = function(e) {
134
108
  container.style.height = (startHeight + startY - e.clientY) + 'px';
135
- consoleInner.scrollTop = innerScrollTopStart + (innerClientHeightStart - consoleInner.clientHeight);
109
+ consoleOuter.scrollTop = scrollTopStart + (clientHeightStart - consoleOuter.clientHeight);
110
+ shiftConsoleActions();
136
111
  };
137
112
 
138
113
  var stopDrag = function(e) {
@@ -142,12 +117,50 @@ REPLConsole.prototype.install = function(container) {
142
117
 
143
118
  document.documentElement.addEventListener('mousemove', doDrag, false);
144
119
  document.documentElement.addEventListener('mouseup', stopDrag, false);
145
- });
120
+ }
121
+
122
+ function closeContainer(ev) {
123
+ container.parentNode.removeChild(container);
124
+ }
125
+
126
+ var shifted = false;
127
+ function shiftConsoleActions() {
128
+ if (consoleOuter.scrollHeight > consoleOuter.clientHeight) {
129
+ var widthDiff = document.documentElement.clientWidth - consoleOuter.clientWidth;
130
+ if (shifted || ! widthDiff) return;
131
+ shifted = true;
132
+ consoleActions.style.marginRight = widthDiff + 'px';
133
+ } else if (shifted) {
134
+ shifted = false;
135
+ consoleActions.style.marginRight = '0px';
136
+ }
137
+ }
146
138
 
147
139
  // Initialize
148
- this.inner = container.getElementsByClassName('console-inner')[0];
149
- this.clipboard = document.getElementById('clipboard');
140
+ this.outer = consoleOuter;
141
+ this.inner = findChild(this.outer, 'console-inner');
142
+ this.clipboard = findChild(container, 'clipboard');
143
+ this.remotePath = container.dataset.remotePath;
150
144
  this.newPromptBox();
145
+ this.insertCss();
146
+
147
+ findChild(container, 'resizer').addEventListener('mousedown', resizeContainer);
148
+ findChild(consoleActions, 'close-button').addEventListener('click', closeContainer);
149
+ consoleOuter.addEventListener('DOMNodeInserted', shiftConsoleActions);
150
+
151
+ REPLConsole.currentSession = this;
152
+ };
153
+
154
+ // Add CSS styles dynamically. This probably doesnt work for IE <8.
155
+ REPLConsole.prototype.insertCss = function() {
156
+ if (document.getElementById(styleElementId)) {
157
+ return; // already inserted
158
+ }
159
+ var style = document.createElement('style');
160
+ style.type = 'text/css';
161
+ style.innerHTML = consoleStyleCss;
162
+ style.id = styleElementId;
163
+ document.getElementsByTagName('head')[0].appendChild(style);
151
164
  };
152
165
 
153
166
  REPLConsole.prototype.focus = function() {
@@ -367,7 +380,117 @@ REPLConsole.prototype.insertAtCurrent = function(char) {
367
380
  };
368
381
 
369
382
  REPLConsole.prototype.scrollToBottom = function() {
370
- this.inner.scrollTop = this.inner.scrollHeight;
383
+ this.outer.scrollTop = this.outer.scrollHeight;
371
384
  };
372
385
 
386
+ // Change the binding of the console
387
+ REPLConsole.prototype.switchBindingTo = function(frameId, callback) {
388
+ var url = this.remotePath + "/trace";
389
+ var params = "frame_id=" + encodeURIComponent(frameId);
390
+ postRequest(url, params, callback);
391
+ };
392
+
393
+ /**
394
+ * Install the console into the element with a specific ID.
395
+ * Example: REPLConsole.installInto("target-id")
396
+ */
397
+ REPLConsole.installInto = function(id) {
398
+ var consoleElement = document.getElementById(id);
399
+ var remotePath = consoleElement.dataset.remotePath;
400
+ var replConsole = new REPLConsole({
401
+ promptLabel: consoleElement.dataset.initialPrompt,
402
+ commandHandle: function(line) {
403
+ var _this = this;
404
+ var url = remotePath;
405
+ var params = "input=" + encodeURIComponent(line);
406
+ putRequest(url, params, function(xhr) {
407
+ var response = JSON.parse(xhr.responseText);
408
+ _this.writeOutput(response.output);
409
+ });
410
+ }
411
+ });
412
+
413
+ replConsole.install(consoleElement);
414
+ return replConsole;
415
+ };
416
+
417
+ // This is to store the latest single session, and the stored session
418
+ // is updated by the REPLConsole#install() method.
419
+ // It allows to operate the current session from the other scripts.
420
+ REPLConsole.currentSession = null;
421
+
422
+ // DOM helpers
423
+ function hasClass(el, className) {
424
+ var regex = new RegExp('(?:^|\\s)' + className + '(?!\\S)', 'g');
425
+ return el.className && el.className.match(regex);
426
+ }
427
+
428
+ function isNodeList(el) {
429
+ return typeof el.length === 'number' &&
430
+ typeof el.item === 'function';
431
+ }
432
+
433
+ function addClass(el, className) {
434
+ if (isNodeList(el)) {
435
+ for (var i = 0; i < el.length; ++ i) {
436
+ addClass(el[i], className);
437
+ }
438
+ } else {
439
+ el.className += " " + className;
440
+ }
441
+ }
442
+
443
+ function removeClass(el, className) {
444
+ var regex = new RegExp('(?:^|\\s)' + className + '(?!\\S)', 'g');
445
+ el.className = el.className.replace(regex, '');
446
+ }
447
+
448
+ function removeAllChildren(el) {
449
+ while (el.firstChild) {
450
+ el.removeChild(el.firstChild);
451
+ }
452
+ }
453
+
454
+ function findChild(el, className) {
455
+ for (var i = 0; i < el.childNodes.length; ++ i) {
456
+ if (hasClass(el.childNodes[i], className)) {
457
+ return el.childNodes[i];
458
+ }
459
+ }
460
+ }
461
+
462
+ function escapeHTML(html) {
463
+ return html
464
+ .replace(/&/g, '&amp;')
465
+ .replace(/</g, '&lt;')
466
+ .replace(/>/g, '&gt;')
467
+ .replace(/"/g, '&quot;')
468
+ .replace(/'/g, '&#x27;')
469
+ .replace(/`/g, '&#x60;');
470
+ }
471
+
472
+ // XHR helpers
473
+ function request(method, url, params, callback) {
474
+ var xhr = new XMLHttpRequest();
475
+
476
+ xhr.open(method, url, true);
477
+ xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
478
+ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
479
+ xhr.send(params);
480
+
481
+ xhr.onreadystatechange = function() {
482
+ if (xhr.readyState === 4) {
483
+ callback(xhr);
484
+ }
485
+ }
486
+ }
487
+
488
+ function postRequest(url, params, callback) {
489
+ request("POST", url, params, callback);
490
+ }
491
+
492
+ function putRequest(url, params, callback) {
493
+ request("PUT", url, params, callback);
494
+ }
495
+
373
496
  window.REPLConsole = REPLConsole;