s3_cors_fileupload 0.1.5 → 0.2.0.pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. checksums.yaml +7 -0
  2. data/.document +4 -3
  3. data/.gitignore +3 -7
  4. data/CHANGELOG.md +9 -1
  5. data/Gemfile +11 -11
  6. data/Gemfile.lock +66 -74
  7. data/README.md +4 -3
  8. data/Rakefile +7 -9
  9. data/lib/generators/s3_cors_fileupload/install/templates/s3_uploads.js +16 -22
  10. data/lib/generators/s3_cors_fileupload/install/templates/s3_uploads_controller.rb +1 -1
  11. data/lib/generators/s3_cors_fileupload/install/templates/source_file.rb +2 -2
  12. data/lib/generators/s3_cors_fileupload/install/templates/views/erb/_template_download.html.erb +2 -2
  13. data/lib/generators/s3_cors_fileupload/install/templates/views/erb/_template_upload.html.erb +4 -4
  14. data/lib/generators/s3_cors_fileupload/install/templates/views/erb/_template_uploaded.html.erb +1 -1
  15. data/lib/generators/s3_cors_fileupload/install/templates/views/erb/index.html.erb +2 -2
  16. data/lib/generators/s3_cors_fileupload/install/templates/views/haml/_template_download.html.haml +2 -2
  17. data/lib/generators/s3_cors_fileupload/install/templates/views/haml/_template_upload.html.haml +4 -4
  18. data/lib/generators/s3_cors_fileupload/install/templates/views/haml/_template_uploaded.html.haml +2 -2
  19. data/lib/generators/s3_cors_fileupload/install/templates/views/haml/index.html.haml +2 -2
  20. data/lib/s3_cors_fileupload/rails/config.rb +0 -1
  21. data/lib/s3_cors_fileupload/rails/form_helper.rb +2 -4
  22. data/lib/s3_cors_fileupload/rails/policy_helper.rb +2 -2
  23. data/lib/s3_cors_fileupload/version.rb +3 -3
  24. data/s3_cors_fileupload.gemspec +12 -15
  25. data/spec/dummy/Rakefile +7 -0
  26. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  27. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  28. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  29. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  30. data/spec/dummy/app/models/.gitkeep +0 -0
  31. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  32. data/spec/dummy/config.ru +4 -0
  33. data/spec/dummy/config/amazon_s3.yml +17 -0
  34. data/spec/dummy/config/application.rb +64 -0
  35. data/spec/dummy/config/boot.rb +10 -0
  36. data/spec/dummy/config/database.yml +25 -0
  37. data/spec/dummy/config/environment.rb +5 -0
  38. data/spec/dummy/config/environments/development.rb +37 -0
  39. data/spec/dummy/config/environments/production.rb +67 -0
  40. data/spec/dummy/config/environments/test.rb +37 -0
  41. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  42. data/spec/dummy/config/initializers/inflections.rb +15 -0
  43. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  44. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  45. data/spec/dummy/config/initializers/session_store.rb +8 -0
  46. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  47. data/spec/dummy/config/locales/en.yml +5 -0
  48. data/spec/dummy/config/routes.rb +58 -0
  49. data/spec/dummy/db/.gitkeep +0 -0
  50. data/spec/dummy/lib/assets/.gitkeep +0 -0
  51. data/spec/dummy/log/.gitkeep +0 -0
  52. data/spec/dummy/public/404.html +26 -0
  53. data/spec/dummy/public/422.html +26 -0
  54. data/spec/dummy/public/500.html +25 -0
  55. data/spec/dummy/public/favicon.ico +0 -0
  56. data/spec/dummy/script/rails +6 -0
  57. data/spec/lib/generators/install/install_generator_spec.rb +160 -0
  58. data/spec/lib/s3_cors_fileupload/rails/config_spec.rb +43 -0
  59. data/spec/{s3_cors_fileupload → lib/s3_cors_fileupload}/version_spec.rb +0 -0
  60. data/spec/spec_helper.rb +11 -2
  61. data/vendor/assets/javascripts/s3_cors_fileupload/jquery.fileupload-ui.js +117 -43
  62. data/vendor/assets/javascripts/s3_cors_fileupload/jquery.fileupload.js +187 -92
  63. data/vendor/assets/javascripts/s3_cors_fileupload/jquery.iframe-transport.js +20 -7
  64. data/vendor/assets/javascripts/s3_cors_fileupload/vendor/jquery.ui.widget.js +72 -53
  65. data/vendor/assets/javascripts/s3_cors_fileupload/vendor/load-image.js +225 -37
  66. data/vendor/assets/stylesheets/jquery.fileupload-ui.css.erb +6 -8
  67. metadata +78 -21
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+ require 'ffaker'
3
+
4
+ describe S3CorsFileupload::Config do
5
+ subject { S3CorsFileupload::Config }
6
+
7
+ it { should be_instance_of(Module) }
8
+
9
+ describe :method_missing do
10
+ context "arguments received" do
11
+ it "should raise a NoMethodError" do
12
+ expect { subject.foobar(:arg) }.to raise_error(NoMethodError)
13
+ expect { subject.access_key_id(:hai) }.to raise_error(NoMethodError)
14
+ end
15
+ end
16
+
17
+ context "block received" do
18
+ it "should raise a NoMethodError" do
19
+ expect { subject.foobar { :foobar } }.to raise_error(NoMethodError)
20
+ expect { subject.access_key_id { :block } }.to raise_error(NoMethodError)
21
+ end
22
+ end
23
+
24
+ context "no arguments or block received" do
25
+ context "key doesn't exist on `config/amazon_s3.yml`" do
26
+ its(:foobar) { should be_nil }
27
+ 10.times do
28
+ its(Faker::Lorem.word.to_sym) { should be_nil }
29
+ end
30
+ end
31
+
32
+ context "key exists on `config/amazon_s3.yml`" do
33
+ describe "it should return the value associated with the key" do
34
+ its(:access_key_id) { should == 'your_access_key_id' }
35
+ its(:secret_access_key) { should == 'your_secret_access_key' }
36
+ its(:acl) { should == 'public-read' }
37
+ its(:max_file_size) { should == 524288000 }
38
+ its(:bucket) { should == 'your_development_bucket_name' }
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  ENV["RAILS_ENV"] ||= 'test'
2
2
  require File.expand_path('../dummy/config/environment', __FILE__)
3
+ # the gem's library gets included through a require statement on 'dummy/config/application.rb'
3
4
  require 'rspec/rails'
4
5
  require 'rspec/autorun'
5
6
 
@@ -10,7 +11,7 @@ Dir[::Rails.root.join("spec/support/**/*.rb")].each {|f| require f}
10
11
  # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
11
12
  RSpec.configure do |config|
12
13
  config.treat_symbols_as_metadata_keys_with_true_values = true
13
-
14
+
14
15
  # ## Mock Framework
15
16
  #
16
17
  # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
@@ -20,7 +21,7 @@ RSpec.configure do |config|
20
21
  # config.mock_with :rr
21
22
 
22
23
  # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
23
- config.fixture_path = "#{::Rails.root}/spec/fixtures"
24
+ # config.fixture_path = "#{::Rails.root}/spec/fixtures"
24
25
 
25
26
  # If you're not using ActiveRecord, or you'd prefer not to run each of your
26
27
  # examples within a transaction, remove the following line or assign false
@@ -37,4 +38,12 @@ RSpec.configure do |config|
37
38
  # the seed, which is printed after each run.
38
39
  # --seed 1234
39
40
  config.order = 'random'
41
+
42
+ # Prep for running the test suite
43
+ config.before(:all) do
44
+ # Invoke the generator for the gem.
45
+ # ::Rails::Generators.invoke('s3_cors_fileupload:install')
46
+ # Run any migrations for the dummy app prior to running the spec suite.
47
+ # ActiveRecord::Migrator.migrate "#{::Rails.root}/db/migrate"
48
+ end
40
49
  end
@@ -1,5 +1,5 @@
1
1
  /*
2
- * jQuery File Upload User Interface Plugin 6.10
2
+ * jQuery File Upload User Interface Plugin 7.4.1
3
3
  * https://github.com/blueimp/jQuery-File-Upload
4
4
  *
5
5
  * Copyright 2010, Sebastian Tschan
@@ -83,7 +83,8 @@
83
83
  // widget (via file input selection, drag & drop or add API call).
84
84
  // See the basic file upload widget for more information:
85
85
  add: function (e, data) {
86
- var that = $(this).data('fileupload'),
86
+ var that = $(this).data('blueimp-fileupload') ||
87
+ $(this).data('fileupload'),
87
88
  options = that.options,
88
89
  files = data.files;
89
90
  $(this).fileupload('process', data).done(function () {
@@ -94,7 +95,7 @@
94
95
  options.filesContainer[
95
96
  options.prependFiles ? 'prepend' : 'append'
96
97
  ](data.context);
97
- that._renderPreviews(files, data.context);
98
+ that._renderPreviews(data);
98
99
  that._forceReflow(data.context);
99
100
  that._transition(data.context).done(
100
101
  function () {
@@ -109,7 +110,8 @@
109
110
  },
110
111
  // Callback for the start of each file upload request:
111
112
  send: function (e, data) {
112
- var that = $(this).data('fileupload');
113
+ var that = $(this).data('blueimp-fileupload') ||
114
+ $(this).data('fileupload');
113
115
  if (!data.isValidated) {
114
116
  if (!data.maxNumberOfFilesAdjusted) {
115
117
  that._adjustMaxNumberOfFiles(-data.files.length);
@@ -138,13 +140,16 @@
138
140
  },
139
141
  // Callback for successful uploads:
140
142
  done: function (e, data) {
141
- var that = $(this).data('fileupload'),
142
- template;
143
+ var that = $(this).data('blueimp-fileupload') ||
144
+ $(this).data('fileupload'),
145
+ files = that._getFilesFromResponse(data),
146
+ template,
147
+ deferred;
143
148
  if (data.context) {
144
149
  data.context.each(function (index) {
145
- var file = ($.isArray(data.result) &&
146
- data.result[index]) ||
147
- {error: 'Empty file upload result'};
150
+ var file = files[index] ||
151
+ {error: 'Empty file upload result'},
152
+ deferred = that._addFinishedDeferreds();
148
153
  if (file.error) {
149
154
  that._adjustMaxNumberOfFiles(1);
150
155
  }
@@ -158,14 +163,16 @@
158
163
  function () {
159
164
  data.context = $(this);
160
165
  that._trigger('completed', e, data);
166
+ that._trigger('finished', e, data);
167
+ deferred.resolve();
161
168
  }
162
169
  );
163
170
  }
164
171
  );
165
172
  });
166
173
  } else {
167
- if ($.isArray(data.result)) {
168
- $.each(data.result, function (index, file) {
174
+ if (files.length) {
175
+ $.each(files, function (index, file) {
169
176
  if (data.maxNumberOfFilesAdjusted && file.error) {
170
177
  that._adjustMaxNumberOfFiles(1);
171
178
  } else if (!data.maxNumberOfFilesAdjusted &&
@@ -175,21 +182,26 @@
175
182
  });
176
183
  data.maxNumberOfFilesAdjusted = true;
177
184
  }
178
- template = that._renderDownload(data.result)
185
+ template = that._renderDownload(files)
179
186
  .appendTo(that.options.filesContainer);
180
187
  that._forceReflow(template);
188
+ deferred = that._addFinishedDeferreds();
181
189
  that._transition(template).done(
182
190
  function () {
183
191
  data.context = $(this);
184
192
  that._trigger('completed', e, data);
193
+ that._trigger('finished', e, data);
194
+ deferred.resolve();
185
195
  }
186
196
  );
187
197
  }
188
198
  },
189
199
  // Callback for failed (abort or error) uploads:
190
200
  fail: function (e, data) {
191
- var that = $(this).data('fileupload'),
192
- template;
201
+ var that = $(this).data('blueimp-fileupload') ||
202
+ $(this).data('fileupload'),
203
+ template,
204
+ deferred;
193
205
  if (data.maxNumberOfFilesAdjusted) {
194
206
  that._adjustMaxNumberOfFiles(data.files.length);
195
207
  }
@@ -199,6 +211,7 @@
199
211
  var file = data.files[index];
200
212
  file.error = file.error || data.errorThrown ||
201
213
  true;
214
+ deferred = that._addFinishedDeferreds();
202
215
  that._transition($(this)).done(
203
216
  function () {
204
217
  var node = $(this);
@@ -209,15 +222,20 @@
209
222
  function () {
210
223
  data.context = $(this);
211
224
  that._trigger('failed', e, data);
225
+ that._trigger('finished', e, data);
226
+ deferred.resolve();
212
227
  }
213
228
  );
214
229
  }
215
230
  );
216
231
  } else {
232
+ deferred = that._addFinishedDeferreds();
217
233
  that._transition($(this)).done(
218
234
  function () {
219
235
  $(this).remove();
220
236
  that._trigger('failed', e, data);
237
+ that._trigger('finished', e, data);
238
+ deferred.resolve();
221
239
  }
222
240
  );
223
241
  }
@@ -227,20 +245,25 @@
227
245
  .appendTo(that.options.filesContainer)
228
246
  .data('data', data);
229
247
  that._forceReflow(data.context);
248
+ deferred = that._addFinishedDeferreds();
230
249
  that._transition(data.context).done(
231
250
  function () {
232
251
  data.context = $(this);
233
252
  that._trigger('failed', e, data);
253
+ that._trigger('finished', e, data);
254
+ deferred.resolve();
234
255
  }
235
256
  );
236
257
  } else {
237
258
  that._trigger('failed', e, data);
259
+ that._trigger('finished', e, data);
260
+ that._addFinishedDeferreds().resolve();
238
261
  }
239
262
  },
240
263
  // Callback for upload progress events:
241
264
  progress: function (e, data) {
242
265
  if (data.context) {
243
- var progress = parseInt(data.loaded / data.total * 100, 10);
266
+ var progress = Math.floor(data.loaded / data.total * 100);
244
267
  data.context.find('.progress')
245
268
  .attr('aria-valuenow', progress)
246
269
  .find('.bar').css(
@@ -252,13 +275,14 @@
252
275
  // Callback for global upload progress events:
253
276
  progressall: function (e, data) {
254
277
  var $this = $(this),
255
- progress = parseInt(data.loaded / data.total * 100, 10),
278
+ progress = Math.floor(data.loaded / data.total * 100),
256
279
  globalProgressNode = $this.find('.fileupload-progress'),
257
280
  extendedProgressNode = globalProgressNode
258
281
  .find('.progress-extended');
259
282
  if (extendedProgressNode.length) {
260
283
  extendedProgressNode.html(
261
- $this.data('fileupload')._renderExtendedProgress(data)
284
+ ($this.data('blueimp-fileupload') || $this.data('fileupload'))
285
+ ._renderExtendedProgress(data)
262
286
  );
263
287
  }
264
288
  globalProgressNode
@@ -271,7 +295,9 @@
271
295
  },
272
296
  // Callback for uploads start, equivalent to the global ajaxStart event:
273
297
  start: function (e) {
274
- var that = $(this).data('fileupload');
298
+ var that = $(this).data('blueimp-fileupload') ||
299
+ $(this).data('fileupload');
300
+ that._resetFinishedDeferreds();
275
301
  that._transition($(this).find('.fileupload-progress')).done(
276
302
  function () {
277
303
  that._trigger('started', e);
@@ -280,22 +306,29 @@
280
306
  },
281
307
  // Callback for uploads stop, equivalent to the global ajaxStop event:
282
308
  stop: function (e) {
283
- var that = $(this).data('fileupload');
309
+ var that = $(this).data('blueimp-fileupload') ||
310
+ $(this).data('fileupload'),
311
+ deferred = that._addFinishedDeferreds();
312
+ $.when.apply($, that._getFinishedDeferreds())
313
+ .done(function () {
314
+ that._trigger('stopped', e);
315
+ });
284
316
  that._transition($(this).find('.fileupload-progress')).done(
285
317
  function () {
286
318
  $(this).find('.progress')
287
319
  .attr('aria-valuenow', '0')
288
320
  .find('.bar').css('width', '0%');
289
321
  $(this).find('.progress-extended').html(' ');
290
- that._trigger('stopped', e);
322
+ deferred.resolve();
291
323
  }
292
324
  );
293
325
  },
294
326
  // Callback for file deletion:
295
327
  destroy: function (e, data) {
296
- if (data.confirm && !confirm(data.confirm))
328
+ if (data.confirmation && !confirm(data.confirmation))
297
329
  return; // abort the deletion if the user rejects the confirmation dialog
298
- var that = $(this).data('fileupload');
330
+ var that = $(this).data('blueimp-fileupload') ||
331
+ $(this).data('fileupload');
299
332
  if (data.url) {
300
333
  $.ajax(data);
301
334
  that._adjustMaxNumberOfFiles(1);
@@ -309,6 +342,29 @@
309
342
  }
310
343
  },
311
344
 
345
+ _resetFinishedDeferreds: function () {
346
+ this._finishedUploads = [];
347
+ },
348
+
349
+ _addFinishedDeferreds: function (deferred) {
350
+ if (!deferred) {
351
+ deferred = $.Deferred();
352
+ }
353
+ this._finishedUploads.push(deferred);
354
+ return deferred;
355
+ },
356
+
357
+ _getFinishedDeferreds: function () {
358
+ return this._finishedUploads;
359
+ },
360
+
361
+ _getFilesFromResponse: function (data) {
362
+ if (data.result && $.isArray(data.result.files)) {
363
+ return data.result.files;
364
+ }
365
+ return [];
366
+ },
367
+
312
368
  // Link handler, that allows to download files
313
369
  // by drag & drop of the links to the desktop:
314
370
  _enableDragToDesktop: function () {
@@ -363,12 +419,12 @@
363
419
  if (bits >= 1000) {
364
420
  return (bits / 1000).toFixed(2) + ' kbit/s';
365
421
  }
366
- return bits + ' bit/s';
422
+ return bits.toFixed(2) + ' bit/s';
367
423
  },
368
424
 
369
425
  _formatTime: function (seconds) {
370
426
  var date = new Date(seconds * 1000),
371
- days = parseInt(seconds / 86400, 10);
427
+ days = Math.floor(seconds / 86400);
372
428
  days = days ? days + 'd ' : '';
373
429
  return days +
374
430
  ('0' + date.getUTCHours()).slice(-2) + ':' +
@@ -465,6 +521,12 @@
465
521
  // so we have to resolve manually:
466
522
  dfd.resolveWith(node);
467
523
  }
524
+ node.on('remove', function () {
525
+ // If the element is removed before the
526
+ // transition finishes, transition events are
527
+ // not triggered, resolve manually:
528
+ dfd.resolveWith(node);
529
+ });
468
530
  },
469
531
  {
470
532
  maxWidth: options.previewMaxWidth,
@@ -474,18 +536,22 @@
474
536
  )) || dfd.resolveWith(node)) && dfd;
475
537
  },
476
538
 
477
- _renderPreviews: function (files, nodes) {
539
+ _renderPreviews: function (data) {
478
540
  var that = this,
479
541
  options = this.options;
480
- nodes.find('.preview span').each(function (index, element) {
481
- var file = files[index];
542
+ data.context.find('.preview span').each(function (index, element) {
543
+ var file = data.files[index];
482
544
  if (options.previewSourceFileTypes.test(file.type) &&
483
545
  ($.type(options.previewSourceMaxFileSize) !== 'number' ||
484
546
  file.size < options.previewSourceMaxFileSize)) {
485
547
  that._processingQueue = that._processingQueue.pipe(function () {
486
- var dfd = $.Deferred();
548
+ var dfd = $.Deferred(),
549
+ ev = $.Event('previewdone', {
550
+ target: element
551
+ });
487
552
  that._renderPreview(file, $(element)).done(
488
553
  function () {
554
+ that._trigger(ev.type, ev, data);
489
555
  dfd.resolveWith(that);
490
556
  }
491
557
  );
@@ -535,13 +601,11 @@
535
601
  _deleteHandler: function (e) {
536
602
  e.preventDefault();
537
603
  var button = $(e.currentTarget);
538
- this._trigger('destroy', e, {
604
+ this._trigger('destroy', e, $.extend({
539
605
  context: button.closest('.template-download'),
540
- url: button.attr('data-url'),
541
- type: button.attr('data-type') || 'DELETE',
542
- confirm: button.attr('data-confirmation'),
606
+ type: 'DELETE',
543
607
  dataType: this.options.dataType
544
- });
608
+ }, button.data()));
545
609
  },
546
610
 
547
611
  _forceReflow: function (node) {
@@ -551,7 +615,7 @@
551
615
 
552
616
  _transition: function (node) {
553
617
  var dfd = $.Deferred();
554
- if ($.support.transition && node.hasClass('fade')) {
618
+ if ($.support.transition && node.hasClass('fade') && node.is(':visible')) {
555
619
  node.bind(
556
620
  $.support.transition.end,
557
621
  function (e) {
@@ -576,27 +640,28 @@
576
640
  this._on(fileUploadButtonBar.find('.start'), {
577
641
  click: function (e) {
578
642
  e.preventDefault();
579
- filesList.find('.start button').click();
643
+ filesList.find('.start').click();
580
644
  }
581
645
  });
582
646
  this._on(fileUploadButtonBar.find('.cancel'), {
583
647
  click: function (e) {
584
648
  e.preventDefault();
585
- filesList.find('.cancel button').click();
649
+ filesList.find('.cancel').click();
586
650
  }
587
651
  });
588
652
  this._on(fileUploadButtonBar.find('.delete'), {
589
653
  click: function (e) {
590
654
  e.preventDefault();
591
- filesList.find('.delete input:checked')
592
- .siblings('button').click();
655
+ filesList.find('.toggle:checked')
656
+ .closest('.template-download')
657
+ .find('.delete').click();
593
658
  fileUploadButtonBar.find('.toggle')
594
659
  .prop('checked', false);
595
660
  }
596
661
  });
597
662
  this._on(fileUploadButtonBar.find('.toggle'), {
598
663
  change: function (e) {
599
- filesList.find('.delete input').prop(
664
+ filesList.find('.toggle').prop(
600
665
  'checked',
601
666
  $(e.currentTarget).is(':checked')
602
667
  );
@@ -606,7 +671,8 @@
606
671
 
607
672
  _destroyButtonBarEventHandlers: function () {
608
673
  this._off(
609
- this.element.find('.fileupload-buttonbar button'),
674
+ this.element.find('.fileupload-buttonbar')
675
+ .find('.start, .cancel, .delete'),
610
676
  'click'
611
677
  );
612
678
  this._off(
@@ -618,9 +684,9 @@
618
684
  _initEventHandlers: function () {
619
685
  this._super();
620
686
  this._on(this.options.filesContainer, {
621
- 'click .start button': this._startHandler,
622
- 'click .cancel button': this._cancelHandler,
623
- 'click .delete button': this._deleteHandler
687
+ 'click .start': this._startHandler,
688
+ 'click .cancel': this._cancelHandler,
689
+ 'click .delete': this._deleteHandler
624
690
  });
625
691
  this._initButtonBarEventHandlers();
626
692
  },
@@ -695,6 +761,13 @@
695
761
  this._initRegExpOptions();
696
762
  },
697
763
 
764
+ _setOption: function (key, value) {
765
+ this._super(key, value);
766
+ if (key === 'maxNumberOfFiles') {
767
+ this._adjustMaxNumberOfFiles(0);
768
+ }
769
+ },
770
+
698
771
  _create: function () {
699
772
  this._super();
700
773
  this._refreshOptionsList.push(
@@ -708,6 +781,7 @@
708
781
  return this._processingQueue;
709
782
  };
710
783
  }
784
+ this._resetFinishedDeferreds();
711
785
  },
712
786
 
713
787
  enable: function () {