terminus 0.2.0 → 0.3.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.
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
+ })