application/js/field/image.js
// console.log('Loading ImageField...')
Spontaneous.Field.Image = (function($, S) {
'use strict';
var dom = S.Dom;
var ImageFieldConflictView = new JS.Class(S.Field.String.ConflictView, {
panel: function() {
var labels = dom.div('.image-field-conflict.labels.differences'),
outer = dom.div(),
image_outer = dom.div('.image-field-conflict.changes.differences'),
original = dom.div('.original.diff'),
edited = dom.div('.final.diff');
var local_label = dom.div('.diff').text('Server version');
var server_label = dom.div('.diff').text('Your version');
original.append(dom.img().attr('src', this.values.server_original)).click(function() {
this.useValue(this.values.server_original);
edited.add(original).removeClass('selected');
original.addClass('selected');
}.bind(this));
edited.append(dom.img().attr('src', this.values.local_edited)).click(function() {
this.useValue(this.values.local_edited);
edited.add(original).removeClass('selected');
edited.addClass('selected');
}.bind(this));
labels.append(local_label, server_label);
image_outer.append(original, edited);
outer.append(labels, image_outer);
return outer;
}
});
var ImageField = new JS.Class(Spontaneous.Field.File, {
is_image: function() {
return true;
},
unload: function() {
this.callSuper();
this.image = null;
this._progress_bar = null;
},
// progress_bar: function() {
// if (!this._progress_bar) {
// var progress_outer = dom.div('.drop-upload-outer').hide();
// var progress_inner = dom.div('.drop-upload-inner').css('width', 0);
// progress_outer.append(progress_inner);
// this._progress_bar = progress_inner;
// }
// return this._progress_bar;
// },
upload_progress: function(position, total) {
this.spinner().stop();
this.waiting.find(':visible').hide();
this.callSuper();
},
currentValue: function() {
var pending, v = this.get('value');
if ((pending = v.__pending__)) {
pending.path = pending.src;
return pending.value;
}
return v.__ui__ || v.original || {};
},
currentEditValue: function() {
var value, pending, ui, v = this.get('value');
if ((pending = v.__pending__)) {
return pending.value;
}
value = v.original;
if ((ui = v.__ui__)) {
value.path = value.src;
value.src = ui.src;
}
return value;
},
currentFilename: function() {
var v = this.get('value');
return (v.__pending__ || v.original).filename;
},
/*
* HACK: The async nature of image updates means that the version setting
* may be out of date not because of the actions of another, but because
* the field version has been updated in the background.
* The right way to do this would be to use an event to update the field
* values at the point where the update is complete, but that's a big change.
*
* If I do that then I could use it to update all field values across all sessions
* and avoid most conflicts by keeping the field values up-to-date automatically
* but I'm not ready for that just yet...
*
* Instead hackily use the pending version and hope it's not going to cause
* weird problems with simultaneous updates.
*/
version: function() {
var pending, value = this.get('value');
if ((pending = value.__pending__)) {
return pending.version;
}
return this.data.version;
},
preview: function() {
Spontaneous.UploadManager.register(this);
var self = this
, value = this.currentValue()
, src = value.src
, img = null
, dim = 45;
// , container = container.parent('li');
if (src === '') {
img = dom.img('.missing-image', {'src':''});
} else {
img = dom.img();
img.load(function() {
var r = this.width/this.height, $this = $(this), h = $this.height(), dh = 0;
if (r >= 1) { // landscape -- fit image vertically
// tag for extra css styles applicable to landscape images
// container.addClass('landscape');
if (h <= dim) {
dh = (dim - h)/2;
}
}
$this.css('top', dom.px(dh));
});
img.attr({'src':src});
}
img.error(function() {
$(this).addClass('missing');
});
this.image = img;
var outer = dom.div('.image-outer');
var dropper = dom.div('.image-drop');
var waiting = dom.div('.waiting').hide();
outer.append(img);
outer.append(waiting);
outer.append(dropper);
// outer.append(download);
dropper.append(this.progress_bar().parent());
this.waiting = waiting;
var drop = function(event) {
event.stopPropagation();
event.preventDefault();
dropper.removeClass('drop-active').addClass('uploading');
var files = event.dataTransfer.files;
this.waiting.show();
this.spinner().indeterminate();
if (files.length > 0) {
this.select_files(files);
var file = files[0],
url = this.createObjectURL(file)
, image = this.image;
this._edited_value = url;
image.__start_upload = true;
image.bind('load', function() {
if (this.image.__start_upload) {
image.__start_upload = false;
S.Ajax.test_field_versions(this.content, [this], this.upload_values.bind(this), this.upload_conflict.bind(this));
}
var img = image[0], w = img.width, h = img.height, r = w/h;
if (r > 1) {
// container.addClass('landscape');
} else {
// container.removeClass('landscape');
}
}.bind(this));
image.attr('src', url);
}
return false;
}.bind(this);
var drag_enter = function(event) {
event.stopPropagation();
event.preventDefault();
$(this).addClass('drop-active');
return false;
}.bind(dropper);
var drag_over = function(event) {
event.stopPropagation();
event.preventDefault();
return false;
}.bind(dropper);
var drag_leave = function(event) {
event.stopPropagation();
event.preventDefault();
$(this).removeClass('drop-active');
return false;
}.bind(dropper);
dropper.get(0).addEventListener('drop', drop, true);
dropper.bind('dragenter', drag_enter).bind('dragover', drag_over).bind('dragleave', drag_leave);
this.value_wrap = outer;
this.drop_target = dropper;
this.preview_img = img;
return outer;
},
conflicts_resolved: function(resolution_list) {
// console.log('conflicts_resolved', resolution_list)
var resolution = resolution_list[0];
this.set_edited_value(resolution.value);
this.set_version(resolution.version);
if (this.is_modified()) {
this.upload_values();
} else {
this.disable_progress();
}
},
disable_progress: function() {
this.spinner().stop();
this.callSuper();
},
spinner: function() {
if (!this._spinner) {
this._spinner = Spontaneous.Progress(this.waiting[0], 16, {
spinner_fg_color: '#fff',
period: 800
});
this._spinner.init();
}
return this._spinner;
},
upload_complete: function(values) {
this.mark_unmodified();
this.callSuper(values);
if (values) {
var value = this.currentValue();
if (this.image) {
var img = new Image();
img.onload = function() {
this.image.attr('src', value.src);
}.bind(this);
img.src = value.src;
}
}
},
width: function() {
if (this.data.values && this.currentValue()) {
return this.currentValue().width;
}
return 0;
},
height: function() {
if (this.data.values && this.currentValue()) {
return this.currentValue().height;
}
return 0;
},
edit: function() {
var wrap = dom.div(),
drop_wrap = dom.div({'style':'position:relative;'}),
value = this.currentEditValue(),
src = value.src,
img = dom.img({'src':src}),
info, sizes, filename_info, filesize_info, dimensions_info;
if (value.width >= value.height) {
wrap.addClass('landscape');
} else {
wrap.removeClass('landscape');
}
info = dom.div('.info');
sizes = dom.div('.sizes');
filename_info = dom.div('.filename');
filesize_info = dom.div('.filesize');
dimensions_info = dom.div('.dimensions');
sizes.append(filesize_info, dimensions_info);
info.append(filename_info);
info.append(sizes);
var set_dimensions = function(width, height) {
if (width && height) {
dimensions_info.text(width + 'x' + height);
} else {
dimensions_info.text('');
}
};
var set_info = function(filename, filesize, width, height) {
filename_info.text(filename);
if (filesize) {
filesize_info.text(parseFloat(filesize, 10).to_filesize());
} else if (filesize === 0 || filesize === '0') {
filesize_info.text('');
}
set_dimensions(width, height);
};
var files_selected = function(files) {
if (files.length > 0) {
var file = files[0], url = this.createObjectURL(file);
img.attr('src', url).removeClass('empty');
info.removeClass('empty')
this.select_files(files);
img.attr('src', url);
this._edited_value = url;
this.image.attr('src', url);
set_info(File.filename(file), file.fileSize, null, null);
}
}.bind(this);
var onchange = function() {
var files = this.input[0].files;
files_selected(files);
}.bind(this);
var input = this.get_input().change(onchange);
var onclick = function() {
input.trigger('click');
return false;
};
if (src === '') {
img.addClass('empty');
info.addClass('empty');
}
var dropper = dom.div('.image-drop').click(onclick);
var actions = dom.div('.actions');
var value = this.get('value');
var original_src = function() {
if (value.original) {
return value.original.path
}
}();
actions.append(input);
if (original_src !== '') {
var clear = dom.a('.button.clear').text('Clear').click(function() {
img.css({width: dom.px(img.width()), height: dom.px(img.height())}).attr('src', '/@spontaneous/static/px.gif');
set_info('', 0, null, null);
this.clear_file();
}.bind(this));
var download = dom.a('.button.image-download-original', {href: original_src, download: true}).text('Download');
actions.append(clear, download);
}
drop_wrap.append(dropper);
var drop = function(event) {
event.stopPropagation();
event.preventDefault();
dropper.removeClass('drop-active');
var files = event.dataTransfer.files;
files_selected(files);
return false;
}.bind(this);
var drag_enter = function(event) {
event.stopPropagation();
event.preventDefault();
$(this).addClass('drop-active');
return false;
}.bind(dropper);
var drag_over = function(event) {
event.stopPropagation();
event.preventDefault();
return false;
}.bind(dropper);
var drag_leave = function(event) {
event.stopPropagation();
event.preventDefault();
$(this).removeClass('drop-active');
return false;
}.bind(dropper);
dropper.get(0).addEventListener('drop', drop, true);
dropper.bind('dragenter', drag_enter).bind('dragover', drag_over).bind('dragleave', drag_leave);
drop_wrap.append(img, info);
wrap.append(drop_wrap, actions);
if (value) {
// var s = value.path.split('/'), filename = s[s.length - 1];
set_info(this.currentFilename(), value.filesize, value.width, value.height);
}
this.preview_img = img;
return wrap;
},
select_files: function(files) {
this.selected_files = files;
this.mark_modified();
},
is_modified: function() {
return this.get_modified_state();
},
get_input: function() {
this.input = this.generate_input();
return this.input;
},
cancel_edit: function() {
this.image.attr('src', this.currentValue().src);
},
conflict_view: function(dialogue, conflict) {
return new ImageFieldConflictView(dialogue, conflict);
},
edited_value: function() {
return this._edited_value;
},
set_edited_value: function(value) {
this.preview_img.attr('src', value);
this.callSuper(value);
},
accept_mimetype: 'image/*'
});
ImageField.ConflictView = ImageFieldConflictView;
return ImageField;
})(jQuery, Spontaneous);