api_taster 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/.gitignore +11 -0
  2. data/.rspec +1 -0
  3. data/.travis.yml +3 -0
  4. data/Gemfile +6 -0
  5. data/README.md +68 -0
  6. data/Rakefile +40 -0
  7. data/api_taster.gemspec +27 -0
  8. data/app/assets/images/api_taster/.gitkeep +0 -0
  9. data/app/assets/javascripts/api_taster/01-prettify.js +28 -0
  10. data/app/assets/javascripts/api_taster/02-jquery.form.js +1076 -0
  11. data/app/assets/javascripts/api_taster/app.js +88 -0
  12. data/app/assets/javascripts/api_taster/application.js +16 -0
  13. data/app/assets/stylesheets/api_taster/01-prettify.css +1 -0
  14. data/app/assets/stylesheets/api_taster/02-prettify-sunburst.css +51 -0
  15. data/app/assets/stylesheets/api_taster/application.css +13 -0
  16. data/app/assets/stylesheets/api_taster/layout.css.scss +51 -0
  17. data/app/controllers/api_taster/application_controller.rb +5 -0
  18. data/app/controllers/api_taster/routes_controller.rb +12 -0
  19. data/app/helpers/api_taster/application_helper.rb +4 -0
  20. data/app/views/api_taster/routes/_param_form_element.html.erb +6 -0
  21. data/app/views/api_taster/routes/_param_form_legend.html.erb +1 -0
  22. data/app/views/api_taster/routes/_undefined_route.html.erb +7 -0
  23. data/app/views/api_taster/routes/index.html.erb +24 -0
  24. data/app/views/api_taster/routes/show.html.erb +55 -0
  25. data/app/views/layouts/api_taster/application.html.erb +29 -0
  26. data/config/routes.rb +5 -0
  27. data/lib/api_taster.rb +13 -0
  28. data/lib/api_taster/engine.rb +17 -0
  29. data/lib/api_taster/form_builder.rb +49 -0
  30. data/lib/api_taster/mapper.rb +30 -0
  31. data/lib/api_taster/route.rb +63 -0
  32. data/lib/api_taster/version.rb +3 -0
  33. data/lib/tasks/api_taster_tasks.rake +0 -0
  34. data/script/rails +8 -0
  35. data/spec/controllers/api_taster/routes_controller_spec.rb +21 -0
  36. data/spec/dummy/Rakefile +7 -0
  37. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  38. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  39. data/spec/dummy/app/controllers/application_controller.rb +4 -0
  40. data/spec/dummy/app/controllers/users_controller.rb +21 -0
  41. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  42. data/spec/dummy/app/mailers/.gitkeep +0 -0
  43. data/spec/dummy/app/models/.gitkeep +0 -0
  44. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  45. data/spec/dummy/config.ru +4 -0
  46. data/spec/dummy/config/application.rb +67 -0
  47. data/spec/dummy/config/boot.rb +10 -0
  48. data/spec/dummy/config/environment.rb +5 -0
  49. data/spec/dummy/config/environments/development.rb +37 -0
  50. data/spec/dummy/config/environments/production.rb +67 -0
  51. data/spec/dummy/config/environments/test.rb +37 -0
  52. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  53. data/spec/dummy/config/initializers/inflections.rb +15 -0
  54. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  55. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  56. data/spec/dummy/config/initializers/session_store.rb +8 -0
  57. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  58. data/spec/dummy/config/locales/en.yml +5 -0
  59. data/spec/dummy/config/routes.rb +33 -0
  60. data/spec/dummy/lib/assets/.gitkeep +0 -0
  61. data/spec/dummy/log/.gitkeep +0 -0
  62. data/spec/dummy/script/rails +6 -0
  63. data/spec/form_builder_spec.rb +29 -0
  64. data/spec/mapper_spec.rb +41 -0
  65. data/spec/route_spec.rb +71 -0
  66. data/spec/spec_helper.rb +12 -0
  67. metadata +503 -0
@@ -0,0 +1,11 @@
1
+ .DS_Store
2
+ ._*
3
+ .bundle/
4
+ coverage/
5
+ log/*.log
6
+ pkg/
7
+ Gemfile.lock
8
+ spec/dummy/db/*.sqlite3
9
+ spec/dummy/log/*.log
10
+ spec/dummy/tmp/
11
+ spec/dummy/.sass-cache
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "jquery-rails"
6
+ gem "bootstrap-sass"
@@ -0,0 +1,68 @@
1
+ # ApiTaster [![Build Status](https://secure.travis-ci.org/fredwu/api_taster.png?branch=master)](http://travis-ci.org/fredwu/api_taster) [![Dependency Status](https://gemnasium.com/fredwu/api_taster.png)](https://gemnasium.com/fredwu/api_taster)
2
+
3
+ A quick and easy way to visually test out your application's API.
4
+
5
+ ![](http://i.imgur.com/1kyEk.png)
6
+
7
+ ## Why?
8
+
9
+ There are already many awesome API clients (such as [Postman](https://chrome.google.com/webstore/detail/fdmmgilgnpjigdojojpjoooidkmcomcm)), so why reinvent the wheel?
10
+
11
+ API Taster compared to alternatives, have the following advantages:
12
+
13
+ - API endpoints are automatically generated from your Rails routes definition
14
+ - Defining post params is as easy as defining routes
15
+ - Post params can be shared with your test factories
16
+
17
+ ## Usage
18
+
19
+ Add API Taster in your gemfile:
20
+
21
+ ```ruby
22
+ gem 'api_taster'
23
+ ```
24
+ Mount API Taster, this will allow you to visit API Taster from within your app. For example:
25
+
26
+ ```ruby
27
+ Rails.application.routes.draw do
28
+ mount ApiTaster::Engine => "/api_taster"
29
+ end
30
+ ```
31
+
32
+ In `routes.rb`, define parameters for each API endpoint after the normal routes definition block. For example:
33
+
34
+ ```ruby
35
+ ApiTaster.routes do
36
+ get '/users'
37
+
38
+ post '/users', {
39
+ :user => {
40
+ :name => 'Fred'
41
+ }
42
+ }
43
+
44
+ get '/users/:id', {
45
+ :id => 1
46
+ }
47
+
48
+ put '/users/:id', {
49
+ :id => 1, :user => {
50
+ :name => 'Awesome'
51
+ }
52
+ }
53
+
54
+ delete '/users/:id', {
55
+ :id => 1
56
+ }
57
+ end
58
+ ```
59
+
60
+ That's it! Enjoy! :)
61
+
62
+ ## License
63
+
64
+ This gem is released under the [MIT License](http://www.opensource.org/licenses/mit-license.php).
65
+
66
+ ## Author
67
+
68
+ [Fred Wu](https://github.com/fredwu), originally built for [Locomote](http://locomote.com.au).
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'ApiTaster'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
30
+ require 'rake/testtask'
31
+
32
+ Rake::TestTask.new(:test) do |t|
33
+ t.libs << 'lib'
34
+ t.libs << 'spec'
35
+ t.pattern = 'spec/**/*_spec.rb'
36
+ t.verbose = false
37
+ end
38
+
39
+
40
+ task :default => :test
@@ -0,0 +1,27 @@
1
+ $:.push File.expand_path("../lib", __FILE__)
2
+
3
+ require "api_taster/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "api_taster"
7
+ s.version = ApiTaster::VERSION
8
+ s.authors = ["Fred Wu"]
9
+ s.email = ["ifredwu@gmail.com"]
10
+ s.homepage = "https://github.com/fredwu/api_taster"
11
+ s.summary = "A quick and easy way to visually test out your application's API."
12
+ s.description = s.summary
13
+
14
+ s.files = `git ls-files`.split($\)
15
+ s.test_files = Dir["spec/**/*"]
16
+
17
+ s.add_dependency 'rails', '>= 3.1.0'
18
+ s.add_dependency 'jquery-rails'
19
+ s.add_dependency 'sass-rails'
20
+ s.add_dependency 'bootstrap-sass', '~> 2.0.3'
21
+
22
+ s.add_development_dependency 'rake'
23
+ s.add_development_dependency 'simplecov'
24
+ s.add_development_dependency 'rspec-rails'
25
+ s.add_development_dependency 'pry'
26
+ s.add_development_dependency 'thin'
27
+ end
File without changes
@@ -0,0 +1,28 @@
1
+ var q=null;window.PR_SHOULD_USE_CONTINUATION=!0;
2
+ (function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a=
3
+ [],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c<i;++c){var j=f[c];if(/\\[bdsw]/i.test(j))a.push(j);else{var j=m(j),d;c+2<i&&"-"===f[c+1]?(d=m(f[c+2]),c+=2):d=j;b.push([j,d]);d<65||j>122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;c<b.length;++c)i=b[c],i[0]<=j[1]+1?j[1]=Math.max(j[1],i[1]):f.push(j=i);b=["["];o&&b.push("^");b.push.apply(b,a);for(c=0;c<
4
+ f.length;++c)i=f[c],b.push(e(i[0])),i[1]>i[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c<b;++c){var j=f[c];j==="("?++i:"\\"===j.charAt(0)&&(j=+j.substring(1))&&j<=i&&(d[j]=-1)}for(c=1;c<d.length;++c)-1===d[c]&&(d[c]=++t);for(i=c=0;c<b;++c)j=f[c],j==="("?(++i,d[i]===void 0&&(f[c]="(?:")):"\\"===j.charAt(0)&&
5
+ (j=+j.substring(1))&&j<=i&&(f[c]="\\"+d[i]);for(i=c=0;c<b;++c)"^"===f[c]&&"^"!==f[c+1]&&(f[c]="");if(a.ignoreCase&&s)for(c=0;c<b;++c)j=f[c],a=j.charAt(0),j.length>=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p<d;++p){var g=a[p];if(g.ignoreCase)l=!0;else if(/[a-z]/i.test(g.source.replace(/\\u[\da-f]{4}|\\x[\da-f]{2}|\\[^UXux]/gi,""))){s=!0;l=!1;break}}for(var r=
6
+ {b:8,t:9,n:10,v:11,f:12,r:13},n=[],p=0,d=a.length;p<d;++p){g=a[p];if(g.global||g.multiline)throw Error(""+g);n.push("(?:"+y(g)+")")}return RegExp(n.join("|"),l?"gi":"g")}function M(a){function m(a){switch(a.nodeType){case 1:if(e.test(a.className))break;for(var g=a.firstChild;g;g=g.nextSibling)m(g);g=a.nodeName;if("BR"===g||"LI"===g)h[s]="\n",t[s<<1]=y++,t[s++<<1|1]=a;break;case 3:case 4:g=a.nodeValue,g.length&&(g=p?g.replace(/\r\n?/g,"\n"):g.replace(/[\t\n\r ]+/g," "),h[s]=g,t[s<<1]=y,y+=g.length,
7
+ t[s++<<1|1]=a)}}var e=/(?:^|\s)nocode(?:\s|$)/,h=[],y=0,t=[],s=0,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=document.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);m(a);return{a:h.join("").replace(/\n$/,""),c:t}}function B(a,m,e,h){m&&(a={a:m,d:a},e(a),h.push.apply(h,a.e))}function x(a,m){function e(a){for(var l=a.d,p=[l,"pln"],d=0,g=a.a.match(y)||[],r={},n=0,z=g.length;n<z;++n){var f=g[n],b=r[f],o=void 0,c;if(typeof b===
8
+ "string")c=!1;else{var i=h[f.charAt(0)];if(i)o=f.match(i[1]),b=i[0];else{for(c=0;c<t;++c)if(i=m[c],o=f.match(i[1])){b=i[0];break}o||(b="pln")}if((c=b.length>=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m),
9
+ l=[],p={},d=0,g=e.length;d<g;++d){var r=e[d],n=r[3];if(n)for(var k=n.length;--k>=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/,
10
+ q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/,
11
+ q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g,
12
+ "");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a),
13
+ a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e}
14
+ for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g<d.length;++g)e(d[g]);m===(m|0)&&d[0].setAttribute("value",
15
+ m);var r=s.createElement("OL");r.className="linenums";for(var n=Math.max(0,m-1|0)||0,g=0,z=d.length;g<z;++g)l=d[g],l.className="L"+(g+n)%10,l.firstChild||l.appendChild(s.createTextNode("\xa0")),r.appendChild(l);a.appendChild(r)}function k(a,m){for(var e=m.length;--e>=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*</.test(m)?"default-markup":"default-code";return A[a]}function E(a){var m=
16
+ a.g;try{var e=M(a.h),h=e.a;a.a=h;a.c=e.c;a.d=0;C(m,h)(a);var k=/\bMSIE\b/.test(navigator.userAgent),m=/\n/g,t=a.a,s=t.length,e=0,l=a.c,p=l.length,h=0,d=a.e,g=d.length,a=0;d[g]=s;var r,n;for(n=r=0;n<g;)d[n]!==d[n+2]?(d[r++]=d[n++],d[r++]=d[n++]):n+=2;g=r;for(n=r=0;n<g;){for(var z=d[n],f=d[n+1],b=n+2;b+2<=g&&d[b+1]===f;)b+=2;d[r++]=z;d[r++]=f;n=b}for(d.length=r;h<p;){var o=l[h+2]||s,c=d[a+2]||s,b=Math.min(o,c),i=l[h+1],j;if(i.nodeType!==1&&(j=t.substring(e,b))){k&&(j=j.replace(m,"\r"));i.nodeValue=
17
+ j;var u=i.ownerDocument,v=u.createElement("SPAN");v.className=d[a+1];var x=i.parentNode;x.replaceChild(v,i);v.appendChild(i);e<o&&(l[h+1]=i=u.createTextNode(t.substring(b,o)),x.insertBefore(i,v.nextSibling))}e=b;e>=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"],
18
+ "catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"],
19
+ H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"],
20
+ J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+
21
+ I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^<?]+/],["dec",/^<!\w[^>]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^<xmp\b[^>]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^<script\b[^>]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^<style\b[^>]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),
22
+ ["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css",
23
+ /^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}),
24
+ ["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes",
25
+ hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p<h.length&&l.now()<e;p++){var n=h[p],k=n.className;if(k.indexOf("prettyprint")>=0){var k=k.match(g),f,b;if(b=
26
+ !k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p<h.length?setTimeout(m,
27
+ 250):a&&a()}for(var e=[document.getElementsByTagName("pre"),document.getElementsByTagName("code"),document.getElementsByTagName("xmp")],h=[],k=0;k<e.length;++k)for(var t=0,s=e[k].length;t<s;++t)h.push(e[k][t]);var e=q,l=Date;l.now||(l={now:function(){return+new Date}});var p=0,d,g=/\blang(?:uage)?-([\w.]+)(?!\S)/;m()};window.PR={createSimpleLexer:x,registerLangHandler:k,sourceDecorator:u,PR_ATTRIB_NAME:"atn",PR_ATTRIB_VALUE:"atv",PR_COMMENT:"com",PR_DECLARATION:"dec",PR_KEYWORD:"kwd",PR_LITERAL:"lit",
28
+ PR_NOCODE:"nocode",PR_PLAIN:"pln",PR_PUNCTUATION:"pun",PR_SOURCE:"src",PR_STRING:"str",PR_TAG:"tag",PR_TYPE:"typ"}})();
@@ -0,0 +1,1076 @@
1
+ /*!
2
+ * jQuery Form Plugin
3
+ * version: 3.09 (16-APR-2012)
4
+ * @requires jQuery v1.3.2 or later
5
+ *
6
+ * Examples and documentation at: http://malsup.com/jquery/form/
7
+ * Project repository: https://github.com/malsup/form
8
+ * Dual licensed under the MIT and GPL licenses:
9
+ * http://malsup.github.com/mit-license.txt
10
+ * http://malsup.github.com/gpl-license-v2.txt
11
+ */
12
+ /*global ActiveXObject alert */
13
+ ;(function($) {
14
+ "use strict";
15
+
16
+ /*
17
+ Usage Note:
18
+ -----------
19
+ Do not use both ajaxSubmit and ajaxForm on the same form. These
20
+ functions are mutually exclusive. Use ajaxSubmit if you want
21
+ to bind your own submit handler to the form. For example,
22
+
23
+ $(document).ready(function() {
24
+ $('#myForm').on('submit', function(e) {
25
+ e.preventDefault(); // <-- important
26
+ $(this).ajaxSubmit({
27
+ target: '#output'
28
+ });
29
+ });
30
+ });
31
+
32
+ Use ajaxForm when you want the plugin to manage all the event binding
33
+ for you. For example,
34
+
35
+ $(document).ready(function() {
36
+ $('#myForm').ajaxForm({
37
+ target: '#output'
38
+ });
39
+ });
40
+
41
+ You can also use ajaxForm with delegation (requires jQuery v1.7+), so the
42
+ form does not have to exist when you invoke ajaxForm:
43
+
44
+ $('#myForm').ajaxForm({
45
+ delegation: true,
46
+ target: '#output'
47
+ });
48
+
49
+ When using ajaxForm, the ajaxSubmit function will be invoked for you
50
+ at the appropriate time.
51
+ */
52
+
53
+ /**
54
+ * Feature detection
55
+ */
56
+ var feature = {};
57
+ feature.fileapi = $("<input type='file'/>").get(0).files !== undefined;
58
+ feature.formdata = window.FormData !== undefined;
59
+
60
+ /**
61
+ * ajaxSubmit() provides a mechanism for immediately submitting
62
+ * an HTML form using AJAX.
63
+ */
64
+ $.fn.ajaxSubmit = function(options) {
65
+ /*jshint scripturl:true */
66
+
67
+ // fast fail if nothing selected (http://dev.jquery.com/ticket/2752)
68
+ if (!this.length) {
69
+ log('ajaxSubmit: skipping submit process - no element selected');
70
+ return this;
71
+ }
72
+
73
+ var method, action, url, $form = this;
74
+
75
+ if (typeof options == 'function') {
76
+ options = { success: options };
77
+ }
78
+
79
+ method = this.attr('method');
80
+ action = this.attr('action');
81
+ url = (typeof action === 'string') ? $.trim(action) : '';
82
+ url = url || window.location.href || '';
83
+ if (url) {
84
+ // clean url (don't include hash vaue)
85
+ url = (url.match(/^([^#]+)/)||[])[1];
86
+ }
87
+
88
+ options = $.extend(true, {
89
+ url: url,
90
+ success: $.ajaxSettings.success,
91
+ type: method || 'GET',
92
+ iframeSrc: /^https/i.test(window.location.href || '') ? 'javascript:false' : 'about:blank'
93
+ }, options);
94
+
95
+ // hook for manipulating the form data before it is extracted;
96
+ // convenient for use with rich editors like tinyMCE or FCKEditor
97
+ var veto = {};
98
+ this.trigger('form-pre-serialize', [this, options, veto]);
99
+ if (veto.veto) {
100
+ log('ajaxSubmit: submit vetoed via form-pre-serialize trigger');
101
+ return this;
102
+ }
103
+
104
+ // provide opportunity to alter form data before it is serialized
105
+ if (options.beforeSerialize && options.beforeSerialize(this, options) === false) {
106
+ log('ajaxSubmit: submit aborted via beforeSerialize callback');
107
+ return this;
108
+ }
109
+
110
+ var traditional = options.traditional;
111
+ if ( traditional === undefined ) {
112
+ traditional = $.ajaxSettings.traditional;
113
+ }
114
+
115
+ var elements = [];
116
+ var qx, a = this.formToArray(options.semantic, elements);
117
+ if (options.data) {
118
+ options.extraData = options.data;
119
+ qx = $.param(options.data, traditional);
120
+ }
121
+
122
+ // give pre-submit callback an opportunity to abort the submit
123
+ if (options.beforeSubmit && options.beforeSubmit(a, this, options) === false) {
124
+ log('ajaxSubmit: submit aborted via beforeSubmit callback');
125
+ return this;
126
+ }
127
+
128
+ // fire vetoable 'validate' event
129
+ this.trigger('form-submit-validate', [a, this, options, veto]);
130
+ if (veto.veto) {
131
+ log('ajaxSubmit: submit vetoed via form-submit-validate trigger');
132
+ return this;
133
+ }
134
+
135
+ var q = $.param(a, traditional);
136
+ if (qx) {
137
+ q = ( q ? (q + '&' + qx) : qx );
138
+ }
139
+ if (options.type.toUpperCase() == 'GET') {
140
+ options.url += (options.url.indexOf('?') >= 0 ? '&' : '?') + q;
141
+ options.data = null; // data is null for 'get'
142
+ }
143
+ else {
144
+ options.data = q; // data is the query string for 'post'
145
+ }
146
+
147
+ var callbacks = [];
148
+ if (options.resetForm) {
149
+ callbacks.push(function() { $form.resetForm(); });
150
+ }
151
+ if (options.clearForm) {
152
+ callbacks.push(function() { $form.clearForm(options.includeHidden); });
153
+ }
154
+
155
+ // perform a load on the target only if dataType is not provided
156
+ if (!options.dataType && options.target) {
157
+ var oldSuccess = options.success || function(){};
158
+ callbacks.push(function(data) {
159
+ var fn = options.replaceTarget ? 'replaceWith' : 'html';
160
+ $(options.target)[fn](data).each(oldSuccess, arguments);
161
+ });
162
+ }
163
+ else if (options.success) {
164
+ callbacks.push(options.success);
165
+ }
166
+
167
+ options.success = function(data, status, xhr) { // jQuery 1.4+ passes xhr as 3rd arg
168
+ var context = options.context || options; // jQuery 1.4+ supports scope context
169
+ for (var i=0, max=callbacks.length; i < max; i++) {
170
+ callbacks[i].apply(context, [data, status, xhr || $form, $form]);
171
+ }
172
+ };
173
+
174
+ // are there files to upload?
175
+ var fileInputs = $('input:file:enabled[value]', this); // [value] (issue #113)
176
+ var hasFileInputs = fileInputs.length > 0;
177
+ var mp = 'multipart/form-data';
178
+ var multipart = ($form.attr('enctype') == mp || $form.attr('encoding') == mp);
179
+
180
+ var fileAPI = feature.fileapi && feature.formdata;
181
+ log("fileAPI :" + fileAPI);
182
+ var shouldUseFrame = (hasFileInputs || multipart) && !fileAPI;
183
+
184
+ // options.iframe allows user to force iframe mode
185
+ // 06-NOV-09: now defaulting to iframe mode if file input is detected
186
+ if (options.iframe !== false && (options.iframe || shouldUseFrame)) {
187
+ // hack to fix Safari hang (thanks to Tim Molendijk for this)
188
+ // see: http://groups.google.com/group/jquery-dev/browse_thread/thread/36395b7ab510dd5d
189
+ if (options.closeKeepAlive) {
190
+ $.get(options.closeKeepAlive, function() {
191
+ fileUploadIframe(a);
192
+ });
193
+ }
194
+ else {
195
+ fileUploadIframe(a);
196
+ }
197
+ }
198
+ else if ((hasFileInputs || multipart) && fileAPI) {
199
+ fileUploadXhr(a);
200
+ }
201
+ else {
202
+ $.ajax(options);
203
+ }
204
+
205
+ // clear element array
206
+ for (var k=0; k < elements.length; k++)
207
+ elements[k] = null;
208
+
209
+ // fire 'notify' event
210
+ this.trigger('form-submit-notify', [this, options]);
211
+ return this;
212
+
213
+ // XMLHttpRequest Level 2 file uploads (big hat tip to francois2metz)
214
+ function fileUploadXhr(a) {
215
+ var formdata = new FormData();
216
+
217
+ for (var i=0; i < a.length; i++) {
218
+ formdata.append(a[i].name, a[i].value);
219
+ }
220
+
221
+ if (options.extraData) {
222
+ for (var p in options.extraData)
223
+ if (options.extraData.hasOwnProperty(p))
224
+ formdata.append(p, options.extraData[p]);
225
+ }
226
+
227
+ options.data = null;
228
+
229
+ var s = $.extend(true, {}, $.ajaxSettings, options, {
230
+ contentType: false,
231
+ processData: false,
232
+ cache: false,
233
+ type: 'POST'
234
+ });
235
+
236
+ if (options.uploadProgress) {
237
+ // workaround because jqXHR does not expose upload property
238
+ s.xhr = function() {
239
+ var xhr = jQuery.ajaxSettings.xhr();
240
+ if (xhr.upload) {
241
+ xhr.upload.onprogress = function(event) {
242
+ var percent = 0;
243
+ var position = event.loaded || event.position; /*event.position is deprecated*/
244
+ var total = event.total;
245
+ if (event.lengthComputable) {
246
+ percent = Math.ceil(position / total * 100);
247
+ }
248
+ options.uploadProgress(event, position, total, percent);
249
+ };
250
+ }
251
+ return xhr;
252
+ };
253
+ }
254
+
255
+ s.data = null;
256
+ var beforeSend = s.beforeSend;
257
+ s.beforeSend = function(xhr, o) {
258
+ o.data = formdata;
259
+ if(beforeSend)
260
+ beforeSend.call(o, xhr, options);
261
+ };
262
+ $.ajax(s);
263
+ }
264
+
265
+ // private function for handling file uploads (hat tip to YAHOO!)
266
+ function fileUploadIframe(a) {
267
+ var form = $form[0], el, i, s, g, id, $io, io, xhr, sub, n, timedOut, timeoutHandle;
268
+ var useProp = !!$.fn.prop;
269
+
270
+ if ($(':input[name=submit],:input[id=submit]', form).length) {
271
+ // if there is an input with a name or id of 'submit' then we won't be
272
+ // able to invoke the submit fn on the form (at least not x-browser)
273
+ alert('Error: Form elements must not have name or id of "submit".');
274
+ return;
275
+ }
276
+
277
+ if (a) {
278
+ // ensure that every serialized input is still enabled
279
+ for (i=0; i < elements.length; i++) {
280
+ el = $(elements[i]);
281
+ if ( useProp )
282
+ el.prop('disabled', false);
283
+ else
284
+ el.removeAttr('disabled');
285
+ }
286
+ }
287
+
288
+ s = $.extend(true, {}, $.ajaxSettings, options);
289
+ s.context = s.context || s;
290
+ id = 'jqFormIO' + (new Date().getTime());
291
+ if (s.iframeTarget) {
292
+ $io = $(s.iframeTarget);
293
+ n = $io.attr('name');
294
+ if (!n)
295
+ $io.attr('name', id);
296
+ else
297
+ id = n;
298
+ }
299
+ else {
300
+ $io = $('<iframe name="' + id + '" src="'+ s.iframeSrc +'" />');
301
+ $io.css({ position: 'absolute', top: '-1000px', left: '-1000px' });
302
+ }
303
+ io = $io[0];
304
+
305
+
306
+ xhr = { // mock object
307
+ aborted: 0,
308
+ responseText: null,
309
+ responseXML: null,
310
+ status: 0,
311
+ statusText: 'n/a',
312
+ getAllResponseHeaders: function() {},
313
+ getResponseHeader: function() {},
314
+ setRequestHeader: function() {},
315
+ abort: function(status) {
316
+ var e = (status === 'timeout' ? 'timeout' : 'aborted');
317
+ log('aborting upload... ' + e);
318
+ this.aborted = 1;
319
+ $io.attr('src', s.iframeSrc); // abort op in progress
320
+ xhr.error = e;
321
+ if (s.error)
322
+ s.error.call(s.context, xhr, e, status);
323
+ if (g)
324
+ $.event.trigger("ajaxError", [xhr, s, e]);
325
+ if (s.complete)
326
+ s.complete.call(s.context, xhr, e);
327
+ }
328
+ };
329
+
330
+ g = s.global;
331
+ // trigger ajax global events so that activity/block indicators work like normal
332
+ if (g && 0 === $.active++) {
333
+ $.event.trigger("ajaxStart");
334
+ }
335
+ if (g) {
336
+ $.event.trigger("ajaxSend", [xhr, s]);
337
+ }
338
+
339
+ if (s.beforeSend && s.beforeSend.call(s.context, xhr, s) === false) {
340
+ if (s.global) {
341
+ $.active--;
342
+ }
343
+ return;
344
+ }
345
+ if (xhr.aborted) {
346
+ return;
347
+ }
348
+
349
+ // add submitting element to data if we know it
350
+ sub = form.clk;
351
+ if (sub) {
352
+ n = sub.name;
353
+ if (n && !sub.disabled) {
354
+ s.extraData = s.extraData || {};
355
+ s.extraData[n] = sub.value;
356
+ if (sub.type == "image") {
357
+ s.extraData[n+'.x'] = form.clk_x;
358
+ s.extraData[n+'.y'] = form.clk_y;
359
+ }
360
+ }
361
+ }
362
+
363
+ var CLIENT_TIMEOUT_ABORT = 1;
364
+ var SERVER_ABORT = 2;
365
+
366
+ function getDoc(frame) {
367
+ var doc = frame.contentWindow ? frame.contentWindow.document : frame.contentDocument ? frame.contentDocument : frame.document;
368
+ return doc;
369
+ }
370
+
371
+ // Rails CSRF hack (thanks to Yvan Barthelemy)
372
+ var csrf_token = $('meta[name=csrf-token]').attr('content');
373
+ var csrf_param = $('meta[name=csrf-param]').attr('content');
374
+ if (csrf_param && csrf_token) {
375
+ s.extraData = s.extraData || {};
376
+ s.extraData[csrf_param] = csrf_token;
377
+ }
378
+
379
+ // take a breath so that pending repaints get some cpu time before the upload starts
380
+ function doSubmit() {
381
+ // make sure form attrs are set
382
+ var t = $form.attr('target'), a = $form.attr('action');
383
+
384
+ // update form attrs in IE friendly way
385
+ form.setAttribute('target',id);
386
+ if (!method) {
387
+ form.setAttribute('method', 'POST');
388
+ }
389
+ if (a != s.url) {
390
+ form.setAttribute('action', s.url);
391
+ }
392
+
393
+ // ie borks in some cases when setting encoding
394
+ if (! s.skipEncodingOverride && (!method || /post/i.test(method))) {
395
+ $form.attr({
396
+ encoding: 'multipart/form-data',
397
+ enctype: 'multipart/form-data'
398
+ });
399
+ }
400
+
401
+ // support timout
402
+ if (s.timeout) {
403
+ timeoutHandle = setTimeout(function() { timedOut = true; cb(CLIENT_TIMEOUT_ABORT); }, s.timeout);
404
+ }
405
+
406
+ // look for server aborts
407
+ function checkState() {
408
+ try {
409
+ var state = getDoc(io).readyState;
410
+ log('state = ' + state);
411
+ if (state && state.toLowerCase() == 'uninitialized')
412
+ setTimeout(checkState,50);
413
+ }
414
+ catch(e) {
415
+ log('Server abort: ' , e, ' (', e.name, ')');
416
+ cb(SERVER_ABORT);
417
+ if (timeoutHandle)
418
+ clearTimeout(timeoutHandle);
419
+ timeoutHandle = undefined;
420
+ }
421
+ }
422
+
423
+ // add "extra" data to form if provided in options
424
+ var extraInputs = [];
425
+ try {
426
+ if (s.extraData) {
427
+ for (var n in s.extraData) {
428
+ if (s.extraData.hasOwnProperty(n)) {
429
+ extraInputs.push(
430
+ $('<input type="hidden" name="'+n+'">').attr('value',s.extraData[n])
431
+ .appendTo(form)[0]);
432
+ }
433
+ }
434
+ }
435
+
436
+ if (!s.iframeTarget) {
437
+ // add iframe to doc and submit the form
438
+ $io.appendTo('body');
439
+ if (io.attachEvent)
440
+ io.attachEvent('onload', cb);
441
+ else
442
+ io.addEventListener('load', cb, false);
443
+ }
444
+ setTimeout(checkState,15);
445
+ form.submit();
446
+ }
447
+ finally {
448
+ // reset attrs and remove "extra" input elements
449
+ form.setAttribute('action',a);
450
+ if(t) {
451
+ form.setAttribute('target', t);
452
+ } else {
453
+ $form.removeAttr('target');
454
+ }
455
+ $(extraInputs).remove();
456
+ }
457
+ }
458
+
459
+ if (s.forceSync) {
460
+ doSubmit();
461
+ }
462
+ else {
463
+ setTimeout(doSubmit, 10); // this lets dom updates render
464
+ }
465
+
466
+ var data, doc, domCheckCount = 50, callbackProcessed;
467
+
468
+ function cb(e) {
469
+ if (xhr.aborted || callbackProcessed) {
470
+ return;
471
+ }
472
+ try {
473
+ doc = getDoc(io);
474
+ }
475
+ catch(ex) {
476
+ log('cannot access response document: ', ex);
477
+ e = SERVER_ABORT;
478
+ }
479
+ if (e === CLIENT_TIMEOUT_ABORT && xhr) {
480
+ xhr.abort('timeout');
481
+ return;
482
+ }
483
+ else if (e == SERVER_ABORT && xhr) {
484
+ xhr.abort('server abort');
485
+ return;
486
+ }
487
+
488
+ if (!doc || doc.location.href == s.iframeSrc) {
489
+ // response not received yet
490
+ if (!timedOut)
491
+ return;
492
+ }
493
+ if (io.detachEvent)
494
+ io.detachEvent('onload', cb);
495
+ else
496
+ io.removeEventListener('load', cb, false);
497
+
498
+ var status = 'success', errMsg;
499
+ try {
500
+ if (timedOut) {
501
+ throw 'timeout';
502
+ }
503
+
504
+ var isXml = s.dataType == 'xml' || doc.XMLDocument || $.isXMLDoc(doc);
505
+ log('isXml='+isXml);
506
+ if (!isXml && window.opera && (doc.body === null || !doc.body.innerHTML)) {
507
+ if (--domCheckCount) {
508
+ // in some browsers (Opera) the iframe DOM is not always traversable when
509
+ // the onload callback fires, so we loop a bit to accommodate
510
+ log('requeing onLoad callback, DOM not available');
511
+ setTimeout(cb, 250);
512
+ return;
513
+ }
514
+ // let this fall through because server response could be an empty document
515
+ //log('Could not access iframe DOM after mutiple tries.');
516
+ //throw 'DOMException: not available';
517
+ }
518
+
519
+ //log('response detected');
520
+ var docRoot = doc.body ? doc.body : doc.documentElement;
521
+ xhr.responseText = docRoot ? docRoot.innerHTML : null;
522
+ xhr.responseXML = doc.XMLDocument ? doc.XMLDocument : doc;
523
+ if (isXml)
524
+ s.dataType = 'xml';
525
+ xhr.getResponseHeader = function(header){
526
+ var headers = {'content-type': s.dataType};
527
+ return headers[header];
528
+ };
529
+ // support for XHR 'status' & 'statusText' emulation :
530
+ if (docRoot) {
531
+ xhr.status = Number( docRoot.getAttribute('status') ) || xhr.status;
532
+ xhr.statusText = docRoot.getAttribute('statusText') || xhr.statusText;
533
+ }
534
+
535
+ var dt = (s.dataType || '').toLowerCase();
536
+ var scr = /(json|script|text)/.test(dt);
537
+ if (scr || s.textarea) {
538
+ // see if user embedded response in textarea
539
+ var ta = doc.getElementsByTagName('textarea')[0];
540
+ if (ta) {
541
+ xhr.responseText = ta.value;
542
+ // support for XHR 'status' & 'statusText' emulation :
543
+ xhr.status = Number( ta.getAttribute('status') ) || xhr.status;
544
+ xhr.statusText = ta.getAttribute('statusText') || xhr.statusText;
545
+ }
546
+ else if (scr) {
547
+ // account for browsers injecting pre around json response
548
+ var pre = doc.getElementsByTagName('pre')[0];
549
+ var b = doc.getElementsByTagName('body')[0];
550
+ if (pre) {
551
+ xhr.responseText = pre.textContent ? pre.textContent : pre.innerText;
552
+ }
553
+ else if (b) {
554
+ xhr.responseText = b.textContent ? b.textContent : b.innerText;
555
+ }
556
+ }
557
+ }
558
+ else if (dt == 'xml' && !xhr.responseXML && xhr.responseText) {
559
+ xhr.responseXML = toXml(xhr.responseText);
560
+ }
561
+
562
+ try {
563
+ data = httpData(xhr, dt, s);
564
+ }
565
+ catch (e) {
566
+ status = 'parsererror';
567
+ xhr.error = errMsg = (e || status);
568
+ }
569
+ }
570
+ catch (e) {
571
+ log('error caught: ',e);
572
+ status = 'error';
573
+ xhr.error = errMsg = (e || status);
574
+ }
575
+
576
+ if (xhr.aborted) {
577
+ log('upload aborted');
578
+ status = null;
579
+ }
580
+
581
+ if (xhr.status) { // we've set xhr.status
582
+ status = (xhr.status >= 200 && xhr.status < 300 || xhr.status === 304) ? 'success' : 'error';
583
+ }
584
+
585
+ // ordering of these callbacks/triggers is odd, but that's how $.ajax does it
586
+ if (status === 'success') {
587
+ if (s.success)
588
+ s.success.call(s.context, data, 'success', xhr);
589
+ if (g)
590
+ $.event.trigger("ajaxSuccess", [xhr, s]);
591
+ }
592
+ else if (status) {
593
+ if (errMsg === undefined)
594
+ errMsg = xhr.statusText;
595
+ if (s.error)
596
+ s.error.call(s.context, xhr, status, errMsg);
597
+ if (g)
598
+ $.event.trigger("ajaxError", [xhr, s, errMsg]);
599
+ }
600
+
601
+ if (g)
602
+ $.event.trigger("ajaxComplete", [xhr, s]);
603
+
604
+ if (g && ! --$.active) {
605
+ $.event.trigger("ajaxStop");
606
+ }
607
+
608
+ if (s.complete)
609
+ s.complete.call(s.context, xhr, status);
610
+
611
+ callbackProcessed = true;
612
+ if (s.timeout)
613
+ clearTimeout(timeoutHandle);
614
+
615
+ // clean up
616
+ setTimeout(function() {
617
+ if (!s.iframeTarget)
618
+ $io.remove();
619
+ xhr.responseXML = null;
620
+ }, 100);
621
+ }
622
+
623
+ var toXml = $.parseXML || function(s, doc) { // use parseXML if available (jQuery 1.5+)
624
+ if (window.ActiveXObject) {
625
+ doc = new ActiveXObject('Microsoft.XMLDOM');
626
+ doc.async = 'false';
627
+ doc.loadXML(s);
628
+ }
629
+ else {
630
+ doc = (new DOMParser()).parseFromString(s, 'text/xml');
631
+ }
632
+ return (doc && doc.documentElement && doc.documentElement.nodeName != 'parsererror') ? doc : null;
633
+ };
634
+ var parseJSON = $.parseJSON || function(s) {
635
+ /*jslint evil:true */
636
+ return window['eval']('(' + s + ')');
637
+ };
638
+
639
+ var httpData = function( xhr, type, s ) { // mostly lifted from jq1.4.4
640
+
641
+ var ct = xhr.getResponseHeader('content-type') || '',
642
+ xml = type === 'xml' || !type && ct.indexOf('xml') >= 0,
643
+ data = xml ? xhr.responseXML : xhr.responseText;
644
+
645
+ if (xml && data.documentElement.nodeName === 'parsererror') {
646
+ if ($.error)
647
+ $.error('parsererror');
648
+ }
649
+ if (s && s.dataFilter) {
650
+ data = s.dataFilter(data, type);
651
+ }
652
+ if (typeof data === 'string') {
653
+ if (type === 'json' || !type && ct.indexOf('json') >= 0) {
654
+ data = parseJSON(data);
655
+ } else if (type === "script" || !type && ct.indexOf("javascript") >= 0) {
656
+ $.globalEval(data);
657
+ }
658
+ }
659
+ return data;
660
+ };
661
+ }
662
+ };
663
+
664
+ /**
665
+ * ajaxForm() provides a mechanism for fully automating form submission.
666
+ *
667
+ * The advantages of using this method instead of ajaxSubmit() are:
668
+ *
669
+ * 1: This method will include coordinates for <input type="image" /> elements (if the element
670
+ * is used to submit the form).
671
+ * 2. This method will include the submit element's name/value data (for the element that was
672
+ * used to submit the form).
673
+ * 3. This method binds the submit() method to the form for you.
674
+ *
675
+ * The options argument for ajaxForm works exactly as it does for ajaxSubmit. ajaxForm merely
676
+ * passes the options argument along after properly binding events for submit elements and
677
+ * the form itself.
678
+ */
679
+ $.fn.ajaxForm = function(options) {
680
+ options = options || {};
681
+ options.delegation = options.delegation && $.isFunction($.fn.on);
682
+
683
+ // in jQuery 1.3+ we can fix mistakes with the ready state
684
+ if (!options.delegation && this.length === 0) {
685
+ var o = { s: this.selector, c: this.context };
686
+ if (!$.isReady && o.s) {
687
+ log('DOM not ready, queuing ajaxForm');
688
+ $(function() {
689
+ $(o.s,o.c).ajaxForm(options);
690
+ });
691
+ return this;
692
+ }
693
+ // is your DOM ready? http://docs.jquery.com/Tutorials:Introducing_$(document).ready()
694
+ log('terminating; zero elements found by selector' + ($.isReady ? '' : ' (DOM not ready)'));
695
+ return this;
696
+ }
697
+
698
+ if ( options.delegation ) {
699
+ $(document)
700
+ .off('submit.form-plugin', this.selector, doAjaxSubmit)
701
+ .off('click.form-plugin', this.selector, captureSubmittingElement)
702
+ .on('submit.form-plugin', this.selector, options, doAjaxSubmit)
703
+ .on('click.form-plugin', this.selector, options, captureSubmittingElement);
704
+ return this;
705
+ }
706
+
707
+ return this.ajaxFormUnbind()
708
+ .bind('submit.form-plugin', options, doAjaxSubmit)
709
+ .bind('click.form-plugin', options, captureSubmittingElement);
710
+ };
711
+
712
+ // private event handlers
713
+ function doAjaxSubmit(e) {
714
+ /*jshint validthis:true */
715
+ var options = e.data;
716
+ if (!e.isDefaultPrevented()) { // if event has been canceled, don't proceed
717
+ e.preventDefault();
718
+ $(this).ajaxSubmit(options);
719
+ }
720
+ }
721
+
722
+ function captureSubmittingElement(e) {
723
+ /*jshint validthis:true */
724
+ var target = e.target;
725
+ var $el = $(target);
726
+ if (!($el.is(":submit,input:image"))) {
727
+ // is this a child element of the submit el? (ex: a span within a button)
728
+ var t = $el.closest(':submit');
729
+ if (t.length === 0) {
730
+ return;
731
+ }
732
+ target = t[0];
733
+ }
734
+ var form = this;
735
+ form.clk = target;
736
+ if (target.type == 'image') {
737
+ if (e.offsetX !== undefined) {
738
+ form.clk_x = e.offsetX;
739
+ form.clk_y = e.offsetY;
740
+ } else if (typeof $.fn.offset == 'function') {
741
+ var offset = $el.offset();
742
+ form.clk_x = e.pageX - offset.left;
743
+ form.clk_y = e.pageY - offset.top;
744
+ } else {
745
+ form.clk_x = e.pageX - target.offsetLeft;
746
+ form.clk_y = e.pageY - target.offsetTop;
747
+ }
748
+ }
749
+ // clear form vars
750
+ setTimeout(function() { form.clk = form.clk_x = form.clk_y = null; }, 100);
751
+ }
752
+
753
+
754
+ // ajaxFormUnbind unbinds the event handlers that were bound by ajaxForm
755
+ $.fn.ajaxFormUnbind = function() {
756
+ return this.unbind('submit.form-plugin click.form-plugin');
757
+ };
758
+
759
+ /**
760
+ * formToArray() gathers form element data into an array of objects that can
761
+ * be passed to any of the following ajax functions: $.get, $.post, or load.
762
+ * Each object in the array has both a 'name' and 'value' property. An example of
763
+ * an array for a simple login form might be:
764
+ *
765
+ * [ { name: 'username', value: 'jresig' }, { name: 'password', value: 'secret' } ]
766
+ *
767
+ * It is this array that is passed to pre-submit callback functions provided to the
768
+ * ajaxSubmit() and ajaxForm() methods.
769
+ */
770
+ $.fn.formToArray = function(semantic, elements) {
771
+ var a = [];
772
+ if (this.length === 0) {
773
+ return a;
774
+ }
775
+
776
+ var form = this[0];
777
+ var els = semantic ? form.getElementsByTagName('*') : form.elements;
778
+ if (!els) {
779
+ return a;
780
+ }
781
+
782
+ var i,j,n,v,el,max,jmax;
783
+ for(i=0, max=els.length; i < max; i++) {
784
+ el = els[i];
785
+ n = el.name;
786
+ if (!n) {
787
+ continue;
788
+ }
789
+
790
+ if (semantic && form.clk && el.type == "image") {
791
+ // handle image inputs on the fly when semantic == true
792
+ if(!el.disabled && form.clk == el) {
793
+ a.push({name: n, value: $(el).val(), type: el.type });
794
+ a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
795
+ }
796
+ continue;
797
+ }
798
+
799
+ v = $.fieldValue(el, true);
800
+ if (v && v.constructor == Array) {
801
+ if (elements)
802
+ elements.push(el);
803
+ for(j=0, jmax=v.length; j < jmax; j++) {
804
+ a.push({name: n, value: v[j]});
805
+ }
806
+ }
807
+ else if (feature.fileapi && el.type == 'file' && !el.disabled) {
808
+ if (elements)
809
+ elements.push(el);
810
+ var files = el.files;
811
+ if (files.length) {
812
+ for (j=0; j < files.length; j++) {
813
+ a.push({name: n, value: files[j], type: el.type});
814
+ }
815
+ }
816
+ else {
817
+ // #180
818
+ a.push({ name: n, value: '', type: el.type });
819
+ }
820
+ }
821
+ else if (v !== null && typeof v != 'undefined') {
822
+ if (elements)
823
+ elements.push(el);
824
+ a.push({name: n, value: v, type: el.type, required: el.required});
825
+ }
826
+ }
827
+
828
+ if (!semantic && form.clk) {
829
+ // input type=='image' are not found in elements array! handle it here
830
+ var $input = $(form.clk), input = $input[0];
831
+ n = input.name;
832
+ if (n && !input.disabled && input.type == 'image') {
833
+ a.push({name: n, value: $input.val()});
834
+ a.push({name: n+'.x', value: form.clk_x}, {name: n+'.y', value: form.clk_y});
835
+ }
836
+ }
837
+ return a;
838
+ };
839
+
840
+ /**
841
+ * Serializes form data into a 'submittable' string. This method will return a string
842
+ * in the format: name1=value1&amp;name2=value2
843
+ */
844
+ $.fn.formSerialize = function(semantic) {
845
+ //hand off to jQuery.param for proper encoding
846
+ return $.param(this.formToArray(semantic));
847
+ };
848
+
849
+ /**
850
+ * Serializes all field elements in the jQuery object into a query string.
851
+ * This method will return a string in the format: name1=value1&amp;name2=value2
852
+ */
853
+ $.fn.fieldSerialize = function(successful) {
854
+ var a = [];
855
+ this.each(function() {
856
+ var n = this.name;
857
+ if (!n) {
858
+ return;
859
+ }
860
+ var v = $.fieldValue(this, successful);
861
+ if (v && v.constructor == Array) {
862
+ for (var i=0,max=v.length; i < max; i++) {
863
+ a.push({name: n, value: v[i]});
864
+ }
865
+ }
866
+ else if (v !== null && typeof v != 'undefined') {
867
+ a.push({name: this.name, value: v});
868
+ }
869
+ });
870
+ //hand off to jQuery.param for proper encoding
871
+ return $.param(a);
872
+ };
873
+
874
+ /**
875
+ * Returns the value(s) of the element in the matched set. For example, consider the following form:
876
+ *
877
+ * <form><fieldset>
878
+ * <input name="A" type="text" />
879
+ * <input name="A" type="text" />
880
+ * <input name="B" type="checkbox" value="B1" />
881
+ * <input name="B" type="checkbox" value="B2"/>
882
+ * <input name="C" type="radio" value="C1" />
883
+ * <input name="C" type="radio" value="C2" />
884
+ * </fieldset></form>
885
+ *
886
+ * var v = $(':text').fieldValue();
887
+ * // if no values are entered into the text inputs
888
+ * v == ['','']
889
+ * // if values entered into the text inputs are 'foo' and 'bar'
890
+ * v == ['foo','bar']
891
+ *
892
+ * var v = $(':checkbox').fieldValue();
893
+ * // if neither checkbox is checked
894
+ * v === undefined
895
+ * // if both checkboxes are checked
896
+ * v == ['B1', 'B2']
897
+ *
898
+ * var v = $(':radio').fieldValue();
899
+ * // if neither radio is checked
900
+ * v === undefined
901
+ * // if first radio is checked
902
+ * v == ['C1']
903
+ *
904
+ * The successful argument controls whether or not the field element must be 'successful'
905
+ * (per http://www.w3.org/TR/html4/interact/forms.html#successful-controls).
906
+ * The default value of the successful argument is true. If this value is false the value(s)
907
+ * for each element is returned.
908
+ *
909
+ * Note: This method *always* returns an array. If no valid value can be determined the
910
+ * array will be empty, otherwise it will contain one or more values.
911
+ */
912
+ $.fn.fieldValue = function(successful) {
913
+ for (var val=[], i=0, max=this.length; i < max; i++) {
914
+ var el = this[i];
915
+ var v = $.fieldValue(el, successful);
916
+ if (v === null || typeof v == 'undefined' || (v.constructor == Array && !v.length)) {
917
+ continue;
918
+ }
919
+ if (v.constructor == Array)
920
+ $.merge(val, v);
921
+ else
922
+ val.push(v);
923
+ }
924
+ return val;
925
+ };
926
+
927
+ /**
928
+ * Returns the value of the field element.
929
+ */
930
+ $.fieldValue = function(el, successful) {
931
+ var n = el.name, t = el.type, tag = el.tagName.toLowerCase();
932
+ if (successful === undefined) {
933
+ successful = true;
934
+ }
935
+
936
+ if (successful && (!n || el.disabled || t == 'reset' || t == 'button' ||
937
+ (t == 'checkbox' || t == 'radio') && !el.checked ||
938
+ (t == 'submit' || t == 'image') && el.form && el.form.clk != el ||
939
+ tag == 'select' && el.selectedIndex == -1)) {
940
+ return null;
941
+ }
942
+
943
+ if (tag == 'select') {
944
+ var index = el.selectedIndex;
945
+ if (index < 0) {
946
+ return null;
947
+ }
948
+ var a = [], ops = el.options;
949
+ var one = (t == 'select-one');
950
+ var max = (one ? index+1 : ops.length);
951
+ for(var i=(one ? index : 0); i < max; i++) {
952
+ var op = ops[i];
953
+ if (op.selected) {
954
+ var v = op.value;
955
+ if (!v) { // extra pain for IE...
956
+ v = (op.attributes && op.attributes['value'] && !(op.attributes['value'].specified)) ? op.text : op.value;
957
+ }
958
+ if (one) {
959
+ return v;
960
+ }
961
+ a.push(v);
962
+ }
963
+ }
964
+ return a;
965
+ }
966
+ return $(el).val();
967
+ };
968
+
969
+ /**
970
+ * Clears the form data. Takes the following actions on the form's input fields:
971
+ * - input text fields will have their 'value' property set to the empty string
972
+ * - select elements will have their 'selectedIndex' property set to -1
973
+ * - checkbox and radio inputs will have their 'checked' property set to false
974
+ * - inputs of type submit, button, reset, and hidden will *not* be effected
975
+ * - button elements will *not* be effected
976
+ */
977
+ $.fn.clearForm = function(includeHidden) {
978
+ return this.each(function() {
979
+ $('input,select,textarea', this).clearFields(includeHidden);
980
+ });
981
+ };
982
+
983
+ /**
984
+ * Clears the selected form elements.
985
+ */
986
+ $.fn.clearFields = $.fn.clearInputs = function(includeHidden) {
987
+ var re = /^(?:color|date|datetime|email|month|number|password|range|search|tel|text|time|url|week)$/i; // 'hidden' is not in this list
988
+ return this.each(function() {
989
+ var t = this.type, tag = this.tagName.toLowerCase();
990
+ if (re.test(t) || tag == 'textarea') {
991
+ this.value = '';
992
+ }
993
+ else if (t == 'checkbox' || t == 'radio') {
994
+ this.checked = false;
995
+ }
996
+ else if (tag == 'select') {
997
+ this.selectedIndex = -1;
998
+ }
999
+ else if (includeHidden) {
1000
+ // includeHidden can be the valud true, or it can be a selector string
1001
+ // indicating a special test; for example:
1002
+ // $('#myForm').clearForm('.special:hidden')
1003
+ // the above would clean hidden inputs that have the class of 'special'
1004
+ if ( (includeHidden === true && /hidden/.test(t)) ||
1005
+ (typeof includeHidden == 'string' && $(this).is(includeHidden)) )
1006
+ this.value = '';
1007
+ }
1008
+ });
1009
+ };
1010
+
1011
+ /**
1012
+ * Resets the form data. Causes all form elements to be reset to their original value.
1013
+ */
1014
+ $.fn.resetForm = function() {
1015
+ return this.each(function() {
1016
+ // guard against an input with the name of 'reset'
1017
+ // note that IE reports the reset function as an 'object'
1018
+ if (typeof this.reset == 'function' || (typeof this.reset == 'object' && !this.reset.nodeType)) {
1019
+ this.reset();
1020
+ }
1021
+ });
1022
+ };
1023
+
1024
+ /**
1025
+ * Enables or disables any matching elements.
1026
+ */
1027
+ $.fn.enable = function(b) {
1028
+ if (b === undefined) {
1029
+ b = true;
1030
+ }
1031
+ return this.each(function() {
1032
+ this.disabled = !b;
1033
+ });
1034
+ };
1035
+
1036
+ /**
1037
+ * Checks/unchecks any matching checkboxes or radio buttons and
1038
+ * selects/deselects and matching option elements.
1039
+ */
1040
+ $.fn.selected = function(select) {
1041
+ if (select === undefined) {
1042
+ select = true;
1043
+ }
1044
+ return this.each(function() {
1045
+ var t = this.type;
1046
+ if (t == 'checkbox' || t == 'radio') {
1047
+ this.checked = select;
1048
+ }
1049
+ else if (this.tagName.toLowerCase() == 'option') {
1050
+ var $sel = $(this).parent('select');
1051
+ if (select && $sel[0] && $sel[0].type == 'select-one') {
1052
+ // deselect all other options
1053
+ $sel.find('option').selected(false);
1054
+ }
1055
+ this.selected = select;
1056
+ }
1057
+ });
1058
+ };
1059
+
1060
+ // expose debug var
1061
+ $.fn.ajaxSubmit.debug = false;
1062
+
1063
+ // helper fn for console logging
1064
+ function log() {
1065
+ if (!$.fn.ajaxSubmit.debug)
1066
+ return;
1067
+ var msg = '[jquery.form] ' + Array.prototype.join.call(arguments,'');
1068
+ if (window.console && window.console.log) {
1069
+ window.console.log(msg);
1070
+ }
1071
+ else if (window.opera && window.opera.postError) {
1072
+ window.opera.postError(msg);
1073
+ }
1074
+ }
1075
+
1076
+ })(jQuery);