activeadmin-settings 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ .DS_Store
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ ## activeadmin-settings
2
+
3
+ Easy to use general purpose settings backend for activeadmin.
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require 'rubygems/package_task'
2
+
3
+ spec = Gem::Specification.load(Dir['*.gemspec'].first)
4
+ gem = Gem::PackageTask.new(spec)
5
+ gem.define()
6
+
7
+ #gem push pkg/activeadmin-settings-version.gem
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/activeadmin-settings/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Alex Kravets"]
6
+ gem.email = ["santyor@gmail.com"]
7
+ gem.description = "Easy to use general purpose settings backend for activeadmin"
8
+ gem.summary = ""
9
+ gem.homepage = "https://github.com/alexkravets/activeadmin-settings"
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "activeadmin-settings"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = ActiveadminSettings::VERSION
17
+
18
+ #gem.add_dependency("mime-types")
19
+ #gem.add_dependency("orm_adapter")
20
+ end
@@ -0,0 +1,21 @@
1
+ <div class="value <%= setting.type %>">
2
+ <%= setting.value %>
3
+ </div>
4
+
5
+ <div class="form">
6
+ <%= semantic_form_for setting,
7
+ :method => :put,
8
+ :url => admin_setting_path(setting) do |f| %>
9
+ <%= f.inputs do %>
10
+ <% case setting.type %>
11
+ <% when "file" %>
12
+ <%= f.input :file %>
13
+ <%= f.input :remove_file, :as => :boolean %>
14
+ <% when "text" %>
15
+ <%= f.input :string, :as => :text %>
16
+ <% else %>
17
+ <%= f.input :string, :placeholder => "Default: " + setting.default_value %>
18
+ <% end %>
19
+ <% end %>
20
+ <% end %>
21
+ </div>
@@ -0,0 +1,16 @@
1
+ require "activeadmin-settings/version"
2
+
3
+ module ActiveadminSettings
4
+ require 'activeadmin-settings/engine'
5
+ require 'activeadmin-settings/helper'
6
+
7
+ def self.settings
8
+ config_file = ::Rails.root.join("config/activeadmin_settings.yml")
9
+ @settings = {}
10
+
11
+ if File.exists?(config_file)
12
+ data = YAML::load(ERB.new(IO.read(config_file)).result)
13
+ @settings = data if data
14
+ end
15
+ end
16
+ end
Binary file
@@ -0,0 +1,10 @@
1
+ module ActiveadminSettings
2
+ class Engine < Rails::Engine
3
+ isolate_namespace ActiveadminSettings
4
+ initializer "helper" do |app|
5
+ ActiveSupport.on_load(:action_view) do
6
+ include ActiveadminSettings::Helpers
7
+ end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,19 @@
1
+ module ActiveadminSettings
2
+ module Helpers
3
+ def setting_tag(name)
4
+ Setting.find_or_create_by(name: name).value
5
+ end
6
+
7
+ def link_setting_tag(name, html_options={})
8
+ val = Setting.find_or_create_by(name: name).value
9
+
10
+ if not val.empty? # add regular expression check here
11
+ title, url = val.split(')')
12
+ title.gsub!('(', '').strip!
13
+ url.strip!
14
+
15
+ link_to(title, url, html_options)
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,3 @@
1
+ module ActiveadminSettings
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,48 @@
1
+ module ActiveadminSettings
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ desc << "Description:\n Copies source files to your application's app directory and required gems."
5
+
6
+ source_root File.expand_path('../templates', __FILE__)
7
+
8
+ def copy_files
9
+ # models
10
+ puts "Installing models:"
11
+ copy_file "models/setting.rb", "app/models/activeadmin_settings/setting.rb"
12
+
13
+ # uploaders
14
+ puts "Installing uploaders:"
15
+ copy_file "uploaders/settings_file_uploader.rb", "app/uploaders/settings_file_uploader.rb"
16
+
17
+ # admin
18
+ puts "Installing admin:"
19
+ copy_file "admin/admin_users.rb", "app/admin/admin_users.rb"
20
+ copy_file "admin/settings.rb", "app/admin/settings.rb"
21
+
22
+ # config
23
+ puts "Installing config:"
24
+ copy_file "config/activeadmin_settings.yml", "config/activeadmin_settings.yml"
25
+ end
26
+
27
+ def add_assets
28
+ if File.exist?('app/assets/javascripts/active_admin.js')
29
+ insert_into_file "app/assets/javascripts/active_admin.js",
30
+ "//= require activeadmin_settings\n", :after => "base\n"
31
+ else
32
+ puts "It doesn't look like you've installed activeadmin: active_admin.js is missing.\nPlease install it and try again."
33
+ end
34
+
35
+ if File.exist?('app/assets/stylesheets/active_admin.css.scss')
36
+ insert_into_file "app/assets/stylesheets/active_admin.css.scss",
37
+ "//= require activeadmin_settings\n", :before => "// Active Admin CSS Styles\n"
38
+ else
39
+ puts "It doesn't look like you've installed activeadmin: active_admin.scss is missing.\nPlease install it and try again."
40
+ end
41
+ end
42
+
43
+ def add_gems
44
+ gem "activeadmin-mongoid-reorder"
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,23 @@
1
+ ActiveAdmin.register AdminUser, :as => "Admin" do
2
+ menu :parent => "Settings", :priority => 2
3
+
4
+ index do
5
+ column :email
6
+ default_actions
7
+ end
8
+
9
+ show :title => :email do
10
+ panel("Admin Details") do
11
+ attributes_table_for resource, :id, :email
12
+ end
13
+ end
14
+
15
+ form do |f|
16
+ f.inputs "Credentials" do
17
+ f.input :email
18
+ f.input :password
19
+ f.input :password_confirmation
20
+ end
21
+ f.buttons
22
+ end
23
+ end
@@ -0,0 +1,44 @@
1
+ ActiveAdmin.register ActiveadminSettings::Setting, :as => "Setting" do
2
+ menu :parent => "Settings", :priority => 1, :label => "General"
3
+
4
+ actions :index, :update, :destroy
5
+
6
+ controller do
7
+ def update
8
+ update! do |format|
9
+ format.html { render :text => resource.value }
10
+ end
11
+ end
12
+ end
13
+
14
+ index :as => :reorder_table do
15
+ column :name do |s|
16
+ "<strong>#{s.name}</strong><br/><em>#{s.description}</em>".html_safe
17
+ end
18
+
19
+ column :value do |s|
20
+ render :partial => "value", :locals => {:setting => s}
21
+ end
22
+
23
+ column "" do |s|
24
+ link_to("Edit", "#", :class => "member_link edit_setting_link", "data-setting-id" => s.id) +
25
+ link_to("Update", "#", :class => "member_link update_setting_link", "data-setting-id" => s.id) +
26
+ link_to("Delete", admin_setting_path(s), :class => "member_link", :method => :delete, :confirm => "Are you sure?")
27
+ end
28
+ end
29
+
30
+ form do |f|
31
+ f.inputs do
32
+ f.input :name
33
+ f.input :type
34
+ f.input :description
35
+ f.input :string
36
+ end
37
+
38
+ f.buttons
39
+ end
40
+
41
+ collection_action :reorder, :method => :put do
42
+ render :text => resource_class.reorder_objects(params[:ids])
43
+ end
44
+ end
@@ -0,0 +1,17 @@
1
+ # ================================================================================
2
+ # activeadmin_settings.yml
3
+ # ---configuration file v1.0---
4
+ # ================================================================================
5
+ #
6
+ # This file is used to store a list of activeadmin settings.
7
+ #
8
+ # Here is a setting template:
9
+ # Setting Title:
10
+ # type: string (default) / text / file / link
11
+ # description: setting one sentence description
12
+ # default_value: default value is used when setting value is blank
13
+ #
14
+ # Type details:
15
+ # Link setting value expected to be in a format of: (link title) http://link.url
16
+ #
17
+ # ================================================================================
@@ -0,0 +1,37 @@
1
+ class ActiveadminSettings::Setting
2
+ include Mongoid::Document
3
+ include Mongoid::Timestamps
4
+ include Mongoid::Reorder
5
+
6
+ # Fields
7
+ field :name
8
+ field :string, :default => ""
9
+ mount_uploader :file, SettingsFileUploader
10
+
11
+ # Validators
12
+ validates_presence_of :name
13
+ validates_uniqueness_of :name
14
+ validates_length_of :name, minimum: 1
15
+
16
+ # Indexes
17
+ index :name
18
+
19
+ # Instance
20
+ def type
21
+ ActiveadminSettings.settings[name]["type"] ||= "string"
22
+ end
23
+
24
+ def description
25
+ ActiveadminSettings.settings[name]["description"] ||= ""
26
+ end
27
+
28
+ def default_value
29
+ ActiveadminSettings.settings[name]["default_value"] ||= ""
30
+ end
31
+
32
+ def value
33
+ val = respond_to?(type) ? send(type).to_s : send(:string).to_s
34
+ val = default_value if val.empty?
35
+ val.html_safe
36
+ end
37
+ end
@@ -0,0 +1,5 @@
1
+ class SettingsFileUploader < CarrierWave::Uploader::Base
2
+ def store_dir
3
+ "system/settings/files/#{model.id}"
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ # encoding: UTF-8
2
+
3
+ namespace :activeadmin do
4
+ desc "Create default admin user"
5
+ task :create_admin => :environment do
6
+ AdminUser.create :email => 'admin@example.com', :password => 'password', :password_confirmation => 'password'
7
+ puts "New admin user created:
8
+ email: admin@example.com
9
+ password: password"
10
+ end
11
+ end
@@ -0,0 +1,33 @@
1
+ #= require jquery.form
2
+
3
+ $ ->
4
+ $('#settings .form form').submit ->
5
+ false
6
+
7
+ $('.edit_setting_link').click (e) ->
8
+ e.preventDefault()
9
+ id = $(this).attr "data-setting-id"
10
+ setting = $("#setting_#{id}")
11
+ setting.find(".value").hide()
12
+ setting.find(".form").show()
13
+ setting.find(".update_setting_link").show()
14
+ $(this).hide()
15
+
16
+ $('.update_setting_link').click (e) ->
17
+ e.preventDefault()
18
+ btn = $(this)
19
+ id = btn.attr "data-setting-id"
20
+ setting = $("#setting_#{id}")
21
+ setting.find(".form").hide()
22
+
23
+ # submit form, should use jquery form for file submissions
24
+ form = setting.find(".form form")
25
+ form.submit =>
26
+ form.ajaxSubmit
27
+ success: (value) =>
28
+ setting.find('.value').html(value)
29
+ setting.find(".value").show()
30
+ setting.find(".edit_setting_link").show()
31
+ btn.hide()
32
+ false
33
+ form.submit()
@@ -0,0 +1,1076 @@
1
+ /*!
2
+ * jQuery Form Plugin
3
+ * version: 3.10 (20-JUL-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(this, xhr, o);
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);