Как изображение, вставляемое в imgur, работает в Firefox?

  • Откройте программу редактирования изображений, скопируйте изображение (не копируйте из браузера - я объясню, почему позже).
  • Откройте Firefox и перейдите в http://imgur.com
  • Нажмите Ctrl + V
  • Посмотрите с полным изумлением на изображение, которое вы скопировали перед загрузкой.

Я знаю о API буфера обмена HTML5, который отлично работает с Chrome. В Chrome, когда вы вставляете данные двоичного изображения, браузер запускает событие paste, содержащее event.clipboardData.types, которое равно ['Files'], поэтому я могу получить свое изображение в буфере обмена с помощью

var index = event.clipboardData.types.indexOf('Files');
if(index !== -1) {
    var blob = event.clipboardData.items[index].getAsFile();
}

В Firefox, когда я вставляю данные двоичного изображения, браузер также запускает событие вставки, но event.clipboardData.types пуст (имеет length === 0) и event.clipboardData.getData('Files') возвращает явно "".

Кстати, копирование изображения из браузера также устанавливает элемент "text/html" в clipboardData, который содержит скопированный элемент <img>. Поэтому в этих случаях я мог бы решить проблему, отправив удаленный URL-адрес на сервер, который затем загрузил бы само изображение (если у сервера есть доступ к удаленному местоположению, что на самом деле не гарантировано).

StackOverflow предлагает следующее: Как получить данные из буфера обмена в Firefox (создайте contenteditable <div>, попросите пользователя вставить в него и затем скопировать содержимое.)

Однако imgur этого не делает. Как это работает?

Ответы

Ответ 1

Отказ от ответственности: я не знал систему загрузки imgur перед публикацией, но надеюсь, что это поможет вам. Это сообщение написано, просто просмотрев код и с помощью некоторых знаний HTML/JS:)

imgur, похоже, использует другой параметр вставки для Firefox (и браузеры Gecko в целом). Действительно, там есть div с идентификатором upload-global-FF-paste-box в индексе HTML с атрибутом contenteditable="true". В главном js мы можем найти инициализацию свойства $FFPasteBox:

init: function (a) {
    this._ = $.extend({
        el: {
            $computerButton: $("#gallery-upload-buttons #file-wrapper"),
            $webButton: $("#gallery-upload-buttons #url"),
            $computerButtonGlobal: $("#upload-global-file"),
            $FFPasteBox: $("#upload-global-FF-paste-box")  // <--- this line
        }
    }, a)
},

Затем, немного выкопав файл global.js, я нашел эти функции (см. выше). В основном, процесс:

  • Подождите, пока пользователь выполнит действие Ctrl-V
  • Когда он выстрелил, сделайте contenteditable div увиденным ранее в фокусе, чтобы получить данные
  • Ожидаемые данные полностью отправляются (это может быть долго с фотографией с высоким разрешением)
  • Проанализируйте полученную строку base64.

Здесь код, извлеченный из global.js и прокомментированный мной:

// When a paste action is trigger, set the focus on this div and wait the data to be sent
initPasteUploadMozilla: function () {
    $(document).on("paste", _.bind(function (a) {
        $(a.target).is("input") || this.isInView() && (Imgur.Upload.Index && this.showColorBox(), this._.el.$FFPasteBox.focus(), this.waitForPasteData(this._.el.$FFPasteBox[0]))
    }, this))
},

// Listen the user, and waiting that the node is created by the paste action
waitForPasteData: function (a) {
    a.childNodes && a.childNodes.length > 0 ? this.processPaste(a) : setTimeout(this.waitForPasteData.bind(this, a), 20)
},

// Check data sent
processPaste: function (a) {
    var b = a.innerHTML,
        // Check if the thing pasted is a <img tag or a png or at least base64 stuff
        c = {
            image: -1 != b.indexOf("<img") && -1 != b.indexOf("src="),
            base64: -1 != b.indexOf("base64,"),
            png: -1 != b.indexOf("iVBORw0K")
        };
    if (c.image && c.base64 && c.png) {
        var d = b.split('<img src="');
        d = d[1].split('" alt="">');
        var b = {
            dataURL: d[0],
            file: {
                size: this.bytesizeBase64String(d[0].length)
            }
        };
        this.addBase64(b)
    } else {
        var e = $(a).find("img"),
            f = null;
        if (e.length > 0 && e.attr("src").length > 0 ? f = e.attr("src") : b.length > 0 && (f = b), null !== f && f.match(this._.urlRegex)) this.queueUrl(f);
        else if (null !== f) {
            var g = $(f).filter("a");
            g.length && g.attr("href").match(this._.urlRegex) ? this.queueUrl(g.attr("href")) : this.showError(this._.messages.urlInvalidError)
        }
    }
    a.innerHTML = ""
},