mirror of
https://github.com/codeninjasllc/discourse.git
synced 2024-11-23 15:48:43 -05:00
FIX: 🐛 upload on IE9 wasn't working :'(
- FIX: make sure we set a default name to a pasted image only on Chrome (the only browser that supports it)
- FIX: use ".json" extension to uploads endpoints since IE9 doesn't pass the correct header
- FIX: pass the CSRF token in a query parameter since IE9 doesn't pass it in the headers
- FIX: display error messages comming from the server when there is one over the default error message
- FIX: HACK around IE9 security issue when clicking a file input via JavaScript (use a label and set `visibility:hidden` on the input)
- FIX: hide the "cancel" upload on IE9 since it's not supported
- FIX: return "text/plain" content-type when uploading a file for IE9 in order to prevent it from displaying the save dialog
- FIX: check the maximum file size on the server 💥
- update jQuery File Upload Plugin to v. 5.42.2
- update JQuery IFram Transport Plugin to v. 1.8.5
- update jQuery UI Widget to v. 1.11.1
This commit is contained in:
parent
053d3120f7
commit
cd2c9edb46
17 changed files with 341 additions and 179 deletions
|
@ -164,7 +164,9 @@ Discourse.Utilities = {
|
||||||
var upload = files[0];
|
var upload = files[0];
|
||||||
|
|
||||||
// CHROME ONLY: if the image was pasted, sets its name to a default one
|
// CHROME ONLY: if the image was pasted, sets its name to a default one
|
||||||
if (upload instanceof Blob && !(upload instanceof File) && upload.type === "image/png") { upload.name = "blob.png"; }
|
if (typeof Blob !== "undefined" && typeof File !== "undefined") {
|
||||||
|
if (upload instanceof Blob && !(upload instanceof File) && upload.type === "image/png") { upload.name = "blob.png"; }
|
||||||
|
}
|
||||||
|
|
||||||
var type = Discourse.Utilities.isAnImage(upload.name) ? 'image' : 'attachment';
|
var type = Discourse.Utilities.isAnImage(upload.name) ? 'image' : 'attachment';
|
||||||
|
|
||||||
|
@ -287,7 +289,7 @@ Discourse.Utilities = {
|
||||||
// deal with meaningful errors first
|
// deal with meaningful errors first
|
||||||
if (data.jqXHR) {
|
if (data.jqXHR) {
|
||||||
switch (data.jqXHR.status) {
|
switch (data.jqXHR.status) {
|
||||||
// cancel from the user
|
// cancelled by the user
|
||||||
case 0: return;
|
case 0: return;
|
||||||
|
|
||||||
// entity too large, usually returned from the web server
|
// entity too large, usually returned from the web server
|
||||||
|
|
|
@ -11,17 +11,15 @@ export default Em.Mixin.create({
|
||||||
},
|
},
|
||||||
|
|
||||||
_initializeUploader: function() {
|
_initializeUploader: function() {
|
||||||
// NOTE: we can't cache this as fileupload replaces the input after upload
|
var $upload = this.$(),
|
||||||
// cf. https://github.com/blueimp/jQuery-File-Upload/wiki/Frequently-Asked-Questions#why-is-the-file-input-field-cloned-and-replaced-after-each-selection
|
self = this,
|
||||||
var $upload = this.$('input[type=file]'),
|
csrf = Discourse.Session.currentProp("csrfToken");
|
||||||
self = this;
|
|
||||||
|
|
||||||
$upload.fileupload({
|
$upload.fileupload({
|
||||||
url: this.get('uploadUrl'),
|
url: this.get('uploadUrl') + ".json?authenticity_token=" + encodeURIComponent(csrf),
|
||||||
dataType: "json",
|
dataType: "json",
|
||||||
fileInput: $upload,
|
dropZone: $upload,
|
||||||
dropZone: this.$(),
|
pasteZone: $upload
|
||||||
pasteZone: this.$()
|
|
||||||
});
|
});
|
||||||
|
|
||||||
$upload.on('fileuploadsubmit', function (e, data) {
|
$upload.on('fileuploadsubmit', function (e, data) {
|
||||||
|
@ -39,14 +37,20 @@ export default Em.Mixin.create({
|
||||||
});
|
});
|
||||||
|
|
||||||
$upload.on("fileuploaddone", function(e, data) {
|
$upload.on("fileuploaddone", function(e, data) {
|
||||||
if(data.result.url) {
|
if (data.result) {
|
||||||
self.uploadDone(data);
|
if (data.result.url) {
|
||||||
} else {
|
self.uploadDone(data);
|
||||||
if (data.result.message) {
|
|
||||||
bootbox.alert(data.result.message);
|
|
||||||
} else {
|
} else {
|
||||||
bootbox.alert(I18n.t('post.errors.upload'));
|
if (data.result.message) {
|
||||||
|
bootbox.alert(data.result.message);
|
||||||
|
} else if (data.result.length > 0) {
|
||||||
|
bootbox.alert(data.result.join("\n"));
|
||||||
|
} else {
|
||||||
|
bootbox.alert(I18n.t('post.errors.upload'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
bootbox.alert(I18n.t('post.errors.upload'));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -60,12 +64,9 @@ export default Em.Mixin.create({
|
||||||
}.on('didInsertElement'),
|
}.on('didInsertElement'),
|
||||||
|
|
||||||
_destroyUploader: function() {
|
_destroyUploader: function() {
|
||||||
this.$('input[type=file]').fileupload('destroy');
|
var $upload = this.$();
|
||||||
}.on('willDestroyElement'),
|
try { $upload.fileupload('destroy'); }
|
||||||
|
catch (e) { /* wasn't initialized yet */ }
|
||||||
actions: {
|
$upload.off();
|
||||||
selectFile: function() {
|
}.on('willDestroyElement')
|
||||||
this.$('input[type=file]').click();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
<input type="file" accept="image/*" style="display:none" />
|
<label class="btn" {{bind-attr disabled="uploading"}} title="{{i18n 'user.change_avatar.upload_title'}}">
|
||||||
<button class="btn" {{action "selectFile"}} {{bind-attr disabled="uploading"}} title="{{i18n 'user.change_avatar.upload_title'}}">
|
{{fa-icon "picture-o"}} {{uploadButtonText}}
|
||||||
<i class="fa fa-picture-o"></i> {{uploadButtonText}}
|
<input {{bind-attr disabled="uploading"}} type="file" accept="image/*" style="visibility: hidden; position: absolute;" />
|
||||||
</button>
|
</label>
|
||||||
{{#if uploading}}
|
{{#if uploading}}
|
||||||
<span>{{i18n 'upload_selector.uploading'}} {{view.uploadProgress}}%</span>
|
<span>{{i18n 'upload_selector.uploading'}} {{view.uploadProgress}}%</span>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{{text-field name="name" placeholderKey="admin.emoji.name" value=name}}
|
{{text-field name="name" placeholderKey="admin.emoji.name" value=name}}
|
||||||
<input type="file" accept=".png,.gif" style="display:none" />
|
<label class="btn btn-primary" {{bind-attr disabled="addDisabled"}}>
|
||||||
<button {{bind-attr disabled="addDisabled"}} {{action "selectFile"}} class='btn btn-primary'>
|
|
||||||
{{fa-icon "plus"}}
|
{{fa-icon "plus"}}
|
||||||
{{i18n 'admin.emoji.add'}}
|
{{i18n 'admin.emoji.add'}}
|
||||||
</button>
|
<input {{bind-attr disabled="addDisabled"}} type="file" accept=".png,.gif" style="visibility: hidden; position: absolute;" />
|
||||||
|
</label>
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
<input type="file" accept="image/*" style="display:none" />
|
|
||||||
<div class="uploaded-image-preview" class="input-xxlarge" {{bind-attr style="backgroundStyle"}}>
|
<div class="uploaded-image-preview" class="input-xxlarge" {{bind-attr style="backgroundStyle"}}>
|
||||||
<div class="image-upload-controls">
|
<div class="image-upload-controls">
|
||||||
<button {{action "selectFile"}} class="btn pad-left no-text">{{fa-icon "picture-o"}}</button>
|
<label class="btn pad-left no-text" {{bind-attr disabled="uploading"}}>
|
||||||
|
{{fa-icon "picture-o"}}
|
||||||
|
<input {{bind-attr disabled="uploading"}} type="file" accept="image/*" style="visibility: hidden; position: absolute;" />
|
||||||
|
</label>
|
||||||
{{#if backgroundStyle}}
|
{{#if backgroundStyle}}
|
||||||
<button {{action "trash"}} class="btn btn-danger pad-left no-text">{{fa-icon "trash-o"}}</button>
|
<button {{action "trash"}} class="btn btn-danger pad-left no-text">{{fa-icon "trash-o"}}</button>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|
|
@ -307,11 +307,14 @@ var ComposerView = Discourse.View.extend(Ember.Evented, {
|
||||||
// in case it's still bound somehow
|
// in case it's still bound somehow
|
||||||
this._unbindUploadTarget();
|
this._unbindUploadTarget();
|
||||||
|
|
||||||
var $uploadTarget = $('#reply-control');
|
var $uploadTarget = $('#reply-control'),
|
||||||
|
csrf = Discourse.Session.currentProp('csrfToken'),
|
||||||
|
cancelledByTheUser;
|
||||||
|
|
||||||
|
// NOTE: we need both the .json extension and the CSRF token as a query parameter for IE9
|
||||||
$uploadTarget.fileupload({
|
$uploadTarget.fileupload({
|
||||||
url: Discourse.getURL('/uploads'),
|
url: Discourse.getURL('/uploads.json?authenticity_token=' + encodeURIComponent(csrf)),
|
||||||
dataType: 'json',
|
dataType: 'json'
|
||||||
});
|
});
|
||||||
|
|
||||||
// submit - this event is triggered for each upload
|
// submit - this event is triggered for each upload
|
||||||
|
@ -324,22 +327,27 @@ var ComposerView = Discourse.View.extend(Ember.Evented, {
|
||||||
|
|
||||||
// send - this event is triggered when the upload request is about to start
|
// send - this event is triggered when the upload request is about to start
|
||||||
$uploadTarget.on('fileuploadsend', function (e, data) {
|
$uploadTarget.on('fileuploadsend', function (e, data) {
|
||||||
|
cancelledByTheUser = false;
|
||||||
// hide the "file selector" modal
|
// hide the "file selector" modal
|
||||||
self.get('controller').send('closeModal');
|
self.get('controller').send('closeModal');
|
||||||
// cf. https://github.com/blueimp/jQuery-File-Upload/wiki/API#how-to-cancel-an-upload
|
// NOTE: IE9 doesn't support XHR
|
||||||
var jqXHR = data.xhr();
|
if (data["xhr"]) {
|
||||||
// need to wait for the link to show up in the DOM
|
var jqHXR = data.xhr();
|
||||||
Em.run.schedule('afterRender', function() {
|
if (jqHXR) {
|
||||||
// bind on the click event on the cancel link
|
// need to wait for the link to show up in the DOM
|
||||||
$('#cancel-file-upload').on('click', function() {
|
Em.run.schedule('afterRender', function() {
|
||||||
// cancel the upload
|
// bind on the click event on the cancel link
|
||||||
self.set('isUploading', false);
|
$('#cancel-file-upload').on('click', function() {
|
||||||
// NOTE: this might trigger a 'fileuploadfail' event with status = 0
|
// cancel the upload
|
||||||
if (jqXHR) jqXHR.abort();
|
self.set('isUploading', false);
|
||||||
// unbind
|
// NOTE: this might trigger a 'fileuploadfail' event with status = 0
|
||||||
$(this).off('click');
|
if (jqHXR) { cancelledByTheUser = true; jqHXR.abort(); }
|
||||||
});
|
// unbind
|
||||||
});
|
$(this).off('click');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// progress all
|
// progress all
|
||||||
|
@ -350,14 +358,17 @@ var ComposerView = Discourse.View.extend(Ember.Evented, {
|
||||||
|
|
||||||
// done
|
// done
|
||||||
$uploadTarget.on('fileuploaddone', function (e, data) {
|
$uploadTarget.on('fileuploaddone', function (e, data) {
|
||||||
// make sure we have a url
|
if (!cancelledByTheUser) {
|
||||||
if (data.result.url) {
|
// make sure we have a url
|
||||||
var markdown = Discourse.Utilities.getUploadMarkdown(data.result);
|
if (data.result.url) {
|
||||||
// appends a space at the end of the inserted markdown
|
var markdown = Discourse.Utilities.getUploadMarkdown(data.result);
|
||||||
self.addMarkdown(markdown + " ");
|
// appends a space at the end of the inserted markdown
|
||||||
self.set('isUploading', false);
|
self.addMarkdown(markdown + " ");
|
||||||
} else {
|
self.set('isUploading', false);
|
||||||
bootbox.alert(I18n.t('post.errors.upload'));
|
} else {
|
||||||
|
// display the error message sent by the server
|
||||||
|
bootbox.alert(data.result.join("\n"));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -365,8 +376,10 @@ var ComposerView = Discourse.View.extend(Ember.Evented, {
|
||||||
$uploadTarget.on('fileuploadfail', function (e, data) {
|
$uploadTarget.on('fileuploadfail', function (e, data) {
|
||||||
// hide upload status
|
// hide upload status
|
||||||
self.set('isUploading', false);
|
self.set('isUploading', false);
|
||||||
// display an error message
|
if (!cancelledByTheUser) {
|
||||||
Discourse.Utilities.displayErrorForUpload(data);
|
// display an error message
|
||||||
|
Discourse.Utilities.displayErrorForUpload(data);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// contenteditable div hack for getting image paste to upload working in
|
// contenteditable div hack for getting image paste to upload working in
|
||||||
|
|
|
@ -81,6 +81,9 @@
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// hide cancel upload link on IE9 (not supported)
|
||||||
|
.ie9 #cancel-file-upload { display: none; }
|
||||||
|
|
||||||
#reply-control {
|
#reply-control {
|
||||||
.toggle-preview, #draft-status, #file-uploading {
|
.toggle-preview, #draft-status, #file-uploading {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|
|
@ -36,4 +36,7 @@
|
||||||
|
|
||||||
.image-upload-controls {
|
.image-upload-controls {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
|
label.btn {
|
||||||
|
padding: 7px 10px 5px 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,17 +4,17 @@ class UploadsController < ApplicationController
|
||||||
|
|
||||||
def create
|
def create
|
||||||
file = params[:file] || params[:files].first
|
file = params[:file] || params[:files].first
|
||||||
|
|
||||||
filesize = File.size(file.tempfile)
|
filesize = File.size(file.tempfile)
|
||||||
upload = Upload.create_for(current_user.id, file.tempfile, file.original_filename, filesize, { content_type: file.content_type })
|
upload = Upload.create_for(current_user.id, file.tempfile, file.original_filename, filesize, { content_type: file.content_type })
|
||||||
|
|
||||||
if current_user.admin?
|
if upload.errors.empty? && current_user.admin?
|
||||||
retain_hours = params[:retain_hours].to_i
|
retain_hours = params[:retain_hours].to_i
|
||||||
if retain_hours > 0
|
upload.update_columns(retain_hours: retain_hours) if retain_hours > 0
|
||||||
upload.update_columns(retain_hours: retain_hours)
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# HACK FOR IE9 to prevent the "download dialog"
|
||||||
|
response.headers["Content-Type"] = "text/plain" if request.user_agent =~ /MSIE 9/
|
||||||
|
|
||||||
if upload.errors.empty?
|
if upload.errors.empty?
|
||||||
render_serialized(upload, UploadSerializer, root: false)
|
render_serialized(upload, UploadSerializer, root: false)
|
||||||
else
|
else
|
||||||
|
|
|
@ -441,6 +441,9 @@ class UsersController < ApplicationController
|
||||||
|
|
||||||
file = params[:file] || params[:files].first
|
file = params[:file] || params[:files].first
|
||||||
|
|
||||||
|
# HACK FOR IE9 to prevent the "download dialog"
|
||||||
|
response.headers["Content-Type"] = "text/plain" if request.user_agent =~ /MSIE 9/
|
||||||
|
|
||||||
begin
|
begin
|
||||||
image = build_user_image_from(file)
|
image = build_user_image_from(file)
|
||||||
rescue Discourse::InvalidParameters
|
rescue Discourse::InvalidParameters
|
||||||
|
|
|
@ -72,39 +72,23 @@ class Upload < ActiveRecord::Base
|
||||||
# trim the origin if any
|
# trim the origin if any
|
||||||
upload.origin = options[:origin][0...1000] if options[:origin]
|
upload.origin = options[:origin][0...1000] if options[:origin]
|
||||||
|
|
||||||
# deal with width & height for images
|
# check the size of the upload
|
||||||
if FileHelper.is_image?(filename)
|
if FileHelper.is_image?(filename)
|
||||||
begin
|
if SiteSetting.max_image_size_kb > 0 && filesize >= SiteSetting.max_image_size_kb.kilobytes
|
||||||
if filename =~ /\.svg$/i
|
upload.errors.add(:base, I18n.t("upload.images.too_large", max_size_kb: SiteSetting.max_image_size_kb))
|
||||||
svg = Nokogiri::XML(file).at_css("svg")
|
else
|
||||||
width, height = svg["width"].to_i, svg["height"].to_i
|
# deal with width & height for images
|
||||||
if width == 0 || height == 0
|
upload = Upload.resize_image(filename, file, upload)
|
||||||
upload.errors.add(:base, I18n.t("upload.images.size_not_found"))
|
end
|
||||||
else
|
else
|
||||||
upload.width, upload.height = ImageSizer.resize(width, height)
|
if SiteSetting.max_attachment_size_kb > 0 && filesize >= SiteSetting.max_attachment_size_kb.kilobytes
|
||||||
end
|
upload.errors.add(:base, I18n.t("upload.attachments.too_large", max_size_kb: SiteSetting.max_attachment_size_kb))
|
||||||
else
|
|
||||||
# fix orientation first
|
|
||||||
Upload.fix_image_orientation(file.path)
|
|
||||||
# retrieve image info
|
|
||||||
image_info = FastImage.new(file, raise_on_failure: true)
|
|
||||||
# compute image aspect ratio
|
|
||||||
upload.width, upload.height = ImageSizer.resize(*image_info.size)
|
|
||||||
end
|
|
||||||
# make sure we're at the beginning of the file
|
|
||||||
# (FastImage and Nokogiri move the pointer)
|
|
||||||
file.rewind
|
|
||||||
rescue FastImage::ImageFetchFailure
|
|
||||||
upload.errors.add(:base, I18n.t("upload.images.fetch_failure"))
|
|
||||||
rescue FastImage::UnknownImageType
|
|
||||||
upload.errors.add(:base, I18n.t("upload.images.unknown_image_type"))
|
|
||||||
rescue FastImage::SizeNotFound
|
|
||||||
upload.errors.add(:base, I18n.t("upload.images.size_not_found"))
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return upload unless upload.errors.empty?
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# make sure there is no error
|
||||||
|
return upload unless upload.errors.empty?
|
||||||
|
|
||||||
# create a db record (so we can use the id)
|
# create a db record (so we can use the id)
|
||||||
return upload unless upload.save
|
return upload unless upload.save
|
||||||
|
|
||||||
|
@ -122,6 +106,38 @@ class Upload < ActiveRecord::Base
|
||||||
upload
|
upload
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def self.resize_image(filename, file, upload)
|
||||||
|
begin
|
||||||
|
if filename =~ /\.svg$/i
|
||||||
|
svg = Nokogiri::XML(file).at_css("svg")
|
||||||
|
width, height = svg["width"].to_i, svg["height"].to_i
|
||||||
|
if width == 0 || height == 0
|
||||||
|
upload.errors.add(:base, I18n.t("upload.images.size_not_found"))
|
||||||
|
else
|
||||||
|
upload.width, upload.height = ImageSizer.resize(width, height)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
# fix orientation first
|
||||||
|
Upload.fix_image_orientation(file.path)
|
||||||
|
# retrieve image info
|
||||||
|
image_info = FastImage.new(file, raise_on_failure: true)
|
||||||
|
# compute image aspect ratio
|
||||||
|
upload.width, upload.height = ImageSizer.resize(*image_info.size)
|
||||||
|
end
|
||||||
|
# make sure we're at the beginning of the file
|
||||||
|
# (FastImage and Nokogiri move the pointer)
|
||||||
|
file.rewind
|
||||||
|
rescue FastImage::ImageFetchFailure
|
||||||
|
upload.errors.add(:base, I18n.t("upload.images.fetch_failure"))
|
||||||
|
rescue FastImage::UnknownImageType
|
||||||
|
upload.errors.add(:base, I18n.t("upload.images.unknown_image_type"))
|
||||||
|
rescue FastImage::SizeNotFound
|
||||||
|
upload.errors.add(:base, I18n.t("upload.images.size_not_found"))
|
||||||
|
end
|
||||||
|
|
||||||
|
upload
|
||||||
|
end
|
||||||
|
|
||||||
def self.get_from_url(url)
|
def self.get_from_url(url)
|
||||||
return if url.blank?
|
return if url.blank?
|
||||||
# we store relative urls, so we need to remove any host/cdn
|
# we store relative urls, so we need to remove any host/cdn
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="<%= SiteSetting.default_locale %>" class="<%= html_classes %>">
|
<!--[if IE 9]><html lang="<%= SiteSetting.default_locale %>" class="ie9 <%= html_classes %>"><![endif]-->
|
||||||
|
<!--[if (!IE 9) | (!IE)]><!--><html lang="<%= SiteSetting.default_locale %>" class="<%= html_classes %>"><!--<![endif]-->
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8">
|
<meta charset="utf-8">
|
||||||
<title><%= content_for?(:title) ? yield(:title) + ' - ' + SiteSetting.title : SiteSetting.title %></title>
|
<title><%= content_for?(:title) ? yield(:title) + ' - ' + SiteSetting.title : SiteSetting.title %></title>
|
||||||
|
|
|
@ -1828,9 +1828,9 @@ en:
|
||||||
pasted_image_filename: "Pasted image"
|
pasted_image_filename: "Pasted image"
|
||||||
store_failure: "Failed to store upload #%{upload_id} for user #%{user_id}."
|
store_failure: "Failed to store upload #%{upload_id} for user #%{user_id}."
|
||||||
attachments:
|
attachments:
|
||||||
too_large: "Sorry, the file you are trying to upload is too big (maximum size is %{max_size_kb}%kb)."
|
too_large: "Sorry, the file you are trying to upload is too big (maximum size is %{max_size_kb}KB)."
|
||||||
images:
|
images:
|
||||||
too_large: "Sorry, the image you are trying to upload is too big (maximum size is %{max_size_kb}%kb), please resize it and try again."
|
too_large: "Sorry, the image you are trying to upload is too big (maximum size is %{max_size_kb}KB), please resize it and try again."
|
||||||
fetch_failure: "Sorry, there has been an error while fetching the image."
|
fetch_failure: "Sorry, there has been an error while fetching the image."
|
||||||
unknown_image_type: "Sorry, but the file you tried to upload doesn't appear to be an image."
|
unknown_image_type: "Sorry, but the file you tried to upload doesn't appear to be an image."
|
||||||
size_not_found: "Sorry, but we couldn't determine the size of the image. Maybe your image is corrupted?"
|
size_not_found: "Sorry, but we couldn't determine the size of the image. Maybe your image is corrupted?"
|
||||||
|
|
|
@ -84,6 +84,18 @@ describe Upload do
|
||||||
expect(upload.errors.size).to be > 0
|
expect(upload.errors.size).to be > 0
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it "generates an error when the image is too large" do
|
||||||
|
SiteSetting.stubs(:max_image_size_kb).returns(1)
|
||||||
|
upload = Upload.create_for(user_id, image, image_filename, image_filesize)
|
||||||
|
expect(upload.errors.size).to be > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
it "generates an error when the attachment is too large" do
|
||||||
|
SiteSetting.stubs(:max_attachment_size_kb).returns(1)
|
||||||
|
upload = Upload.create_for(user_id, attachment, attachment_filename, attachment_filesize)
|
||||||
|
expect(upload.errors.size).to be > 0
|
||||||
|
end
|
||||||
|
|
||||||
it "saves proper information" do
|
it "saves proper information" do
|
||||||
store = {}
|
store = {}
|
||||||
Discourse.expects(:store).returns(store)
|
Discourse.expects(:store).returns(store)
|
||||||
|
|
76
vendor/assets/javascripts/jquery.fileupload.js
vendored
76
vendor/assets/javascripts/jquery.fileupload.js
vendored
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* jQuery File Upload Plugin 5.40.3
|
* jQuery File Upload Plugin 5.42.2
|
||||||
* https://github.com/blueimp/jQuery-File-Upload
|
* https://github.com/blueimp/jQuery-File-Upload
|
||||||
*
|
*
|
||||||
* Copyright 2010, Sebastian Tschan
|
* Copyright 2010, Sebastian Tschan
|
||||||
|
@ -10,7 +10,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* jshint nomen:false */
|
/* jshint nomen:false */
|
||||||
/* global define, window, document, location, Blob, FormData */
|
/* global define, require, window, document, location, Blob, FormData */
|
||||||
|
|
||||||
(function (factory) {
|
(function (factory) {
|
||||||
'use strict';
|
'use strict';
|
||||||
|
@ -20,6 +20,12 @@
|
||||||
'jquery',
|
'jquery',
|
||||||
'jquery.ui.widget'
|
'jquery.ui.widget'
|
||||||
], factory);
|
], factory);
|
||||||
|
} else if (typeof exports === 'object') {
|
||||||
|
// Node/CommonJS:
|
||||||
|
factory(
|
||||||
|
require('jquery'),
|
||||||
|
require('./vendor/jquery.ui.widget')
|
||||||
|
);
|
||||||
} else {
|
} else {
|
||||||
// Browser globals:
|
// Browser globals:
|
||||||
factory(window.jQuery);
|
factory(window.jQuery);
|
||||||
|
@ -51,6 +57,25 @@
|
||||||
$.support.blobSlice = window.Blob && (Blob.prototype.slice ||
|
$.support.blobSlice = window.Blob && (Blob.prototype.slice ||
|
||||||
Blob.prototype.webkitSlice || Blob.prototype.mozSlice);
|
Blob.prototype.webkitSlice || Blob.prototype.mozSlice);
|
||||||
|
|
||||||
|
// Helper function to create drag handlers for dragover/dragenter/dragleave:
|
||||||
|
function getDragHandler(type) {
|
||||||
|
var isDragOver = type === 'dragover';
|
||||||
|
return function (e) {
|
||||||
|
e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
|
||||||
|
var dataTransfer = e.dataTransfer;
|
||||||
|
if (dataTransfer && $.inArray('Files', dataTransfer.types) !== -1 &&
|
||||||
|
this._trigger(
|
||||||
|
type,
|
||||||
|
$.Event(type, {delegatedEvent: e})
|
||||||
|
) !== false) {
|
||||||
|
e.preventDefault();
|
||||||
|
if (isDragOver) {
|
||||||
|
dataTransfer.dropEffect = 'copy';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// The fileupload widget listens for change events on file input fields defined
|
// The fileupload widget listens for change events on file input fields defined
|
||||||
// via fileInput setting and paste or drop events of the given dropZone.
|
// via fileInput setting and paste or drop events of the given dropZone.
|
||||||
// In addition to the default jQuery Widget methods, the fileupload widget
|
// In addition to the default jQuery Widget methods, the fileupload widget
|
||||||
|
@ -65,9 +90,9 @@
|
||||||
// The drop target element(s), by the default the complete document.
|
// The drop target element(s), by the default the complete document.
|
||||||
// Set to null to disable drag & drop support:
|
// Set to null to disable drag & drop support:
|
||||||
dropZone: $(document),
|
dropZone: $(document),
|
||||||
// The paste target element(s), by the default the complete document.
|
// The paste target element(s), by the default undefined.
|
||||||
// Set to null to disable paste support:
|
// Set to a DOM node or jQuery object to enable file pasting:
|
||||||
pasteZone: $(document),
|
pasteZone: undefined,
|
||||||
// The file input field(s), that are listened to for change events.
|
// The file input field(s), that are listened to for change events.
|
||||||
// If undefined, it is set to the file input fields inside
|
// If undefined, it is set to the file input fields inside
|
||||||
// of the widget element on plugin initialization.
|
// of the widget element on plugin initialization.
|
||||||
|
@ -1015,8 +1040,11 @@
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
|
|
||||||
_replaceFileInput: function (input) {
|
_replaceFileInput: function (data) {
|
||||||
var inputClone = input.clone(true);
|
var input = data.fileInput,
|
||||||
|
inputClone = input.clone(true);
|
||||||
|
// Add a reference for the new cloned file input to the data argument:
|
||||||
|
data.fileInputClone = inputClone;
|
||||||
$('<form></form>').append(inputClone)[0].reset();
|
$('<form></form>').append(inputClone)[0].reset();
|
||||||
// Detaching allows to insert the fileInput on another form
|
// Detaching allows to insert the fileInput on another form
|
||||||
// without loosing the file input value:
|
// without loosing the file input value:
|
||||||
|
@ -1187,7 +1215,7 @@
|
||||||
this._getFileInputFiles(data.fileInput).always(function (files) {
|
this._getFileInputFiles(data.fileInput).always(function (files) {
|
||||||
data.files = files;
|
data.files = files;
|
||||||
if (that.options.replaceFileInput) {
|
if (that.options.replaceFileInput) {
|
||||||
that._replaceFileInput(data.fileInput);
|
that._replaceFileInput(data);
|
||||||
}
|
}
|
||||||
if (that._trigger(
|
if (that._trigger(
|
||||||
'change',
|
'change',
|
||||||
|
@ -1240,24 +1268,21 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
_onDragOver: function (e) {
|
_onDragOver: getDragHandler('dragover'),
|
||||||
e.dataTransfer = e.originalEvent && e.originalEvent.dataTransfer;
|
|
||||||
var dataTransfer = e.dataTransfer;
|
_onDragEnter: getDragHandler('dragenter'),
|
||||||
if (dataTransfer && $.inArray('Files', dataTransfer.types) !== -1 &&
|
|
||||||
this._trigger(
|
_onDragLeave: getDragHandler('dragleave'),
|
||||||
'dragover',
|
|
||||||
$.Event('dragover', {delegatedEvent: e})
|
|
||||||
) !== false) {
|
|
||||||
e.preventDefault();
|
|
||||||
dataTransfer.dropEffect = 'copy';
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
_initEventHandlers: function () {
|
_initEventHandlers: function () {
|
||||||
if (this._isXHRUpload(this.options)) {
|
if (this._isXHRUpload(this.options)) {
|
||||||
this._on(this.options.dropZone, {
|
this._on(this.options.dropZone, {
|
||||||
dragover: this._onDragOver,
|
dragover: this._onDragOver,
|
||||||
drop: this._onDrop
|
drop: this._onDrop,
|
||||||
|
// event.preventDefault() on dragenter is required for IE10+:
|
||||||
|
dragenter: this._onDragEnter,
|
||||||
|
// dragleave is not required, but added for completeness:
|
||||||
|
dragleave: this._onDragLeave
|
||||||
});
|
});
|
||||||
this._on(this.options.pasteZone, {
|
this._on(this.options.pasteZone, {
|
||||||
paste: this._onPaste
|
paste: this._onPaste
|
||||||
|
@ -1271,7 +1296,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
_destroyEventHandlers: function () {
|
_destroyEventHandlers: function () {
|
||||||
this._off(this.options.dropZone, 'dragover drop');
|
this._off(this.options.dropZone, 'dragenter dragleave dragover drop');
|
||||||
this._off(this.options.pasteZone, 'paste');
|
this._off(this.options.pasteZone, 'paste');
|
||||||
this._off(this.options.fileInput, 'change');
|
this._off(this.options.fileInput, 'change');
|
||||||
},
|
},
|
||||||
|
@ -1319,10 +1344,13 @@
|
||||||
_initDataAttributes: function () {
|
_initDataAttributes: function () {
|
||||||
var that = this,
|
var that = this,
|
||||||
options = this.options,
|
options = this.options,
|
||||||
clone = $(this.element[0].cloneNode(false));
|
clone = $(this.element[0].cloneNode(false)),
|
||||||
|
data = clone.data();
|
||||||
|
// Avoid memory leaks:
|
||||||
|
clone.remove();
|
||||||
// Initialize options set via HTML5 data-attributes:
|
// Initialize options set via HTML5 data-attributes:
|
||||||
$.each(
|
$.each(
|
||||||
clone.data(),
|
data,
|
||||||
function (key, value) {
|
function (key, value) {
|
||||||
var dataAttributeName = 'data-' +
|
var dataAttributeName = 'data-' +
|
||||||
// Convert camelCase to hyphen-ated key:
|
// Convert camelCase to hyphen-ated key:
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
* jQuery Iframe Transport Plugin 1.5
|
* jQuery Iframe Transport Plugin 1.8.3
|
||||||
* https://github.com/blueimp/jQuery-File-Upload
|
* https://github.com/blueimp/jQuery-File-Upload
|
||||||
*
|
*
|
||||||
* Copyright 2011, Sebastian Tschan
|
* Copyright 2011, Sebastian Tschan
|
||||||
|
@ -9,14 +9,16 @@
|
||||||
* http://www.opensource.org/licenses/MIT
|
* http://www.opensource.org/licenses/MIT
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*jslint unparam: true, nomen: true */
|
/* global define, require, window, document */
|
||||||
/*global define, window, document */
|
|
||||||
|
|
||||||
(function (factory) {
|
(function (factory) {
|
||||||
'use strict';
|
'use strict';
|
||||||
if (typeof define === 'function' && define.amd) {
|
if (typeof define === 'function' && define.amd) {
|
||||||
// Register as an anonymous AMD module:
|
// Register as an anonymous AMD module:
|
||||||
define(['jquery'], factory);
|
define(['jquery'], factory);
|
||||||
|
} else if (typeof exports === 'object') {
|
||||||
|
// Node/CommonJS:
|
||||||
|
factory(require('jquery'));
|
||||||
} else {
|
} else {
|
||||||
// Browser globals:
|
// Browser globals:
|
||||||
factory(window.jQuery);
|
factory(window.jQuery);
|
||||||
|
@ -27,7 +29,7 @@
|
||||||
// Helper variable to create unique names for the transport iframes:
|
// Helper variable to create unique names for the transport iframes:
|
||||||
var counter = 0;
|
var counter = 0;
|
||||||
|
|
||||||
// The iframe transport accepts three additional options:
|
// The iframe transport accepts four additional options:
|
||||||
// options.fileInput: a jQuery collection of file input fields
|
// options.fileInput: a jQuery collection of file input fields
|
||||||
// options.paramName: the parameter name for the file form data,
|
// options.paramName: the parameter name for the file form data,
|
||||||
// overrides the name property of the file input field(s),
|
// overrides the name property of the file input field(s),
|
||||||
|
@ -35,22 +37,41 @@
|
||||||
// options.formData: an array of objects with name and value properties,
|
// options.formData: an array of objects with name and value properties,
|
||||||
// equivalent to the return data of .serializeArray(), e.g.:
|
// equivalent to the return data of .serializeArray(), e.g.:
|
||||||
// [{name: 'a', value: 1}, {name: 'b', value: 2}]
|
// [{name: 'a', value: 1}, {name: 'b', value: 2}]
|
||||||
|
// options.initialIframeSrc: the URL of the initial iframe src,
|
||||||
|
// by default set to "javascript:false;"
|
||||||
$.ajaxTransport('iframe', function (options) {
|
$.ajaxTransport('iframe', function (options) {
|
||||||
if (options.async && (options.type === 'POST' || options.type === 'GET')) {
|
if (options.async) {
|
||||||
var form,
|
// javascript:false as initial iframe src
|
||||||
iframe;
|
// prevents warning popups on HTTPS in IE6:
|
||||||
|
/*jshint scripturl: true */
|
||||||
|
var initialIframeSrc = options.initialIframeSrc || 'javascript:false;',
|
||||||
|
/*jshint scripturl: false */
|
||||||
|
form,
|
||||||
|
iframe,
|
||||||
|
addParamChar;
|
||||||
return {
|
return {
|
||||||
send: function (_, completeCallback) {
|
send: function (_, completeCallback) {
|
||||||
form = $('<form style="display:none;"></form>');
|
form = $('<form style="display:none;"></form>');
|
||||||
form.attr('accept-charset', options.formAcceptCharset);
|
form.attr('accept-charset', options.formAcceptCharset);
|
||||||
// javascript:false as initial iframe src
|
addParamChar = /\?/.test(options.url) ? '&' : '?';
|
||||||
// prevents warning popups on HTTPS in IE6.
|
// XDomainRequest only supports GET and POST:
|
||||||
|
if (options.type === 'DELETE') {
|
||||||
|
options.url = options.url + addParamChar + '_method=DELETE';
|
||||||
|
options.type = 'POST';
|
||||||
|
} else if (options.type === 'PUT') {
|
||||||
|
options.url = options.url + addParamChar + '_method=PUT';
|
||||||
|
options.type = 'POST';
|
||||||
|
} else if (options.type === 'PATCH') {
|
||||||
|
options.url = options.url + addParamChar + '_method=PATCH';
|
||||||
|
options.type = 'POST';
|
||||||
|
}
|
||||||
// IE versions below IE8 cannot set the name property of
|
// IE versions below IE8 cannot set the name property of
|
||||||
// elements that have already been added to the DOM,
|
// elements that have already been added to the DOM,
|
||||||
// so we set the name along with the iframe HTML markup:
|
// so we set the name along with the iframe HTML markup:
|
||||||
|
counter += 1;
|
||||||
iframe = $(
|
iframe = $(
|
||||||
'<iframe src="javascript:false;" name="iframe-transport-' +
|
'<iframe src="' + initialIframeSrc +
|
||||||
(counter += 1) + '"></iframe>'
|
'" name="iframe-transport-' + counter + '"></iframe>'
|
||||||
).bind('load', function () {
|
).bind('load', function () {
|
||||||
var fileInputClones,
|
var fileInputClones,
|
||||||
paramNames = $.isArray(options.paramName) ?
|
paramNames = $.isArray(options.paramName) ?
|
||||||
|
@ -81,9 +102,14 @@
|
||||||
);
|
);
|
||||||
// Fix for IE endless progress bar activity bug
|
// Fix for IE endless progress bar activity bug
|
||||||
// (happens on form submits to iframe targets):
|
// (happens on form submits to iframe targets):
|
||||||
$('<iframe src="javascript:false;"></iframe>')
|
$('<iframe src="' + initialIframeSrc + '"></iframe>')
|
||||||
.appendTo(form);
|
.appendTo(form);
|
||||||
form.remove();
|
window.setTimeout(function () {
|
||||||
|
// Removing the form in a setTimeout call
|
||||||
|
// allows Chrome's developer tools to display
|
||||||
|
// the response result
|
||||||
|
form.remove();
|
||||||
|
}, 0);
|
||||||
});
|
});
|
||||||
form
|
form
|
||||||
.prop('target', iframe.prop('name'))
|
.prop('target', iframe.prop('name'))
|
||||||
|
@ -119,6 +145,8 @@
|
||||||
.prop('enctype', 'multipart/form-data')
|
.prop('enctype', 'multipart/form-data')
|
||||||
// enctype must be set as encoding for IE:
|
// enctype must be set as encoding for IE:
|
||||||
.prop('encoding', 'multipart/form-data');
|
.prop('encoding', 'multipart/form-data');
|
||||||
|
// Remove the HTML5 form attribute from the input(s):
|
||||||
|
options.fileInput.removeAttr('form');
|
||||||
}
|
}
|
||||||
form.submit();
|
form.submit();
|
||||||
// Insert the file input fields at their original location
|
// Insert the file input fields at their original location
|
||||||
|
@ -126,7 +154,10 @@
|
||||||
if (fileInputClones && fileInputClones.length) {
|
if (fileInputClones && fileInputClones.length) {
|
||||||
options.fileInput.each(function (index, input) {
|
options.fileInput.each(function (index, input) {
|
||||||
var clone = $(fileInputClones[index]);
|
var clone = $(fileInputClones[index]);
|
||||||
$(input).prop('name', clone.prop('name'));
|
// Restore the original name and form properties:
|
||||||
|
$(input)
|
||||||
|
.prop('name', clone.prop('name'))
|
||||||
|
.attr('form', clone.attr('form'));
|
||||||
clone.replaceWith(input);
|
clone.replaceWith(input);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -140,7 +171,7 @@
|
||||||
// concat is used to avoid the "Script URL" JSLint error:
|
// concat is used to avoid the "Script URL" JSLint error:
|
||||||
iframe
|
iframe
|
||||||
.unbind('load')
|
.unbind('load')
|
||||||
.prop('src', 'javascript'.concat(':false;'));
|
.prop('src', initialIframeSrc);
|
||||||
}
|
}
|
||||||
if (form) {
|
if (form) {
|
||||||
form.remove();
|
form.remove();
|
||||||
|
@ -151,20 +182,34 @@
|
||||||
});
|
});
|
||||||
|
|
||||||
// The iframe transport returns the iframe content document as response.
|
// The iframe transport returns the iframe content document as response.
|
||||||
// The following adds converters from iframe to text, json, html, and script:
|
// The following adds converters from iframe to text, json, html, xml
|
||||||
|
// and script.
|
||||||
|
// Please note that the Content-Type for JSON responses has to be text/plain
|
||||||
|
// or text/html, if the browser doesn't include application/json in the
|
||||||
|
// Accept header, else IE will show a download dialog.
|
||||||
|
// The Content-Type for XML responses on the other hand has to be always
|
||||||
|
// application/xml or text/xml, so IE properly parses the XML response.
|
||||||
|
// See also
|
||||||
|
// https://github.com/blueimp/jQuery-File-Upload/wiki/Setup#content-type-negotiation
|
||||||
$.ajaxSetup({
|
$.ajaxSetup({
|
||||||
converters: {
|
converters: {
|
||||||
'iframe text': function (iframe) {
|
'iframe text': function (iframe) {
|
||||||
return $(iframe[0].body).text();
|
return iframe && $(iframe[0].body).text();
|
||||||
},
|
},
|
||||||
'iframe json': function (iframe) {
|
'iframe json': function (iframe) {
|
||||||
return $.parseJSON($(iframe[0].body).text());
|
return iframe && $.parseJSON($(iframe[0].body).text());
|
||||||
},
|
},
|
||||||
'iframe html': function (iframe) {
|
'iframe html': function (iframe) {
|
||||||
return $(iframe[0].body).html();
|
return iframe && $(iframe[0].body).html();
|
||||||
|
},
|
||||||
|
'iframe xml': function (iframe) {
|
||||||
|
var xmlDoc = iframe && iframe[0];
|
||||||
|
return xmlDoc && $.isXMLDoc(xmlDoc) ? xmlDoc :
|
||||||
|
$.parseXML((xmlDoc.XMLDocument && xmlDoc.XMLDocument.xml) ||
|
||||||
|
$(xmlDoc.body).html());
|
||||||
},
|
},
|
||||||
'iframe script': function (iframe) {
|
'iframe script': function (iframe) {
|
||||||
return $.globalEval($(iframe[0].body).text());
|
return iframe && $.globalEval($(iframe[0].body).text());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
109
vendor/assets/javascripts/jquery.ui.widget.js
vendored
109
vendor/assets/javascripts/jquery.ui.widget.js
vendored
|
@ -1,6 +1,27 @@
|
||||||
|
/*! jQuery UI - v1.11.1+CommonJS - 2014-09-17
|
||||||
|
* http://jqueryui.com
|
||||||
|
* Includes: widget.js
|
||||||
|
* Copyright 2014 jQuery Foundation and other contributors; Licensed MIT */
|
||||||
|
|
||||||
|
(function( factory ) {
|
||||||
|
if ( typeof define === "function" && define.amd ) {
|
||||||
|
|
||||||
|
// AMD. Register as an anonymous module.
|
||||||
|
define([ "jquery" ], factory );
|
||||||
|
|
||||||
|
} else if (typeof exports === "object") {
|
||||||
|
// Node/CommonJS:
|
||||||
|
factory(require("jquery"));
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Browser globals
|
||||||
|
factory( jQuery );
|
||||||
|
}
|
||||||
|
}(function( $ ) {
|
||||||
/*!
|
/*!
|
||||||
* jQuery UI Widget 1.10.4+amd
|
* jQuery UI Widget 1.11.1
|
||||||
* https://github.com/blueimp/jQuery-File-Upload
|
* http://jqueryui.com
|
||||||
*
|
*
|
||||||
* Copyright 2014 jQuery Foundation and other contributors
|
* Copyright 2014 jQuery Foundation and other contributors
|
||||||
* Released under the MIT license.
|
* Released under the MIT license.
|
||||||
|
@ -9,28 +30,28 @@
|
||||||
* http://api.jqueryui.com/jQuery.widget/
|
* http://api.jqueryui.com/jQuery.widget/
|
||||||
*/
|
*/
|
||||||
|
|
||||||
(function (factory) {
|
|
||||||
if (typeof define === "function" && define.amd) {
|
|
||||||
// Register as an anonymous AMD module:
|
|
||||||
define(["jquery"], factory);
|
|
||||||
} else {
|
|
||||||
// Browser globals:
|
|
||||||
factory(jQuery);
|
|
||||||
}
|
|
||||||
}(function( $, undefined ) {
|
|
||||||
|
|
||||||
var uuid = 0,
|
var widget_uuid = 0,
|
||||||
slice = Array.prototype.slice,
|
widget_slice = Array.prototype.slice;
|
||||||
_cleanData = $.cleanData;
|
|
||||||
$.cleanData = function( elems ) {
|
$.cleanData = (function( orig ) {
|
||||||
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
|
return function( elems ) {
|
||||||
try {
|
var events, elem, i;
|
||||||
$( elem ).triggerHandler( "remove" );
|
for ( i = 0; (elem = elems[i]) != null; i++ ) {
|
||||||
// http://bugs.jquery.com/ticket/8235
|
try {
|
||||||
} catch( e ) {}
|
|
||||||
}
|
// Only trigger remove when necessary to save time
|
||||||
_cleanData( elems );
|
events = $._data( elem, "events" );
|
||||||
};
|
if ( events && events.remove ) {
|
||||||
|
$( elem ).triggerHandler( "remove" );
|
||||||
|
}
|
||||||
|
|
||||||
|
// http://bugs.jquery.com/ticket/8235
|
||||||
|
} catch( e ) {}
|
||||||
|
}
|
||||||
|
orig( elems );
|
||||||
|
};
|
||||||
|
})( $.cleanData );
|
||||||
|
|
||||||
$.widget = function( name, base, prototype ) {
|
$.widget = function( name, base, prototype ) {
|
||||||
var fullName, existingConstructor, constructor, basePrototype,
|
var fullName, existingConstructor, constructor, basePrototype,
|
||||||
|
@ -143,10 +164,12 @@ $.widget = function( name, base, prototype ) {
|
||||||
}
|
}
|
||||||
|
|
||||||
$.widget.bridge( name, constructor );
|
$.widget.bridge( name, constructor );
|
||||||
|
|
||||||
|
return constructor;
|
||||||
};
|
};
|
||||||
|
|
||||||
$.widget.extend = function( target ) {
|
$.widget.extend = function( target ) {
|
||||||
var input = slice.call( arguments, 1 ),
|
var input = widget_slice.call( arguments, 1 ),
|
||||||
inputIndex = 0,
|
inputIndex = 0,
|
||||||
inputLength = input.length,
|
inputLength = input.length,
|
||||||
key,
|
key,
|
||||||
|
@ -175,7 +198,7 @@ $.widget.bridge = function( name, object ) {
|
||||||
var fullName = object.prototype.widgetFullName || name;
|
var fullName = object.prototype.widgetFullName || name;
|
||||||
$.fn[ name ] = function( options ) {
|
$.fn[ name ] = function( options ) {
|
||||||
var isMethodCall = typeof options === "string",
|
var isMethodCall = typeof options === "string",
|
||||||
args = slice.call( arguments, 1 ),
|
args = widget_slice.call( arguments, 1 ),
|
||||||
returnValue = this;
|
returnValue = this;
|
||||||
|
|
||||||
// allow multiple hashes to be passed on init
|
// allow multiple hashes to be passed on init
|
||||||
|
@ -187,6 +210,10 @@ $.widget.bridge = function( name, object ) {
|
||||||
this.each(function() {
|
this.each(function() {
|
||||||
var methodValue,
|
var methodValue,
|
||||||
instance = $.data( this, fullName );
|
instance = $.data( this, fullName );
|
||||||
|
if ( options === "instance" ) {
|
||||||
|
returnValue = instance;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
if ( !instance ) {
|
if ( !instance ) {
|
||||||
return $.error( "cannot call methods on " + name + " prior to initialization; " +
|
return $.error( "cannot call methods on " + name + " prior to initialization; " +
|
||||||
"attempted to call method '" + options + "'" );
|
"attempted to call method '" + options + "'" );
|
||||||
|
@ -206,7 +233,10 @@ $.widget.bridge = function( name, object ) {
|
||||||
this.each(function() {
|
this.each(function() {
|
||||||
var instance = $.data( this, fullName );
|
var instance = $.data( this, fullName );
|
||||||
if ( instance ) {
|
if ( instance ) {
|
||||||
instance.option( options || {} )._init();
|
instance.option( options || {} );
|
||||||
|
if ( instance._init ) {
|
||||||
|
instance._init();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
$.data( this, fullName, new object( options, this ) );
|
$.data( this, fullName, new object( options, this ) );
|
||||||
}
|
}
|
||||||
|
@ -233,7 +263,7 @@ $.Widget.prototype = {
|
||||||
_createWidget: function( options, element ) {
|
_createWidget: function( options, element ) {
|
||||||
element = $( element || this.defaultElement || this )[ 0 ];
|
element = $( element || this.defaultElement || this )[ 0 ];
|
||||||
this.element = $( element );
|
this.element = $( element );
|
||||||
this.uuid = uuid++;
|
this.uuid = widget_uuid++;
|
||||||
this.eventNamespace = "." + this.widgetName + this.uuid;
|
this.eventNamespace = "." + this.widgetName + this.uuid;
|
||||||
this.options = $.widget.extend( {},
|
this.options = $.widget.extend( {},
|
||||||
this.options,
|
this.options,
|
||||||
|
@ -276,9 +306,6 @@ $.Widget.prototype = {
|
||||||
// all event bindings should go through this._on()
|
// all event bindings should go through this._on()
|
||||||
this.element
|
this.element
|
||||||
.unbind( this.eventNamespace )
|
.unbind( this.eventNamespace )
|
||||||
// 1.9 BC for #7810
|
|
||||||
// TODO remove dual storage
|
|
||||||
.removeData( this.widgetName )
|
|
||||||
.removeData( this.widgetFullName )
|
.removeData( this.widgetFullName )
|
||||||
// support: jquery <1.6.3
|
// support: jquery <1.6.3
|
||||||
// http://bugs.jquery.com/ticket/9413
|
// http://bugs.jquery.com/ticket/9413
|
||||||
|
@ -354,20 +381,23 @@ $.Widget.prototype = {
|
||||||
|
|
||||||
if ( key === "disabled" ) {
|
if ( key === "disabled" ) {
|
||||||
this.widget()
|
this.widget()
|
||||||
.toggleClass( this.widgetFullName + "-disabled ui-state-disabled", !!value )
|
.toggleClass( this.widgetFullName + "-disabled", !!value );
|
||||||
.attr( "aria-disabled", value );
|
|
||||||
this.hoverable.removeClass( "ui-state-hover" );
|
// If the widget is becoming disabled, then nothing is interactive
|
||||||
this.focusable.removeClass( "ui-state-focus" );
|
if ( value ) {
|
||||||
|
this.hoverable.removeClass( "ui-state-hover" );
|
||||||
|
this.focusable.removeClass( "ui-state-focus" );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
|
|
||||||
enable: function() {
|
enable: function() {
|
||||||
return this._setOption( "disabled", false );
|
return this._setOptions({ disabled: false });
|
||||||
},
|
},
|
||||||
disable: function() {
|
disable: function() {
|
||||||
return this._setOption( "disabled", true );
|
return this._setOptions({ disabled: true });
|
||||||
},
|
},
|
||||||
|
|
||||||
_on: function( suppressDisabledCheck, element, handlers ) {
|
_on: function( suppressDisabledCheck, element, handlers ) {
|
||||||
|
@ -387,7 +417,6 @@ $.Widget.prototype = {
|
||||||
element = this.element;
|
element = this.element;
|
||||||
delegateElement = this.widget();
|
delegateElement = this.widget();
|
||||||
} else {
|
} else {
|
||||||
// accept selectors, DOM elements
|
|
||||||
element = delegateElement = $( element );
|
element = delegateElement = $( element );
|
||||||
this.bindings = this.bindings.add( element );
|
this.bindings = this.bindings.add( element );
|
||||||
}
|
}
|
||||||
|
@ -412,7 +441,7 @@ $.Widget.prototype = {
|
||||||
handler.guid || handlerProxy.guid || $.guid++;
|
handler.guid || handlerProxy.guid || $.guid++;
|
||||||
}
|
}
|
||||||
|
|
||||||
var match = event.match( /^(\w+)\s*(.*)$/ ),
|
var match = event.match( /^([\w:-]*)\s*(.*)$/ ),
|
||||||
eventName = match[1] + instance.eventNamespace,
|
eventName = match[1] + instance.eventNamespace,
|
||||||
selector = match[2];
|
selector = match[2];
|
||||||
if ( selector ) {
|
if ( selector ) {
|
||||||
|
@ -527,4 +556,8 @@ $.each( { show: "fadeIn", hide: "fadeOut" }, function( method, defaultEffect ) {
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var widget = $.widget;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
Loading…
Reference in a new issue