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
@@ -1,22 +1,26 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
1
4
  root = File.dirname(__FILE__) + '/../'
2
5
 
3
6
  require root + 'vendor/capybara/spec/spec_helper'
4
7
  require root + 'lib/terminus'
5
8
 
6
- class TestApp
7
- template :layout do
8
- <<-HTML
9
- <html>
10
- <head>
11
- <meta http-equiv="Content-type" content="text/html; charset=utf-8">
12
- <title>Capybara Driver Test</title>
13
- </head>
14
- <body>
15
- <%= yield %>
16
- <%= Terminus.driver_script request.host %>
17
- </body>
18
- </html>
19
- HTML
9
+ def select_browser
10
+ if ua = ENV['USER_AGENT']
11
+ Terminus.browser = {:name => ua}
12
+ else
13
+ Terminus.browser = :docked
20
14
  end
21
15
  end
22
16
 
17
+ # We use WEBrick to boot the test app, because if we use Thin (the default) the
18
+ # slow response used to test Ajax resynchronization blocks the event loop. This
19
+ # stops Terminus receiving messages and causes false positives: the client is
20
+ # not really waiting for Ajax to complete, it's just having its messages blocked
21
+ # because EventMachine is frozen.
22
+
23
+ Capybara.server do |app, port|
24
+ handler = Rack::Handler.get('webrick')
25
+ handler.run(app, :Port => port, :AccessLog => [], :Logger => WEBrick::Log::new(nil, 0))
26
+ end
@@ -3,17 +3,19 @@ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
3
  describe Capybara::Driver::Terminus do
4
4
  before do
5
5
  @driver = Capybara::Driver::Terminus.new(TestApp)
6
- Terminus.ensure_docked_browser
7
- Terminus.browser = :docked
6
+ select_browser
8
7
  end
9
8
 
10
9
  after do
11
- Terminus.return_to_dock
10
+ Terminus.browser.return_to_dock
12
11
  end
13
-
12
+
14
13
  it_should_behave_like "driver"
14
+ it_should_behave_like "driver with header support"
15
+ it_should_behave_like "driver with status code support"
15
16
  it_should_behave_like "driver with javascript support"
16
- it_should_behave_like "driver without status code support"
17
+ it_should_behave_like "driver with resynchronization support"
18
+ it_should_behave_like "driver with frame support"
17
19
  it_should_behave_like "driver with support for window switching"
18
20
  it_should_behave_like "driver with cookies support"
19
21
  it_should_behave_like "driver with infinite redirect detection"
@@ -4,29 +4,16 @@ describe Capybara::Session do
4
4
  context 'with terminus driver' do
5
5
  before do
6
6
  @session = Capybara::Session.new(:terminus, TestApp)
7
- Terminus.ensure_docked_browser
8
- Terminus.browser = :docked
7
+ select_browser
9
8
  end
10
9
 
11
10
  after do
12
- Terminus.return_to_dock
11
+ Terminus.browser.return_to_dock
13
12
  end
14
-
15
- describe '#driver' do
16
- it "should be a terminus driver" do
17
- @session.driver.should be_an_instance_of(Capybara::Driver::Terminus)
18
- end
19
- end
20
-
21
- describe '#mode' do
22
- it "should remember the mode" do
23
- @session.mode.should == :terminus
24
- end
25
- end
26
-
13
+
27
14
  it_should_behave_like "session"
28
- it_should_behave_like "session without headers support"
15
+ it_should_behave_like "session with headers support"
16
+ it_should_behave_like "session with status code support"
29
17
  it_should_behave_like "session with javascript support"
30
- it_should_behave_like "session without status code support"
31
18
  end
32
19
  end
metadata CHANGED
@@ -1,12 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: terminus
3
3
  version: !ruby/object:Gem::Version
4
- prerelease: false
5
- segments:
6
- - 0
7
- - 2
8
- - 0
9
- version: 0.2.0
4
+ prerelease:
5
+ version: 0.3.0
10
6
  platform: ruby
11
7
  authors:
12
8
  - James Coglan
@@ -14,7 +10,7 @@ autorequire:
14
10
  bindir: bin
15
11
  cert_chain: []
16
12
 
17
- date: 2010-10-24 00:00:00 +01:00
13
+ date: 2011-07-20 00:00:00 +01:00
18
14
  default_executable:
19
15
  dependencies:
20
16
  - !ruby/object:Gem::Dependency
@@ -25,9 +21,6 @@ dependencies:
25
21
  requirements:
26
22
  - - ">="
27
23
  - !ruby/object:Gem::Version
28
- segments:
29
- - 1
30
- - 0
31
24
  version: "1.0"
32
25
  type: :runtime
33
26
  version_requirements: *id001
@@ -39,9 +32,6 @@ dependencies:
39
32
  requirements:
40
33
  - - ">="
41
34
  - !ruby/object:Gem::Version
42
- segments:
43
- - 1
44
- - 2
45
35
  version: "1.2"
46
36
  type: :runtime
47
37
  version_requirements: *id002
@@ -53,9 +43,6 @@ dependencies:
53
43
  requirements:
54
44
  - - ">="
55
45
  - !ruby/object:Gem::Version
56
- segments:
57
- - 0
58
- - 12
59
46
  version: "0.12"
60
47
  type: :runtime
61
48
  version_requirements: *id003
@@ -65,72 +52,88 @@ dependencies:
65
52
  requirement: &id004 !ruby/object:Gem::Requirement
66
53
  none: false
67
54
  requirements:
68
- - - ">="
55
+ - - ~>
69
56
  - !ruby/object:Gem::Version
70
- segments:
71
- - 0
72
- - 5
73
- - 2
74
- version: 0.5.2
57
+ version: 0.6.3
75
58
  type: :runtime
76
59
  version_requirements: *id004
77
60
  - !ruby/object:Gem::Dependency
78
- name: capybara
61
+ name: sinatra
79
62
  prerelease: false
80
63
  requirement: &id005 !ruby/object:Gem::Requirement
81
64
  none: false
82
65
  requirements:
83
66
  - - ">="
84
67
  - !ruby/object:Gem::Version
85
- segments:
86
- - 0
87
- - 4
88
- - 0
89
- version: 0.4.0
68
+ version: "1.0"
90
69
  type: :runtime
91
70
  version_requirements: *id005
92
71
  - !ruby/object:Gem::Dependency
93
- name: sinatra
72
+ name: packr
94
73
  prerelease: false
95
74
  requirement: &id006 !ruby/object:Gem::Requirement
96
75
  none: false
97
76
  requirements:
98
77
  - - ">="
99
78
  - !ruby/object:Gem::Version
100
- segments:
101
- - 1
102
- - 0
103
- version: "1.0"
79
+ version: "3.1"
104
80
  type: :runtime
105
81
  version_requirements: *id006
106
82
  - !ruby/object:Gem::Dependency
107
- name: packr
83
+ name: capybara
108
84
  prerelease: false
109
85
  requirement: &id007 !ruby/object:Gem::Requirement
110
86
  none: false
111
87
  requirements:
112
88
  - - ">="
113
89
  - !ruby/object:Gem::Version
114
- segments:
115
- - 3
116
- - 1
117
- version: "3.1"
90
+ version: 0.4.0
118
91
  type: :runtime
119
92
  version_requirements: *id007
120
93
  - !ruby/object:Gem::Dependency
121
- name: oyster
94
+ name: rack-proxy
122
95
  prerelease: false
123
96
  requirement: &id008 !ruby/object:Gem::Requirement
124
97
  none: false
125
98
  requirements:
126
99
  - - ">="
127
100
  - !ruby/object:Gem::Version
128
- segments:
129
- - 0
130
- - 9
131
- version: "0.9"
101
+ version: "0.3"
132
102
  type: :runtime
133
103
  version_requirements: *id008
104
+ - !ruby/object:Gem::Dependency
105
+ name: useragent
106
+ prerelease: false
107
+ requirement: &id009 !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: "0.3"
113
+ type: :runtime
114
+ version_requirements: *id009
115
+ - !ruby/object:Gem::Dependency
116
+ name: oyster
117
+ prerelease: false
118
+ requirement: &id010 !ruby/object:Gem::Requirement
119
+ none: false
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: "0.9"
124
+ type: :runtime
125
+ version_requirements: *id010
126
+ - !ruby/object:Gem::Dependency
127
+ name: rspec
128
+ prerelease: false
129
+ requirement: &id011 !ruby/object:Gem::Requirement
130
+ none: false
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: "0"
135
+ type: :development
136
+ version_requirements: *id011
134
137
  description:
135
138
  email: jcoglan@gmail.com
136
139
  executables:
@@ -141,26 +144,41 @@ extra_rdoc_files:
141
144
  - README.rdoc
142
145
  files:
143
146
  - README.rdoc
144
- - lib/views/index.erb
145
- - lib/views/bookmarklet.erb
146
- - lib/public/loader.js
147
- - lib/public/syn.js
148
- - lib/public/terminus.js
149
- - lib/public/style.css
147
+ - bin/terminus
150
148
  - lib/terminus.rb
151
149
  - lib/terminus/node.rb
150
+ - lib/terminus/proxy.rb
152
151
  - lib/terminus/timeouts.rb
153
152
  - lib/terminus/browser.rb
153
+ - lib/terminus/proxy/driver_body.rb
154
+ - lib/terminus/proxy/external.rb
155
+ - lib/terminus/proxy/rewrite.rb
156
+ - lib/terminus/views/bootstrap.erb
157
+ - lib/terminus/views/infinite.html
158
+ - lib/terminus/views/index.erb
154
159
  - lib/terminus/application.rb
155
160
  - lib/terminus/server.rb
161
+ - lib/terminus/public/loader.js
162
+ - lib/terminus/public/terminus.js
163
+ - lib/terminus/public/icon.png
164
+ - lib/terminus/public/style.css
156
165
  - lib/terminus/controller.rb
166
+ - lib/terminus/host.rb
157
167
  - lib/capybara/driver/terminus.rb
168
+ - spec/reports/firefox.txt
169
+ - spec/reports/chrome.txt
170
+ - spec/reports/opera.txt
171
+ - spec/reports/safari.txt
158
172
  - spec/terminus_session_spec.rb
159
173
  - spec/terminus_driver_spec.rb
160
174
  - spec/spec_helper.rb
161
- - bin/terminus
175
+ - lib/terminus/public/syn/synthetic.js
176
+ - lib/terminus/public/syn/mouse.js
177
+ - lib/terminus/public/syn/browsers.js
178
+ - lib/terminus/public/syn/key.js
179
+ - lib/terminus/public/syn/drag/drag.js
162
180
  has_rdoc: true
163
- homepage: http://github.com/jcoglan/terminus
181
+ homepage: http://terminus.jcoglan.com
164
182
  licenses: []
165
183
 
166
184
  post_install_message:
@@ -174,23 +192,19 @@ required_ruby_version: !ruby/object:Gem::Requirement
174
192
  requirements:
175
193
  - - ">="
176
194
  - !ruby/object:Gem::Version
177
- segments:
178
- - 0
179
195
  version: "0"
180
196
  required_rubygems_version: !ruby/object:Gem::Requirement
181
197
  none: false
182
198
  requirements:
183
199
  - - ">="
184
200
  - !ruby/object:Gem::Version
185
- segments:
186
- - 0
187
201
  version: "0"
188
202
  requirements: []
189
203
 
190
204
  rubyforge_project:
191
- rubygems_version: 1.3.7
205
+ rubygems_version: 1.6.2
192
206
  signing_key:
193
207
  specification_version: 3
194
- summary: Capybara driver for cross-browser testing
208
+ summary: Capybara driver for cross-browser and remote scripting
195
209
  test_files: []
196
210
 
@@ -1 +0,0 @@
1
- JS=(typeof JS==='undefined')?{}:JS;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._b=new b();this._3={};this._7={}};(function(g){var m=g.OrderedSet=function(a,b){this._c=this.list=[];this._5={};if(!a)return;for(var c=0,d=a.length;c<d;c++)this.push(b?b(a[c]):a[c])};m.prototype.push=function(a){var b=(a.id!==undefined)?a.id:a,c=this._5;if(c.hasOwnProperty(b))return;c[b]=this._c.length;this._c.push(a)};g._4=this;if((this.document||{}).getElementsByTagName){var n=document.getElementsByTagName('script')[0];g._k=(n.readyState!==undefined)}var j=g.prototype;j.addDependency=function(a){this._6.push(a)};j.addSoftDependency=function(a){this._b.push(a)};j.addName=function(a){this._2.push(a);g.getFromCache(a).pkg=this};j.onload=function(a){this._d=a};j.on=function(a,b,c){if(this._7[a])return b.call(c);var d=this._3[a]=this._3[a]||[];d.push([b,c])};j.fire=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,d=b.length;c<d;c++)b[c][0].call(b[c][1]);return true};j.isLoaded=function(a){if(!a&&this._8!==undefined)return this._8;var b=this._2.list,c=b.length,d,f;while(c--){d=b[c];f=g.getObject(d);if(f!==undefined)continue;if(a)throw new Error('Expected package at '+this._0+' to define '+d);else return this._8=false}return this._8=true};j.load=function(){if(!this.fire('request'))return;var c=this._6.list.concat(this._b.list),d='load',f={};f[d]=this._6.list;g.when(f,function(){g.when({complete:c,load:[this]},function(){this.fire('complete')},this);var a=this,b=function(){if(a._d)a._d();a.isLoaded(true);a.fire('load')};if(this.isLoaded()){this.fire('download');return this.fire('load')}if(this._0===undefined)throw new Error('No load path found for '+this._2.list[0]);typeof this._0==='function'?this._0(b):g.Loader.loadFile(this._0,b);this.fire('download')},this)};j.toString=function(){return'Package:'+this._2.list.join(',')};g.when=function(b,c,d){var f=[],h,k,i;for(h in b){if(!b.hasOwnProperty(h))continue;k=new g.OrderedSet(b[h],function(a){return g.getByName(a)});i=k.list.length;while(i--)f.push([h,k.list[i]])}var l=i=f.length;if(l===0)return c&&c.call(d);while(i--){f[i][1].on(f[i][0],function(){l-=1;if(l===0&&c)c.call(d)});f[i][1].load()}};g._e=1;g._f={};g._g={};g._h=[];g._5=function(a){a.id=this._e;this._e+=1};g.getByPath=function(a){var b=a.toString();return this._f[b]=this._f[b]||new this(a)};g.getByName=function(a){if(typeof a!=='string')return a;var b=this.getFromCache(a);if(b.pkg)return b.pkg;var c=this._i(a);if(c)return c;var d=new this();d.addName(a);return d};g.autoload=function(a,b){this._h.push([a,b])};g._i=function(d){var f=this._h,h=f.length,k,i,l;for(k=0;k<h;k++){i=f[k];if(!i[0].test(d))continue;l=i[1].from+'/'+d.replace(/([a-z])([A-Z])/g,function(a,b,c){return b+'_'+c}).replace(/\./g,'/').toLowerCase()+'.js';pkg=new this(l);pkg.addName(d);if(l=i[1].require)pkg.addDependency(d.replace(i[0],l));return pkg}return null};g.getFromCache=function(a){return this._g[a]=this._g[a]||{}};g.getObject=function(a){var b=this.getFromCache(a);if(b.obj!==undefined)return b.obj;var c=this._4,d=a.split('.'),f;while(f=d.shift())c=c&&c[f];return this.getFromCache(a).obj=c}})(JS.Package);JS.Package.DomLoader={usable:function(){return!!JS.Package.getObject('window.document.getElementsByTagName')},__FILE__:function(){var a=document.getElementsByTagName('script');return a[a.length-1].src},loadFile:function(c,d){var f=this,h=document.createElement('script');h.type='text/javascript';h.src=c;h.onload=h.onreadystatechange=function(){var a=h.readyState,b=h.status;if(!a||a==='loaded'||a==='complete'||(a===4&&b===200)){d();h.onload=h.onreadystatechange=f._j;h=null}};if(window.console&&console.info)console.info('Loading '+c);document.getElementsByTagName('head')[0].appendChild(h)},_j:function(){}};JS.Package.CommonJSLoader={usable:function(){return typeof require==='function'&&typeof exports==='object'},setup:function(){var b=this;require=(function(a){return function(){b._1=arguments[0]+'.js';return a.apply(JS.Package._4,arguments)}})(require)},__FILE__:function(){return this._1},loadFile:function(a,b){var c=(typeof process==='object'),d=c?process.cwd():require('file').cwd(),f=a.replace(/\.[^\.]+$/g,''),h=c?require('path'):require('file');require(h.join(d,f));b()}};JS.Package.ServerLoader={usable:function(){return typeof JS.Package.getObject('load')==='function'&&typeof JS.Package.getObject('version')==='function'},setup:function(){var b=this;load=(function(a){return function(){b._1=arguments[0];return a.apply(JS.Package._4,arguments)}})(load)},__FILE__:function(){return this._1},loadFile:function(a,b){load(a);b()}};JS.Package.WshLoader={usable:function(){return!!JS.Package.getObject('ActiveXObject')&&!!JS.Package.getObject('WScript')},__FILE__:function(){return this._1},loadFile:function(a,b){this._1=a;var c=new ActiveXObject('Scripting.FileSystemObject'),d,f;try{d=c.OpenTextFile(a);f=function(){eval(d.ReadAll())};f();b()}finally{try{if(d)d.Close()}catch(e){}}}};(function(){var a=[JS.Package.DomLoader,JS.Package.CommonJSLoader,JS.Package.ServerLoader,JS.Package.WshLoader],b=a.length,c,d;for(c=0;c<b;c++){d=a[c];if(d.usable()){JS.Package.Loader=d;if(d.setup)d.setup();break}}})();JS.Package.DSL={__FILE__:function(){return JS.Package.Loader.__FILE__()},pkg:function(a,b){var c=b?JS.Package.getByPath(b):JS.Package.getByName(a);c.addName(a);return new JS.Package.Description(c)},file:function(a){var b=JS.Package.getByPath(a);return new JS.Package.Description(b)},load:function(a,b){JS.Package.Loader.loadFile(a,b)},autoload:function(a,b){JS.Package.autoload(a,b)}};JS.Package.Description=function(a){this._9=a};(function(f){f._a=function(a,b){var c=b.length,a=this._9[a],d;for(d=0;d<c;d++)a.call(this._9,b[d]);return this};f.provides=function(){return this._a('addName',arguments)};f.requires=function(){return this._a('addDependency',arguments)};f.uses=function(){return this._a('addSoftDependency',arguments)};f.setup=function(a){this._9.onload(a);return this}})(JS.Package.Description.prototype);JS.Package.DSL.loader=JS.Package.DSL.file;JS.Packages=function(a){a.call(JS.Package.DSL)};JS.require=function(){var a=[],b=0;while(typeof arguments[b]==='string'){a.push(arguments[b]);b+=1}JS.Package.when({complete:a},arguments[b],arguments[b+1])};JS.Packages(function(){with(this){var b=JS.Package._4.JSCLASS_PATH||__FILE__().replace(/[^\/]*$/g,'');if(!/\/$/.test(b))b=b+'/';var c=function(a){return file(b+a+'.js')};c('core').provides('JS.Module','JS.Class','JS.Kernel','JS.Singleton','JS.Interface');c('comparable').provides('JS.Comparable').requires('JS.Module');c('constant_scope').provides('JS.ConstantScope').requires('JS.Module');c('forwardable').provides('JS.Forwardable').requires('JS.Module');c('enumerable').provides('JS.Enumerable').requires('JS.Module','JS.Class');c('observable').provides('JS.Observable').requires('JS.Module');c('hash').provides('JS.Hash').requires('JS.Class','JS.Enumerable','JS.Comparable');c('set').provides('JS.Set','JS.HashSet','JS.SortedSet').requires('JS.Class','JS.Enumerable').uses('JS.Hash');c('linked_list').provides('JS.LinkedList','JS.LinkedList.Doubly','JS.LinkedList.Doubly.Circular').requires('JS.Class','JS.Enumerable');c('command').provides('JS.Command','JS.Command.Stack').requires('JS.Class','JS.Enumerable','JS.Observable');c('decorator').provides('JS.Decorator').requires('JS.Module','JS.Class');c('method_chain').provides('JS.MethodChain').requires('JS.Module','JS.Kernel');c('proxy').provides('JS.Proxy','JS.Proxy.Virtual').requires('JS.Module','JS.Class');c('ruby').provides('JS.Ruby').requires('JS.Class');c('stack_trace').provides('JS.StackTrace').requires('JS.Module','JS.Singleton');c('state').provides('JS.State').requires('JS.Module','JS.Class')}});
@@ -1,49 +0,0 @@
1
- body {
2
- background: #222;
3
- margin: 0;
4
- padding: 40px;
5
- }
6
-
7
- .container {
8
- background: #fff;
9
- color: #444;
10
- margin: 0 auto;
11
- padding: 40px;
12
- width: 400px;
13
- font: 16px/1.5 Georgia, Times New Roman, serif;
14
- }
15
-
16
- .footer {
17
- margin: 0 auto;
18
- padding: 40px;
19
- width: 400px;
20
- color: #fff;
21
- font: 13px Helvetica, Arial, sans-serif;
22
- }
23
-
24
- .footer p {
25
- margin: 0;
26
- line-height: 1.5em;
27
- }
28
-
29
- h1 {
30
- font: bold 64px FreeSans, Helvetica, Arial, sans-serif;
31
- letter-spacing: -0.03em;
32
- margin: 0 0 32px;
33
- color: #f00846;
34
- }
35
-
36
- a {
37
- color: #f00846;
38
- text-decoration: none;
39
- }
40
-
41
- a:hover {
42
- background: #f00846;
43
- color: #fff;
44
- }
45
-
46
- p {
47
- margin: 1.5em 0 0;
48
- }
49
-
@@ -1,2355 +0,0 @@
1
- // funcunit/synthetic/synthetic.js
2
-
3
- (function($){
4
-
5
-
6
-
7
- var extend = function(d, s) { for (var p in s) d[p] = s[p]; return d;},
8
- // only uses browser detection for key events
9
- browser = {
10
- msie: !!(window.attachEvent && !window.opera),
11
- opera: !!window.opera,
12
- webkit : navigator.userAgent.indexOf('AppleWebKit/') > -1,
13
- safari: navigator.userAgent.indexOf('AppleWebKit/') > -1 && navigator.userAgent.indexOf('Chrome/') == -1,
14
- gecko: navigator.userAgent.indexOf('Gecko') > -1,
15
- mobilesafari: !!navigator.userAgent.match(/Apple.*Mobile.*Safari/),
16
- rhino : navigator.userAgent.match(/Rhino/) && true
17
- },
18
- createEventObject = function(type, options, element){
19
- var event = element.ownerDocument.createEventObject();
20
- return extend(event, options);
21
- },
22
- data = {},
23
- id = 0,
24
- expando = "_synthetic"+(new Date() - 0),
25
- bind,
26
- unbind,
27
- key = /keypress|keyup|keydown/,
28
- page = /load|unload|abort|error|select|change|submit|reset|focus|blur|resize|scroll/,
29
-
30
- /**
31
- * @constructor Syn
32
- * @download funcunit/dist/syn.js
33
- * @test funcunit/synthetic/qunit.html
34
- * Syn is used to simulate user actions. It creates synthetic events and
35
- * performs their default behaviors.
36
- *
37
- * <h2>Basic Use</h2>
38
- * The following clicks an input element with <code>id='description'</code>
39
- * and then types <code>'Hello World'</code>.
40
- *
41
- @codestart
42
- Syn.click({},'description')
43
- .type("Hello World")
44
- @codeend
45
- * <h2>User Actions and Events</h2>
46
- * <p>Syn is typically used to simulate user actions as opposed to triggering events. Typing characters
47
- * is an example of a user action. The keypress that represents an <code>'a'</code>
48
- * character being typed is an example of an event.
49
- * </p>
50
- * <p>
51
- * While triggering events is supported, it's much more useful to simulate actual user behavior. The
52
- * following actions are supported by Syn:
53
- * </p>
54
- * <ul>
55
- * <li><code>[Syn.prototype.click click]</code> - a mousedown, focus, mouseup, and click.</li>
56
- * <li><code>[Syn.prototype.dblclick dblclick]</code> - two <code>click!</code> events followed by a <code>dblclick</code>.</li>
57
- * <li><code>[Syn.prototype.key key]</code> - types a single character (keydown, keypress, keyup).</li>
58
- * <li><code>[Syn.prototype.type type]</code> - types multiple characters into an element.</li>
59
- * <li><code>[Syn.prototype.move move]</code> - moves the mouse from one position to another (triggering mouseover / mouseouts).</li>
60
- * <li><code>[Syn.prototype.drag drag]</code> - a mousedown, followed by mousemoves, and a mouseup.</li>
61
- * </ul>
62
- * All actions run asynchronously.
63
- * Click on the links above for more
64
- * information on how to use the specific action.
65
- * <h2>Asynchronous Callbacks</h2>
66
- * Actions don't complete immediately. This is almost
67
- * entirely because <code>focus()</code>
68
- * doesn't run immediately in IE.
69
- * If you provide a callback function to Syn, it will
70
- * be called after the action is completed.
71
- * <br/>The following checks that "Hello World" was entered correctly:
72
- @codestart
73
- Syn.click({},'description')
74
- .type("Hello World", function(){
75
-
76
- ok("Hello World" == document.getElementById('description').value)
77
- })
78
- @codeend
79
- <h2>Asynchronous Chaining</h2>
80
- <p>You might have noticed the [Syn.prototype.then then] method. It provides chaining
81
- so you can do a sequence of events with a single (final) callback.
82
- </p><p>
83
- If an element isn't provided to then, it uses the previous Syn's element.
84
- </p>
85
- The following does a lot of stuff before checking the result:
86
- @codestart
87
- Syn.type('ice water','title')
88
- .type('ice and water','description')
89
- .click({},'create')
90
- .drag({to: 'favorites'},'newRecipe',
91
- function(){
92
- ok($('#newRecipe').parents('#favorites').length);
93
- })
94
- @codeend
95
-
96
- <h2>jQuery Helper</h2>
97
- If jQuery is present, Syn adds a triggerSyn helper you can use like:
98
- @codestart
99
- $("#description").triggerSyn("type","Hello World");
100
- @codeend
101
- * <h2>Key Event Recording</h2>
102
- * <p>Every browser has very different rules for dispatching key events.
103
- * As there is no way to feature detect how a browser handles key events,
104
- * synthetic uses a description of how the browser behaves generated
105
- * by a recording application. </p>
106
- * <p>
107
- * If you want to support a browser not currently supported, you can
108
- * record that browser's key event description and add it to
109
- * <code>Syn.key.browsers</code> by it's navigator agent.
110
- * </p>
111
- @codestart
112
- Syn.key.browsers["Envjs\ Resig/20070309 PilotFish/1.2.0.10\1.6"] = {
113
- 'prevent':
114
- {"keyup":[],"keydown":["char","keypress"],"keypress":["char"]},
115
- 'character':
116
- { ... }
117
- }
118
- @codeend
119
- * <h2>Limitations</h2>
120
- * Syn fully supports IE 6+, FF 3+, Chrome, Safari, Opera 10+.
121
- * With FF 1+, drag / move events are only partially supported. They will
122
- * not trigger mouseover / mouseout events.<br/>
123
- * Safari crashes when a mousedown is triggered on a select. Syn will not
124
- * create this event.
125
- * <h2>Contributing to Syn</h2>
126
- * Have we missed something? We happily accept patches. The following are
127
- * important objects and properties of Syn:
128
- * <ul>
129
- * <li><code>Syn.create</code> - contains methods to setup, convert options, and create an event of a specific type.</li>
130
- * <li><code>Syn.defaults</code> - default behavior by event type (except for keys).</li>
131
- * <li><code>Syn.key.defaults</code> - default behavior by key.</li>
132
- * <li><code>Syn.keycodes</code> - supported keys you can type.</li>
133
- * </ul>
134
- * <h2>Roll Your Own Functional Test Framework</h2>
135
- * <p>Syn is really the foundation of JavaScriptMVC's functional testing framework - [FuncUnit].
136
- * But, we've purposely made Syn work without any dependencies in the hopes that other frameworks or
137
- * testing solutions can use it as well.
138
- * </p>
139
- * @init
140
- * Creates a synthetic event on the element.
141
- * @param {Object} type
142
- * @param {Object} options
143
- * @param {Object} element
144
- * @param {Object} callback
145
- * @return Syn
146
- */
147
- Syn = function(type, options, element, callback){
148
- return ( new Syn.init(type, options, element, callback) )
149
- }
150
-
151
- if(window.addEventListener){ // Mozilla, Netscape, Firefox
152
- bind = function(el, ev, f){
153
- el.addEventListener(ev, f, false)
154
- }
155
- unbind = function(el, ev, f){
156
- el.removeEventListener(ev, f, false)
157
- }
158
- }else{
159
- bind = function(el, ev, f){
160
- el.attachEvent("on"+ev, f)
161
- }
162
- unbind = function(el, ev, f){
163
- el.detachEvent("on"+ev, f)
164
- }
165
- }
166
- /**
167
- * @Static
168
- */
169
- extend(Syn,{
170
- /**
171
- * Creates a new synthetic event instance
172
- * @hide
173
- * @param {Object} type
174
- * @param {Object} options
175
- * @param {Object} element
176
- * @param {Object} callback
177
- */
178
- init : function(type, options, element, callback){
179
- var args = Syn.args(options,element, callback),
180
- self = this;
181
- this.queue = [];
182
- this.element = args.element;
183
-
184
- //run event
185
- if(typeof this[type] == "function") {
186
- this[type](args.options, args.element, function(defaults,el ){
187
- args.callback && args.callback.apply(self, arguments);
188
- self.done.apply(self, arguments)
189
- })
190
- }else{
191
- this.result = Syn.trigger(type, args.options, args.element);
192
- args.callback && args.callback.call(this, args.element, this.result);
193
- }
194
- },
195
- jquery : function(el, fast){
196
- if(window.FuncUnit && window.FuncUnit.jquery){
197
- return window.FuncUnit.jquery
198
- } if (el){
199
- return Syn.helpers.getWindow(el).jQuery || window.jQuery
200
- }
201
- else{
202
- return window.jQuery
203
- }
204
- },
205
- /**
206
- * Returns an object with the args for a Syn.
207
- * @hide
208
- * @return {Object}
209
- */
210
- args : function(){
211
- var res = {}
212
- for(var i=0; i < arguments.length; i++){
213
- if(typeof arguments[i] == 'function'){
214
- res.callback = arguments[i]
215
- }else if(arguments[i] && arguments[i].jquery){
216
- res.element = arguments[i][0];
217
- }else if(arguments[i] && arguments[i].nodeName){
218
- res.element = arguments[i];
219
- }else if(res.options && typeof arguments[i] == 'string'){ //we can get by id
220
- res.element = document.getElementById(arguments[i])
221
- }
222
- else if(arguments[i]){
223
- res.options = arguments[i];
224
- }
225
- }
226
- return res;
227
- },
228
- click : function( options, element, callback){
229
- Syn('click!',options,element, callback);
230
- },
231
- /**
232
- * @attribute defaults
233
- * Default actions for events. Each default function is called with this as its
234
- * element. It should return true if a timeout
235
- * should happen after it. If it returns an element, a timeout will happen
236
- * and the next event will happen on that element.
237
- */
238
- defaults : {
239
- focus : function(){
240
- if(!Syn.support.focusChanges){
241
- var element = this,
242
- nodeName = element.nodeName.toLowerCase();
243
- Syn.data(element,"syntheticvalue", element.value)
244
-
245
- if(nodeName == "input"){
246
-
247
- bind(element, "blur", function(){
248
-
249
- if( Syn.data(element,"syntheticvalue") != element.value){
250
-
251
- Syn.trigger("change", {}, element);
252
- }
253
- unbind(element,"blur", arguments.callee)
254
- })
255
-
256
- }
257
- }
258
- },
259
- submit : function(){
260
- Syn.onParents(this, function(el){
261
- if( el.nodeName.toLowerCase() == 'form'){
262
- el.submit()
263
- return false;
264
- }
265
- });
266
- }
267
- },
268
- changeOnBlur : function(element, prop, value){
269
-
270
- bind(element, "blur", function(){
271
- if( value != element[prop]){
272
- Syn.trigger("change", {}, element);
273
- }
274
- unbind(element,"blur", arguments.callee)
275
- })
276
-
277
- },
278
- /**
279
- * Returns the closest element of a particular type.
280
- * @hide
281
- * @param {Object} el
282
- * @param {Object} type
283
- */
284
- closest : function(el, type){
285
- while(el && el.nodeName.toLowerCase() != type.toLowerCase()){
286
- el = el.parentNode
287
- }
288
- return el;
289
- },
290
- /**
291
- * adds jQuery like data (adds an expando) and data exists FOREVER :)
292
- * @hide
293
- * @param {Object} el
294
- * @param {Object} key
295
- * @param {Object} value
296
- */
297
- data : function(el, key, value){
298
- var d;
299
- if(!el[expando]){
300
- el[expando] = id++;
301
- }
302
- if(!data[el[expando]]){
303
- data[el[expando]] = {};
304
- }
305
- d = data[el[expando]]
306
- if(value){
307
- data[el[expando]][key] = value;
308
- }else{
309
- return data[el[expando]][key];
310
- }
311
- },
312
- /**
313
- * Calls a function on the element and all parents of the element until the function returns
314
- * false.
315
- * @hide
316
- * @param {Object} el
317
- * @param {Object} func
318
- */
319
- onParents : function(el, func){
320
- var res;
321
- while(el && res !== false){
322
- res = func(el)
323
- el = el.parentNode
324
- }
325
- return el;
326
- },
327
- //regex to match focusable elements
328
- focusable : /^(a|area|frame|iframe|label|input|select|textarea|button|html|object)$/i,
329
- /**
330
- * Returns if an element is focusable
331
- * @hide
332
- * @param {Object} elem
333
- */
334
- isFocusable : function(elem){
335
- var attributeNode;
336
- return ( this.focusable.test(elem.nodeName) || (
337
- (attributeNode = elem.getAttributeNode( "tabIndex" )) && attributeNode.specified ) )
338
- && Syn.isVisible(elem)
339
- },
340
- /**
341
- * Returns if an element is visible or not
342
- * @hide
343
- * @param {Object} elem
344
- */
345
- isVisible : function(elem){
346
- return (elem.offsetWidth && elem.offsetHeight) || (elem.clientWidth && elem.clientHeight)
347
- },
348
- /**
349
- * Gets the tabIndex as a number or null
350
- * @hide
351
- * @param {Object} elem
352
- */
353
- tabIndex : function(elem){
354
- var attributeNode = elem.getAttributeNode( "tabIndex" );
355
- return attributeNode && attributeNode.specified && ( parseInt( elem.getAttribute('tabIndex') ) || 0 )
356
- },
357
- bind : bind,
358
- unbind : unbind,
359
- browser: browser,
360
- //some generic helpers
361
- helpers : {
362
- createEventObject : createEventObject,
363
- createBasicStandardEvent : function(type, defaults){
364
- var event;
365
- try {
366
- event = document.createEvent("Events");
367
- } catch(e2) {
368
- event = document.createEvent("UIEvents");
369
- } finally {
370
- event.initEvent(type, true, true);
371
- extend(event, defaults);
372
- }
373
- return event;
374
- },
375
- inArray : function(item, array){
376
- for(var i =0; i < array.length; i++){
377
- if(array[i] == item){
378
- return i;
379
- }
380
- }
381
- return -1;
382
- },
383
- getWindow : function(element){
384
- return element.ownerDocument.defaultView || element.ownerDocument.parentWindow
385
- },
386
- extend: extend,
387
- scrollOffset : function(win){
388
- var doc = win.document.documentElement,
389
- body = win.document.body;
390
- return {
391
- left : (doc && doc.scrollLeft || body && body.scrollLeft || 0) + (doc.clientLeft || 0),
392
- top : (doc && doc.scrollTop || body && body.scrollTop || 0) + (doc.clientTop || 0)
393
- }
394
-
395
- },
396
- addOffset : function(options, el){
397
- var jq = Syn.jquery(el)
398
- if(typeof options == 'object' &&
399
- options.clientX === undefined &&
400
- options.clientY === undefined &&
401
- options.pageX === undefined &&
402
- options.pageY === undefined && jq){
403
- var el = jq(el)
404
- off = el.offset();
405
- options.pageX = off.left + el.width() /2 ;
406
- options.pageY = off.top + el.height() /2 ;
407
- }
408
- }
409
- },
410
- // place for key data
411
- key : {
412
- ctrlKey : null,
413
- altKey : null,
414
- shiftKey : null,
415
- metaKey : null
416
- },
417
- //triggers an event on an element, returns true if default events should be run
418
- /**
419
- * Dispatches an event and returns true if default events should be run.
420
- * @hide
421
- * @param {Object} event
422
- * @param {Object} element
423
- * @param {Object} type
424
- * @param {Object} autoPrevent
425
- */
426
- dispatch : (document.documentElement.dispatchEvent ?
427
- function(event, element, type, autoPrevent){
428
- var preventDefault = event.preventDefault,
429
- prevents = autoPrevent ? -1 : 0;
430
-
431
- //automatically prevents the default behavior for this event
432
- //this is to protect agianst nasty browser freezing bug in safari
433
- if(autoPrevent){
434
- bind(element, type, function(ev){
435
- ev.preventDefault()
436
- unbind(this, type, arguments.callee)
437
- })
438
- }
439
-
440
-
441
- event.preventDefault = function(){
442
- prevents++;
443
- if(++prevents > 0){
444
- preventDefault.apply(this,[]);
445
- }
446
- }
447
- element.dispatchEvent(event)
448
- return prevents <= 0;
449
- } :
450
- function(event, element, type){
451
- try {window.event = event;}catch(e) {}
452
- //source element makes sure element is still in the document
453
- return element.sourceIndex <= 0 || element.fireEvent('on'+type, event)
454
- }
455
- ),
456
- /**
457
- * @attribute
458
- * @hide
459
- * An object of eventType -> function that create that event.
460
- */
461
- create : {
462
- //-------- PAGE EVENTS ---------------------
463
- page : {
464
- event : document.createEvent ? function(type, options, element){
465
- var event = element.ownerDocument.createEvent("Events");
466
- event.initEvent(type, true, true );
467
- return event;
468
- } : createEventObject
469
- },
470
- // unique events
471
- focus : {
472
- event : function(type, options, element){
473
- Syn.onParents(element, function(el){
474
- if( Syn.isFocusable(el)){
475
- if(el.nodeName.toLowerCase() != 'html'){
476
- el.focus();
477
- }
478
- return false
479
- }
480
- });
481
- return true;
482
- }
483
- }
484
- },
485
- /**
486
- * @attribute support
487
- * Feature detected properties of a browser's event system.
488
- * Support has the following properties:
489
- * <ul>
490
- * <li><code>clickChanges</code> - clicking on an option element creates a change event.</li>
491
- * <li><code>clickSubmits</code> - clicking on a form button submits the form.</li>
492
- * <li><code>mouseupSubmits</code> - a mouseup on a form button submits the form.</li>
493
- * <li><code>radioClickChanges</code> - clicking a radio button changes the radio.</li>
494
- * <li><code>focusChanges</code> - focus/blur creates a change event.</li>
495
- * <li><code>linkHrefJS</code> - An achor's href JavaScript is run.</li>
496
- * <li><code>mouseDownUpClicks</code> - A mousedown followed by mouseup creates a click event.</li>
497
- * <li><code>tabKeyTabs</code> - A tab key changes tabs.</li>
498
- * <li><code>keypressOnAnchorClicks</code> - Keying enter on an anchor triggers a click.</li>
499
- * </ul>
500
- */
501
- support : {
502
- clickChanges : false,
503
- clickSubmits : false,
504
- keypressSubmits : false,
505
- mouseupSubmits: false,
506
- radioClickChanges : false,
507
- focusChanges : false,
508
- linkHrefJS : false,
509
- keyCharacters : false,
510
- backspaceWorks : false,
511
- mouseDownUpClicks : false,
512
- tabKeyTabs : false,
513
- keypressOnAnchorClicks : false,
514
- optionClickBubbles : false
515
- },
516
- /**
517
- * Creates a synthetic event and dispatches it on the element.
518
- * This will run any default actions for the element.
519
- * Typically you want to use Syn, but if you want the return value, use this.
520
- * @param {String} type
521
- * @param {Object} options
522
- * @param {HTMLElement} element
523
- * @return {Boolean} true if default events were run, false if otherwise.
524
- */
525
- trigger : function(type, options, element){
526
- options || (options = {});
527
-
528
- var create = Syn.create,
529
- setup = create[type] && create[type].setup,
530
- kind = key.test(type) ?
531
- 'key' :
532
- ( page.test(type) ?
533
- "page" : "mouse" ),
534
- createType = create[type] || {},
535
- createKind = create[kind],
536
- event,
537
- ret,
538
- autoPrevent = options._autoPrevent,
539
- dispatchEl = element;
540
-
541
- //any setup code?
542
- Syn.support.ready && setup && setup(type, options, element);
543
-
544
-
545
- //get kind
546
-
547
- delete options._autoPrevent;
548
-
549
- if(createType.event){
550
- ret = createType.event(type, options, element)
551
- }else{
552
- //convert options
553
- options = createKind.options ? createKind.options(type,options,element) : options;
554
-
555
- if(!Syn.support.changeBubbles && /option/i.test(element.nodeName)){
556
- dispatchEl = element.parentNode; //jQuery expects clicks on select
557
- }
558
-
559
- //create the event
560
- event = createKind.event(type,options,dispatchEl)
561
-
562
- //send the event
563
- ret = Syn.dispatch(event, dispatchEl, type, autoPrevent)
564
- }
565
-
566
- //run default behavior
567
- ret && Syn.support.ready
568
- && Syn.defaults[type]
569
- && Syn.defaults[type].call(element, options, autoPrevent);
570
- return ret;
571
- },
572
- eventSupported: function( eventName ) {
573
- var el = document.createElement("div");
574
- eventName = "on" + eventName;
575
-
576
- var isSupported = (eventName in el);
577
- if ( !isSupported ) {
578
- el.setAttribute(eventName, "return;");
579
- isSupported = typeof el[eventName] === "function";
580
- }
581
- el = null;
582
-
583
- return isSupported;
584
- }
585
-
586
- });
587
- var h = Syn.helpers;
588
- /**
589
- * @Prototype
590
- */
591
- extend(Syn.init.prototype,{
592
- /**
593
- * @function then
594
- * <p>
595
- * Then is used to chain a sequence of actions to be run one after the other.
596
- * This is useful when many asynchronous actions need to be performed before some
597
- * final check needs to be made.
598
- * </p>
599
- * <p>The following clicks and types into the <code>id='age'</code> element and then checks that only numeric characters can be entered.</p>
600
- * <h3>Example</h3>
601
- * @codestart
602
- * Syn('click',{},'age')
603
- * .then('type','I am 12',function(){
604
- * equals($('#age').val(),"12")
605
- * })
606
- * @codeend
607
- * If the element argument is undefined, then the last element is used.
608
- *
609
- * @param {String} type The type of event or action to create: "_click", "_dblclick", "_drag", "_type".
610
- * @param {Object} options Optiosn to pass to the event.
611
- * @param {String|HTMLElement} [element] A element's id or an element. If undefined, defaults to the previous element.
612
- * @param {Function} [callback] A function to callback after the action has run, but before any future chained actions are run.
613
- */
614
- then : function(type, options, element, callback){
615
- if(Syn.autoDelay){
616
- this.delay();
617
- }
618
- var args = Syn.args(options,element, callback),
619
- self = this;
620
-
621
-
622
- //if stack is empty run right away
623
-
624
- //otherwise ... unshift it
625
- this.queue.unshift(function(el, prevented){
626
-
627
- if(typeof this[type] == "function") {
628
- this.element = args.element || el;
629
- this[type](args.options, this.element, function(defaults, el){
630
- args.callback && args.callback.apply(self, arguments);
631
- self.done.apply(self, arguments)
632
- })
633
- }else{
634
- this.result = Syn.trigger(type, args.options, args.element);
635
- args.callback && args.callback.call(this, args.element, this.result);
636
- return this;
637
- }
638
- })
639
- return this;
640
- },
641
- /**
642
- * Delays the next command a set timeout.
643
- * @param {Number} [timeout]
644
- * @param {Function} [callback]
645
- */
646
- delay : function(timeout, callback){
647
- if(typeof timeout == 'function'){
648
- callback = timeout;
649
- timeout = null;
650
- }
651
- timeout = timeout || 600
652
- var self = this;
653
- this.queue.unshift(function(){
654
- setTimeout(function(){
655
- callback && callback.apply(self,[])
656
- self.done.apply(self, arguments)
657
- },timeout)
658
- })
659
- return this;
660
- },
661
- done : function( defaults, el){
662
- el && (this.element = el);;
663
- if(this.queue.length){
664
- this.queue.pop().call(this, this.element, defaults);
665
- }
666
-
667
- },
668
- /**
669
- * @function click
670
- * Clicks an element by triggering a mousedown,
671
- * mouseup,
672
- * and a click event.
673
- * <h3>Example</h3>
674
- * @codestart
675
- * Syn.click({},'create',function(){
676
- * //check something
677
- * })
678
- * @codeend
679
- * You can also provide the coordinates of the click.
680
- * If jQuery is present, it will set clientX and clientY
681
- * for you. Here's how to set it yourself:
682
- * @codestart
683
- * Syn.click(
684
- * {clientX: 20, clientY: 100},
685
- * 'create',
686
- * function(){
687
- * //check something
688
- * })
689
- * @codeend
690
- * You can also provide pageX and pageY and Syn will convert it for you.
691
- * @param {Object} options
692
- * @param {HTMLElement} element
693
- * @param {Function} callback
694
- */
695
- "_click" : function(options, element, callback, force){
696
- Syn.helpers.addOffset(options, element);
697
- Syn.trigger("mousedown", options, element);
698
-
699
- //timeout is b/c IE is stupid and won't call focus handlers
700
- setTimeout(function(){
701
- Syn.trigger("mouseup", options, element)
702
- if(!Syn.support.mouseDownUpClicks || force){
703
- Syn.trigger("click", options, element)
704
- callback(true)
705
- }else{
706
- //we still have to run the default (presumably)
707
- Syn.create.click.setup('click',options,element)
708
- Syn.defaults.click.call(element)
709
- //must give time for callback
710
- setTimeout(function(){
711
- callback(true)
712
- },1)
713
- }
714
-
715
- },1)
716
- },
717
- /**
718
- * Right clicks in browsers that support it (everyone but opera).
719
- * @param {Object} options
720
- * @param {Object} element
721
- * @param {Object} callback
722
- */
723
- "_rightClick" : function(options, element, callback){
724
- Syn.helpers.addOffset(options, element);
725
- var mouseopts = extend( extend({},Syn.mouse.browser.right.mouseup ), options)
726
-
727
- Syn.trigger("mousedown", mouseopts, element);
728
-
729
- //timeout is b/c IE is stupid and won't call focus handlers
730
- setTimeout(function(){
731
- Syn.trigger("mouseup", mouseopts, element)
732
- if (Syn.mouse.browser.contextmenu) {
733
- Syn.trigger("contextmenu",
734
- extend( extend({},Syn.mouse.browser.right.contextmenu ), options),
735
- element)
736
- }
737
- callback(true)
738
- },1)
739
- },
740
- /**
741
- * @function dblclick
742
- * Dblclicks an element. This runs two [Syn.prototype.click click] events followed by
743
- * a dblclick on the element.
744
- * <h3>Example</h3>
745
- * @codestart
746
- * Syn.dblclick({},'open')
747
- * @codeend
748
- * @param {Object} options
749
- * @param {HTMLElement} element
750
- * @param {Function} callback
751
- */
752
- "_dblclick" : function(options, element, callback){
753
- Syn.helpers.addOffset(options, element);
754
- var self = this;
755
- this._click(options, element, function(){
756
- setTimeout(function(){
757
- self._click(options, element, function(){
758
- Syn.trigger("dblclick", options, element)
759
- callback(true)
760
- },true)
761
- },2)
762
-
763
- })
764
- }
765
- })
766
-
767
- var actions = ["click","dblclick","move","drag","key","type",'rightClick'],
768
- makeAction = function(name){
769
- Syn[name] = function(options, element, callback){
770
- return Syn("_"+name, options, element, callback)
771
- }
772
- Syn.init.prototype[name] = function(options, element, callback){
773
- return this.then("_"+name, options, element, callback)
774
- }
775
- }
776
- for(var i=0; i < actions.length; i++){
777
- makeAction(actions[i]);
778
- }
779
- /**
780
- * Used for creating and dispatching synthetic events.
781
- * @codestart
782
- * new MVC.Syn('click').send(MVC.$E('id'))
783
- * @codeend
784
- * @init Sets up a synthetic event.
785
- * @param {String} type type of event, ex: 'click'
786
- * @param {optional:Object} options
787
- */
788
-
789
- if (window.jQuery || (window.FuncUnit && window.FuncUnit.jquery)) {
790
- (window.jQuery || window.FuncUnit.jquery).fn.triggerSyn = function(type, options, callback){
791
- Syn(type, options, this[0], callback)
792
- return this;
793
- };
794
- }
795
-
796
- window.Syn = Syn;
797
-
798
-
799
- })();
800
-
801
- // funcunit/synthetic/mouse.js
802
-
803
- (function($){
804
-
805
-
806
- var h = Syn.helpers;
807
-
808
- Syn.mouse = {};
809
- h.extend(Syn.defaults,{
810
- mousedown : function(options){
811
- Syn.trigger("focus", {}, this)
812
- },
813
- click : function(){
814
- // prevents the access denied issue in IE if the click causes the element to be destroyed
815
- var element = this;
816
- try {
817
- element.nodeType;
818
- } catch(e){
819
- return;
820
- }
821
- //get old values
822
- var href,
823
- checked = Syn.data(element,"checked"),
824
- scope = Syn.helpers.getWindow(element),
825
- nodeName = element.nodeName.toLowerCase();
826
-
827
- if( (href = Syn.data(element,"href") ) ){
828
- element.setAttribute('href',href)
829
- }
830
-
831
-
832
-
833
- //run href javascript
834
- if(!Syn.support.linkHrefJS
835
- && /^\s*javascript:/.test(element.href)){
836
- //eval js
837
- var code = element.href.replace(/^\s*javascript:/,"")
838
-
839
- //try{
840
- if (code != "//" && code.indexOf("void(0)") == -1) {
841
- if(window.selenium){
842
- eval("with(selenium.browserbot.getCurrentWindow()){"+code+"}")
843
- }else{
844
- eval("with(scope){"+code+"}")
845
- }
846
- }
847
- }
848
-
849
- //submit a form
850
- if(nodeName == "input"
851
- && element.type == "submit"
852
- && !(Syn.support.clickSubmits)){
853
-
854
- var form = Syn.closest(element, "form");
855
- if(form){
856
- Syn.trigger("submit",{},form)
857
- }
858
-
859
- }
860
- //follow a link, probably needs to check if in an a.
861
- if(nodeName == "a"
862
- && element.href
863
- && !/^\s*javascript:/.test(element.href)){
864
-
865
- scope.location.href = element.href;
866
-
867
- }
868
-
869
- //change a checkbox
870
- if(nodeName == "input"
871
- && element.type == "checkbox"){
872
-
873
- if(!Syn.support.clickChecks && !Syn.support.changeChecks){
874
- element.checked = !element.checked;
875
- }
876
- if(!Syn.support.clickChanges){
877
- Syn.trigger("change",{}, element );
878
- }
879
- }
880
-
881
- //change a radio button
882
- if(nodeName == "input" && element.type == "radio"){ // need to uncheck others if not checked
883
-
884
- if(!Syn.support.clickChecks && !Syn.support.changeChecks){
885
- //do the checks manually
886
- if(!element.checked){ //do nothing, no change
887
- element.checked = true;
888
- }
889
- }
890
- if(checked != element.checked && !Syn.support.radioClickChanges){
891
- Syn.trigger("change",{}, element );
892
- }
893
- }
894
- // change options
895
- if(nodeName == "option" && Syn.data(element,"createChange")){
896
- Syn.trigger("change",{}, element.parentNode);//does not bubble
897
- Syn.data(element,"createChange",false)
898
- }
899
- }
900
- })
901
-
902
-
903
- //add create and setup behavior for mosue events
904
- h.extend(Syn.create,{
905
- mouse : {
906
- options : function(type, options, element){
907
- var doc = document.documentElement, body = document.body,
908
- center = [options.pageX || 0, options.pageY || 0],
909
- //browser might not be loaded yet (doing support code)
910
- left = Syn.mouse.browser && Syn.mouse.browser.left[type],
911
- right = Syn.mouse.browser && Syn.mouse.browser.right[type];
912
- return h.extend({
913
- bubbles : true,cancelable : true,
914
- view : window,
915
- detail : 1,
916
- screenX : 1, screenY : 1,
917
- clientX : options.clientX || center[0] -(doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc.clientLeft || 0),
918
- clientY : options.clientY || center[1] -(doc && doc.scrollTop || body && body.scrollTop || 0) - (doc.clientTop || 0),
919
- ctrlKey : !!Syn.key.ctrlKey,
920
- altKey : !!Syn.key.altKey,
921
- shiftKey : !!Syn.key.shiftKey,
922
- metaKey : !!Syn.key.metaKey,
923
- button : left && left.button != null ?
924
- left.button :
925
- right && right.button || (type == 'contextmenu' ? 2 : 0),
926
- relatedTarget : document.documentElement
927
- }, options);
928
- },
929
- event : document.createEvent ?
930
- function(type, defaults, element){ //Everyone Else
931
- var event;
932
-
933
- try {
934
- event = element.ownerDocument.createEvent('MouseEvents');
935
- event.initMouseEvent(type,
936
- defaults.bubbles, defaults.cancelable,
937
- defaults.view,
938
- defaults.detail,
939
- defaults.screenX, defaults.screenY,defaults.clientX,defaults.clientY,
940
- defaults.ctrlKey,defaults.altKey,defaults.shiftKey,defaults.metaKey,
941
- defaults.button,defaults.relatedTarget);
942
- } catch(e) {
943
- event = h.createBasicStandardEvent(type,defaults)
944
- }
945
- event.synthetic = true;
946
- return event;
947
- } :
948
- h.createEventObject
949
- },
950
- click : {
951
- setup : function(type, options, element){
952
- try{
953
- Syn.data(element,"checked", element.checked);
954
- }catch(e){}
955
- if(
956
- element.nodeName.toLowerCase() == "a"
957
- && element.href
958
- && !/^\s*javascript:/.test(element.href)){
959
-
960
- //save href
961
- Syn.data(element,"href", element.href)
962
-
963
- //remove b/c safari/opera will open a new tab instead of changing the page
964
- element.setAttribute('href','javascript://')
965
- }
966
- //if select or option, save old value and mark to change
967
- if(/option/i.test(element.nodeName)){
968
- var child = element.parentNode.firstChild,
969
- i = -1;
970
- while(child){
971
- if(child.nodeType ==1){
972
- i++;
973
- if(child == element) break;
974
- }
975
- child = child.nextSibling;
976
- }
977
- if(i !== element.parentNode.selectedIndex){
978
- //shouldn't this wait on triggering
979
- //change?
980
- element.parentNode.selectedIndex = i;
981
- Syn.data(element,"createChange",true)
982
- }
983
- }
984
- }
985
- },
986
- mousedown : {
987
- setup : function(type,options, element){
988
- var nn = element.nodeName.toLowerCase();
989
- //we have to auto prevent default to prevent freezing error in safari
990
- if(Syn.browser.safari && (nn == "select" || nn == "option" )){
991
- options._autoPrevent = true;
992
- }
993
- }
994
- }
995
- });
996
- //do support code
997
- (function(){
998
- if(!document.body){
999
- setTimeout(arguments.callee,1)
1000
- return;
1001
- }
1002
- var oldSynth = window.__synthTest;
1003
- window.__synthTest = function(){
1004
- Syn.support.linkHrefJS = true;
1005
- }
1006
- var div = document.createElement("div"),
1007
- checkbox,
1008
- submit,
1009
- form,
1010
- input,
1011
- select;
1012
-
1013
- div.innerHTML = "<form id='outer'>"+
1014
- "<input name='checkbox' type='checkbox'/>"+
1015
- "<input name='radio' type='radio' />"+
1016
- "<input type='submit' name='submitter'/>"+
1017
- "<input type='input' name='inputter'/>"+
1018
- "<input name='one'>"+
1019
- "<input name='two'/>"+
1020
- "<a href='javascript:__synthTest()' id='synlink'></a>"+
1021
- "<select><option></option></select>"+
1022
- "</form>";
1023
- document.documentElement.appendChild(div);
1024
- form = div.firstChild
1025
- checkbox = form.childNodes[0];
1026
- submit = form.childNodes[2];
1027
- select = form.getElementsByTagName('select')[0]
1028
-
1029
- checkbox.checked = false;
1030
- checkbox.onchange = function(){
1031
- Syn.support.clickChanges = true;
1032
- }
1033
-
1034
- Syn.trigger("click", {}, checkbox)
1035
- Syn.support.clickChecks = checkbox.checked;
1036
- checkbox.checked = false;
1037
-
1038
- Syn.trigger("change", {}, checkbox);
1039
-
1040
- Syn.support.changeChecks = checkbox.checked;
1041
-
1042
- form.onsubmit = function(ev){
1043
- if (ev.preventDefault)
1044
- ev.preventDefault();
1045
- Syn.support.clickSubmits = true;
1046
- return false;
1047
- }
1048
- Syn.trigger("click", {}, submit)
1049
-
1050
-
1051
-
1052
- form.childNodes[1].onchange = function(){
1053
- Syn.support.radioClickChanges = true;
1054
- }
1055
- Syn.trigger("click", {}, form.childNodes[1])
1056
-
1057
-
1058
- Syn.bind(div, 'click', function(){
1059
- Syn.support.optionClickBubbles = true;
1060
- Syn.unbind(div,'click', arguments.callee)
1061
- })
1062
- Syn.trigger("click",{},select.firstChild)
1063
-
1064
-
1065
- Syn.support.changeBubbles = Syn.eventSupported('change');
1066
-
1067
- //test if mousedown followed by mouseup causes click (opera), make sure there are no clicks after this
1068
- var clicksCount = 0
1069
- div.onclick = function(){
1070
- Syn.support.mouseDownUpClicks = true;
1071
- //we should use this to check for opera potentially, but would
1072
- //be difficult to remove element correctly
1073
- //Syn.support.mouseDownUpRepeatClicks = (2 == (++clicksCount))
1074
- }
1075
- Syn.trigger("mousedown",{},div)
1076
- Syn.trigger("mouseup",{},div)
1077
-
1078
- //setTimeout(function(){
1079
- // Syn.trigger("mousedown",{},div)
1080
- // Syn.trigger("mouseup",{},div)
1081
- //},1)
1082
-
1083
-
1084
- document.documentElement.removeChild(div);
1085
-
1086
- //check stuff
1087
- window.__synthTest = oldSynth;
1088
- //support.ready = true;
1089
- })();
1090
-
1091
-
1092
-
1093
- })();
1094
-
1095
- // funcunit/synthetic/browsers.js
1096
-
1097
- (function($){
1098
-
1099
- Syn.key.browsers = {
1100
- webkit : {
1101
- 'prevent':
1102
- {"keyup":[],"keydown":["char","keypress"],"keypress":["char"]},
1103
- 'character':
1104
- {"keydown":[0,"key"],"keypress":["char","char"],"keyup":[0,"key"]},
1105
- 'specialChars':
1106
- {"keydown":[0,"char"],"keyup":[0,"char"]},
1107
- 'navigation':
1108
- {"keydown":[0,"key"],"keyup":[0,"key"]},
1109
- 'special':
1110
- {"keydown":[0,"key"],"keyup":[0,"key"]},
1111
- 'tab':
1112
- {"keydown":[0,"char"],"keyup":[0,"char"]},
1113
- 'pause-break':
1114
- {"keydown":[0,"key"],"keyup":[0,"key"]},
1115
- 'caps':
1116
- {"keydown":[0,"key"],"keyup":[0,"key"]},
1117
- 'escape':
1118
- {"keydown":[0,"key"],"keyup":[0,"key"]},
1119
- 'num-lock':
1120
- {"keydown":[0,"key"],"keyup":[0,"key"]},
1121
- 'scroll-lock':
1122
- {"keydown":[0,"key"],"keyup":[0,"key"]},
1123
- 'print':
1124
- {"keyup":[0,"key"]},
1125
- 'function':
1126
- {"keydown":[0,"key"],"keyup":[0,"key"]},
1127
- '\r':
1128
- {"keydown":[0,"key"],"keypress":["char","key"],"keyup":[0,"key"]}
1129
- },
1130
- gecko : {
1131
- 'prevent':
1132
- {"keyup":[],"keydown":["char"],"keypress":["char"]},
1133
- 'character':
1134
- {"keydown":[0,"key"],"keypress":["char",0],"keyup":[0,"key"]},
1135
- 'specialChars':
1136
- {"keydown":[0,"key"],"keypress":[0,"key"],"keyup":[0,"key"]},
1137
- 'navigation':
1138
- {"keydown":[0,"key"],"keypress":[0,"key"],"keyup":[0,"key"]},
1139
- 'special':
1140
- {"keydown":[0,"key"],"keyup":[0,"key"]},
1141
- '\t':
1142
- {"keydown":[0,"key"],"keypress":[0,"key"],"keyup":[0,"key"]},
1143
- 'pause-break':
1144
- {"keydown":[0,"key"],"keypress":[0,"key"],"keyup":[0,"key"]},
1145
- 'caps':
1146
- {"keydown":[0,"key"],"keyup":[0,"key"]},
1147
- 'escape':
1148
- {"keydown":[0,"key"],"keypress":[0,"key"],"keyup":[0,"key"]},
1149
- 'num-lock':
1150
- {"keydown":[0,"key"],"keyup":[0,"key"]},
1151
- 'scroll-lock':
1152
- {"keydown":[0,"key"],"keyup":[0,"key"]},
1153
- 'print':
1154
- {"keyup":[0,"key"]},
1155
- 'function':
1156
- {"keydown":[0,"key"],"keyup":[0,"key"]},
1157
- '\r':
1158
- {"keydown":[0,"key"],"keypress":[0,"key"],"keyup":[0,"key"]}
1159
- },
1160
- msie : {
1161
- 'prevent':{"keyup":[],"keydown":["char","keypress"],"keypress":["char"]},
1162
- 'character':{"keydown":[null,"key"],"keypress":[null,"char"],"keyup":[null,"key"]},
1163
- 'specialChars':{"keydown":[null,"char"],"keyup":[null,"char"]},
1164
- 'navigation':{"keydown":[null,"key"],"keyup":[null,"key"]},
1165
- 'special':{"keydown":[null,"key"],"keyup":[null,"key"]},
1166
- 'tab':{"keydown":[null,"char"],"keyup":[null,"char"]},
1167
- 'pause-break':{"keydown":[null,"key"],"keyup":[null,"key"]},
1168
- 'caps':{"keydown":[null,"key"],"keyup":[null,"key"]},
1169
- 'escape':{"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]},
1170
- 'num-lock':{"keydown":[null,"key"],"keyup":[null,"key"]},
1171
- 'scroll-lock':{"keydown":[null,"key"],"keyup":[null,"key"]},
1172
- 'print':{"keyup":[null,"key"]},
1173
- 'function':{"keydown":[null,"key"],"keyup":[null,"key"]},
1174
- '\r':{"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]}
1175
- },
1176
- opera : {
1177
- 'prevent':
1178
- {"keyup":[],"keydown":[],"keypress":["char"]},
1179
- 'character':
1180
- {"keydown":[null,"key"],"keypress":[null,"char"],"keyup":[null,"key"]},
1181
- 'specialChars':
1182
- {"keydown":[null,"char"],"keypress":[null,"char"],"keyup":[null,"char"]},
1183
- 'navigation':
1184
- {"keydown":[null,"key"],"keypress":[null,"key"]},
1185
- 'special':
1186
- {"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]},
1187
- 'tab':
1188
- {"keydown":[null,"char"],"keypress":[null,"char"],"keyup":[null,"char"]},
1189
- 'pause-break':
1190
- {"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]},
1191
- 'caps':
1192
- {"keydown":[null,"key"],"keyup":[null,"key"]},
1193
- 'escape':
1194
- {"keydown":[null,"key"],"keypress":[null,"key"]},
1195
- 'num-lock':
1196
- {"keyup":[null,"key"],"keydown":[null,"key"],"keypress":[null,"key"]},
1197
- 'scroll-lock':
1198
- {"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]},
1199
- 'print':
1200
- {},
1201
- 'function':
1202
- {"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]},
1203
- '\r':
1204
- {"keydown":[null,"key"],"keypress":[null,"key"],"keyup":[null,"key"]}
1205
- }
1206
- };
1207
-
1208
- Syn.mouse.browsers = {
1209
- webkit : {"right":{"mousedown":{"button":2,"which":3},"mouseup":{"button":2,"which":3},"contextmenu":{"button":2,"which":3}},
1210
- "left":{"mousedown":{"button":0,"which":1},"mouseup":{"button":0,"which":1},"click":{"button":0,"which":1}}},
1211
- opera: {"right":{"mousedown":{"button":2,"which":3},"mouseup":{"button":2,"which":3}},
1212
- "left":{"mousedown":{"button":0,"which":1},"mouseup":{"button":0,"which":1},"click":{"button":0,"which":1}}},
1213
- msie: { "right":{"mousedown":{"button":2},"mouseup":{"button":2},"contextmenu":{"button":0}},
1214
- "left":{"mousedown":{"button":1},"mouseup":{"button":1},"click":{"button":0}}},
1215
- chrome : {"right":{"mousedown":{"button":2,"which":3},"mouseup":{"button":2,"which":3},"contextmenu":{"button":2,"which":3}},
1216
- "left":{"mousedown":{"button":0,"which":1},"mouseup":{"button":0,"which":1},"click":{"button":0,"which":1}}},
1217
- gecko: {"left":{"mousedown":{"button":0,"which":1},"mouseup":{"button":0,"which":1},"click":{"button":0,"which":1}},
1218
- "right":{"mousedown":{"button":2,"which":3},"mouseup":{"button":2,"which":3},"contextmenu":{"button":2,"which":3}}}
1219
- }
1220
-
1221
- //set browser
1222
- Syn.key.browser =
1223
- (function(){
1224
- if(Syn.key.browsers[window.navigator.userAgent]){
1225
- return Syn.key.browsers[window.navigator.userAgent];
1226
- }
1227
- for(var browser in Syn.browser){
1228
- if(Syn.browser[browser] && Syn.key.browsers[browser]){
1229
- return Syn.key.browsers[browser]
1230
- }
1231
- }
1232
- return Syn.key.browsers.gecko;
1233
- })();
1234
-
1235
- Syn.mouse.browser =
1236
- (function(){
1237
- if(Syn.mouse.browsers[window.navigator.userAgent]){
1238
- return Syn.mouse.browsers[window.navigator.userAgent];
1239
- }
1240
- for(var browser in Syn.browser){
1241
- if(Syn.browser[browser] && Syn.mouse.browsers[browser]){
1242
- return Syn.mouse.browsers[browser]
1243
- }
1244
- }
1245
- return Syn.mouse.browsers.gecko;
1246
- })();
1247
-
1248
-
1249
- })();
1250
-
1251
- // funcunit/synthetic/key.js
1252
-
1253
- (function($){
1254
-
1255
-
1256
- var h = Syn.helpers,
1257
- S = Syn,
1258
-
1259
- // gets the selection of an input or textarea
1260
- getSelection = function(el){
1261
- // use selectionStart if we can
1262
- if (el.selectionStart !== undefined) {
1263
- // this is for opera, so we don't have to focus to type how we think we would
1264
- if(document.activeElement
1265
- && document.activeElement != el
1266
- && el.selectionStart == el.selectionEnd
1267
- && el.selectionStart == 0){
1268
- return {start: el.value.length, end: el.value.length};
1269
- }
1270
- return {start: el.selectionStart, end: el.selectionEnd}
1271
- }else{
1272
- //check if we aren't focused
1273
- //if(document.activeElement && document.activeElement != el){
1274
-
1275
-
1276
- //}
1277
- try {
1278
- //try 2 different methods that work differently (IE breaks depending on type)
1279
- if (el.nodeName.toLowerCase() == 'input') {
1280
- var real = h.getWindow(el).document.selection.createRange(), r = el.createTextRange();
1281
- r.setEndPoint("EndToStart", real);
1282
-
1283
- var start = r.text.length
1284
- return {
1285
- start: start,
1286
- end: start + real.text.length
1287
- }
1288
- }
1289
- else {
1290
- var real = h.getWindow(el).document.selection.createRange(), r = real.duplicate(), r2 = real.duplicate(), r3 = real.duplicate();
1291
- r2.collapse();
1292
- r3.collapse(false);
1293
- r2.moveStart('character', -1)
1294
- r3.moveStart('character', -1)
1295
- //select all of our element
1296
- r.moveToElementText(el)
1297
- //now move our endpoint to the end of our real range
1298
- r.setEndPoint('EndToEnd', real);
1299
- var start = r.text.length - real.text.length, end = r.text.length;
1300
- if (start != 0 && r2.text == "") {
1301
- start += 2;
1302
- }
1303
- if (end != 0 && r3.text == "") {
1304
- end += 2;
1305
- }
1306
- //if we aren't at the start, but previous is empty, we are at start of newline
1307
- return {
1308
- start: start,
1309
- end: end
1310
- }
1311
- }
1312
- }catch(e){
1313
- return {start: el.value.length, end: el.value.length};
1314
- }
1315
- }
1316
- },
1317
- // gets all focusable elements
1318
- getFocusable = function(el){
1319
- var document = h.getWindow(el).document,
1320
- res = [];
1321
-
1322
- var els = document.getElementsByTagName('*'),
1323
- len = els.length;
1324
-
1325
- for(var i=0; i< len; i++){
1326
- Syn.isFocusable(els[i]) && els[i] != document.documentElement && res.push(els[i])
1327
- }
1328
- return res;
1329
-
1330
-
1331
- };
1332
-
1333
- /**
1334
- * @add Syn static
1335
- */
1336
- h.extend(Syn,{
1337
- /**
1338
- * @attribute
1339
- * A list of the keys and their keycodes codes you can type.
1340
- * You can add type keys with
1341
- * @codestart
1342
- * Syn('key','delete','title');
1343
- *
1344
- * //or
1345
- *
1346
- * Syn('type','One Two Three[left][left][delete]','title')
1347
- * @codeend
1348
- *
1349
- * The following are a list of keys you can type:
1350
- * @codestart text
1351
- * \b - backspace
1352
- * \t - tab
1353
- * \r - enter
1354
- * ' ' - space
1355
- * a-Z 0-9 - normal characters
1356
- * /!@#$*,.? - All other typeable characters
1357
- * page-up - scrolls up
1358
- * page-down - scrolls down
1359
- * end - scrolls to bottom
1360
- * home - scrolls to top
1361
- * insert - changes how keys are entered
1362
- * delete - deletes the next character
1363
- * left - moves cursor left
1364
- * right - moves cursor right
1365
- * up - moves the cursor up
1366
- * down - moves the cursor down
1367
- * f1-12 - function buttons
1368
- * shift, ctrl, alt - special keys
1369
- * pause-break - the pause button
1370
- * scroll-lock - locks scrolling
1371
- * caps - makes caps
1372
- * escape - escape button
1373
- * num-lock - allows numbers on keypad
1374
- * print - screen capture
1375
- * @codeend
1376
- */
1377
- keycodes: {
1378
- //backspace
1379
- '\b':'8',
1380
-
1381
- //tab
1382
- '\t':'9',
1383
-
1384
- //enter
1385
- '\r':'13',
1386
-
1387
- //special
1388
- 'shift':'16','ctrl':'17','alt':'18',
1389
-
1390
- //weird
1391
- 'pause-break':'19',
1392
- 'caps':'20',
1393
- 'escape':'27',
1394
- 'num-lock':'144',
1395
- 'scroll-lock':'145',
1396
- 'print' : '44',
1397
-
1398
- //navigation
1399
- 'page-up':'33','page-down':'34','end':'35','home':'36',
1400
- 'left':'37','up':'38','right':'39','down':'40','insert':'45','delete':'46',
1401
-
1402
- //normal characters
1403
- ' ':'32',
1404
- '0':'48','1':'49','2':'50','3':'51','4':'52','5':'53','6':'54','7':'55','8':'56','9':'57',
1405
- 'a':'65','b':'66','c':'67','d':'68','e':'69','f':'70','g':'71','h':'72','i':'73','j':'74','k':'75','l':'76','m':'77',
1406
- 'n':'78','o':'79','p':'80','q':'81','r':'82','s':'83','t':'84','u':'85','v':'86','w':'87','x':'88','y':'89','z':'90',
1407
- //normal-characters, numpad
1408
- 'num0':'96','num1':'97','num2':'98','num3':'99','num4':'100','num5':'101','num6':'102','num7':'103','num8':'104','num9':'105',
1409
- '*':'106','+':'107','-':'109','.':'110',
1410
- //normal-characters, others
1411
- '/':'111',
1412
- ';':'186',
1413
- '=':'187',
1414
- ',':'188',
1415
- '-':'189',
1416
- '.':'190',
1417
- '/':'191',
1418
- '`':'192',
1419
- '[':'219',
1420
- '\\':'220',
1421
- ']':'221',
1422
- "'":'222',
1423
-
1424
- //ignore these, you shouldn't use them
1425
- 'left window key':'91','right window key':'92','select key':'93',
1426
-
1427
-
1428
- 'f1':'112','f2':'113','f3':'114','f4':'115','f5':'116','f6':'117',
1429
- 'f7':'118','f8':'119','f9':'120','f10':'121','f11':'122','f12':'123'
1430
- },
1431
-
1432
- // what we can type in
1433
- typeable : /input|textarea/i,
1434
-
1435
- // selects text on an element
1436
- selectText: function(el, start, end){
1437
- if(el.setSelectionRange){
1438
- if(!end){
1439
- el.focus();
1440
- el.setSelectionRange(start, start);
1441
- } else {
1442
- el.selectionStart = start;
1443
- el.selectionEnd = end;
1444
- }
1445
- }else if (el.createTextRange) {
1446
- //el.focus();
1447
- var r = el.createTextRange();
1448
- r.moveStart('character', start);
1449
- end = end || start;
1450
- r.moveEnd('character', end - el.value.length);
1451
-
1452
- r.select();
1453
- }
1454
- },
1455
- getText: function(el){
1456
- //first check if the el has anything selected ..
1457
- if(Syn.typeable.test(el.nodeName)){
1458
- var sel = getSelection(el);
1459
- return el.value.substring(sel.start, sel.end)
1460
- }
1461
- //otherwise get from page
1462
- var win = Syn.helpers.getWindow(el);
1463
- if (win.getSelection) {
1464
- return win.getSelection().toString();
1465
- }
1466
- else if (win.document.getSelection) {
1467
- return win.document.getSelection().toString()
1468
- }
1469
- else {
1470
- return win.document.selection.createRange().text;
1471
- }
1472
- },
1473
- getSelection : getSelection
1474
- });
1475
-
1476
- h.extend(Syn.key,{
1477
- // retrieves a description of what events for this character should look like
1478
- data : function(key){
1479
- //check if it is described directly
1480
- if(S.key.browser[key]){
1481
- return S.key.browser[key];
1482
- }
1483
- for(var kind in S.key.kinds){
1484
- if(h.inArray(key, S.key.kinds[kind] ) > -1){
1485
- return S.key.browser[kind]
1486
- }
1487
- }
1488
- return S.key.browser.character
1489
- },
1490
-
1491
- //returns the special key if special
1492
- isSpecial : function(keyCode){
1493
- var specials = S.key.kinds.special;
1494
- for(var i=0; i < specials.length; i++){
1495
- if(Syn.keycodes[ specials[i] ] == keyCode){
1496
- return specials[i];
1497
- }
1498
- }
1499
- },
1500
- /**
1501
- * @hide
1502
- * gets the options for a key and event type ...
1503
- * @param {Object} key
1504
- * @param {Object} event
1505
- */
1506
- options : function(key, event){
1507
- var keyData = Syn.key.data(key);
1508
-
1509
- if(!keyData[event]){
1510
- //we shouldn't be creating this event
1511
- return null;
1512
- }
1513
-
1514
- var charCode = keyData[event][0],
1515
- keyCode = keyData[event][1],
1516
- result = {};
1517
-
1518
- if(keyCode == 'key'){
1519
- result.keyCode = Syn.keycodes[key]
1520
- } else if (keyCode == 'char'){
1521
- result.keyCode = key.charCodeAt(0)
1522
- }else{
1523
- result.keyCode = keyCode;
1524
- }
1525
-
1526
- if(charCode == 'char'){
1527
- result.charCode = key.charCodeAt(0)
1528
- }else if(charCode !== null){
1529
- result.charCode = charCode;
1530
- }
1531
-
1532
-
1533
- return result
1534
- },
1535
- //types of event keys
1536
- kinds : {
1537
- special : ["shift",'ctrl','alt','caps'],
1538
- specialChars : ["\b"],
1539
- navigation: ["page-up",'page-down','end','home','left','up','right','down','insert','delete'],
1540
- 'function' : ['f1','f2','f3','f4','f5','f6','f7','f8','f9','f10','f11','f12']
1541
- },
1542
- //returns the default function
1543
- getDefault : function(key){
1544
- //check if it is described directly
1545
- if(Syn.key.defaults[key]){
1546
- return Syn.key.defaults[key];
1547
- }
1548
- for(var kind in Syn.key.kinds){
1549
- if(h.inArray(key, Syn.key.kinds[kind])> -1 && Syn.key.defaults[kind] ){
1550
- return Syn.key.defaults[kind];
1551
- }
1552
- }
1553
- return Syn.key.defaults.character
1554
- },
1555
- // default behavior when typing
1556
- defaults : {
1557
- 'character' : function(options, scope, key, force, sel){
1558
- if(/num\d+/.test(key)){
1559
- key = key.match(/\d+/)[0]
1560
- }
1561
-
1562
- if(force || (!S.support.keyCharacters && Syn.typeable.test(this.nodeName))){
1563
- var current = this.value,
1564
- before = current.substr(0,sel.start),
1565
- after = current.substr(sel.end),
1566
- character = key;
1567
-
1568
- this.value = before+character+after;
1569
- //handle IE inserting \r\n
1570
- var charLength = character == "\n" && S.support.textareaCarriage ? 2 : character.length;
1571
- Syn.selectText(this, before.length + charLength)
1572
- }
1573
- },
1574
- 'c' : function(options, scope, key, force, sel){
1575
- if(Syn.key.ctrlKey){
1576
- Syn.key.clipboard = Syn.getText(this)
1577
- }else{
1578
- Syn.key.defaults.character.apply(this, arguments);
1579
- }
1580
- },
1581
- 'v' : function(options, scope, key, force, sel){
1582
- if(Syn.key.ctrlKey){
1583
- Syn.key.defaults.character.call(this, options,scope, Syn.key.clipboard, true,sel);
1584
- }else{
1585
- Syn.key.defaults.character.apply(this, arguments);
1586
- }
1587
- },
1588
- 'a' : function(options, scope, key, force, sel){
1589
- if(Syn.key.ctrlKey){
1590
- Syn.selectText(this, 0, this.value.length)
1591
- }else{
1592
- Syn.key.defaults.character.apply(this, arguments);
1593
- }
1594
- },
1595
- 'home' : function(){
1596
- Syn.onParents(this, function(el){
1597
- if(el.scrollHeight != el.clientHeight){
1598
- el.scrollTop = 0;
1599
- return false;
1600
- }
1601
- })
1602
- },
1603
- 'end' : function(){
1604
- Syn.onParents(this, function(el){
1605
- if(el.scrollHeight != el.clientHeight){
1606
- el.scrollTop = el.scrollHeight;
1607
- return false;
1608
- }
1609
- })
1610
- },
1611
- 'page-down' : function(){
1612
- //find the first parent we can scroll
1613
- Syn.onParents(this, function(el){
1614
- if(el.scrollHeight != el.clientHeight){
1615
- var ch = el.clientHeight
1616
- el.scrollTop += ch;
1617
- return false;
1618
- }
1619
- })
1620
- },
1621
- 'page-up' : function(){
1622
- Syn.onParents(this, function(el){
1623
- if(el.scrollHeight != el.clientHeight){
1624
- var ch = el.clientHeight
1625
- el.scrollTop -= ch;
1626
- return false;
1627
- }
1628
- })
1629
- },
1630
- '\b' : function(options, scope, key, force, sel){
1631
- //this assumes we are deleting from the end
1632
- if(!S.support.backspaceWorks && Syn.typeable.test(this.nodeName)){
1633
- var current = this.value,
1634
- before = current.substr(0,sel.start),
1635
- after = current.substr(sel.end);
1636
-
1637
- if(sel.start == sel.end && sel.start > 0){
1638
- //remove a character
1639
- this.value = before.substring(0, before.length - 1)+after
1640
- Syn.selectText(this, sel.start-1)
1641
- }else{
1642
- this.value = before+after;
1643
- Syn.selectText(this, sel.start)
1644
- }
1645
-
1646
- //set back the selection
1647
- }
1648
- },
1649
- 'delete' : function(options, scope, key, force, sel){
1650
- if(!S.support.backspaceWorks && Syn.typeable.test(this.nodeName)){
1651
- var current = this.value,
1652
- before = current.substr(0,sel.start),
1653
- after = current.substr(sel.end);
1654
- if(sel.start == sel.end && sel.start <= this.value.length - 1){
1655
- this.value = before+after.substring(1)
1656
- }else{
1657
- this.value = before+after;
1658
-
1659
- }
1660
- Syn.selectText(this, sel.start)
1661
- }
1662
- },
1663
- '\r' : function(options, scope, key, force, sel){
1664
-
1665
- var nodeName = this.nodeName.toLowerCase()
1666
- // submit a form
1667
- if(!S.support.keypressSubmits && nodeName == 'input'){
1668
- var form = Syn.closest(this, "form");
1669
- if(form){
1670
- Syn.trigger("submit", {}, form);
1671
- }
1672
-
1673
- }
1674
- //newline in textarea
1675
- if(!S.support.keyCharacters && nodeName == 'textarea'){
1676
- Syn.key.defaults.character.call(this, options, scope, "\n", undefined, sel)
1677
- }
1678
- // 'click' hyperlinks
1679
- if(!S.support.keypressOnAnchorClicks && nodeName == 'a'){
1680
- Syn.trigger("click", {}, this);
1681
- }
1682
- },
1683
- //
1684
- // Gets all focusable elements. If the element (this)
1685
- // doesn't have a tabindex, finds the next element after.
1686
- // If the element (this) has a tabindex finds the element
1687
- // with the next higher tabindex OR the element with the same
1688
- // tabindex after it in the document.
1689
- // @return the next element
1690
- //
1691
- '\t' : function(options, scope){
1692
- // focusable elements
1693
- var focusEls = getFocusable(this),
1694
- // the current element's tabindex
1695
- tabIndex = Syn.tabIndex(this),
1696
- // will be set to our guess for the next element
1697
- current = null,
1698
- // the next index we care about
1699
- currentIndex = 1000000000,
1700
- // set to true once we found 'this' element
1701
- found = false,
1702
- i = 0,
1703
- el,
1704
- //the tabindex of the tabable element we are looking at
1705
- elIndex,
1706
- firstNotIndexed,
1707
- prev;
1708
- orders = [];
1709
- for(; i< focusEls.length; i++){
1710
- orders.push([focusEls[i], i]);
1711
- }
1712
- var sort = function(order1, order2){
1713
- var el1 = order1[0],
1714
- el2 = order2[0],
1715
- tab1 = Syn.tabIndex(el1) || 0,
1716
- tab2 = Syn.tabIndex(el2) || 0;
1717
- if(tab1 == tab2){
1718
- return order1[1] - order2[1]
1719
- }else{
1720
- if(tab1 == 0){
1721
- return 1;
1722
- }else if(tab2 == 0){
1723
- return -1;
1724
- }else{
1725
- return tab1-tab2;
1726
- }
1727
- }
1728
- }
1729
- orders.sort(sort);
1730
- //now find current
1731
- for(i=0; i< orders.length; i++){
1732
- el = orders[i][0];
1733
- if(this== el ){
1734
- if(!Syn.key.shiftKey){
1735
- current = orders[i+1][0];
1736
- if(!current){
1737
- current = orders[0][0]
1738
- }
1739
- }else{
1740
- current = orders[i-1][0];
1741
- if(!current){
1742
- current = orders[focusEls.length-1][0]
1743
- }
1744
- }
1745
-
1746
- }
1747
- }
1748
-
1749
- //restart if we didn't find anything
1750
- if(!current){
1751
- current = firstNotIndexed;
1752
- }
1753
- current && current.focus();
1754
- return current;
1755
- },
1756
- 'left' : function(options, scope, key, force, sel){
1757
- if( Syn.typeable.test(this.nodeName) ){
1758
- if(Syn.key.shiftKey){
1759
- Syn.selectText(this, sel.start == 0 ? 0 : sel.start - 1, sel.end)
1760
- }else{
1761
- Syn.selectText(this, sel.start == 0 ? 0 : sel.start - 1)
1762
- }
1763
- }
1764
- },
1765
- 'right' : function(options, scope, key, force, sel){
1766
- if( Syn.typeable.test(this.nodeName) ){
1767
- if(Syn.key.shiftKey){
1768
- Syn.selectText(this, sel.start, sel.end+1 > this.value.length ? this.value.length : sel.end+1)
1769
- }else{
1770
- Syn.selectText(this, sel.end+1 > this.value.length ? this.value.length : sel.end+1)
1771
- }
1772
- }
1773
- },
1774
- 'up' : function(){
1775
- if(/select/i.test(this.nodeName)){
1776
-
1777
- this.selectedIndex = this.selectedIndex ? this.selectedIndex-1 : 0;
1778
- //set this to change on blur?
1779
- }
1780
- },
1781
- 'down' : function(){
1782
- if(/select/i.test(this.nodeName)){
1783
- Syn.changeOnBlur(this, "selectedIndex", this.selectedIndex)
1784
- this.selectedIndex = this.selectedIndex+1;
1785
- //set this to change on blur?
1786
- }
1787
- },
1788
- 'shift' : function(){
1789
- return null;
1790
- }
1791
- }
1792
- });
1793
-
1794
-
1795
- h.extend(Syn.create,{
1796
- keydown : {
1797
- setup : function(type, options, element){
1798
- if(h.inArray(options,Syn.key.kinds.special ) != -1){
1799
- Syn.key[options+"Key"] = element;
1800
- }
1801
- }
1802
- },
1803
- keyup : {
1804
- setup : function(type, options, element){
1805
- if(h.inArray(options,Syn.key.kinds.special )!= -1){
1806
- Syn.key[options+"Key"] = null;
1807
- }
1808
- }
1809
- },
1810
- key : {
1811
- // return the options for a key event
1812
- options : function(type, options, element){
1813
- //check if options is character or has character
1814
- options = typeof options != "object" ? {character : options} : options;
1815
-
1816
- //don't change the orignial
1817
- options = h.extend({}, options)
1818
- if(options.character){
1819
- h.extend(options, S.key.options(options.character, type));
1820
- delete options.character;
1821
- }
1822
-
1823
- options = h.extend({
1824
- ctrlKey: !!Syn.key.ctrlKey,
1825
- altKey: !!Syn.key.altKey,
1826
- shiftKey: !!Syn.key.shiftKey,
1827
- metaKey: !!Syn.key.metaKey
1828
- }, options)
1829
-
1830
- return options;
1831
- },
1832
- // creates a key event
1833
- event : document.createEvent ?
1834
- function(type, options, element){ //Everyone Else
1835
- var event;
1836
-
1837
- try {
1838
-
1839
- event = element.ownerDocument.createEvent("KeyEvents");
1840
- event.initKeyEvent(type, true, true, window,
1841
- options.ctrlKey, options.altKey, options.shiftKey, options.metaKey,
1842
- options.keyCode, options.charCode );
1843
- } catch(e) {
1844
- event = h.createBasicStandardEvent(type,options)
1845
- }
1846
- event.synthetic = true;
1847
- return event;
1848
-
1849
- } :
1850
- function(type, options, element){
1851
- var event = h.createEventObject.apply(this,arguments);
1852
- h.extend(event, options)
1853
-
1854
- return event;
1855
- }
1856
- }
1857
- });
1858
-
1859
- var convert = {
1860
- "enter" : "\r",
1861
- "backspace" : "\b",
1862
- "tab" : "\t",
1863
- "space" : " "
1864
- }
1865
-
1866
- /**
1867
- * @add Syn prototype
1868
- */
1869
- h.extend(Syn.init.prototype,
1870
- {
1871
- /**
1872
- * @function key
1873
- * Types a single key. The key should be
1874
- * a string that matches a
1875
- * [Syn.static.keycodes].
1876
- *
1877
- * The following sends a carridge return
1878
- * to the 'name' element.
1879
- * @codestart
1880
- * Syn.key('\r','name')
1881
- * @codeend
1882
- * For each character, a keydown, keypress, and keyup is triggered if
1883
- * appropriate.
1884
- * @param {String} options
1885
- * @param {HTMLElement} [element]
1886
- * @param {Function} [callback]
1887
- * @return {HTMLElement} the element currently focused.
1888
- */
1889
- _key : function(options, element, callback){
1890
- //first check if it is a special up
1891
- if(/-up$/.test(options)
1892
- && h.inArray(options.replace("-up",""),Syn.key.kinds.special )!= -1){
1893
- Syn.trigger('keyup',options.replace("-up",""), element )
1894
- callback(true, element);
1895
- return;
1896
- }
1897
-
1898
-
1899
- var caret = Syn.typeable.test(element.nodeName) && getSelection(element),
1900
- key = convert[options] || options,
1901
- // should we run default events
1902
- runDefaults = Syn.trigger('keydown',key, element ),
1903
-
1904
- // a function that gets the default behavior for a key
1905
- getDefault = Syn.key.getDefault,
1906
-
1907
- // how this browser handles preventing default events
1908
- prevent = Syn.key.browser.prevent,
1909
-
1910
- // the result of the default event
1911
- defaultResult,
1912
-
1913
- // options for keypress
1914
- keypressOptions = Syn.key.options(key, 'keypress')
1915
-
1916
-
1917
- if(runDefaults){
1918
- //if the browser doesn't create keypresses for this key, run default
1919
- if(!keypressOptions){
1920
- defaultResult = getDefault(key).call(element, keypressOptions, h.getWindow(element), key, undefined, caret)
1921
- }else{
1922
- //do keypress
1923
- runDefaults = Syn.trigger('keypress',keypressOptions, element )
1924
- if(runDefaults){
1925
- defaultResult = getDefault(key).call(element, keypressOptions, h.getWindow(element), key, undefined, caret)
1926
- }
1927
- }
1928
- }else{
1929
- //canceled ... possibly don't run keypress
1930
- if(keypressOptions && h.inArray('keypress',prevent.keydown) == -1 ){
1931
- Syn.trigger('keypress',keypressOptions, element )
1932
- }
1933
- }
1934
- if(defaultResult && defaultResult.nodeName){
1935
- element = defaultResult
1936
- }
1937
-
1938
- if(defaultResult !== null){
1939
- setTimeout(function(){
1940
- Syn.trigger('keyup',Syn.key.options(key, 'keyup'), element )
1941
- callback(runDefaults, element)
1942
- },1)
1943
- }else{
1944
- callback(runDefaults, element)
1945
- }
1946
-
1947
-
1948
- //do mouseup
1949
-
1950
- return element;
1951
- // is there a keypress? .. if not , run default
1952
- // yes -> did we prevent it?, if not run ...
1953
-
1954
- },
1955
- /**
1956
- * @function type
1957
- * Types sequence of [Syn.prototype.key key actions]. Each
1958
- * character is typed, one at a type.
1959
- * Multi-character keys like 'left' should be
1960
- * enclosed in square brackents.
1961
- *
1962
- * The following types 'JavaScript MVC' then deletes the space.
1963
- * @codestart
1964
- * Syn.type('JavaScript MVC[left][left][left]\b','name')
1965
- * @codeend
1966
- *
1967
- * Type is able to handle (and move with) tabs (\t).
1968
- * The following simulates tabing and entering values in a form and
1969
- * eventually submitting the form.
1970
- * @codestart
1971
- * Syn.type("Justin\tMeyer\t27\tjustinbmeyer@gmail.com\r")
1972
- * @codeend
1973
- * @param {String} options the text to type
1974
- * @param {HTMLElement} [element] an element or an id of an element
1975
- * @param {Function} [callback] a function to callback
1976
- */
1977
- _type : function(options, element, callback){
1978
- //break it up into parts ...
1979
- //go through each type and run
1980
- var parts = options.match(/(\[[^\]]+\])|([^\[])/g),
1981
- self = this,
1982
- runNextPart = function(runDefaults, el){
1983
- var part = parts.shift();
1984
- if(!part){
1985
- callback(runDefaults, el);
1986
- return;
1987
- }
1988
- el = el || element;
1989
- if(part.length > 1){
1990
- part = part.substr(1,part.length - 2)
1991
- }
1992
- self._key(part, el, runNextPart)
1993
- }
1994
-
1995
- runNextPart();
1996
-
1997
- }
1998
- });
1999
-
2000
-
2001
- //do support code
2002
- (function(){
2003
- if(!document.body){
2004
- setTimeout(arguments.callee,1)
2005
- return;
2006
- }
2007
-
2008
- var div = document.createElement("div"),
2009
- checkbox,
2010
- submit,
2011
- form,
2012
- input,
2013
- submitted = false,
2014
- anchor,
2015
- textarea;
2016
-
2017
- div.innerHTML = "<form id='outer'>"+
2018
- "<input name='checkbox' type='checkbox'/>"+
2019
- "<input name='radio' type='radio' />"+
2020
- "<input type='submit' name='submitter'/>"+
2021
- "<input type='input' name='inputter'/>"+
2022
- "<input name='one'>"+
2023
- "<input name='two'/>"+
2024
- "<a href='#abc'></a>"+
2025
- "<textarea>1\n2</textarea>"
2026
- "</form>";
2027
-
2028
- document.documentElement.appendChild(div);
2029
- form = div.firstChild;
2030
- checkbox = form.childNodes[0];
2031
- submit = form.childNodes[2];
2032
- anchor = form.getElementsByTagName("a")[0];
2033
- textarea = form.getElementsByTagName("textarea")[0]
2034
- form.onsubmit = function(ev){
2035
- if (ev.preventDefault)
2036
- ev.preventDefault();
2037
- S.support.keypressSubmits = true;
2038
- ev.returnValue = false;
2039
- return false;
2040
- }
2041
- Syn.trigger("keypress", "\r", form.childNodes[3]);
2042
-
2043
-
2044
- Syn.trigger("keypress", "a", form.childNodes[3]);
2045
- S.support.keyCharacters = form.childNodes[3].value == "a";
2046
-
2047
-
2048
- form.childNodes[3].value = "a"
2049
- Syn.trigger("keypress", "\b", form.childNodes[3]);
2050
- S.support.backspaceWorks = form.childNodes[3].value == "";
2051
-
2052
-
2053
-
2054
- form.childNodes[3].onchange = function(){
2055
- S.support.focusChanges = true;
2056
- }
2057
- form.childNodes[3].focus();
2058
- Syn.trigger("keypress", "a", form.childNodes[3]);
2059
- form.childNodes[5].focus();
2060
-
2061
- //test keypress \r on anchor submits
2062
- S.bind(anchor,"click",function(ev){
2063
- if (ev.preventDefault)
2064
- ev.preventDefault();
2065
- S.support.keypressOnAnchorClicks = true;
2066
- ev.returnValue = false;
2067
- return false;
2068
- })
2069
- Syn.trigger("keypress", "\r", anchor);
2070
-
2071
- S.support.textareaCarriage = textarea.value.length == 4
2072
- document.documentElement.removeChild(div);
2073
-
2074
- S.support.ready = true;
2075
- })();
2076
-
2077
-
2078
-
2079
-
2080
-
2081
- })();
2082
-
2083
- // funcunit/synthetic/drag/drag.js
2084
-
2085
- (function($){
2086
-
2087
- // document body has to exists for this test
2088
-
2089
- (function(){
2090
- if (!document.body) {
2091
- setTimeout(arguments.callee, 1)
2092
- return;
2093
- }
2094
- var div = document.createElement('div')
2095
- document.body.appendChild(div);
2096
- Syn.helpers.extend(div.style, {
2097
- width: "100px",
2098
- height: "10000px",
2099
- backgroundColor: "blue",
2100
- position: "absolute",
2101
- top: "10px",
2102
- left: "0px",
2103
- zIndex: 19999
2104
- });
2105
- document.body.scrollTop = 11;
2106
- if(!document.elementFromPoint){
2107
- return;
2108
- }
2109
- var el = document.elementFromPoint(3, 1)
2110
- if (el == div) {
2111
- Syn.support.elementFromClient = true;
2112
- }
2113
- else {
2114
- Syn.support.elementFromPage = true;
2115
- }
2116
- document.body.removeChild(div);
2117
- document.body.scrollTop = 0;
2118
- })();
2119
-
2120
-
2121
- //gets an element from a point
2122
- var elementFromPoint = function(point, element){
2123
- var clientX = point.clientX, clientY = point.clientY, win = Syn.helpers.getWindow(element)
2124
-
2125
- if (Syn.support.elementFromPage) {
2126
- var off = Syn.helpers.scrollOffset(win);
2127
- clientX = clientX + off.left; //convert to pageX
2128
- clientY = clientY + off.top; //convert to pageY
2129
- }
2130
-
2131
- return win.document.elementFromPoint ? win.document.elementFromPoint(clientX, clientY) : element;
2132
- }, //creates an event at a certain point
2133
- createEventAtPoint = function(event, point, element){
2134
- var el = elementFromPoint(point, element)
2135
- Syn.trigger(event, point, el || element)
2136
- return el;
2137
- }, // creates a mousemove event, but first triggering mouseout / mouseover if appropriate
2138
- mouseMove = function(point, element, last){
2139
- var el = elementFromPoint(point, element)
2140
- if (last != el && el && last) {
2141
- var options = Syn.helpers.extend({},point);
2142
- options.relatedTarget = el;
2143
- Syn.trigger("mouseout", options, last);
2144
- options.relatedTarget = last;
2145
- Syn.trigger("mouseover", options, el);
2146
- }
2147
-
2148
- Syn.trigger("mousemove", point, el || element)
2149
- return el;
2150
- }, // start and end are in clientX, clientY
2151
- startMove = function(start, end, duration, element, callback){
2152
- var startTime = new Date(),
2153
- distX = end.clientX - start.clientX,
2154
- distY = end.clientY - start.clientY,
2155
- win = Syn.helpers.getWindow(element),
2156
- current = elementFromPoint(start, element),
2157
- cursor = win.document.createElement('div'),
2158
- calls = 0;
2159
- move = function(){
2160
- //get what fraction we are at
2161
- var now = new Date(),
2162
- scrollOffset = Syn.helpers.scrollOffset(win),
2163
- fraction = ( calls == 0 ? 0 : now - startTime) / duration,
2164
- options = {
2165
- clientX: distX * fraction + start.clientX,
2166
- clientY: distY * fraction + start.clientY
2167
- };
2168
- calls++;
2169
- if (fraction < 1) {
2170
- Syn.helpers.extend(cursor.style, {
2171
- left: (options.clientX + scrollOffset.left + 2) + "px",
2172
- top: (options.clientY + scrollOffset.top + 2) + "px"
2173
- })
2174
- current = mouseMove(options, element, current)
2175
- setTimeout(arguments.callee, 15)
2176
- }
2177
- else {
2178
- current = mouseMove(end, element, current);
2179
- win.document.body.removeChild(cursor)
2180
- callback();
2181
- }
2182
- }
2183
- Syn.helpers.extend(cursor.style, {
2184
- height: "5px",
2185
- width: "5px",
2186
- backgroundColor: "red",
2187
- position: "absolute",
2188
- zIndex: 19999,
2189
- fontSize: "1px"
2190
- })
2191
- win.document.body.appendChild(cursor)
2192
- move();
2193
- },
2194
- startDrag = function(start, end, duration, element, callback){
2195
- createEventAtPoint("mousedown", start, element);
2196
- startMove(start, end, duration, element, function(){
2197
- createEventAtPoint("mouseup", end, element);
2198
- callback();
2199
- })
2200
- },
2201
- center = function(el){
2202
- var j = Syn.jquery()(el),
2203
- o = j.offset();
2204
- return{
2205
- pageX: o.left + (j.width() / 2),
2206
- pageY: o.top + (j.height() / 2)
2207
- }
2208
- },
2209
- convertOption = function(option, win, from){
2210
- var page = /(\d+)[x ](\d+)/,
2211
- client = /(\d+)X(\d+)/,
2212
- relative = /([+-]\d+)[xX ]([+-]\d+)/
2213
- //check relative "+22x-44"
2214
- if (typeof option == 'string' && relative.test(option) && from) {
2215
- var cent = center(from),
2216
- parts = option.match(relative);
2217
- option = {
2218
- pageX: cent.pageX + parseInt(parts[1]),
2219
- pageY: cent.pageY +parseInt(parts[2])
2220
- }
2221
- }
2222
- if (typeof option == 'string' && page.test(option)) {
2223
- var parts = option.match(page)
2224
- option = {
2225
- pageX: parseInt(parts[1]),
2226
- pageY: parseInt(parts[2])
2227
- }
2228
- }
2229
- if (typeof option == 'string' && client.test(option)) {
2230
- var parts = option.match(client)
2231
- option = {
2232
- clientX: parseInt(parts[1]),
2233
- clientY: parseInt(parts[2])
2234
- }
2235
- }
2236
- if (typeof option == 'string') {
2237
- option = Syn.jquery()(option, win.document)[0];
2238
- }
2239
- if (option.nodeName) {
2240
- option = center(option)
2241
- }
2242
- if (option.pageX) {
2243
- var off = Syn.helpers.scrollOffset(win);
2244
- option = {
2245
- clientX: option.pageX - off.left,
2246
- clientY: option.pageY - off.top
2247
- }
2248
- }
2249
- return option;
2250
- }
2251
- /**
2252
- * @add Syn prototype
2253
- */
2254
- Syn.helpers.extend(Syn.init.prototype,{
2255
- /**
2256
- * @function move
2257
- * Moves the cursor from one point to another.
2258
- * <h3>Quick Example</h3>
2259
- * The following moves the cursor from (0,0) in
2260
- * the window to (100,100) in 1 second.
2261
- * @codestart
2262
- * Syn.move(
2263
- * {
2264
- * from: {clientX: 0, clientY: 0},
2265
- * to: {clientX: 100, clientY: 100},
2266
- * duration: 1000
2267
- * },
2268
- * document.document)
2269
- * @codeend
2270
- * <h2>Options</h2>
2271
- * There are many ways to configure the endpoints of the move.
2272
- *
2273
- * <h3>PageX and PageY</h3>
2274
- * If you pass pageX or pageY, these will get converted
2275
- * to client coordinates.
2276
- * @codestart
2277
- * Syn.move(
2278
- * {
2279
- * from: {pageX: 0, pageY: 0},
2280
- * to: {pageX: 100, pageY: 100}
2281
- * },
2282
- * document.document)
2283
- * @codeend
2284
- * <h3>String Coordinates</h3>
2285
- * You can set the pageX and pageY as strings like:
2286
- * @codestart
2287
- * Syn.move(
2288
- * {
2289
- * from: "0x0",
2290
- * to: "100x100"
2291
- * },
2292
- * document.document)
2293
- * @codeend
2294
- * <h3>Element Coordinates</h3>
2295
- * If jQuery is present, you can pass an element as the from or to option
2296
- * and the coordinate will be set as the center of the element.
2297
- * @codestart
2298
- * Syn.move(
2299
- * {
2300
- * from: $(".recipe")[0],
2301
- * to: $("#trash")[0]
2302
- * },
2303
- * document.document)
2304
- * @codeend
2305
- * <h3>Query Strings</h3>
2306
- * If jQuery is present, you can pass a query string as the from or to option.
2307
- * @codestart
2308
- * Syn.move(
2309
- * {
2310
- * from: ".recipe",
2311
- * to: "#trash"
2312
- * },
2313
- * document.document)
2314
- * @codeend
2315
- * <h3>No From</h3>
2316
- * If you don't provide a from, the element argument passed to Syn is used.
2317
- * @codestart
2318
- * Syn.move(
2319
- * { to: "#trash" },
2320
- * 'myrecipe')
2321
- * @codeend
2322
- * @param {Object} options
2323
- * @param {HTMLElement} from
2324
- * @param {Function} callback
2325
- */
2326
- _move : function(options, from, callback){
2327
- //need to convert if elements
2328
- var win = Syn.helpers.getWindow(from),
2329
- fro = convertOption(options.from || from, win),
2330
- to = convertOption(options.to || options, win);
2331
-
2332
- startMove(fro, to, options.duration || 500, from, callback);
2333
- },
2334
- /**
2335
- * @function drag
2336
- * Creates a mousedown and drags from one point to another.
2337
- * Check out [Syn.prototype.move move] for API details.
2338
- *
2339
- * @param {Object} options
2340
- * @param {Object} from
2341
- * @param {Object} callback
2342
- */
2343
- _drag : function(options, from, callback){
2344
- //need to convert if elements
2345
- var win = Syn.helpers.getWindow(from),
2346
- fro = convertOption(options.from || from, win, from),
2347
- to = convertOption(options.to || options, win, from);
2348
-
2349
- startDrag(fro, to, options.duration || 500, from, callback);
2350
- }
2351
- })
2352
-
2353
-
2354
- })();
2355
-