terminus 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/README.rdoc +27 -89
  2. data/bin/terminus +7 -23
  3. data/lib/capybara/driver/terminus.rb +30 -11
  4. data/lib/terminus.rb +33 -22
  5. data/lib/terminus/application.rb +13 -7
  6. data/lib/terminus/browser.rb +110 -28
  7. data/lib/terminus/controller.rb +51 -20
  8. data/lib/terminus/host.rb +22 -0
  9. data/lib/terminus/node.rb +23 -4
  10. data/lib/terminus/proxy.rb +62 -0
  11. data/lib/terminus/proxy/driver_body.rb +63 -0
  12. data/lib/terminus/proxy/external.rb +25 -0
  13. data/lib/terminus/proxy/rewrite.rb +20 -0
  14. data/lib/terminus/public/icon.png +0 -0
  15. data/lib/terminus/public/loader.js +1 -0
  16. data/lib/terminus/public/style.css +43 -0
  17. data/lib/terminus/public/syn/browsers.js +150 -0
  18. data/lib/terminus/public/syn/drag/drag.js +322 -0
  19. data/lib/terminus/public/syn/key.js +905 -0
  20. data/lib/terminus/public/syn/mouse.js +284 -0
  21. data/lib/terminus/public/syn/synthetic.js +830 -0
  22. data/lib/{public → terminus/public}/terminus.js +109 -40
  23. data/lib/terminus/server.rb +5 -12
  24. data/lib/terminus/timeouts.rb +2 -2
  25. data/lib/{views/bookmarklet.erb → terminus/views/bootstrap.erb} +17 -12
  26. data/lib/terminus/views/index.erb +21 -0
  27. data/lib/terminus/views/infinite.html +16 -0
  28. data/spec/reports/chrome.txt +748 -0
  29. data/spec/reports/firefox.txt +748 -0
  30. data/spec/reports/opera.txt +748 -0
  31. data/spec/reports/safari.txt +748 -0
  32. data/spec/spec_helper.rb +18 -14
  33. data/spec/terminus_driver_spec.rb +7 -5
  34. data/spec/terminus_session_spec.rb +5 -18
  35. metadata +71 -57
  36. data/lib/public/loader.js +0 -1
  37. data/lib/public/style.css +0 -49
  38. data/lib/public/syn.js +0 -2355
  39. data/lib/views/index.erb +0 -32
@@ -2,13 +2,11 @@ module Terminus
2
2
  class Controller
3
3
 
4
4
  include Timeouts
5
- attr_accessor :dock_host
6
5
 
7
6
  def initialize
8
- @connected = false
9
- @browsers = {}
10
- @ping_callbacks = []
11
- trap('INT') { exit }
7
+ @connected = false
8
+ @browsers = {}
9
+ @host_aliases = {}
12
10
  end
13
11
 
14
12
  def browser(id = nil)
@@ -16,14 +14,15 @@ module Terminus
16
14
  @browsers[id] ||= Browser.new(self)
17
15
  end
18
16
 
17
+ def browsers
18
+ @browsers.values
19
+ end
20
+
19
21
  def browser=(params)
20
- @browser = case params
21
- when :docked then @browsers.values.find { |b| b.docked? }
22
- when Browser then params
23
- when Hash then
24
- wait_with_timeout(:named_window) do
25
- @browsers.values.find { |b| b.name == params[:name] }
26
- end
22
+ ensure_connection
23
+ return @browser = params if Browser === params
24
+ @browser = wait_with_timeout(:selected_browser) do
25
+ @browsers.values.find { |b| b === params }
27
26
  end
28
27
  end
29
28
 
@@ -32,14 +31,9 @@ module Terminus
32
31
  @browser = nil if @browser == browser
33
32
  end
34
33
 
35
- def ensure_browser
36
- ensure_connection
37
- wait_with_timeout(:browser) { @browser }
38
- end
39
-
40
- def ensure_docked_browser
34
+ def ensure_browsers(n = 1)
41
35
  ensure_connection
42
- wait_with_timeout(:docked_browser) { @browsers.any? { |id,b| b.docked? } }
36
+ wait_with_timeout(:browsers) { @browsers.size >= n }
43
37
  end
44
38
 
45
39
  def messenger
@@ -57,6 +51,32 @@ module Terminus
57
51
  @browsers.each { |id, b| b.return_to_dock }
58
52
  end
59
53
 
54
+ def rewrite_local(url, dock_host)
55
+ uri = URI.parse(url)
56
+ uri.host = '127.0.0.1' if uri.host == dock_host
57
+ uri.path = '' if uri.path == '/'
58
+
59
+ # 1.8.7 does not have Hash#key, and 1.9.2 gives warnings for #index
60
+ remote_host = @host_aliases.respond_to?(:key) ?
61
+ @host_aliases.key(Host.new(uri)) :
62
+ @host_aliases.index(Host.new(uri))
63
+
64
+ if remote_host
65
+ uri.host = remote_host.host
66
+ uri.port = remote_host.port
67
+ end
68
+ uri.host = dock_host if dock_host and uri.host =~ LOCALHOST
69
+ uri
70
+ end
71
+
72
+ def rewrite_remote(url, dock_host = nil)
73
+ uri = URI.parse(url)
74
+ return uri unless URI::HTTP === uri and uri.host !~ LOCALHOST and uri.host != dock_host
75
+ server = boot(uri)
76
+ uri.host, uri.port = server.host, server.port
77
+ uri
78
+ end
79
+
60
80
  private
61
81
 
62
82
  def accept_ping(message)
@@ -64,7 +84,18 @@ module Terminus
64
84
  end
65
85
 
66
86
  def accept_result(message)
67
- browser(message['id']).result!(message)
87
+ browser = @browsers[message['id']]
88
+ browser.result!(message) if browser
89
+ end
90
+
91
+ def boot(remote_uri)
92
+ host = Host.new(remote_uri)
93
+ @host_aliases[host] ||= begin
94
+ server = Capybara::Server.new(Proxy[host])
95
+ Thread.new { server.boot }
96
+ sleep 0.1 until server.port
97
+ Host.new(server)
98
+ end
68
99
  end
69
100
 
70
101
  def ensure_connection
@@ -0,0 +1,22 @@
1
+ module Terminus
2
+ class Host
3
+
4
+ attr_reader :host, :port
5
+
6
+ def initialize(uri)
7
+ @host = uri.host
8
+ @port = uri.port
9
+ end
10
+
11
+ def eql?(other)
12
+ host == other.host and
13
+ port == other.port
14
+ end
15
+ alias :== :eql?
16
+
17
+ def hash
18
+ [host, port].hash
19
+ end
20
+
21
+ end
22
+ end
@@ -3,17 +3,30 @@ module Terminus
3
3
 
4
4
  attr_reader :id
5
5
 
6
- def initialize(browser, id)
7
- @browser, @id = browser, id
6
+ def initialize(browser, id, driver = nil)
7
+ @browser, @id, @driver = browser, id, driver
8
+ end
9
+
10
+ def checked?
11
+ !!self['checked']
12
+ end
13
+
14
+ def selected?
15
+ !!self['selected']
8
16
  end
9
17
 
10
18
  def click
11
19
  page = @browser.page_id
12
- command = @browser.tell([:click, @id])
20
+ options = @driver ? @driver.options : {}
21
+ command = @browser.tell([:click, @id, options])
13
22
 
14
- @browser.wait_with_timeout(:click_response) do
23
+ result = @browser.wait_with_timeout(:click_response) do
15
24
  @browser.result(command) || (@browser.page_id != page)
16
25
  end
26
+
27
+ if Hash === result and String === result[:value]
28
+ raise Capybara::TimeoutError, result[:value]
29
+ end
17
30
  end
18
31
 
19
32
  def drag_to(node)
@@ -42,10 +55,16 @@ module Terminus
42
55
  raise Capybara::UnselectNotAllowed.new unless allowed
43
56
  end
44
57
 
58
+ def to_s
59
+ "<#{self.class.name} #{@id}>"
60
+ end
61
+ alias :inspect :to_s
62
+
45
63
  alias :select_option :select
46
64
  alias :unselect_option :unselect
47
65
 
48
66
  SYNC_DSL_METHODS = [ [:[], :attribute],
67
+ [:[]=, :set_attribute],
49
68
  :tag_name,
50
69
  :text,
51
70
  :value,
@@ -0,0 +1,62 @@
1
+ module Terminus
2
+ class Proxy
3
+
4
+ CONTENT_TYPES = %w[text/plain text/html]
5
+ BASIC_RESOURCES = %w[/favicon.ico /robots.txt]
6
+ MAX_REDIRECTS = 5
7
+
8
+ INFINITE_REDIRECT_RESPONSE = [
9
+ 200,
10
+ {'Content-Type' => 'text/html'},
11
+ [File.read(ROOT + '/terminus/views/infinite.html')]
12
+ ]
13
+
14
+ autoload :DriverBody, ROOT + '/terminus/proxy/driver_body'
15
+ autoload :External, ROOT + '/terminus/proxy/external'
16
+ autoload :Rewrite, ROOT + '/terminus/proxy/rewrite'
17
+
18
+ def self.[](app)
19
+ @proxies ||= {}
20
+ @proxies[app] ||= new(app)
21
+ end
22
+
23
+ def self.content_type(response)
24
+ type = response[1].find { |key, _| key =~ /^content-type$/i }
25
+ type && type.last.split(';').first
26
+ end
27
+
28
+ def initialize(app)
29
+ app = External.new(app) if Host === app
30
+ @app = app
31
+ @redirects = 0
32
+ end
33
+
34
+ def call(env)
35
+ response = @app.call(env)
36
+
37
+ if response.first == 302
38
+ @redirects += 1
39
+ if @redirects > MAX_REDIRECTS
40
+ @redirects = 0
41
+ response = INFINITE_REDIRECT_RESPONSE
42
+ end
43
+ else
44
+ @redirects = 0
45
+ end
46
+
47
+ return response if response.first == -1 or # async response
48
+ BASIC_RESOURCES.include?(env['PATH_INFO']) or # not pages - favicon etc
49
+ env.has_key?('HTTP_X_REQUESTED_WITH') # Ajax calls
50
+
51
+ content_type = Proxy.content_type(response)
52
+ return response unless CONTENT_TYPES.include?(content_type)
53
+
54
+ response[1] = response[1].dup
55
+ response[1].delete_if { |key, _| key =~ /^content-length$/i }
56
+ response[2] = DriverBody.new(env, response)
57
+
58
+ response
59
+ end
60
+
61
+ end
62
+ end
@@ -0,0 +1,63 @@
1
+ module Terminus
2
+ class Proxy
3
+
4
+ class DriverBody
5
+ TEMPLATE = ERB.new(<<-DRIVER)
6
+ <script id="terminus-data" type="text/javascript">
7
+ TERMINUS_STATUS = <%= @response.first %>;
8
+ TERMINUS_HEADERS = {};
9
+ <% @response[1].each do |key, value| %>
10
+ TERMINUS_HEADERS[<%= key.inspect %>] = <%= value.inspect %>;
11
+ <% end %>
12
+ TERMINUS_SOURCE = <%= page_source %>;
13
+ </script>
14
+ <script type="text/javascript">
15
+ (function() {
16
+ var terminusScript = document.getElementById('terminus-data');
17
+ terminusScript.parentNode.removeChild(terminusScript);
18
+ })();
19
+ </script>
20
+ <%= driver_script %>
21
+ DRIVER
22
+
23
+ def initialize(env, response)
24
+ @env = env
25
+ @response = response
26
+ @body = response[2]
27
+ end
28
+
29
+ def each(&block)
30
+ script_injected = false
31
+ @source = ''
32
+
33
+ @body.each do |fragment|
34
+ @source << fragment
35
+ output = inject_script(fragment)
36
+ script_injected ||= (output != fragment)
37
+ block.call(output)
38
+ end
39
+
40
+ unless script_injected
41
+ block.call(TEMPLATE.result(binding))
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def inject_script(fragment)
48
+ fragment.gsub(/((?:^\s*)?<\/body>)/i) do
49
+ TEMPLATE.result(binding) + $1
50
+ end
51
+ end
52
+
53
+ def driver_script
54
+ Terminus.driver_script(@env['SERVER_NAME'])
55
+ end
56
+
57
+ def page_source
58
+ @source.inspect.gsub('</script>', '</scr"+"ipt>')
59
+ end
60
+ end
61
+
62
+ end
63
+ end
@@ -0,0 +1,25 @@
1
+ module Terminus
2
+ class Proxy
3
+
4
+ class External < Rack::Proxy
5
+ def initialize(uri)
6
+ @uri = uri
7
+ end
8
+
9
+ def rewrite_env(env)
10
+ env = env.dup
11
+ env['SERVER_NAME'] = @uri.host
12
+ env['SERVER_PORT'] = @uri.port
13
+ env['HTTP_HOST'] = "#{@uri.host}:#{@uri.port}"
14
+ env
15
+ end
16
+
17
+ def call(env)
18
+ response = super
19
+ response[2].extend(Rewrite)
20
+ response
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,20 @@
1
+ module Terminus
2
+ class Proxy
3
+
4
+ module Rewrite
5
+ def each(&block)
6
+ handler = lambda do |fragment|
7
+ block.call(rewrite(fragment))
8
+ end
9
+ super(&handler)
10
+ end
11
+
12
+ def rewrite(fragment)
13
+ fragment.gsub(/\b(action|href)="([^"]*)"/i) do
14
+ %Q{#{$1}="#{ Terminus.rewrite_remote($2) }"}
15
+ end
16
+ end
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1 @@
1
+ (function(){var a=(typeof this.global==='object')?this.global:this;a.JS=a.JS||{};JS.ENV=a})();JS.Package=function(a){var b=JS.Package.OrderedSet;JS.Package._5(this);this._0=a;this._2=new b();this._6=new b();this._c=new b();this._d=new b();this._3={};this._7={}};(function(d){d.displayName='Package';d.toString=function(){return d.displayName};d.log=function(a){if(typeof window==='undefined')return;if(typeof window.runtime==='object')window.runtime.trace(a);if(window.console&&console.info)console.info(a)};var p=d.OrderedSet=function(a){this._e=this.list=[];this._5={};if(!a)return;for(var b=0,c=a.length;b<c;b++)this.push(a[b])};p.prototype.push=function(a){var b=(a.id!==undefined)?a.id:a,c=this._5;if(c.hasOwnProperty(b))return;c[b]=this._e.length;this._e.push(a)};var m=d.Deferred=function(){this._f='deferred';this._g=null;this._h=[]};m.prototype.callback=function(a,b){if(this._f==='succeeded')a.call(b,this._g);else this._h.push([a,b])};m.prototype.succeed=function(a){this._f='succeeded';this._g=a;var b;while(b=this._h.shift())b[0].call(b[1],a)};d.ENV=JS.ENV;if((this.document||{}).getElementsByTagName){var q=document.getElementsByTagName('script')[0];d._w=(q.readyState!==undefined)}d.onerror=function(a){throw a};d._i=function(a){d.onerror(new Error(a));};var j=d.prototype,n=[['requires','_6'],['uses','_c'],['styling','_d']],o=n.length;while(o--)(function(pair){var r=pair[0],s=pair[1];j[r]=function(){var a=arguments.length,b;for(b=0;b<a;b++)this[s].push(arguments[b]);return this}})(n[o]);j.provides=function(){var a=arguments.length,b;for(b=0;b<a;b++){this._2.push(arguments[b]);d._8(arguments[b]).pkg=this}return this};j.setup=function(a){this._j=a;return this};j._r=function(a,b,c){if(this._7[a])return b.call(c);var e=this._3[a]=this._3[a]||[];e.push([b,c]);this._s()};j._1=function(a){if(this._7[a])return false;this._7[a]=true;var b=this._3[a];if(!b)return true;delete this._3[a];for(var c=0,e=b.length;c<e;c++)b[c][0].call(b[c][1]);return true};j._k=function(a){if(!a&&this.__isLoaded!==undefined)return this.__isLoaded;var b=this._2.list,c=b.length,e,f;while(c--){e=b[c];f=d._4(e,this._l);if(f!==undefined)continue;if(a)return d._i('Expected package at '+this._0+' to define '+e);else return this.__isLoaded=false}return this.__isLoaded=true};j._s=function(){if(!this._1('request'))return;this._t();var h=this._6.list.concat(this._c.list),i=h.length;d.when({load:h});d.when({complete:this._6.list},function(){d.when({complete:h,load:[this]},function(){this._1('complete')},this);var b=this,c=function(a){b._l=a;if(b._j)b._j();b._k(true);b._1('load')};if(this._k()){this._1('download');return this._1('load')}if(this._0===undefined)return d._i('No load path found for '+this._2.list[0]);typeof this._0==='function'?this._0(c):d.Loader.loadFile(this._0,c,this._m);if(!d.Loader.loadStyle)return;var e=this._d.list,f=e.length;while(f--)d.Loader.loadStyle(e[f]);this._1('download')},this)};j._t=function(){if(typeof this._0!=='string'||!d.Loader.fetch)return;this._m=this._m||d.Loader.fetch(this._0)};j.toString=function(){return'Package:'+this._2.list.join(',')};d.when=function(a,b,c){var e=[],f={},h,i,g;for(h in a){if(!a.hasOwnProperty(h))continue;f[h]=[];i=new d.OrderedSet(a[h]);g=i.list.length;while(g--)e.push([h,i.list[g],g])}var k=g=e.length;if(k===0)return b&&b.call(c,f);while(g--)(function(h){var l=d._9(h[1]);l._r(h[0],function(){f[h[0]][h[2]]=d._4(h[1],l._l);k-=1;if(k===0&&b)b.call(c,f)})})(e[g])};d._n=1;d._a={};d._b={};d._o=[];d._5=function(a){a.id=this._n;this._n+=1};d._p=function(a){var b=a.toString();return this._a[b]=this._a[b]||new this(a)};d._9=function(a){if(typeof a!=='string')return a;var b=this._8(a);if(b.pkg)return b.pkg;var c=this._u(a);if(c)return c;var e=new this();e.provides(a);return e};d.remove=function(a){var b=this._9(a);delete this._b[a];delete this._a[b._0]};d._v=function(a,b){this._o.push([a,b])};d._u=function(e){var f=this._o,h=f.length,i,g,k;for(i=0;i<h;i++){g=f[i];if(!g[0].test(e))continue;k=g[1].from+'/'+e.replace(/([a-z])([A-Z])/g,function(a,b,c){return b+'_'+c}).replace(/\./g,'/').toLowerCase()+'.js';var l=new this(k);l.provides(e);if(k=g[1].require)l.requires(e.replace(g[0],k));return l}return null};d._8=function(a){return this._b[a]=this._b[a]||{}};d._4=function(a,b){if(typeof a!=='string')return undefined;var c=b?{}:this._8(a);if(c.obj!==undefined)return c.obj;var e=b||this.ENV,f=a.split('.'),h;while(h=f.shift())e=e&&e[h];if(b&&e===undefined)return this._4(a);return c.obj=e}})(JS.Package);JS.Package.DomLoader={HOST_REGEX:/^https?\:\/\/[^\/]+/i,usable:function(){return!!JS.Package._4('window.document.getElementsByTagName')},__FILE__:function(){var a=document.getElementsByTagName('script');return a[a.length-1].src},fetch:function(a){this.HOST=this.HOST||this.HOST_REGEX.exec(window.location.href);var b=this.HOST_REGEX.exec(a);if(!this.HOST||(b&&b[0]!==this.HOST[0]))return null;JS.Package.log('Loading '+a);var c=new JS.Package.Deferred(),e=this,f=window.ActiveXObject?new ActiveXObject("Microsoft.XMLHTTP"):new XMLHttpRequest();f.open('GET',a,true);f.onreadystatechange=function(){if(f.readyState!==4)return;f.onreadystatechange=e._q;c.succeed(f.responseText);f=null};f.send(null);return c},loadFile:function(c,e,f){var h=this,i=document.getElementsByTagName('head')[0],g=document.createElement('script');g.type='text/javascript';if(f)return f.callback(function(code){JS.Package.log('Executing '+c);i.appendChild(g);g.text=code;e()});JS.Package.log('Loading and executing '+c);g.src=c;g.onload=g.onreadystatechange=function(){var a=g.readyState,b=g.status;if(!a||a==='loaded'||a==='complete'||(a===4&&b===200)){e();g.onload=g.onreadystatechange=h._q;i=null;g=null}};i.appendChild(g)},loadStyle:function(a){var b=document.createElement('link');b.rel='stylesheet';b.type='text/css';b.href=a;document.getElementsByTagName('head')[0].appendChild(b)},_q:function(){}};JS.Package.Loader=JS.Package.DomLoader;JS.Package.DSL={__FILE__:function(){return JS.Package.Loader.__FILE__()},pkg:function(a,b){var c=b?JS.Package._p(b):JS.Package._9(a);c.provides(a);return c},file:function(a){return JS.Package._p(a)},load:function(a,b){JS.Package.Loader.loadFile(a,b)},autoload:function(a,b){JS.Package._v(a,b)}};JS.Package.DSL.loader=JS.Package.DSL.file;JS.Packages=function(a){a.call(JS.Package.DSL)};JS.require=function(){var b=[],c=0;while(typeof arguments[c]==='string'){b.push(arguments[c]);c+=1}var e=arguments[c],f=arguments[c+1];JS.Package.when({complete:b},function(a){if(!e)return;e.apply(f||null,a&&a.complete)})};
@@ -0,0 +1,43 @@
1
+ body {
2
+ background: #fff;
3
+ margin: 0;
4
+ padding: 0;
5
+ }
6
+
7
+ .bg {
8
+ position: absolute;
9
+ width: 100%;
10
+ height: 200px;
11
+ background: #f0f0f0;
12
+ border-bottom: 1px solid #ccc;
13
+ z-index: -1;
14
+ }
15
+
16
+ h1 {
17
+ margin: 0 auto;
18
+ padding: 40px 0 0;
19
+ width: 250px;
20
+ }
21
+
22
+ h1 a {
23
+ display: block;
24
+ background: url(/icon.png) center center no-repeat;
25
+ height: 0;
26
+ padding-top: 250px;
27
+ overflow: hidden;
28
+ }
29
+
30
+ p {
31
+ color: #666;
32
+ width: 250px;
33
+ margin: 40px auto;
34
+ font: 12px/1.5 FreeSans, Helvetica, Arial, sans-serif;
35
+ text-align: center;
36
+ }
37
+
38
+ p a {
39
+ color: #28aad4;
40
+ font-weight: bold;
41
+ text-decoration: none;
42
+ }
43
+
@@ -0,0 +1,150 @@
1
+ steal.then(function(){
2
+ Syn.key.browsers = {
3
+ webkit : {
4
+ 'prevent':
5
+ {"keyup":[],"keydown":["char","keypress"],"keypress":["char"]},
6
+ 'character':
7
+ {"keydown":[0,"key"],"keypress":["char","char"],"keyup":[0,"key"]},
8
+ 'specialChars':
9
+ {"keydown":[0,"char"],"keyup":[0,"char"]},
10
+ 'navigation':
11
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
12
+ 'special':
13
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
14
+ 'tab':
15
+ {"keydown":[0,"char"],"keyup":[0,"char"]},
16
+ 'pause-break':
17
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
18
+ 'caps':
19
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
20
+ 'escape':
21
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
22
+ 'num-lock':
23
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
24
+ 'scroll-lock':
25
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
26
+ 'print':
27
+ {"keyup":[0,"key"]},
28
+ 'function':
29
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
30
+ '\r':
31
+ {"keydown":[0,"key"],"keypress":["char","key"],"keyup":[0,"key"]}
32
+ },
33
+ gecko : {
34
+ 'prevent':
35
+ {"keyup":[],"keydown":["char"],"keypress":["char"]},
36
+ 'character':
37
+ {"keydown":[0,"key"],"keypress":["char",0],"keyup":[0,"key"]},
38
+ 'specialChars':
39
+ {"keydown":[0,"key"],"keypress":[0,"key"],"keyup":[0,"key"]},
40
+ 'navigation':
41
+ {"keydown":[0,"key"],"keypress":[0,"key"],"keyup":[0,"key"]},
42
+ 'special':
43
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
44
+ '\t':
45
+ {"keydown":[0,"key"],"keypress":[0,"key"],"keyup":[0,"key"]},
46
+ 'pause-break':
47
+ {"keydown":[0,"key"],"keypress":[0,"key"],"keyup":[0,"key"]},
48
+ 'caps':
49
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
50
+ 'escape':
51
+ {"keydown":[0,"key"],"keypress":[0,"key"],"keyup":[0,"key"]},
52
+ 'num-lock':
53
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
54
+ 'scroll-lock':
55
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
56
+ 'print':
57
+ {"keyup":[0,"key"]},
58
+ 'function':
59
+ {"keydown":[0,"key"],"keyup":[0,"key"]},
60
+ '\r':
61
+ {"keydown":[0,"key"],"keypress":[0,"key"],"keyup":[0,"key"]}
62
+ },
63
+ msie : {
64
+ 'prevent':{"keyup":[],"keydown":["char","keypress"],"keypress":["char"]},
65
+ 'character':{"keydown":[null,"key"],"keypress":[null,"char"],"keyup":[null,"key"]},
66
+ 'specialChars':{"keydown":[null,"char"],"keyup":[null,"char"]},
67
+ 'navigation':{"keydown":[null,"key"],"keyup":[null,"key"]},
68
+ 'special':{"keydown":[null,"key"],"keyup":[null,"key"]},
69
+ 'tab':{"keydown":[null,"char"],"keyup":[null,"char"]},
70
+ 'pause-break':{"keydown":[null,"key"],"keyup":[null,"key"]},
71
+ 'caps':{"keydown":[null,"key"],"keyup":[null,"key"]},
72
+ 'escape':{"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]},
73
+ 'num-lock':{"keydown":[null,"key"],"keyup":[null,"key"]},
74
+ 'scroll-lock':{"keydown":[null,"key"],"keyup":[null,"key"]},
75
+ 'print':{"keyup":[null,"key"]},
76
+ 'function':{"keydown":[null,"key"],"keyup":[null,"key"]},
77
+ '\r':{"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]}
78
+ },
79
+ opera : {
80
+ 'prevent':
81
+ {"keyup":[],"keydown":[],"keypress":["char"]},
82
+ 'character':
83
+ {"keydown":[null,"key"],"keypress":[null,"char"],"keyup":[null,"key"]},
84
+ 'specialChars':
85
+ {"keydown":[null,"char"],"keypress":[null,"char"],"keyup":[null,"char"]},
86
+ 'navigation':
87
+ {"keydown":[null,"key"],"keypress":[null,"key"]},
88
+ 'special':
89
+ {"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]},
90
+ 'tab':
91
+ {"keydown":[null,"char"],"keypress":[null,"char"],"keyup":[null,"char"]},
92
+ 'pause-break':
93
+ {"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]},
94
+ 'caps':
95
+ {"keydown":[null,"key"],"keyup":[null,"key"]},
96
+ 'escape':
97
+ {"keydown":[null,"key"],"keypress":[null,"key"]},
98
+ 'num-lock':
99
+ {"keyup":[null,"key"],"keydown":[null,"key"],"keypress":[null,"key"]},
100
+ 'scroll-lock':
101
+ {"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]},
102
+ 'print':
103
+ {},
104
+ 'function':
105
+ {"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]},
106
+ '\r':
107
+ {"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]}
108
+ }
109
+ };
110
+
111
+ Syn.mouse.browsers = {
112
+ webkit : {"right":{"mousedown":{"button":2,"which":3},"mouseup":{"button":2,"which":3},"contextmenu":{"button":2,"which":3}},
113
+ "left":{"mousedown":{"button":0,"which":1},"mouseup":{"button":0,"which":1},"click":{"button":0,"which":1}}},
114
+ opera: {"right":{"mousedown":{"button":2,"which":3},"mouseup":{"button":2,"which":3}},
115
+ "left":{"mousedown":{"button":0,"which":1},"mouseup":{"button":0,"which":1},"click":{"button":0,"which":1}}},
116
+ msie: { "right":{"mousedown":{"button":2},"mouseup":{"button":2},"contextmenu":{"button":0}},
117
+ "left":{"mousedown":{"button":1},"mouseup":{"button":1},"click":{"button":0}}},
118
+ chrome : {"right":{"mousedown":{"button":2,"which":3},"mouseup":{"button":2,"which":3},"contextmenu":{"button":2,"which":3}},
119
+ "left":{"mousedown":{"button":0,"which":1},"mouseup":{"button":0,"which":1},"click":{"button":0,"which":1}}},
120
+ gecko: {"left":{"mousedown":{"button":0,"which":1},"mouseup":{"button":0,"which":1},"click":{"button":0,"which":1}},
121
+ "right":{"mousedown":{"button":2,"which":3},"mouseup":{"button":2,"which":3},"contextmenu":{"button":2,"which":3}}}
122
+ }
123
+
124
+ //set browser
125
+ Syn.key.browser =
126
+ (function(){
127
+ if(Syn.key.browsers[window.navigator.userAgent]){
128
+ return Syn.key.browsers[window.navigator.userAgent];
129
+ }
130
+ for(var browser in Syn.browser){
131
+ if(Syn.browser[browser] && Syn.key.browsers[browser]){
132
+ return Syn.key.browsers[browser]
133
+ }
134
+ }
135
+ return Syn.key.browsers.gecko;
136
+ })();
137
+
138
+ Syn.mouse.browser =
139
+ (function(){
140
+ if(Syn.mouse.browsers[window.navigator.userAgent]){
141
+ return Syn.mouse.browsers[window.navigator.userAgent];
142
+ }
143
+ for(var browser in Syn.browser){
144
+ if(Syn.browser[browser] && Syn.mouse.browsers[browser]){
145
+ return Syn.mouse.browsers[browser]
146
+ }
147
+ }
148
+ return Syn.mouse.browsers.gecko;
149
+ })();
150
+ })