jscmd 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +8 -0
- data/Manifest.txt +11 -3
- data/Rakefile +1 -1
- data/bin/jscmd +326 -21
- data/examples/get_title.pl +22 -0
- data/lib/jscmd/agent.js +41 -19
- data/lib/jscmd/asynchttpproxy.rb +40 -4
- data/lib/jscmd/inprocessbroker.rb +31 -0
- data/lib/jscmd/message.rb +42 -0
- data/lib/jscmd/pipebroker.rb +62 -0
- data/lib/jscmd/proxyserver.rb +224 -0
- data/lib/jscmd/shell.rb +201 -0
- data/lib/jscmd/stompproxy.rb +36 -0
- data/lib/jscmd/urlforwarder.rb +43 -0
- data/lib/jscmd/version.rb +2 -2
- data/lib/jscmd.rb +14 -1
- data/test/test_helper.rb +4 -2
- data/test/{test_jscmd.rb → test_proxyserver.rb} +40 -46
- data/test/test_shell.rb +56 -0
- metadata +34 -8
- data/lib/jscmd/jscommander.rb +0 -339
@@ -0,0 +1,62 @@
|
|
1
|
+
module JSCommander
|
2
|
+
class PipeBroker
|
3
|
+
class PipeQueue
|
4
|
+
attr_reader :reader, :writer
|
5
|
+
|
6
|
+
def initialize(r, w)
|
7
|
+
@reader, @writer = [r, w]
|
8
|
+
end
|
9
|
+
|
10
|
+
def push(obj)
|
11
|
+
data = Marshal.dump(obj)
|
12
|
+
@writer.write([data.size].pack("N") + data)
|
13
|
+
end
|
14
|
+
|
15
|
+
def pop
|
16
|
+
size = @reader.read(4).unpack("N")[0]
|
17
|
+
Marshal.load(@reader.read(size))
|
18
|
+
end
|
19
|
+
|
20
|
+
def close
|
21
|
+
@reader.close
|
22
|
+
@writer.close
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
PipeMessage = Struct.new(:name, :message)
|
27
|
+
|
28
|
+
def initialize(reader, writer)
|
29
|
+
@subscribers = {}
|
30
|
+
@queue = PipeQueue.new(reader, writer)
|
31
|
+
Thread.start do
|
32
|
+
begin
|
33
|
+
loop do
|
34
|
+
pm = @queue.pop
|
35
|
+
if @subscribers[pm.name]
|
36
|
+
@subscribers[pm.name].each do |proc|
|
37
|
+
proc.call pm.message
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
rescue Exception
|
42
|
+
$stderr.puts $!
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def subscribe(name, &proc)
|
48
|
+
@subscribers[name] ||= []
|
49
|
+
@subscribers[name] << proc
|
50
|
+
proc
|
51
|
+
end
|
52
|
+
|
53
|
+
def unsubscribe(name, subscriber)
|
54
|
+
sub = @subscribers[name].delete(subscriber)
|
55
|
+
end
|
56
|
+
|
57
|
+
def send(name, msg)
|
58
|
+
# puts "send #{name}, #{msg.inspect}"
|
59
|
+
@queue.push(PipeMessage.new(name, msg))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,224 @@
|
|
1
|
+
#
|
2
|
+
# jscommander.rb: Remote JavaScript Console
|
3
|
+
#
|
4
|
+
# Copyright 2007 Shinya Kasatani
|
5
|
+
#
|
6
|
+
|
7
|
+
require 'uri'
|
8
|
+
require 'thread'
|
9
|
+
require 'webrick'
|
10
|
+
require 'zlib'
|
11
|
+
|
12
|
+
require "jscmd/asynchttpproxy"
|
13
|
+
|
14
|
+
module WEBrick
|
15
|
+
class HTTPResponse
|
16
|
+
alias_method :original_send_body, :send_body
|
17
|
+
def send_body(socket)
|
18
|
+
begin
|
19
|
+
original_send_body(socket)
|
20
|
+
rescue Errno::ECONNABORTED
|
21
|
+
raise Errno::ECONNRESET
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module JSCommander
|
28
|
+
class ProxyServer < WEBrick::AsyncHTTPProxyServer
|
29
|
+
SCRIPT_DIR = "/_remote_js_proxy/"
|
30
|
+
|
31
|
+
class CommandDispatcher
|
32
|
+
class CLIENT_ABORTED < StandardError; end
|
33
|
+
class SHUTDOWN < StandardError; end
|
34
|
+
|
35
|
+
def initialize(server, command_queue)
|
36
|
+
@client_mutex = Mutex.new
|
37
|
+
@client_ready = ConditionVariable.new
|
38
|
+
@clients = []
|
39
|
+
@thread = Thread.start do
|
40
|
+
while server.status != :Shutdown
|
41
|
+
command = command_queue.pop
|
42
|
+
@client_mutex.synchronize do
|
43
|
+
while @clients.empty?
|
44
|
+
@client_ready.wait(@client_mutex)
|
45
|
+
end
|
46
|
+
@clients.first.push_command(command)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def shutdown
|
53
|
+
@clients.each do |client|
|
54
|
+
client.push_command SHUTDOWN
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def format_clients
|
59
|
+
@clients.map{|c|"[#{c.hostname}]"}.join(",")
|
60
|
+
end
|
61
|
+
|
62
|
+
def new_client(req, &block)
|
63
|
+
client = Client.new(req)
|
64
|
+
@client_mutex.synchronize do
|
65
|
+
@clients << client
|
66
|
+
@client_ready.signal
|
67
|
+
end
|
68
|
+
begin
|
69
|
+
yield client
|
70
|
+
ensure
|
71
|
+
@client_mutex.synchronize do
|
72
|
+
@clients.delete(client)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
class Client
|
78
|
+
def initialize(req)
|
79
|
+
@request = req
|
80
|
+
@queue = Queue.new
|
81
|
+
req_socket = req.instance_eval{@socket}
|
82
|
+
@thread = Thread.start do
|
83
|
+
sleep 1 until req_socket.eof?
|
84
|
+
@queue.push CLIENT_ABORTED
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def hostname
|
89
|
+
uri = @request.header["referer"].to_s
|
90
|
+
if uri =~ %r{^\w+://([^/]+)}
|
91
|
+
$1
|
92
|
+
else
|
93
|
+
uri
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def push_command(command)
|
98
|
+
@queue.push(command)
|
99
|
+
end
|
100
|
+
|
101
|
+
def pop_command
|
102
|
+
r = @queue.pop
|
103
|
+
# thread is automatically stopped if r == CLIENT_ABORTED
|
104
|
+
if r != CLIENT_ABORTED
|
105
|
+
@thread.kill
|
106
|
+
end
|
107
|
+
r
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def initialize(broker, args = {})
|
113
|
+
@broker = broker
|
114
|
+
@commands = Queue.new
|
115
|
+
@command_dispatcher = CommandDispatcher.new(self, @commands)
|
116
|
+
@broker.subscribe("commands") do |msg|
|
117
|
+
if msg.attributes["type"] == "ping"
|
118
|
+
@broker.send("events", Message.new(nil,
|
119
|
+
:type => "pong",
|
120
|
+
:clients => @command_dispatcher.format_clients,
|
121
|
+
"in-reply-to" => msg.attributes["id"]))
|
122
|
+
else
|
123
|
+
@commands.push(msg)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
@clients = []
|
127
|
+
super({:ProxyContentHandler => method(:handle_content).to_proc}.merge(args))
|
128
|
+
end
|
129
|
+
|
130
|
+
def service(req, res)
|
131
|
+
if req.path == "#{SCRIPT_DIR}agent.js"
|
132
|
+
res.content_type = "application/x-javascript"
|
133
|
+
res.body = File.read(File.join(File.dirname(__FILE__), "agent.js"))
|
134
|
+
elsif req.path == "#{SCRIPT_DIR}poll"
|
135
|
+
serve_script(req, res)
|
136
|
+
else
|
137
|
+
if req.header["accept-encoding"] && !req.header["accept-encoding"].empty?
|
138
|
+
if req.header["accept-encoding"].first.split(/,/).include?("gzip")
|
139
|
+
req.header["accept-encoding"] = ["gzip"]
|
140
|
+
else
|
141
|
+
req.header.delete "accept-encoding"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
super
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def shutdown
|
149
|
+
@command_dispatcher.shutdown
|
150
|
+
super
|
151
|
+
end
|
152
|
+
|
153
|
+
def serve_script(req, res)
|
154
|
+
# puts "serve:#{req.body}"
|
155
|
+
begin
|
156
|
+
@command_dispatcher.new_client(req) do |client|
|
157
|
+
type = req.header["x-jscmd-type"].first
|
158
|
+
if type && type != "connect"
|
159
|
+
@broker.send("events", Message.new(URI.decode(req.body),
|
160
|
+
:type => type,
|
161
|
+
:clients => @command_dispatcher.format_clients,
|
162
|
+
"in-reply-to" => req.header["x-jscmd-in-reply-to"].first))
|
163
|
+
if "true" == req.header["x-jscmd-more"].first
|
164
|
+
# immediately send empty response to wait for another event
|
165
|
+
res.content_type = "text/plain"
|
166
|
+
res.body = ''
|
167
|
+
return
|
168
|
+
end
|
169
|
+
else
|
170
|
+
# new connection
|
171
|
+
@broker.send("events", Message.new(nil, :type => "connect",
|
172
|
+
:clients => @command_dispatcher.format_clients))
|
173
|
+
end
|
174
|
+
|
175
|
+
command = client.pop_command
|
176
|
+
if command.is_a?(Class) # exception class
|
177
|
+
raise command
|
178
|
+
end
|
179
|
+
res.content_type = "text/plain"
|
180
|
+
res.header["X-JSCmd-Command-Id"] = command.attributes["id"]
|
181
|
+
res.header["X-JSCmd-Type"] = command.attributes["type"]
|
182
|
+
res.body = command.body
|
183
|
+
end
|
184
|
+
rescue CommandDispatcher::CLIENT_ABORTED
|
185
|
+
@broker.send("events", Message.new(nil, :type => "abort",
|
186
|
+
:clients => @command_dispatcher.format_clients))
|
187
|
+
raise WEBrick::HTTPStatus::EOFError
|
188
|
+
rescue CommandDispatcher::SHUTDOWN
|
189
|
+
raise WEBrick::HTTPStatus::EOFError
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def handle_content(req, res)
|
194
|
+
# $stderr.puts "handle_content:type=#{res.content_type}, status=#{res.status}, encoding=#{res.header["content-encoding"]}"
|
195
|
+
if res.content_type =~ %r{^text/html} && res.status == 200
|
196
|
+
res.flush_body
|
197
|
+
# we cannot always trust content_type, so check if the content looks like html
|
198
|
+
body = res.body
|
199
|
+
if res.header["content-encoding"] == "gzip"
|
200
|
+
body = Zlib::GzipReader.new(StringIO.new(body)).read
|
201
|
+
end
|
202
|
+
if body =~ /^\s*</
|
203
|
+
body = body.dup
|
204
|
+
# puts "injecting javascript"
|
205
|
+
script_tag = '<script type="text/javascript" src="/_remote_js_proxy/agent.js"></script>'
|
206
|
+
unless body.sub!(%r{<head( .*?)?>}i){|s|s+script_tag}
|
207
|
+
body = script_tag + body
|
208
|
+
end
|
209
|
+
if res.header["content-encoding"] == "gzip"
|
210
|
+
io = StringIO.new
|
211
|
+
writer = Zlib::GzipWriter.new(io)
|
212
|
+
writer.write(body)
|
213
|
+
writer.close
|
214
|
+
res.body = io.string
|
215
|
+
else
|
216
|
+
res.body = body
|
217
|
+
end
|
218
|
+
res.content_length = res.body.size if res.content_length
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
data/lib/jscmd/shell.rb
ADDED
@@ -0,0 +1,201 @@
|
|
1
|
+
begin
|
2
|
+
require "readline"
|
3
|
+
rescue LoadError
|
4
|
+
end
|
5
|
+
|
6
|
+
module JSCommander
|
7
|
+
class Shell
|
8
|
+
class SimpleConsole
|
9
|
+
def initialize(input = $stdin)
|
10
|
+
@input = input
|
11
|
+
end
|
12
|
+
|
13
|
+
def show_banner
|
14
|
+
end
|
15
|
+
|
16
|
+
def readline
|
17
|
+
begin
|
18
|
+
line = @input.readline
|
19
|
+
line.chomp! if line
|
20
|
+
line
|
21
|
+
rescue EOFError
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def close
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class ReadlineConsole
|
31
|
+
HISTORY_FILE = ".jscmd_history"
|
32
|
+
MAX_HISTORY = 200
|
33
|
+
|
34
|
+
def history_path
|
35
|
+
File.join(ENV['HOME'] || ENV['USERPROFILE'], HISTORY_FILE)
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(shell)
|
39
|
+
@shell = shell
|
40
|
+
if File.exist?(history_path)
|
41
|
+
hist = File.readlines(history_path).map{|line| line.chomp}
|
42
|
+
Readline::HISTORY.push(*hist)
|
43
|
+
end
|
44
|
+
if Readline.methods.include?("basic_word_break_characters=")
|
45
|
+
Readline.basic_word_break_characters = " \t\n\\`@><=;|&{([+-*/%"
|
46
|
+
end
|
47
|
+
Readline.completion_append_character = nil
|
48
|
+
Readline.completion_proc = @shell.method(:complete_property).to_proc
|
49
|
+
end
|
50
|
+
|
51
|
+
def show_banner
|
52
|
+
$stderr.puts "Press Ctrl+D to exit."
|
53
|
+
end
|
54
|
+
|
55
|
+
def close
|
56
|
+
open(history_path, "wb") do |f|
|
57
|
+
history = Readline::HISTORY.to_a
|
58
|
+
if history.size > MAX_HISTORY
|
59
|
+
history = history[history.size - MAX_HISTORY, MAX_HISTORY]
|
60
|
+
end
|
61
|
+
history.each{|line| f.puts(line)}
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def readline
|
66
|
+
line = Readline.readline("#{@shell.clients}> ", true)
|
67
|
+
Readline::HISTORY.pop if /^\s*$/ =~ line
|
68
|
+
line
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def console
|
73
|
+
@console ||= $stdin.tty? ? ReadlineConsole.new(self) : SimpleConsole.new
|
74
|
+
# @console ||= SimpleConsole.new
|
75
|
+
end
|
76
|
+
|
77
|
+
attr_reader :clients
|
78
|
+
|
79
|
+
def initialize(broker)
|
80
|
+
@broker = broker
|
81
|
+
@clients = nil
|
82
|
+
@msg_lock = Mutex.new
|
83
|
+
@wait_for_event = {}
|
84
|
+
end
|
85
|
+
|
86
|
+
def generate_id
|
87
|
+
Array.new(16){rand(62)}.pack("C*").tr("\x00-\x3d", "A-Za-z0-9")
|
88
|
+
end
|
89
|
+
|
90
|
+
def send_script(line, type, &handler)
|
91
|
+
queue = Queue.new
|
92
|
+
@msg_lock.synchronize do
|
93
|
+
id = generate_id
|
94
|
+
@broker.send "commands", Message.new(line,
|
95
|
+
:type => type,
|
96
|
+
:id => id)
|
97
|
+
# wait until it gets response
|
98
|
+
@wait_for_event[id] = [queue, proc{|msg| yield msg}]
|
99
|
+
end
|
100
|
+
queue.pop
|
101
|
+
end
|
102
|
+
|
103
|
+
def object_props(object)
|
104
|
+
send_script(object, "properties") do |msg|
|
105
|
+
if msg.attributes["type"] == "value" && msg.body
|
106
|
+
msg.body.split(/,/)
|
107
|
+
else
|
108
|
+
[]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def complete_property(word)
|
114
|
+
if word =~ /\.$/
|
115
|
+
object_props($`).map{|name| word + name}
|
116
|
+
elsif word =~ /\.([^.]+)$/
|
117
|
+
prefix = $1
|
118
|
+
parent = $`
|
119
|
+
props = object_props(parent)
|
120
|
+
props.select{|name|name[0...(prefix.size)] == prefix}.map{|name| "#{parent}.#{name}"}
|
121
|
+
else
|
122
|
+
props = object_props("this")
|
123
|
+
prefix = word
|
124
|
+
props.select{|name|name[0...(prefix.size)] == prefix}
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def run
|
129
|
+
console.show_banner
|
130
|
+
|
131
|
+
main_thread = Thread.current
|
132
|
+
|
133
|
+
@broker.subscribe("events") do |msg|
|
134
|
+
id = msg.attributes["in-reply-to"]
|
135
|
+
type = msg.attributes["type"]
|
136
|
+
@clients = msg.attributes["clients"]
|
137
|
+
if id
|
138
|
+
@msg_lock.synchronize do
|
139
|
+
handler = @wait_for_event.delete(id)
|
140
|
+
if handler
|
141
|
+
queue, proc = handler
|
142
|
+
queue.push(proc.call(msg))
|
143
|
+
end
|
144
|
+
end
|
145
|
+
msg = nil
|
146
|
+
elsif "abort" == type
|
147
|
+
@msg_lock.synchronize do
|
148
|
+
handler = @wait_for_event.values.first
|
149
|
+
if handler
|
150
|
+
queue, proc = handler
|
151
|
+
queue.push(nil)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
unless msg.nil?
|
156
|
+
case type
|
157
|
+
when "value", "error"
|
158
|
+
puts msg.body
|
159
|
+
when "abort"
|
160
|
+
puts "aborted"
|
161
|
+
when "connect"
|
162
|
+
puts
|
163
|
+
end
|
164
|
+
if Process.methods.include?("kill")
|
165
|
+
Process.kill "INT", $$ # interrupt readline
|
166
|
+
else
|
167
|
+
# JRuby
|
168
|
+
main_thread.raise Interrupt
|
169
|
+
end
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
begin
|
174
|
+
loop do
|
175
|
+
break unless line = console.readline
|
176
|
+
if line.chomp == ''
|
177
|
+
send_script(nil, "ping") {}
|
178
|
+
else
|
179
|
+
send_script(line, "eval") do |msg|
|
180
|
+
puts msg.body
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
$stderr.puts "Exiting shell..."
|
185
|
+
rescue Interrupt
|
186
|
+
# $stderr.puts "int"
|
187
|
+
retry
|
188
|
+
rescue SystemExit
|
189
|
+
return
|
190
|
+
rescue Exception => e
|
191
|
+
$stderr.puts "#{e.inspect} at:\n#{e.backtrace.join("\n")}"
|
192
|
+
ensure
|
193
|
+
begin
|
194
|
+
console.close
|
195
|
+
rescue Exception => e
|
196
|
+
$stderr.puts "failed to close console: #{e.inspect}"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module JSCommander
|
2
|
+
# This broker proxy should be instantiated in each process.
|
3
|
+
class StompProxy
|
4
|
+
def initialize(url = nil)
|
5
|
+
require "stomp"
|
6
|
+
|
7
|
+
url = "stomp://localhost:61613/" if url.nil?
|
8
|
+
@stomp = Stomp::Client.new(url)
|
9
|
+
end
|
10
|
+
|
11
|
+
def subscribe(name, &proc)
|
12
|
+
@stomp.subscribe("/topic/#{name}") do |msg|
|
13
|
+
attributes = {}
|
14
|
+
msg.headers.each do |key, value|
|
15
|
+
if key =~ /^jscmd\.(.*)/
|
16
|
+
attributes[$1] = value
|
17
|
+
end
|
18
|
+
end
|
19
|
+
proc.call(Message.new(msg.body, attributes))
|
20
|
+
end
|
21
|
+
proc
|
22
|
+
end
|
23
|
+
|
24
|
+
def unsubscribe(name, subscriber)
|
25
|
+
@stomp.unsubscribe("/topic/#{name}")
|
26
|
+
end
|
27
|
+
|
28
|
+
def send(name, msg)
|
29
|
+
headers = {}
|
30
|
+
msg.attributes.each do |key, value|
|
31
|
+
headers["jscmd." + key] = value
|
32
|
+
end
|
33
|
+
@stomp.send("/topic/#{name}", msg.body || "", headers)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "cgi"
|
2
|
+
|
3
|
+
module JSCommander
|
4
|
+
class URLForwarder < WEBrick::HTTPServer
|
5
|
+
def initialize(broker, args)
|
6
|
+
@broker = broker
|
7
|
+
super(args)
|
8
|
+
mount_proc("/send/", method(:forward_url).to_proc)
|
9
|
+
mount_proc("/", method(:show_bookmarklet).to_proc)
|
10
|
+
end
|
11
|
+
|
12
|
+
def show_bookmarklet(req, res)
|
13
|
+
res.content_type = "text/html"
|
14
|
+
res.body = <<EOS
|
15
|
+
<html>
|
16
|
+
<head>
|
17
|
+
<title>Bookmarklet for forwarding URL</title>
|
18
|
+
<script type="text/javascript">
|
19
|
+
function setBrowserName(name) {
|
20
|
+
var link = document.getElementById("link");
|
21
|
+
link.removeChild(link.firstChild);
|
22
|
+
link.appendChild(document.createTextNode("Open with " + name));
|
23
|
+
}
|
24
|
+
</script>
|
25
|
+
</head>
|
26
|
+
<body>
|
27
|
+
<form onsubmit="return false">Enter the name of another browser: <input type="text" value="Wii" oninput="setBrowserName(this.value)"/></form>
|
28
|
+
<p>Drag the following link to your bookmarks toolbar.</p>
|
29
|
+
<p><a id="link" href="javascript:void(window.open('http://localhost:#{@config[:Port]}/send/'+escape(location.href)))">Open with Wii</a></p>
|
30
|
+
</body>
|
31
|
+
EOS
|
32
|
+
end
|
33
|
+
|
34
|
+
def forward_url(req, res)
|
35
|
+
uri = CGI.unescape(req.unparsed_uri.sub(%r{^/send/}, ""))
|
36
|
+
puts "opening " + uri
|
37
|
+
line = "(function() { var c = location.href; var l = '#{uri.gsub(/\\/, "\\\\").gsub("'", "\\'")}'; location.href = l; if (l.replace(/#.*$/,'') == c.replace(/#.*$/,'')) location.reload(); })()"
|
38
|
+
@broker.send("commands", Message.new(line, :type => "eval"))
|
39
|
+
res.content_type = "text/html"
|
40
|
+
res.body = '<html><head><script type="text/javascript">window.close()</script></head></html>'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/jscmd/version.rb
CHANGED
data/lib/jscmd.rb
CHANGED
@@ -1 +1,14 @@
|
|
1
|
-
Dir[File.join(File.dirname(__FILE__), 'jscmd/**/*.rb')].sort.each { |lib| require lib }
|
1
|
+
# Dir[File.join(File.dirname(__FILE__), 'jscmd/**/*.rb')].sort.each { |lib| require lib }
|
2
|
+
|
3
|
+
# message
|
4
|
+
require "jscmd/message"
|
5
|
+
|
6
|
+
# brokers
|
7
|
+
require "jscmd/inprocessbroker"
|
8
|
+
require "jscmd/pipebroker"
|
9
|
+
require "jscmd/stompproxy"
|
10
|
+
|
11
|
+
# clients
|
12
|
+
require "jscmd/proxyserver"
|
13
|
+
require "jscmd/shell"
|
14
|
+
require "jscmd/urlforwarder"
|
data/test/test_helper.rb
CHANGED