Skip to content

Instantly share code, notes, and snippets.

@rich97
Created July 23, 2012 12:05
Show Gist options
  • Save rich97/3163263 to your computer and use it in GitHub Desktop.
Save rich97/3163263 to your computer and use it in GitHub Desktop.
(function($) {
var $uploader = $('#uploader')
, $feedback = $('#feedback')
, $uploadForm = $('#upload-form')
, $imagePreviews = $('#image-previews')
, $imageErrors = $('#image-errors')
, $imageOrder = $('#image-order')
, $files = $('#files')
, $uid = $('#uid')
, $uploaded = $('#uploaded')
, $addImagesBtn = $('#add-images-btn')
, $progressCancelAll = $('.progress-cancel-all')
, $hiddenSet = $('.hidden-set');
var $previewImages = $imagePreviews
.children('.preview-image');
var uid = $uid.val()
, pid = 0;
var $progressMap = {}
, $previewMap = {};
var previousMessage = {};
var handleFileChange = function(event) {
event.preventDefault();
submitForm(event);
};
var handleCancelAll = function(event, id) {
event.preventDefault();
updateProgress($progressMap[id], id, {
status: 'error', completed: 100
});
$progressMap[id].item.fadeOut('slow', function() {
$(this).remove;
delete $progressMap[id];
});
if ($previewMap[id] !== undefined) {
$.each($previewMap[id], function(key, $file) {
$file.removeLink.click();
});
}
};
// rebind file change event
$files.change(handleFileChange);
// cancel all
$progressCancelAll.click(handleCancelAll);
// rebind trigger event
$addImagesBtn.click(function(event) {
event.preventDefault();
$files.trigger('click');
});
$previewImages.each(function() {
$this = $(this);
var group = $this.data('group')
, index = $this.data('index');
if ($previewMap[group] === undefined) {
$previewMap[group] = {};
}
$previewMap[group][index] = {
item: $this,
content: $this.find('.pi-content'),
loadingOverlay: $this.find('.loading'),
title: $this.find('.pi-header h4'),
removeLink: $this.find('.pi-header a')
};
});
$imagePreviews.sortable({
placeholder: 'order-placeholder',
update: function(event, ui) {
var order = $(this).sortable('toArray').join();
$imageOrder.val(order.replace(/preview-/g, ''));
},
activate: function() { $(this).addClass('sorting'); },
deactivate: function() { $(this).removeClass('sorting'); }
});
$uploader.addClass('active');
$uploadForm.attr('action', $uploadForm.attr('action') + '?async');
var getData = (function() {
function fileAPI(id, event, callback) {
if (event === undefined && callback === undefined) {
return;
}
var data;
if (event.dataTransfer !== undefined) {
data = event.dataTransfer.files;
} else if (event.target !== undefined) {
data = event.target.files;
}
var totalSize = 0
, files = [];
for (var i = 0, f; f = data[i]; i++) {
var reader = new FileReader();
reader.onload = (function(file, index) {
return function(e) {
var $img = $('<img />')
.attr('alt', '')
.attr('src', e.target.result)
.attr('title', file.name);
$img.load(function() {
files.push($.extend(
file,
{content: $(this)}
));
totalSize += file.size;
if (files.length === data.length) {
callback(id, null, {
totalSize: totalSize,
files: files
});
}
});
}
})(f, i);
// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
};
function ajax(id, callback) {
var callbacks = {};
callbacks.on = function(data) {
if (data.error !== null) {
return false;
}
if (data.response !== null) {
var loadScript = $imagePreviews.attr('data-image-load-script');
var totalSize = 0;
var files = [];
for (i in data.response[id]) {
var file = data.response[id][i];
var $img = $('<img />')
.attr('alt', '')
.attr('src', loadScript + '?file=' + file.response.name)
.attr('title', file.response.data.name);
totalSize += file.response.data.size;
$img.load(function() {
files.push({
content: $(this),
name: file.response.name,
size: file.response.data.size,
type: file.response.data.type
});
if (files.length === data.response[id].length) {
callback(id, data, {
totalSize: totalSize,
files: files
});
}
});
}
return false;
}
return true;
};
poll($uploadForm.attr('data-status-script'), { uid: id }, callbacks);
};
return {
'fileAPI': fileAPI,
'ajax': ajax
};
})();
function submitForm(event) {
// generate a new id for each request
var id = uid + '_' + pid;
while ($previewMap[id] !== undefined) {
pid++;
id = uid + '_' + pid;
}
// set form value
$uid.val(id);
// submit to iframe
var $iframe = $('<iframe />')
.addClass('upload-frame')
.attr('name', 'upload-frame' + id);
$uploadForm
.attr('target', 'upload-frame' + id)
.append($iframe);
// trigger submit, we can use forms submit
$('#upload').trigger('click');
// reset form value
$files.remove();
$files = $('<input />')
.attr('type', 'file')
.attr('name', 'files[]')
.attr('id', 'files')
.attr('multiple', 'multiple')
.change(handleFileChange);
$hiddenSet.append($files);
// define handler for data
var dataHandler = function(id, data, fileinfo) {
// create new progress element
$progress = DOM.init('progress', {key: id});
$progress.elements.title.html('Update Progress');
$progress.elements.cancel.click(function(event) {
handleCancelAll(event, id)
});
// append it to feedback
$feedback.append($progress.elements.item);
// pass new job to pollProgress
var callbacks = {}
, dataIsSet = false;
callbacks.on = function(data) {
if (data.error !== null) {
$progress.update({
status: 'error', message: data.error.message, completed: 100
});
} else if (data.response !== null) {
if (dataIsSet === false) {
// generate previews
$.each(fileinfo.files, function(index, file) {
var $preview = DOM.init('preview', {key: id, index: index});
var removeProgress = function(event) {
event.preventDefault();
$preview.remove({
deleteScript: $imagePreviews.attr('data-delete-script')
});
};
$preview.elements.item
.addClass('success')
.attr('id', 'preview-' + file.name);
$preview.elements.title.html(file.name);
$preview.elements.content.html(file.content);
$preview.elements.removeLink.click(removeProgress).data('file-name', file.name);
$preview.update({loaded: true});
$imagePreviews.append($preview.elements.item);
});
dataIsSet = true;
}
if (data.response < 100) {
$progress.update({
status: 'uploading', completed: data.response
});
} else {
$progress.update({
status: 'success', completed: 100
});
$progress.remove();
return false;
}
}
return true;
};
if (data === null) {
callbacks.end = function(data) {
$.get($uploadForm.attr('data-status-script'), {uid: id}, function(data) {
setStatus(data);
});
};
} else {
setStatus(data);
}
poll($uploadForm.attr('data-progress-script'), {
uid: id,
post_size: fileinfo.totalSize
}, callbacks);
}
if (
window.File !== undefined &&
window.FileReader !== undefined &&
window.FileList !== undefined &&
window.Blob !== undefined
) {
getData.fileAPI(id, event, dataHandler);
} else {
getData.ajax(id, dataHandler);
}
}
function poll(script, data, callbacks, callbackData) {
if (
script === undefined ||
data === undefined ||
callbacks === undefined
) {
return false;
}
if (callbacks.start !== undefined) {
callbacks.start(callbackData);
delete callbacks.start;
}
$.get(script, data, function(result) {
var repost;
if (result === null) {
repost = callbacks.on(null, callbackData);
} else {
repost = callbacks.on(result, callbackData);
}
if (repost === true) {
setTimeout(function() {
poll(script, data, callbacks);
}, 1000);
} else {
if (callbacks.end !== undefined) {
callbacks.end(result, callbackData);
}
}
});
}
function setStatus(data) {
if (data.response !== null) {
var response = data.response;
$.each(response, function(key, files) {
if ($previewMap[key] === undefined) {
return;
}
if (files !== null) {
$.each(files, function(index, file) {
if ($previewMap[key][index] === undefined) {
return;
}
var $preview = $previewMap[key][index];
if (file.response !== null || file.error !== null) {
$preview.update({loaded: true});
}
if (file.error !== null) {
$preview.update({error: file.error.message});
$preview.elements.item.appendTo($imageErrors);
delete $previewMap[key][index];
}
if (file.response !== null) {
var current = $uploaded.val();
var id = $preview.elements.item.attr('id');
if (id.indexOf(file.response.name) < 0) {
$preview.elements.item.attr('id', 'preview-' + file.response.name);
current = current.replace(id.replace('preview-', ''), file.response.name);
$uploaded.val(current);
}
if (current.indexOf(file.response.name) < 0) {
if (current.length > 0) {
value = current + ',' + file.response.name;
}
$uploaded.val(value);
}
}
});
}
});
}
}
var DOM = (function() {
function updateProgress(options) {
if (typeof options !== 'object') {
return;
}
$this = this.elements;
if (typeof options.completed === 'number') {
$this.bar.width(options.completed + '%');
}
if (typeof options.status === 'string') {
$this.item
.removeClass()
.addClass('progress-bar ' + options.status);
}
if (typeof options.message === 'string' && options.message !== previousMessage[this.options.key]) {
previousMessage[this.options.key] = options.message;
$listItem = $('<li />')
.addClass('message');
if (typeof options.status === 'string') {
$listItem.addClass(options.status);
}
$this.messages
.append(
$listItem.html(options.message)
);
}
}
function removeProgress() {
var key = this.options.key;
var $this = this.elements;
// remove progress bar
setTimeout(function() {
$this.item.fadeOut('slow', function() {
$this.item.remove;
delete $progressMap[key];
});
}, 5000);
}
function updatePreview(options) {
if (typeof options !== 'object') {
return;
}
$this = this.elements;
if (typeof options.error === 'string') {
$this.content.html('');
var $error = DOM.init('error');
$error.elements.content.html(options.error);
$this.removeLink.remove();
$this.item
.removeClass('success')
.addClass('error');
$this.content
.append(
$error.elements.image,
$error.elements.content
);
$error.elements.image.load(function() {
$this.loadingOverlay.hide();
});
}
if (typeof options.loaded === 'boolean') {
if (options.loaded === true) {
$this.loadingOverlay.fadeOut('slow');
} else {
$this.loadingOverlay.fadeIn('slow');
}
}
}
function removeImage(options) {
$this = this.elements;
var group = $this.item.data('group')
, index = $this.item.data('index');
if ($previewMap[group] === undefined) {
return;
}
$.get(
options.deleteScript,
{key: group, index: index},
function() {
if ($previewMap[group][index] !== undefined) {
delete $previewMap[group][index];
}
$this.item.fadeOut('slow', function() {
$this.item.remove();
});
}
);
}
var init = function(name, options) {
var elems = {
$progress: function(options) {
var key = options.key;
var $cancel = $('<a />')
.addClass('progress-cancel-all')
.attr('href', '#')
.text('Cancel All ')
.append($('<i />').addClass('icon-remove'));
var $messages = $('<ul />').addClass('feedback-messages');
var $title = $('<h3 />');
var $bar = $('<div />').addClass('progress-inner');
var $item = $('<div />').addClass('progress-bar')
.append(
$title,
$cancel,
$('<div />')
.addClass('progress-outer')
.html($bar),
$messages
);
var e = function() {
this.options = options;
this.elements = {
item: $item,
cancel: $cancel,
title: $title,
bar: $bar,
messages: $messages
};
};
e.prototype.update = updateProgress;
e.prototype.remove = removeProgress;
$progressMap[key] = new e();
return $progressMap[key];
},
$preview: function(options) {
var key = options.key;
var index = options.index;
$title = $('<h4 />');
$content = $('<div />')
.addClass('pi-content');
$loadingOverlay = $('<div />')
.addClass('loading')
.html('<img src="/unew/images/loader.gif" alt="loader" />');
$removeLink = $('<a />')
.attr('href', '#')
.addClass('icon-remove')
.html('&nbsp;');
$item = $('<li />')
.addClass('preview-image')
.append(
$('<div />').addClass('pi-header').append($title, $removeLink),
$('<div />').addClass('pi-content-wrapper').append($loadingOverlay, $content)
);
$item.data('group', key).data('index', index);
var e = function() {
this.options = options;
this.elements = {
item: $item,
content: $content,
loadingOverlay: $loadingOverlay,
title: $title,
removeLink: $removeLink
};
};
e.prototype.update = updatePreview;
e.prototype.remove = removeImage;
if ($previewMap[key] === undefined) {
$previewMap[key] = {};
}
$previewMap[key][index] = new e();
return $previewMap[key][index];
},
$error: function() {
$image = $('<img />')
.attr('src', '/unew/images/error.png')
.attr('alt', 'error');
$content = $('<div />');
var e = function() {
this.elements = {
image: $image,
content: $content
};
};
return new e();
}
};
if (typeof elems['$' + name] === 'function') {
return elems['$' + name](options);
}
};
return {
init: init
}
})();
})(jQuery);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment