Fix : Export to GIF : download option

In the GIF export panel, user can now choose between :
- export online (previous 'upload' feature)
- download GIF

Labels have been updated in the PNG export panel to follow the same
convention.

CanvasToBlob library was modified and moved to dedicated utils to handle
not only canvas, but also any base64 dateURI.
This commit is contained in:
jdescottes 2014-06-14 14:21:26 +02:00
parent d501129e8e
commit 2809a551d7
12 changed files with 152 additions and 264 deletions

View File

@ -19,7 +19,10 @@
this.shortcutService = new pskl.service.keyboard.ShortcutService(); this.shortcutService = new pskl.service.keyboard.ShortcutService();
this.shortcutService.init(); this.shortcutService.init();
var size = this.readSizeFromURL_(); var size = {
height : Constants.DEFAULT.HEIGHT,
width : Constants.DEFAULT.WIDTH
};
var descriptor = new pskl.model.piskel.Descriptor('New Piskel', ''); var descriptor = new pskl.model.piskel.Descriptor('New Piskel', '');
var piskel = new pskl.model.Piskel(size.width, size.height, descriptor); var piskel = new pskl.model.Piskel(size.width, size.height, descriptor);
@ -111,35 +114,27 @@
this.initTooltips_(); this.initTooltips_();
if (this.isAppEngineVersion) { var piskelData = this.getPiskelInitData_();
this.finishInitAppEngine_(); if (piskelData && piskelData.piskel) {
} else { this.loadPiskel_(piskelData.piskel, piskelData.descriptor, piskelData.fps);
this.finishInitGithub_();
} }
}, },
finishInitGithub_ : function () { loadPiskel_ : function (serializedPiskel, descriptor, fps) {
var framesheetId = this.readFramesheetIdFromURL_(); pskl.utils.serialization.Deserializer.deserialize(serializedPiskel, function (piskel) {
if (framesheetId) { piskel.setDescriptor(descriptor);
$.publish(Events.SHOW_NOTIFICATION, [{ pskl.app.piskelController.setPiskel(piskel);
"content" : "Loading animation with id : [" + framesheetId + "]" pskl.app.animationController.setFPS(fps);
}]); });
this.loadFramesheetFromService(framesheetId);
}
}, },
finishInitAppEngine_ : function () { getPiskelInitData_ : function () {
if (pskl.appEnginePiskelData_ && pskl.appEnginePiskelData_.piskel) { return pskl.appEnginePiskelData_;
pskl.utils.serialization.Deserializer.deserialize(pskl.appEnginePiskelData_.piskel, function (piskel) {
piskel.setDescriptor(pskl.appEnginePiskelData_.descriptor);
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(pskl.appEnginePiskelData_.fps);
});
}
}, },
isLoggedIn : function () { isLoggedIn : function () {
return pskl.appEnginePiskelData_ && pskl.appEnginePiskelData_.isLoggedIn; var piskelData = this.getPiskelInitData_();
return piskelData && piskelData.isLoggedIn;
}, },
initTooltips_ : function () { initTooltips_ : function () {
@ -154,69 +149,6 @@
this.previewFilmController.render(delta); this.previewFilmController.render(delta);
}, },
readSizeFromURL_ : function () {
var sizeParam = this.readUrlParameter_("size");
var size;
// parameter expected as size=64x48 => size=widthxheight
var parts = sizeParam.split("x");
if (parts && parts.length == 2 && !isNaN(parts[0]) && !isNaN(parts[1])) {
var width = parseInt(parts[0], 10),
height = parseInt(parts[1], 10);
size = {
height : Math.min(height, Constants.MAX_HEIGHT),
width : Math.min(width, Constants.MAX_WIDTH)
};
} else {
size = {
height : Constants.DEFAULT.HEIGHT,
width : Constants.DEFAULT.WIDTH
};
}
return size;
},
readFramesheetIdFromURL_ : function () {
return this.readUrlParameter_("frameId");
},
readUrlParameter_ : function (paramName) {
var searchString = window.location.search.substring(1);
var params = searchString.split("&");
for (var i = 0; i < params.length; i++) {
var param = params[i].split("=");
if (param[0] == paramName) {
return window.unescape(param[1]);
}
}
return "";
},
loadFramesheetFromService : function (frameId) {
var xhr = new XMLHttpRequest();
xhr.open('GET', Constants.STATIC.URL.GET + '?l=' + frameId, true);
xhr.responseType = 'text';
xhr.onload = function (e) {
var res = JSON.parse(this.responseText);
pskl.utils.serialization.Deserializer.deserialize(res.framesheet, function (piskel) {
pskl.app.piskelController.setPiskel(piskel);
pskl.app.animationController.setFPS(res.fps);
$.publish(Events.HIDE_NOTIFICATION);
});
};
xhr.onerror = function () {
$.publish(Events.HIDE_NOTIFICATION);
};
xhr.send();
},
store : function (callbacks) {
this.storageService.store(callbacks);
},
getFirstFrameAsPng : function () { getFirstFrameAsPng : function () {
var firstFrame = this.piskelController.getFrameAt(0); var firstFrame = this.piskelController.getFrameAt(0);
var canvasRenderer = new pskl.rendering.CanvasRenderer(firstFrame, 1); var canvasRenderer = new pskl.rendering.CanvasRenderer(firstFrame, 1);
@ -229,21 +161,6 @@
var renderer = new pskl.rendering.PiskelRenderer(this.piskelController); var renderer = new pskl.rendering.PiskelRenderer(this.piskelController);
var framesheetCanvas = renderer.renderAsCanvas(); var framesheetCanvas = renderer.renderAsCanvas();
return framesheetCanvas.toDataURL("image/png"); return framesheetCanvas.toDataURL("image/png");
},
uploadAsSpritesheetPNG : function () {
var imageData = this.getFramesheetAsPng();
this.imageUploadService.upload(imageData, this.openWindow.bind(this));
},
openWindow : function (url) {
var options = [
"dialog=yes", "scrollbars=no", "status=no",
"width=" + this.piskelController.getWidth() * this.piskelController.getFrameCount(),
"height=" + this.piskelController.getHeight()
].join(",");
window.open(url, "piskel-export", options);
} }
}; };
})(); })();

View File

@ -34,8 +34,13 @@
this.previewContainerEl = document.querySelector(".gif-export-preview"); this.previewContainerEl = document.querySelector(".gif-export-preview");
this.radioGroupEl = document.querySelector(".gif-export-radio-group"); this.radioGroupEl = document.querySelector(".gif-export-radio-group");
this.uploadForm = $("[name=gif-export-upload-form]"); this.uploadButton = $(".gif-upload-button");
this.uploadForm.submit(this.onUploadFormSubmit_.bind(this)); this.uploadButton.click(this.onUploadButtonClick_.bind(this));
this.downloadButton = $(".gif-download-button");
this.downloadButton.click(this.onDownloadButtonClick_.bind(this));
this.exportForm = $(".gif-export-form");
this.exportProgressStatusEl = document.querySelector('.gif-export-progress-status'); this.exportProgressStatusEl = document.querySelector('.gif-export-progress-status');
this.exportProgressBarEl = document.querySelector('.gif-export-progress-bar'); this.exportProgressBarEl = document.querySelector('.gif-export-progress-bar');
@ -43,15 +48,27 @@
this.createRadioElements_(); this.createRadioElements_();
}; };
ns.GifExportController.prototype.onUploadFormSubmit_ = function (evt) { ns.GifExportController.prototype.onUploadButtonClick_ = function (evt) {
evt.originalEvent.preventDefault(); evt.originalEvent.preventDefault();
var selectedZoom = this.getSelectedZoom_(), var zoom = this.getSelectedZoom_(),
fps = this.piskelController.getFPS(), fps = this.piskelController.getFPS();
zoom = selectedZoom;
this.renderAsImageDataAnimatedGIF(zoom, fps, this.onGifRenderingCompleted_.bind(this)); this.renderAsImageDataAnimatedGIF(zoom, fps, this.onGifRenderingCompleted_.bind(this));
}; };
ns.GifExportController.prototype.onDownloadButtonClick_ = function (evt) {
var fileName = this.piskelController.getPiskel().getDescriptor().name + '.gif';
var zoom = this.getSelectedZoom_(),
fps = this.piskelController.getFPS();
this.renderAsImageDataAnimatedGIF(zoom, fps, function (imageData) {
pskl.app.imageUploadService.upload(imageData, this.onImageUploadCompleted_.bind(this));
pskl.utils.ImageToBlob.imageDataToBlob(imageData, "image/gif", function(blob) {
pskl.utils.FileUtils.downloadAsFile(fileName, blob);
});
}.bind(this));
};
ns.GifExportController.prototype.onGifRenderingCompleted_ = function (imageData) { ns.GifExportController.prototype.onGifRenderingCompleted_ = function (imageData) {
this.updatePreview_(imageData); this.updatePreview_(imageData);
this.previewContainerEl.classList.add("preview-upload-ongoing"); this.previewContainerEl.classList.add("preview-upload-ongoing");
@ -62,7 +79,6 @@
this.updatePreview_(imageUrl); this.updatePreview_(imageUrl);
this.updateStatus_(imageUrl); this.updateStatus_(imageUrl);
this.previewContainerEl.classList.remove("preview-upload-ongoing"); this.previewContainerEl.classList.remove("preview-upload-ongoing");
}; };
ns.GifExportController.prototype.updatePreview_ = function (src) { ns.GifExportController.prototype.updatePreview_ = function (src) {
@ -70,7 +86,7 @@
}; };
ns.GifExportController.prototype.getSelectedZoom_ = function () { ns.GifExportController.prototype.getSelectedZoom_ = function () {
var radiosColl = this.uploadForm.get(0).querySelectorAll("[name=gif-zoom-level]"), var radiosColl = this.exportForm.get(0).querySelectorAll("[name=gif-zoom-level]"),
radios = Array.prototype.slice.call(radiosColl,0); radios = Array.prototype.slice.call(radiosColl,0);
var selectedRadios = radios.filter(function(radio) {return !!radio.checked;}); var selectedRadios = radios.filter(function(radio) {return !!radio.checked;});
@ -105,15 +121,6 @@
return radioEl; return radioEl;
}; };
ns.GifExportController.prototype.blobToBase64_ = function(blob, cb) {
var reader = new FileReader();
reader.onload = function() {
var dataUrl = reader.result;
cb(dataUrl);
};
reader.readAsDataURL(blob);
};
ns.GifExportController.prototype.renderAsImageDataAnimatedGIF = function(zoom, fps, cb) { ns.GifExportController.prototype.renderAsImageDataAnimatedGIF = function(zoom, fps, cb) {
var colorCount = pskl.app.currentColorsService.getCurrentColors().length; var colorCount = pskl.app.currentColorsService.getCurrentColors().length;
var preserveColors = colorCount < MAX_GIF_COLORS; var preserveColors = colorCount < MAX_GIF_COLORS;
@ -140,7 +147,7 @@
gif.on('finished', function(blob) { gif.on('finished', function(blob) {
this.hideProgressStatus_(); this.hideProgressStatus_();
this.blobToBase64_(blob, cb); pskl.utils.FileUtils.readFile(blob, cb);
}.bind(this)); }.bind(this));
gif.render(); gif.render();

View File

@ -16,21 +16,19 @@
document.querySelector(".zip-generate-button").addEventListener('click', this.onZipButtonClick_.bind(this)); document.querySelector(".zip-generate-button").addEventListener('click', this.onZipButtonClick_.bind(this));
this.updatePreview_(this.getFramesheetAsBase64Png()); this.setPreviewSrc_(this.getFramesheetAsCanvas().toDataURL("image/png"));
}; };
ns.PngExportController.prototype.onPngDownloadButtonClick_ = function (evt) { ns.PngExportController.prototype.onPngDownloadButtonClick_ = function (evt) {
var fileName = this.getPiskelName_() + '.png'; var fileName = this.getPiskelName_() + '.png';
var renderer = new pskl.rendering.PiskelRenderer(this.piskelController); pskl.utils.ImageToBlob.canvasToBlob(this.getFramesheetAsCanvas(), function(blob) {
var canvas = renderer.renderAsCanvas();
canvas.toBlob(function(blob) {
pskl.utils.FileUtils.downloadAsFile(fileName, blob); pskl.utils.FileUtils.downloadAsFile(fileName, blob);
}); });
}; };
ns.PngExportController.prototype.onPngUploadButtonClick_ = function (evt) { ns.PngExportController.prototype.onPngUploadButtonClick_ = function (evt) {
this.previewContainerEl.classList.add("preview-upload-ongoing"); this.previewContainerEl.classList.add("preview-upload-ongoing");
pskl.app.imageUploadService.upload(this.getFramesheetAsBase64Png(), this.onImageUploadCompleted_.bind(this)); pskl.app.imageUploadService.upload(this.getFramesheetAsCanvas().toDataURL("image/png"), this.onImageUploadCompleted_.bind(this));
}; };
ns.PngExportController.prototype.onZipButtonClick_ = function () { ns.PngExportController.prototype.onZipButtonClick_ = function () {
@ -59,10 +57,9 @@
return this.piskelController.getPiskel().getDescriptor().name; return this.piskelController.getPiskel().getDescriptor().name;
}; };
ns.PngExportController.prototype.getFramesheetAsBase64Png = function () { ns.PngExportController.prototype.getFramesheetAsCanvas = function () {
var renderer = new pskl.rendering.PiskelRenderer(this.piskelController); var renderer = new pskl.rendering.PiskelRenderer(this.piskelController);
var framesheetCanvas = renderer.renderAsCanvas(); return renderer.renderAsCanvas();
return framesheetCanvas.toDataURL("image/png");
}; };
ns.PngExportController.prototype.onImageUploadCompleted_ = function (imageUrl) { ns.PngExportController.prototype.onImageUploadCompleted_ = function (imageUrl) {
@ -84,7 +81,7 @@
} }
}; };
ns.PngExportController.prototype.updatePreview_ = function (src) { ns.PngExportController.prototype.setPreviewSrc_ = function (src) {
this.previewContainerEl.innerHTML = "<img class='light-picker-background' style='max-width:240px;' src='"+src+"'/>"; this.previewContainerEl.innerHTML = "<img class='light-picker-background' style='max-width:240px;' src='"+src+"'/>";
}; };

View File

@ -49,7 +49,7 @@
this.piskelController.getPiskel().setDescriptor(descriptor); this.piskelController.getPiskel().setDescriptor(descriptor);
this.beforeSaving_(); this.beforeSaving_();
pskl.app.store({ pskl.app.storageService.store({
success : this.onSaveSuccess_.bind(this), success : this.onSaveSuccess_.bind(this),
error : this.onSaveError_.bind(this), error : this.onSaveError_.bind(this),
after : this.afterSaving_.bind(this) after : this.afterSaving_.bind(this)

View File

@ -1,107 +0,0 @@
/* canvas-toBlob.js
* A canvas.toBlob() implementation.
* 2011-07-13
*
* By Eli Grey, http://eligrey.com and Devin Samarin, https://github.com/eboyjr
* License: X11/MIT
* See LICENSE.md
*/
/*global self */
/*jslint bitwise: true, regexp: true, confusion: true, es5: true, vars: true, white: true,
plusplus: true */
/*! @source http://purl.eligrey.com/github/canvas-toBlob.js/blob/master/canvas-toBlob.js */
(function(view) {
"use strict";
var
Uint8Array = view.Uint8Array
, HTMLCanvasElement = view.HTMLCanvasElement
, is_base64_regex = /\s*;\s*base64\s*(?:;|$)/i
, base64_ranks
, decode_base64 = function(base64) {
var
len = base64.length
, buffer = new Uint8Array(len / 4 * 3 | 0)
, i = 0
, outptr = 0
, last = [0, 0]
, state = 0
, save = 0
, rank
, code
, undef
;
while (len--) {
code = base64.charCodeAt(i++);
rank = base64_ranks[code-43];
if (rank !== 255 && rank !== undef) {
last[1] = last[0];
last[0] = code;
save = (save << 6) | rank;
state++;
if (state === 4) {
buffer[outptr++] = save >>> 16;
if (last[1] !== 61 /* padding character */) {
buffer[outptr++] = save >>> 8;
}
if (last[0] !== 61 /* padding character */) {
buffer[outptr++] = save;
}
state = 0;
}
}
}
// 2/3 chance there's going to be some null bytes at the end, but that
// doesn't really matter with most image formats.
// If it somehow matters for you, truncate the buffer up outptr.
return buffer.buffer;
}
;
if (Uint8Array) {
base64_ranks = new Uint8Array([
62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1
, -1, -1, 0, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25
, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35
, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
]);
}
if (HTMLCanvasElement && !HTMLCanvasElement.prototype.toBlob) {
HTMLCanvasElement.prototype.toBlob = function(callback, type /*, ...args*/) {
if (!type) {
type = "image/png";
} if (this.mozGetAsFile) {
callback(this.mozGetAsFile("canvas", type));
return;
}
var
args = Array.prototype.slice.call(arguments, 1)
, dataURI = this.toDataURL.apply(this, args)
, header_end = dataURI.indexOf(",")
, data = dataURI.substring(header_end + 1)
, is_base64 = is_base64_regex.test(dataURI.substring(0, header_end))
, blob
;
if (Blob.fake) {
// no reason to decode a data: URI that's just going to become a data URI again
blob = new Blob
if (is_base64) {
blob.encoding = "base64";
} else {
blob.encoding = "URI";
}
blob.data = data;
blob.size = data.length;
} else if (Uint8Array) {
if (is_base64) {
blob = new Blob([decode_base64(data)], {type: type});
} else {
blob = new Blob([decodeURIComponent(data)], {type: type});
}
}
callback(blob);
};
}
}(self));

View File

@ -8,24 +8,6 @@
ns.GithubStorageService.prototype.init = function () {}; ns.GithubStorageService.prototype.init = function () {};
ns.GithubStorageService.prototype.store = function (callbacks) { ns.GithubStorageService.prototype.store = function (callbacks) {
var xhr = new XMLHttpRequest(); throw "Github save is no longer available. Use local save instead";
var formData = new FormData();
formData.append('framesheet_content', this.piskelController.serialize());
formData.append('fps_speed', this.piskelController.getFPS());
xhr.open('POST', Constants.STATIC.URL.SAVE, true);
xhr.onload = function(e) {
if (this.status == 200) {
var baseUrl = window.location.href.replace(window.location.search, "");
window.location.href = baseUrl + "?frameId=" + this.responseText;
} else {
this.onerror(e);
}
};
xhr.onerror = function(e) {
$.publish(Events.SHOW_NOTIFICATION, [{"content": "Saving failed ("+this.status+")"}]);
};
xhr.send(formData);
}; };
})(); })();

51
src/js/utils/Base64.js Normal file
View File

@ -0,0 +1,51 @@
(function () {
var ns = $.namespace('pskl.utils');
var base64_ranks;
if (Uint8Array) {
base64_ranks = new Uint8Array([
62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1,
-1, -1, 0, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
-1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35,
36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
]);
}
ns.Base64 = {
decode : function(base64) {
var outptr = 0;
var last = [0, 0];
var state = 0;
var save = 0;
var undef;
var len = base64.length, i = 0;
var buffer = new Uint8Array(len / 4 * 3 | 0);
while (len--) {
var code = base64.charCodeAt(i++);
var rank = base64_ranks[code-43];
if (rank !== 255 && rank !== undef) {
last[1] = last[0];
last[0] = code;
save = (save << 6) | rank;
state++;
if (state === 4) {
buffer[outptr++] = save >>> 16;
if (last[1] !== 61 /* padding character */) {
buffer[outptr++] = save >>> 8;
}
if (last[0] !== 61 /* padding character */) {
buffer[outptr++] = save;
}
state = 0;
}
}
}
// 2/3 chance there's going to be some null bytes at the end, but that
// doesn't really matter with most image formats.
// If it somehow matters for you, truncate the buffer up outptr.
return buffer.buffer;
}
};
})();

View File

@ -0,0 +1,38 @@
(function () {
var ns = $.namespace('pskl.utils');
var BASE64_REGEX = /\s*;\s*base64\s*(?:;|$)/i;
ns.ImageToBlob = {
imageDataToBlob : function(dataURI, type, callback) {
var header_end = dataURI.indexOf(","),
data = dataURI.substring(header_end + 1),
isBase64 = BASE64_REGEX.test(dataURI.substring(0, header_end)),
blob;
if (Blob.fake) {
// no reason to decode a data: URI that's just going to become a data URI again
blob = new Blob();
blob.encoding = isBase64 ? "base64" : "URI";
blob.data = data;
blob.size = data.length;
} else if (Uint8Array) {
var blobData = isBase64 ? pskl.utils.Base64.decode(data) : decodeURIComponent(data);
blob = new Blob([blobData], {type: type});
}
callback(blob);
},
canvasToBlob : function(canvas, callback, type /*, ...args*/) {
type = type || "image/png";
if (this.mozGetAsFile) {
callback(this.mozGetAsFile("canvas", type));
} else {
var args = Array.prototype.slice.call(arguments, 2);
var dataURI = this.toDataURL.apply(this, args);
pskl.utils.ImageToBlob.imageDataToBlob(dataURI, type, callback);
}
}
};
})();

View File

@ -11,7 +11,6 @@
// JSZip https://github.com/Stuk/jszip // JSZip https://github.com/Stuk/jszip
"js/lib/jszip/jszip.min.js", "js/lib/jszip/jszip.min.js",
"js/lib/canvastoblob/canvasToBlob.js",
// Spectrum color-picker library // Spectrum color-picker library
"js/lib/spectrum/spectrum.js", "js/lib/spectrum/spectrum.js",
@ -24,12 +23,14 @@
// Libraries // Libraries
"js/utils/core.js", "js/utils/core.js",
"js/utils/UserAgent.js", "js/utils/UserAgent.js",
"js/utils/Base64.js",
"js/utils/CanvasUtils.js", "js/utils/CanvasUtils.js",
"js/utils/Dom.js", "js/utils/Dom.js",
"js/utils/Math.js", "js/utils/Math.js",
"js/utils/FileUtils.js", "js/utils/FileUtils.js",
"js/utils/FrameUtils.js", "js/utils/FrameUtils.js",
"js/utils/LayerUtils.js", "js/utils/LayerUtils.js",
"js/utils/ImageToBlob.js",
"js/utils/ImageResizer.js", "js/utils/ImageResizer.js",
"js/utils/PixelUtils.js", "js/utils/PixelUtils.js",
"js/utils/Template.js", "js/utils/Template.js",

View File

@ -29,9 +29,9 @@
<option value="4">Enabled - 4px wide</option> <option value="4">Enabled - 4px wide</option>
</select> </select>
</div> </div>
<div class="settings-item"> <!-- <div class="settings-item">
<label for="tiled-preview">Display tiled preview :</label> <label for="tiled-preview">Display tiled preview :</label>
<input type="checkbox" value="1" id="tiled-preview" name="tiled-preview-checkbox"/> <input type="checkbox" value="1" id="tiled-preview" name="tiled-preview-checkbox"/>
</div> </div> -->
</div> </div>

View File

@ -3,17 +3,19 @@
Export to Animated GIF Export to Animated GIF
</div> </div>
<div class="settings-item"> <div class="settings-item">
<label>Select resolution:</label> <form action="" method="POST" class="gif-export-form">
<form action="" method="POST" name="gif-export-upload-form"> <label>Select resolution:</label>
<script type="text/template" id="gif-export-radio-template"> <script type="text/template" id="gif-export-radio-template">
<label style="display:block"><input type="radio" name="gif-zoom-level" value="{{value}}"/> <label style="display:block"><input type="radio" name="gif-zoom-level" value="{{value}}"/>
{{label}}</label> {{label}}</label>
</script> </script>
<div class="gif-export-radio-group"></div> <div class="gif-export-radio-group"></div>
<input type="submit" class="button button-primary gif-upload-button" value="Upload" />
<span class="gif-export-progress-status"></span> <button type="button" class="button button-primary gif-download-button">Download GIF</button>
<div class="gif-export-progress-bar"></div> <button type="button" class="button button gif-upload-button">Export online</button>
</form> </form>
<span class="gif-export-progress-status"></span>
<div class="gif-export-progress-bar"></div>
<div class="gif-export-preview"></div> <div class="gif-export-preview"></div>
<div class="gif-upload-status"></div> <div class="gif-upload-status"></div>
</div> </div>

View File

@ -6,8 +6,8 @@
<span>Preview : </span> <span>Preview : </span>
<div class="png-export-preview"></div> <div class="png-export-preview"></div>
<div class="png-export-radio-group"></div> <div class="png-export-radio-group"></div>
<input type="button" class="button button-primary png-download-button" value="Download PNG" /> <button type="button" class="button button-primary png-download-button">Download PNG</button>
<input type="button" class="button png-upload-button" value="Upload PNG" /> <button type="button" class="button png-upload-button">Export online</button>
<!-- <input type="button" class="button png-download-button" value="Download" /> --> <!-- <input type="button" class="button png-download-button" value="Download" /> -->
<div class="png-upload-status"></div> <div class="png-upload-status"></div>
</div> </div>