glebtv-rails-uploader 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +46 -6
  3. data/app/assets/javascripts/uploader/application.js +0 -0
  4. data/app/assets/stylesheets/uploader/default.css +0 -0
  5. data/app/controllers/uploader/attachments_controller.rb +0 -0
  6. data/app/views/uploader/default/_container.html.erb +0 -0
  7. data/app/views/uploader/default/_download.html.erb +0 -0
  8. data/app/views/uploader/default/_sortable.html.erb +24 -0
  9. data/config/locales/en.yml +1 -0
  10. data/config/routes.rb +0 -0
  11. data/lib/file_size_validator.rb +2 -0
  12. data/lib/uploader.rb +8 -3
  13. data/lib/uploader/asset.rb +5 -1
  14. data/lib/uploader/fileuploads.rb +7 -13
  15. data/lib/uploader/helpers/field_tag.rb +0 -0
  16. data/lib/uploader/hooks/formtastic.rb +0 -0
  17. data/lib/uploader/hooks/simple_form.rb +0 -0
  18. data/lib/uploader/version.rb +1 -1
  19. data/spec/dummy/app/models/article.rb +7 -3
  20. data/spec/dummy/app/models/asset.rb +19 -26
  21. data/spec/dummy/app/models/picture.rb +4 -4
  22. data/spec/dummy/config/application.rb +7 -5
  23. data/spec/dummy/config/environments/development.rb +5 -1
  24. data/spec/dummy/config/environments/production.rb +4 -0
  25. data/spec/dummy/config/environments/test.rb +6 -2
  26. data/spec/dummy/config/mongoid.yml +12 -0
  27. data/spec/dummy/log/test.log +134 -0
  28. data/spec/dummy/public/uploads/picture/data/5260e2af61732e16d5010000/rails.png +0 -0
  29. data/spec/dummy/public/uploads/picture/data/5260e2af61732e16d5010000/thumb_rails.png +0 -0
  30. data/spec/dummy/public/uploads/picture/data/5260e2af61732e16d5060000/rails.png +0 -0
  31. data/spec/dummy/public/uploads/picture/data/5260e2af61732e16d5060000/thumb_rails.png +0 -0
  32. data/spec/dummy/public/uploads/picture/data/5260e2e261732e1735010000/rails.png +0 -0
  33. data/spec/dummy/public/uploads/picture/data/5260e2e261732e1735010000/thumb_rails.png +0 -0
  34. data/spec/dummy/public/uploads/picture/data/5260e2e261732e1735060000/rails.png +0 -0
  35. data/spec/dummy/public/uploads/picture/data/5260e2e261732e1735060000/thumb_rails.png +0 -0
  36. data/spec/factories/assets.rb +0 -1
  37. data/spec/fileuploads_spec.rb +2 -2
  38. data/spec/mongoid.yml +17 -0
  39. data/spec/mongoid_spec.rb +42 -0
  40. data/spec/requests/attachments_controller_spec.rb +1 -1
  41. data/spec/spec_helper.rb +4 -1
  42. data/vendor/assets/javascripts/uploader/jquery.fileupload-fp.js +223 -0
  43. data/vendor/assets/javascripts/uploader/jquery.ui.widget.js +530 -0
  44. metadata +42 -23
@@ -3,7 +3,6 @@
3
3
  FactoryGirl.define do
4
4
  factory :picture, :class => Picture do
5
5
  data File.open('spec/factories/files/rails.png')
6
- data_content_type "image/png"
7
6
  data_file_name "rails.png"
8
7
 
9
8
  association :assetable, :factory => :article
@@ -19,9 +19,9 @@ describe Uploader::Fileuploads do
19
19
  end
20
20
 
21
21
  it "should update asset target_id by guid" do
22
- Article.fileupload_update(1000, @picture.guid, :picture)
22
+ Article.fileupload_update('507f1f77bcf86cd799439011', @picture.guid, :picture)
23
23
  @picture.reload
24
- @picture.assetable_id.should == 1000
24
+ @picture.assetable_id.to_s.should == '507f1f77bcf86cd799439011'
25
25
  @picture.guid.should be_nil
26
26
  end
27
27
 
data/spec/mongoid.yml ADDED
@@ -0,0 +1,17 @@
1
+ # Tell Mongoid which environment this configuration is for.
2
+ test:
3
+ # This starts the session configuration settings. You may have as
4
+ # many sessions as you like, but you must have at least 1 named
5
+ # 'default'.
6
+ sessions:
7
+ # Define the default session.
8
+ default:
9
+ # A session can have any number of hosts. Usually 1 for a single
10
+ # server setup, and at least 3 for a replica set. Hosts must be
11
+ # an array of host:port pairs. This session is single server.
12
+ hosts:
13
+ - localhost:27017
14
+ # Define the default database name.
15
+ database: rails_uploader_test
16
+ options:
17
+ include_root_in_json: false
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ class MongoidArticle
4
+ include Mongoid::Document
5
+ include Uploader::Fileuploads
6
+
7
+ has_one :mongoid_picture, :as => :assetable
8
+
9
+ fileuploads :mongoid_picture
10
+ end
11
+
12
+ class MongoidPicture
13
+ include Mongoid::Document
14
+ include Uploader::Asset
15
+
16
+ field :guid
17
+
18
+ belongs_to :assetable, polymorphic: true
19
+ end
20
+
21
+ describe Uploader::Asset do
22
+ before do
23
+ @guid = 'guid'
24
+ @picture = MongoidPicture.create!(:guid => @guid, :assetable_type => 'MongoidArticle')
25
+ end
26
+
27
+ it 'should find asset by guid' do
28
+ asset = MongoidArticle.fileupload_find("mongoid_picture", @picture.guid)
29
+ asset.should == @picture
30
+ end
31
+
32
+ it "should update asset target_id by guid" do
33
+ MongoidArticle.fileupload_update('507f1f77bcf86cd799439011', @picture.guid, :mongoid_picture)
34
+ @picture.reload
35
+ @picture.assetable_id.to_s.should == '507f1f77bcf86cd799439011'
36
+ @picture.guid.should be_nil
37
+ end
38
+
39
+ after do
40
+ MongoidPicture.destroy_all
41
+ end
42
+ end
@@ -11,7 +11,7 @@ describe Uploader::AttachmentsController do
11
11
  file = Rack::Test::UploadedFile.new('spec/factories/files/rails.png', "image/png")
12
12
  post "/uploader/attachments", {
13
13
  :klass => "Picture",
14
- :assetable_id => "1",
14
+ :assetable_id => "507f1f77bcf86cd799439011",
15
15
  :assetable_type => "Article",
16
16
  :guid => "SOMESTRING",
17
17
  :asset => {:data => file}
data/spec/spec_helper.rb CHANGED
@@ -18,7 +18,6 @@ ActionMailer::Base.default_url_options[:host] = "test.com"
18
18
  Rails.backtrace_cleaner.remove_silencers!
19
19
 
20
20
  # Run any available migration
21
- ActiveRecord::Migrator.migrate File.expand_path("../dummy/db/migrate/", __FILE__)
22
21
 
23
22
  require 'carrierwave'
24
23
  CarrierWave.configure do |config|
@@ -29,6 +28,10 @@ end
29
28
  # Load support files
30
29
  Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
31
30
 
31
+ # Mongoid
32
+ require 'mongoid'
33
+ Mongoid.load!('spec/mongoid.yml')
34
+
32
35
  RSpec.configure do |config|
33
36
  # Remove this line if you don't want RSpec's should and should_not
34
37
  # methods or matchers
@@ -0,0 +1,223 @@
1
+ /*
2
+ * jQuery File Upload File Processing Plugin 1.2.1
3
+ * https://github.com/blueimp/jQuery-File-Upload
4
+ *
5
+ * Copyright 2012, Sebastian Tschan
6
+ * https://blueimp.net
7
+ *
8
+ * Licensed under the MIT license:
9
+ * http://www.opensource.org/licenses/MIT
10
+ */
11
+
12
+ /*jslint nomen: true, unparam: true, regexp: true */
13
+ /*global define, window, document */
14
+
15
+ (function (factory) {
16
+ 'use strict';
17
+ if (typeof define === 'function' && define.amd) {
18
+ // Register as an anonymous AMD module:
19
+ define([
20
+ 'jquery',
21
+ 'load-image',
22
+ 'canvas-to-blob',
23
+ './jquery.fileupload'
24
+ ], factory);
25
+ } else {
26
+ // Browser globals:
27
+ factory(
28
+ window.jQuery,
29
+ window.loadImage
30
+ );
31
+ }
32
+ }(function ($, loadImage) {
33
+ 'use strict';
34
+
35
+ // The File Upload FP version extends the fileupload widget
36
+ // with file processing functionality:
37
+ $.widget('blueimp.fileupload', $.blueimp.fileupload, {
38
+
39
+ options: {
40
+ // The list of file processing actions:
41
+ process: [
42
+ /*
43
+ {
44
+ action: 'load',
45
+ fileTypes: /^image\/(gif|jpeg|png)$/,
46
+ maxFileSize: 20000000 // 20MB
47
+ },
48
+ {
49
+ action: 'resize',
50
+ maxWidth: 1920,
51
+ maxHeight: 1200,
52
+ minWidth: 800,
53
+ minHeight: 600
54
+ },
55
+ {
56
+ action: 'save'
57
+ }
58
+ */
59
+ ],
60
+
61
+ // The add callback is invoked as soon as files are added to the
62
+ // fileupload widget (via file input selection, drag & drop or add
63
+ // API call). See the basic file upload widget for more information:
64
+ add: function (e, data) {
65
+ $(this).fileupload('process', data).done(function () {
66
+ data.submit();
67
+ });
68
+ }
69
+ },
70
+
71
+ processActions: {
72
+ // Loads the image given via data.files and data.index
73
+ // as img element if the browser supports canvas.
74
+ // Accepts the options fileTypes (regular expression)
75
+ // and maxFileSize (integer) to limit the files to load:
76
+ load: function (data, options) {
77
+ var that = this,
78
+ file = data.files[data.index],
79
+ dfd = $.Deferred();
80
+ if (window.HTMLCanvasElement &&
81
+ window.HTMLCanvasElement.prototype.toBlob &&
82
+ ($.type(options.maxFileSize) !== 'number' ||
83
+ file.size < options.maxFileSize) &&
84
+ (!options.fileTypes ||
85
+ options.fileTypes.test(file.type))) {
86
+ loadImage(
87
+ file,
88
+ function (img) {
89
+ if (!img.src) {
90
+ return dfd.rejectWith(that, [data]);
91
+ }
92
+ data.img = img;
93
+ dfd.resolveWith(that, [data]);
94
+ }
95
+ );
96
+ } else {
97
+ dfd.rejectWith(that, [data]);
98
+ }
99
+ return dfd.promise();
100
+ },
101
+ // Resizes the image given as data.img and updates
102
+ // data.canvas with the resized image as canvas element.
103
+ // Accepts the options maxWidth, maxHeight, minWidth and
104
+ // minHeight to scale the given image:
105
+ resize: function (data, options) {
106
+ var img = data.img,
107
+ canvas;
108
+ options = $.extend({canvas: true}, options);
109
+ if (img) {
110
+ canvas = loadImage.scale(img, options);
111
+ if (canvas.width !== img.width ||
112
+ canvas.height !== img.height) {
113
+ data.canvas = canvas;
114
+ }
115
+ }
116
+ return data;
117
+ },
118
+ // Saves the processed image given as data.canvas
119
+ // inplace at data.index of data.files:
120
+ save: function (data, options) {
121
+ // Do nothing if no processing has happened:
122
+ if (!data.canvas) {
123
+ return data;
124
+ }
125
+ var that = this,
126
+ file = data.files[data.index],
127
+ name = file.name,
128
+ dfd = $.Deferred(),
129
+ callback = function (blob) {
130
+ if (!blob.name) {
131
+ if (file.type === blob.type) {
132
+ blob.name = file.name;
133
+ } else if (file.name) {
134
+ blob.name = file.name.replace(
135
+ /\..+$/,
136
+ '.' + blob.type.substr(6)
137
+ );
138
+ }
139
+ }
140
+ // Store the created blob at the position
141
+ // of the original file in the files list:
142
+ data.files[data.index] = blob;
143
+ dfd.resolveWith(that, [data]);
144
+ };
145
+ // Use canvas.mozGetAsFile directly, to retain the filename, as
146
+ // Gecko doesn't support the filename option for FormData.append:
147
+ if (data.canvas.mozGetAsFile) {
148
+ callback(data.canvas.mozGetAsFile(
149
+ (/^image\/(jpeg|png)$/.test(file.type) && name) ||
150
+ ((name && name.replace(/\..+$/, '')) ||
151
+ 'blob') + '.png',
152
+ file.type
153
+ ));
154
+ } else {
155
+ data.canvas.toBlob(callback, file.type);
156
+ }
157
+ return dfd.promise();
158
+ }
159
+ },
160
+
161
+ // Resizes the file at the given index and stores the created blob at
162
+ // the original position of the files list, returns a Promise object:
163
+ _processFile: function (files, index, options) {
164
+ var that = this,
165
+ dfd = $.Deferred().resolveWith(that, [{
166
+ files: files,
167
+ index: index
168
+ }]),
169
+ chain = dfd.promise();
170
+ that._processing += 1;
171
+ $.each(options.process, function (i, settings) {
172
+ chain = chain.pipe(function (data) {
173
+ return that.processActions[settings.action]
174
+ .call(this, data, settings);
175
+ });
176
+ });
177
+ chain.always(function () {
178
+ that._processing -= 1;
179
+ if (that._processing === 0) {
180
+ that.element
181
+ .removeClass('fileupload-processing');
182
+ }
183
+ });
184
+ if (that._processing === 1) {
185
+ that.element.addClass('fileupload-processing');
186
+ }
187
+ return chain;
188
+ },
189
+
190
+ // Processes the files given as files property of the data parameter,
191
+ // returns a Promise object that allows to bind a done handler, which
192
+ // will be invoked after processing all files (inplace) is done:
193
+ process: function (data) {
194
+ var that = this,
195
+ options = $.extend({}, this.options, data);
196
+ if (options.process && options.process.length &&
197
+ this._isXHRUpload(options)) {
198
+ $.each(data.files, function (index, file) {
199
+ that._processingQueue = that._processingQueue.pipe(
200
+ function () {
201
+ var dfd = $.Deferred();
202
+ that._processFile(data.files, index, options)
203
+ .always(function () {
204
+ dfd.resolveWith(that);
205
+ });
206
+ return dfd.promise();
207
+ }
208
+ );
209
+ });
210
+ }
211
+ return this._processingQueue;
212
+ },
213
+
214
+ _create: function () {
215
+ this._super();
216
+ this._processing = 0;
217
+ this._processingQueue = $.Deferred().resolveWith(this)
218
+ .promise();
219
+ }
220
+
221
+ });
222
+
223
+ }));
@@ -0,0 +1,530 @@
1
+ /*
2
+ * jQuery UI Widget 1.10.0+amd
3
+ * https://github.com/blueimp/jQuery-File-Upload
4
+ *
5
+ * Copyright 2013 jQuery Foundation and other contributors
6
+ * Released under the MIT license.
7
+ * http://jquery.org/license
8
+ *
9
+ * http://api.jqueryui.com/jQuery.widget/
10
+ */
11
+
12
+ (function (factory) {
13
+ if (typeof define === "function" && define.amd) {
14
+ // Register as an anonymous AMD module:
15
+ define(["jquery"], factory);
16
+ } else {
17
+ // Browser globals:
18
+ factory(jQuery);
19
+ }
20
+ }(function( $, undefined ) {
21
+
22
+ var uuid = 0,
23
+ slice = Array.prototype.slice,
24
+ _cleanData = $.cleanData;
25
+ $.cleanData = function( elems ) {
26
+ for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
27
+ try {
28
+ $( elem ).triggerHandler( "remove" );
29
+ // http://bugs.jquery.com/ticket/8235
30
+ } catch( e ) {}
31
+ }
32
+ _cleanData( elems );
33
+ };
34
+
35
+ $.widget = function( name, base, prototype ) {
36
+ var fullName, existingConstructor, constructor, basePrototype,
37
+ // proxiedPrototype allows the provided prototype to remain unmodified
38
+ // so that it can be used as a mixin for multiple widgets (#8876)
39
+ proxiedPrototype = {},
40
+ namespace = name.split( "." )[ 0 ];
41
+
42
+ name = name.split( "." )[ 1 ];
43
+ fullName = namespace + "-" + name;
44
+
45
+ if ( !prototype ) {
46
+ prototype = base;
47
+ base = $.Widget;
48
+ }
49
+
50
+ // create selector for plugin
51
+ $.expr[ ":" ][ fullName.toLowerCase() ] = function( elem ) {
52
+ return !!$.data( elem, fullName );
53
+ };
54
+
55
+ $[ namespace ] = $[ namespace ] || {};
56
+ existingConstructor = $[ namespace ][ name ];
57
+ constructor = $[ namespace ][ name ] = function( options, element ) {
58
+ // allow instantiation without "new" keyword
59
+ if ( !this._createWidget ) {
60
+ return new constructor( options, element );
61
+ }
62
+
63
+ // allow instantiation without initializing for simple inheritance
64
+ // must use "new" keyword (the code above always passes args)
65
+ if ( arguments.length ) {
66
+ this._createWidget( options, element );
67
+ }
68
+ };
69
+ // extend with the existing constructor to carry over any static properties
70
+ $.extend( constructor, existingConstructor, {
71
+ version: prototype.version,
72
+ // copy the object used to create the prototype in case we need to
73
+ // redefine the widget later
74
+ _proto: $.extend( {}, prototype ),
75
+ // track widgets that inherit from this widget in case this widget is
76
+ // redefined after a widget inherits from it
77
+ _childConstructors: []
78
+ });
79
+
80
+ basePrototype = new base();
81
+ // we need to make the options hash a property directly on the new instance
82
+ // otherwise we'll modify the options hash on the prototype that we're
83
+ // inheriting from
84
+ basePrototype.options = $.widget.extend( {}, basePrototype.options );
85
+ $.each( prototype, function( prop, value ) {
86
+ if ( !$.isFunction( value ) ) {
87
+ proxiedPrototype[ prop ] = value;
88
+ return;
89
+ }
90
+ proxiedPrototype[ prop ] = (function() {
91
+ var _super = function() {
92
+ return base.prototype[ prop ].apply( this, arguments );
93
+ },
94
+ _superApply = function( args ) {
95
+ return base.prototype[ prop ].apply( this, args );
96
+ };
97
+ return function() {
98
+ var __super = this._super,
99
+ __superApply = this._superApply,
100
+ returnValue;
101
+
102
+ this._super = _super;
103
+ this._superApply = _superApply;
104
+
105
+ returnValue = value.apply( this, arguments );
106
+
107
+ this._super = __super;
108
+ this._superApply = __superApply;
109
+
110
+ return returnValue;
111
+ };
112
+ })();
113
+ });
114
+ constructor.prototype = $.widget.extend( basePrototype, {
115
+ // TODO: remove support for widgetEventPrefix
116
+ // always use the name + a colon as the prefix, e.g., draggable:start
117
+ // don't prefix for widgets that aren't DOM-based
118
+ widgetEventPrefix: existingConstructor ? basePrototype.widgetEventPrefix : name
119
+ }, proxiedPrototype, {
120
+ constructor: constructor,
121
+ namespace: namespace,
122
+ widgetName: name,
123
+ widgetFullName: fullName
124
+ });
125
+
126
+ // If this widget is being redefined then we need to find all widgets that
127
+ // are inheriting from it and redefine all of them so that they inherit from
128
+ // the new version of this widget. We're essentially trying to replace one
129
+ // level in the prototype chain.
130
+ if ( existingConstructor ) {
131
+ $.each( existingConstructor._childConstructors, function( i, child ) {
132
+ var childPrototype = child.prototype;
133
+
134
+ // redefine the child widget using the same prototype that was
135
+ // originally used, but inherit from the new version of the base
136
+ $.widget( childPrototype.namespace + "." + childPrototype.widgetName, constructor, child._proto );
137
+ });
138
+ // remove the list of existing child constructors from the old constructor
139
+ // so the old child constructors can be garbage collected
140
+ delete existingConstructor._childConstructors;
141
+ } else {
142
+ base._childConstructors.push( constructor );
143
+ }
144
+
145
+ $.widget.bridge( name, constructor );
146
+ };
147
+
148
+ $.widget.extend = function( target ) {
149
+ var input = slice.call( arguments, 1 ),
150
+ inputIndex = 0,
151
+ inputLength = input.length,
152
+ key,
153
+ value;
154
+ for ( ; inputIndex < inputLength; inputIndex++ ) {
155
+ for ( key in input[ inputIndex ] ) {
156
+ value = input[ inputIndex ][ key ];
157
+ if ( input[ inputIndex ].hasOwnProperty( key ) && value !== undefined ) {
158
+ // Clone objects
159
+ if ( $.isPlainObject( value ) ) {
160
+ target[ key ] = $.isPlainObject( target[ key ] ) ?
161
+ $.widget.extend( {}, target[ key ], value ) :
162
+ // Don't extend strings, arrays, etc. with objects
163
+ $.widget.extend( {}, value );
164
+ // Copy everything else by reference
165
+ } else {
166
+ target[ key ] = value;
167
+ }
168
+ }
169
+ }
170
+ }
171
+ return target;
172
+ };
173
+
174
+ $.widget.bridge = function( name, object ) {
175
+ var fullName = object.prototype.widgetFullName || name;
176
+ $.fn[ name ] = function( options ) {
177
+ var isMethodCall = typeof options === "string",
178
+ args = slice.call( arguments, 1 ),
179
+ returnValue = this;
180
+
181
+ // allow multiple hashes to be passed on init
182
+ options = !isMethodCall && args.length ?
183
+ $.widget.extend.apply( null, [ options ].concat(args) ) :
184
+ options;
185
+
186
+ if ( isMethodCall ) {
187
+ this.each(function() {
188
+ var methodValue,
189
+ instance = $.data( this, fullName );
190
+ if ( !instance ) {
191
+ return $.error( "cannot call methods on " + name + " prior to initialization; " +
192
+ "attempted to call method '" + options + "'" );
193
+ }
194
+ if ( !$.isFunction( instance[options] ) || options.charAt( 0 ) === "_" ) {
195
+ return $.error( "no such method '" + options + "' for " + name + " widget instance" );
196
+ }
197
+ methodValue = instance[ options ].apply( instance, args );
198
+ if ( methodValue !== instance && methodValue !== undefined ) {
199
+ returnValue = methodValue && methodValue.jquery ?
200
+ returnValue.pushStack( methodValue.get() ) :
201
+ methodValue;
202
+ return false;
203
+ }
204
+ });
205
+ } else {
206
+ this.each(function() {
207
+ var instance = $.data( this, fullName );
208
+ if ( instance ) {
209
+ instance.option( options || {} )._init();
210
+ } else {
211
+ $.data( this, fullName, new object( options, this ) );
212
+ }
213
+ });
214
+ }
215
+
216
+ return returnValue;
217
+ };
218
+ };
219
+
220
+ $.Widget = function( /* options, element */ ) {};
221
+ $.Widget._childConstructors = [];
222
+
223
+ $.Widget.prototype = {
224
+ widgetName: "widget",
225
+ widgetEventPrefix: "",
226
+ defaultElement: "<div>",
227
+ options: {
228
+ disabled: false,
229
+
230
+ // callbacks
231
+ create: null
232
+ },
233
+ _createWidget: function( options, element ) {
234
+ element = $( element || this.defaultElement || this )[ 0 ];
235
+ this.element = $( element );
236
+ this.uuid = uuid++;
237
+ this.eventNamespace = "." + this.widgetName + this.uuid;
238
+ this.options = $.widget.extend( {},
239
+ this.options,
240
+ this._getCreateOptions(),
241
+ options );
242
+
243
+ this.bindings = $();
244
+ this.hoverable = $();
245
+ this.focusable = $();
246
+
247
+ if ( element !== this ) {
248
+ $.data( element, this.widgetFullName, this );
249
+ this._on( true, this.element, {
250
+ remove: function( event ) {
251
+ if ( event.target === element ) {
252
+ this.destroy();
253
+ }
254
+ }
255
+ });
256
+ this.document = $( element.style ?
257
+ // element within the document
258
+ element.ownerDocument :
259
+ // element is window or document
260
+ element.document || element );
261
+ this.window = $( this.document[0].defaultView || this.document[0].parentWindow );
262
+ }
263
+
264
+ this._create();
265
+ this._trigger( "create", null, this._getCreateEventData() );
266
+ this._init();
267
+ },
268
+ _getCreateOptions: $.noop,
269
+ _getCreateEventData: $.noop,
270
+ _create: $.noop,
271
+ _init: $.noop,
272
+
273
+ destroy: function() {
274
+ this._destroy();
275
+ // we can probably remove the unbind calls in 2.0
276
+ // all event bindings should go through this._on()
277
+ this.element
278
+ .unbind( this.eventNamespace )
279
+ // 1.9 BC for #7810
280
+ // TODO remove dual storage
281
+ .removeData( this.widgetName )
282
+ .removeData( this.widgetFullName )
283
+ // support: jquery <1.6.3
284
+ // http://bugs.jquery.com/ticket/9413
285
+ .removeData( $.camelCase( this.widgetFullName ) );
286
+ this.widget()
287
+ .unbind( this.eventNamespace )
288
+ .removeAttr( "aria-disabled" )
289
+ .removeClass(
290
+ this.widgetFullName + "-disabled " +
291
+ "ui-state-disabled" );
292
+
293
+ // clean up events and states
294
+ this.bindings.unbind( this.eventNamespace );
295
+ this.hoverable.removeClass( "ui-state-hover" );
296
+ this.focusable.removeClass( "ui-state-focus" );
297
+ },
298
+ _destroy: $.noop,
299
+
300
+ widget: function() {
301
+ return this.element;
302
+ },
303
+
304
+ option: function( key, value ) {
305
+ var options = key,
306
+ parts,
307
+ curOption,
308
+ i;
309
+
310
+ if ( arguments.length === 0 ) {
311
+ // don't return a reference to the internal hash
312
+ return $.widget.extend( {}, this.options );
313
+ }
314
+
315
+ if ( typeof key === "string" ) {
316
+ // handle nested keys, e.g., "foo.bar" => { foo: { bar: ___ } }
317
+ options = {};
318
+ parts = key.split( "." );
319
+ key = parts.shift();
320
+ if ( parts.length ) {
321
+ curOption = options[ key ] = $.widget.extend( {}, this.options[ key ] );
322
+ for ( i = 0; i < parts.length - 1; i++ ) {
323
+ curOption[ parts[ i ] ] = curOption[ parts[ i ] ] || {};
324
+ curOption = curOption[ parts[ i ] ];
325
+ }
326
+ key = parts.pop();
327
+ if ( value === undefined ) {
328
+ return curOption[ key ] === undefined ? null : curOption[ key ];
329
+ }
330
+ curOption[ key ] = value;
331
+ } else {
332
+ if ( value === undefined ) {
333
+ return this.options[ key ] === undefined ? null : this.options[ key ];
334
+ }
335
+ options[ key ] = value;
336
+ }
337
+ }
338
+
339
+ this._setOptions( options );
340
+
341
+ return this;
342
+ },
343
+ _setOptions: function( options ) {
344
+ var key;
345
+
346
+ for ( key in options ) {
347
+ this._setOption( key, options[ key ] );
348
+ }
349
+
350
+ return this;
351
+ },
352
+ _setOption: function( key, value ) {
353
+ this.options[ key ] = value;
354
+
355
+ if ( key === "disabled" ) {
356
+ this.widget()
357
+ .toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value )
358
+ .attr( "aria-disabled", value );
359
+ this.hoverable.removeClass( "ui-state-hover" );
360
+ this.focusable.removeClass( "ui-state-focus" );
361
+ }
362
+
363
+ return this;
364
+ },
365
+
366
+ enable: function() {
367
+ return this._setOption( "disabled", false );
368
+ },
369
+ disable: function() {
370
+ return this._setOption( "disabled", true );
371
+ },
372
+
373
+ _on: function( suppressDisabledCheck, element, handlers ) {
374
+ var delegateElement,
375
+ instance = this;
376
+
377
+ // no suppressDisabledCheck flag, shuffle arguments
378
+ if ( typeof suppressDisabledCheck !== "boolean" ) {
379
+ handlers = element;
380
+ element = suppressDisabledCheck;
381
+ suppressDisabledCheck = false;
382
+ }
383
+
384
+ // no element argument, shuffle and use this.element
385
+ if ( !handlers ) {
386
+ handlers = element;
387
+ element = this.element;
388
+ delegateElement = this.widget();
389
+ } else {
390
+ // accept selectors, DOM elements
391
+ element = delegateElement = $( element );
392
+ this.bindings = this.bindings.add( element );
393
+ }
394
+
395
+ $.each( handlers, function( event, handler ) {
396
+ function handlerProxy() {
397
+ // allow widgets to customize the disabled handling
398
+ // - disabled as an array instead of boolean
399
+ // - disabled class as method for disabling individual parts
400
+ if ( !suppressDisabledCheck &&
401
+ ( instance.options.disabled === true ||
402
+ $( this ).hasClass( "ui-state-disabled" ) ) ) {
403
+ return;
404
+ }
405
+ return ( typeof handler === "string" ? instance[ handler ] : handler )
406
+ .apply( instance, arguments );
407
+ }
408
+
409
+ // copy the guid so direct unbinding works
410
+ if ( typeof handler !== "string" ) {
411
+ handlerProxy.guid = handler.guid =
412
+ handler.guid || handlerProxy.guid || $.guid++;
413
+ }
414
+
415
+ var match = event.match( /^(\w+)\s*(.*)$/ ),
416
+ eventName = match[1] + instance.eventNamespace,
417
+ selector = match[2];
418
+ if ( selector ) {
419
+ delegateElement.delegate( selector, eventName, handlerProxy );
420
+ } else {
421
+ element.bind( eventName, handlerProxy );
422
+ }
423
+ });
424
+ },
425
+
426
+ _off: function( element, eventName ) {
427
+ eventName = (eventName || "").split( " " ).join( this.eventNamespace + " " ) + this.eventNamespace;
428
+ element.unbind( eventName ).undelegate( eventName );
429
+ },
430
+
431
+ _delay: function( handler, delay ) {
432
+ function handlerProxy() {
433
+ return ( typeof handler === "string" ? instance[ handler ] : handler )
434
+ .apply( instance, arguments );
435
+ }
436
+ var instance = this;
437
+ return setTimeout( handlerProxy, delay || 0 );
438
+ },
439
+
440
+ _hoverable: function( element ) {
441
+ this.hoverable = this.hoverable.add( element );
442
+ this._on( element, {
443
+ mouseenter: function( event ) {
444
+ $( event.currentTarget ).addClass( "ui-state-hover" );
445
+ },
446
+ mouseleave: function( event ) {
447
+ $( event.currentTarget ).removeClass( "ui-state-hover" );
448
+ }
449
+ });
450
+ },
451
+
452
+ _focusable: function( element ) {
453
+ this.focusable = this.focusable.add( element );
454
+ this._on( element, {
455
+ focusin: function( event ) {
456
+ $( event.currentTarget ).addClass( "ui-state-focus" );
457
+ },
458
+ focusout: function( event ) {
459
+ $( event.currentTarget ).removeClass( "ui-state-focus" );
460
+ }
461
+ });
462
+ },
463
+
464
+ _trigger: function( type, event, data ) {
465
+ var prop, orig,
466
+ callback = this.options[ type ];
467
+
468
+ data = data || {};
469
+ event = $.Event( event );
470
+ event.type = ( type === this.widgetEventPrefix ?
471
+ type :
472
+ this.widgetEventPrefix + type ).toLowerCase();
473
+ // the original event may come from any element
474
+ // so we need to reset the target on the new event
475
+ event.target = this.element[ 0 ];
476
+
477
+ // copy original event properties over to the new event
478
+ orig = event.originalEvent;
479
+ if ( orig ) {
480
+ for ( prop in orig ) {
481
+ if ( !( prop in event ) ) {
482
+ event[ prop ] = orig[ prop ];
483
+ }
484
+ }
485
+ }
486
+
487
+ this.element.trigger( event, data );
488
+ return !( $.isFunction( callback ) &&
489
+ callback.apply( this.element[0], [ event ].concat( data ) ) === false ||
490
+ event.isDefaultPrevented() );
491
+ }
492
+ };
493
+
494
+ $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
495
+ $.Widget.prototype[ "_" + method ] = function( element, options, callback ) {
496
+ if ( typeof options === "string" ) {
497
+ options = { effect: options };
498
+ }
499
+ var hasOptions,
500
+ effectName = !options ?
501
+ method :
502
+ options === true || typeof options === "number" ?
503
+ defaultEffect :
504
+ options.effect || defaultEffect;
505
+ options = options || {};
506
+ if ( typeof options === "number" ) {
507
+ options = { duration: options };
508
+ }
509
+ hasOptions = !$.isEmptyObject( options );
510
+ options.complete = callback;
511
+ if ( options.delay ) {
512
+ element.delay( options.delay );
513
+ }
514
+ if ( hasOptions && $.effects && $.effects.effect[ effectName ] ) {
515
+ element[ method ]( options );
516
+ } else if ( effectName !== method && element[ effectName ] ) {
517
+ element[ effectName ]( options.duration, options.easing, callback );
518
+ } else {
519
+ element.queue(function( next ) {
520
+ $( this )[ method ]();
521
+ if ( callback ) {
522
+ callback.call( element[ 0 ] );
523
+ }
524
+ next();
525
+ });
526
+ }
527
+ };
528
+ });
529
+
530
+ }));