From 1b2f6b763ae1d19ebf5f5fad223fd7078b651f50 Mon Sep 17 00:00:00 2001 From: Marko Strukelj Date: Tue, 25 Oct 2016 16:38:26 +0200 Subject: [PATCH] KEYCLOAK-2366 Unable to download key in Safari - Updated FileSaver.js to version 1.3.3 --- .../resources/lib/filesaver/FileSaver.js | 229 +++++++----------- 1 file changed, 88 insertions(+), 141 deletions(-) diff --git a/themes/src/main/resources/theme/keycloak/common/resources/lib/filesaver/FileSaver.js b/themes/src/main/resources/theme/keycloak/common/resources/lib/filesaver/FileSaver.js index 6095017e94..fb7149425d 100644 --- a/themes/src/main/resources/theme/keycloak/common/resources/lib/filesaver/FileSaver.js +++ b/themes/src/main/resources/theme/keycloak/common/resources/lib/filesaver/FileSaver.js @@ -1,10 +1,11 @@ /* FileSaver.js - * A saveAs() FileSaver implementation. - * 2014-05-27 + * A saveAs() FileSaver implementation. + * 1.3.2 + * 2016-06-16 18:25:19 * - * By Eli Grey, http://eligrey.com - * License: X11/MIT - * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md + * By Eli Grey, http://eligrey.com + * License: MIT + * See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md */ /*global self */ @@ -12,16 +13,10 @@ /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */ -var saveAs = saveAs - // IE 10+ (native saveAs) - || (typeof navigator !== "undefined" && - navigator.msSaveOrOpenBlob && navigator.msSaveOrOpenBlob.bind(navigator)) - // Everyone else - || (function(view) { +var saveAs = saveAs || (function(view) { "use strict"; // IE <10 is explicitly unsupported - if (typeof navigator !== "undefined" && - /MSIE [1-9]\./.test(navigator.userAgent)) { + if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) { return; } var @@ -31,36 +26,30 @@ var saveAs = saveAs return view.URL || view.webkitURL || view; } , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a") - , can_use_save_link = !view.externalHost && "download" in save_link + , can_use_save_link = "download" in save_link , click = function(node) { - var event = doc.createEvent("MouseEvents"); - event.initMouseEvent( - "click", true, false, view, 0, 0, 0, 0, 0 - , false, false, false, false, 0, null - ); + var event = new MouseEvent("click"); node.dispatchEvent(event); } - , webkit_req_fs = view.webkitRequestFileSystem - , req_fs = view.requestFileSystem || webkit_req_fs || view.mozRequestFileSystem + , is_safari = /constructor/i.test(view.HTMLElement) || view.safari + , is_chrome_ios =/CriOS\/[\d]+/.test(navigator.userAgent) , throw_outside = function(ex) { (view.setImmediate || view.setTimeout)(function() { throw ex; }, 0); } , force_saveable_type = "application/octet-stream" - , fs_min_size = 0 - , deletion_queue = [] - , process_deletion_queue = function() { - var i = deletion_queue.length; - while (i--) { - var file = deletion_queue[i]; + // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to + , arbitrary_revoke_timeout = 1000 * 40 // in ms + , revoke = function(file) { + var revoker = function() { if (typeof file === "string") { // file is an object URL get_URL().revokeObjectURL(file); } else { // file is a File file.remove(); } - } - deletion_queue.length = 0; // clear queue + }; + setTimeout(revoker, arbitrary_revoke_timeout); } , dispatch = function(filesaver, event_types, event) { event_types = [].concat(event_types); @@ -76,134 +65,97 @@ var saveAs = saveAs } } } - , FileSaver = function(blob, name) { + , auto_bom = function(blob) { + // prepend BOM for UTF-8 XML and text/* types (including HTML) + // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF + if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) { + return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type}); + } + return blob; + } + , FileSaver = function(blob, name, no_auto_bom) { + if (!no_auto_bom) { + blob = auto_bom(blob); + } // First try a.download, then web filesystem, then object URLs var filesaver = this , type = blob.type - , blob_changed = false + , force = type === force_saveable_type , object_url - , target_view - , get_object_url = function() { - var object_url = get_URL().createObjectURL(blob); - deletion_queue.push(object_url); - return object_url; - } , dispatch_all = function() { dispatch(filesaver, "writestart progress write writeend".split(" ")); } // on any filesys errors revert to saving with object URLs , fs_error = function() { - // don't create more object URLs than needed - if (blob_changed || !object_url) { - object_url = get_object_url(blob); + if ((is_chrome_ios || (force && is_safari)) && view.FileReader) { + // Safari doesn't allow downloading of blob urls + var reader = new FileReader(); + reader.onloadend = function() { + var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;'); + var popup = view.open(url, '_blank'); + if(!popup) view.location.href = url; + url=undefined; // release reference before dispatching + filesaver.readyState = filesaver.DONE; + dispatch_all(); + }; + reader.readAsDataURL(blob); + filesaver.readyState = filesaver.INIT; + return; } - if (target_view) { - target_view.location.href = object_url; + // don't create more object URLs than needed + if (!object_url) { + object_url = get_URL().createObjectURL(blob); + } + if (force) { + view.location.href = object_url; } else { - window.open(object_url, "_blank"); + var opened = view.open(object_url, "_blank"); + if (!opened) { + // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html + view.location.href = object_url; + } } filesaver.readyState = filesaver.DONE; dispatch_all(); + revoke(object_url); } - , abortable = function(func) { - return function() { - if (filesaver.readyState !== filesaver.DONE) { - return func.apply(this, arguments); - } - }; - } - , create_if_not_found = {create: true, exclusive: false} - , slice ; filesaver.readyState = filesaver.INIT; - if (!name) { - name = "download"; - } + if (can_use_save_link) { - object_url = get_object_url(blob); - save_link.href = object_url; - save_link.download = name; - click(save_link); - filesaver.readyState = filesaver.DONE; - dispatch_all(); + object_url = get_URL().createObjectURL(blob); + setTimeout(function() { + save_link.href = object_url; + save_link.download = name; + click(save_link); + dispatch_all(); + revoke(object_url); + filesaver.readyState = filesaver.DONE; + }); return; } - // Object and web filesystem URLs have a problem saving in Google Chrome when - // viewed in a tab, so I force save with application/octet-stream - // http://code.google.com/p/chromium/issues/detail?id=91158 - if (view.chrome && type && type !== force_saveable_type) { - slice = blob.slice || blob.webkitSlice; - blob = slice.call(blob, 0, blob.size, force_saveable_type); - blob_changed = true; - } - // Since I can't be sure that the guessed media type will trigger a download - // in WebKit, I append .download to the filename. - // https://bugs.webkit.org/show_bug.cgi?id=65440 - if (webkit_req_fs && name !== "download") { - name += ".download"; - } - if (type === force_saveable_type || webkit_req_fs) { - target_view = view; - } - if (!req_fs) { - fs_error(); - return; - } - fs_min_size += blob.size; - req_fs(view.TEMPORARY, fs_min_size, abortable(function(fs) { - fs.root.getDirectory("saved", create_if_not_found, abortable(function(dir) { - var save = function() { - dir.getFile(name, create_if_not_found, abortable(function(file) { - file.createWriter(abortable(function(writer) { - writer.onwriteend = function(event) { - target_view.location.href = file.toURL(); - deletion_queue.push(file); - filesaver.readyState = filesaver.DONE; - dispatch(filesaver, "writeend", event); - }; - writer.onerror = function() { - var error = writer.error; - if (error.code !== error.ABORT_ERR) { - fs_error(); - } - }; - "writestart progress write abort".split(" ").forEach(function(event) { - writer["on" + event] = filesaver["on" + event]; - }); - writer.write(blob); - filesaver.abort = function() { - writer.abort(); - filesaver.readyState = filesaver.DONE; - }; - filesaver.readyState = filesaver.WRITING; - }), fs_error); - }), fs_error); - }; - dir.getFile(name, {create: false}, abortable(function(file) { - // delete file if it already exists - file.remove(); - save(); - }), abortable(function(ex) { - if (ex.code === ex.NOT_FOUND_ERR) { - save(); - } else { - fs_error(); - } - })); - }), fs_error); - }), fs_error); + + fs_error(); } , FS_proto = FileSaver.prototype - , saveAs = function(blob, name) { - return new FileSaver(blob, name); + , saveAs = function(blob, name, no_auto_bom) { + return new FileSaver(blob, name || blob.name || "download", no_auto_bom); } ; - FS_proto.abort = function() { - var filesaver = this; - filesaver.readyState = filesaver.DONE; - dispatch(filesaver, "abort"); - }; + // IE 10+ (native saveAs) + if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) { + return function(blob, name, no_auto_bom) { + name = name || blob.name || "download"; + + if (!no_auto_bom) { + blob = auto_bom(blob); + } + return navigator.msSaveOrOpenBlob(blob, name); + }; + } + + FS_proto.abort = function(){}; FS_proto.readyState = FS_proto.INIT = 0; FS_proto.WRITING = 1; FS_proto.DONE = 2; @@ -217,11 +169,6 @@ var saveAs = saveAs FS_proto.onwriteend = null; - view.addEventListener("unload", process_deletion_queue, false); - saveAs.unload = function() { - process_deletion_queue(); - view.removeEventListener("unload", process_deletion_queue, false); - }; return saveAs; }( typeof self !== "undefined" && self @@ -232,10 +179,10 @@ var saveAs = saveAs // while `this` is nsIContentFrameMessageManager // with an attribute `content` that corresponds to the window -if (typeof module !== "undefined" && module !== null) { - module.exports = saveAs; -} else if ((typeof define !== "undefined" && define !== null) && (define.amd != null)) { - define([], function() { +if (typeof module !== "undefined" && module.exports) { + module.exports.saveAs = saveAs; +} else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) { + define("FileSaver.js", function() { return saveAs; }); }