diff --git a/customize.dist/src/less2/include/modals-ui-elements.less b/customize.dist/src/less2/include/modals-ui-elements.less index 7ec19a699..94ec06cc0 100644 --- a/customize.dist/src/less2/include/modals-ui-elements.less +++ b/customize.dist/src/less2/include/modals-ui-elements.less @@ -273,4 +273,19 @@ } } } + #cp-upload-preview-container { + max-width: 100%; + max-height: 300px; + overflow: auto; + media-tag { + max-width: 100%; + iframe { + // pdfs don't take the full width unless we tell them to + width: 100%; + } + & > * { + max-width: 100%; + } + } + } } diff --git a/www/common/common-ui-elements.js b/www/common/common-ui-elements.js index 5bc8bf4f6..f77a9d422 100644 --- a/www/common/common-ui-elements.js +++ b/www/common/common-ui-elements.js @@ -1658,6 +1658,7 @@ define([ UI.openCustomModal(modal); }; + Messages.toolbar_userMenuAlt = "User menu"; // XXX UIElements.createUserAdminMenu = function (Common, config) { var metadataMgr = Common.getMetadataMgr(); @@ -1990,6 +1991,9 @@ define([ */ var $displayName = $userAdmin.find('.'+displayNameCls); + $userAdmin.attr({ // XXX is this on the right element? + alt: Messages.toolbar_userMenuAlt, + }); var $avatar = $userAdmin.find('> button .cp-dropdown-button-title'); var loadingAvatar; diff --git a/www/common/media-tag.js b/www/common/media-tag.js index d1a5ebcda..e8b35a47e 100644 --- a/www/common/media-tag.js +++ b/www/common/media-tag.js @@ -73,7 +73,6 @@ var factory = function () { * @param {object} cfg Object {Plugins, allowed, download, pdf} containing infos about plugins * @param {function} cb Callback function: (err, pluginElement) => {} */ - // XXX add alt attributes if present in metadata text: function (metadata, url, content, cfg, cb) { var plainText = document.createElement('div'); plainText.className = "plain-text-reader"; @@ -88,6 +87,7 @@ var factory = function () { image: function (metadata, url, content, cfg, cb) { var img = document.createElement('img'); img.setAttribute('src', url); + img.setAttribute('alt', metadata.alt || ""); img.blob = content; cb(void 0, img); }, @@ -95,15 +95,19 @@ var factory = function () { var video = document.createElement('video'); video.setAttribute('src', url); video.setAttribute('controls', true); + // https://discuss.codecademy.com/t/can-we-use-an-alt-attribute-with-the-video-tag/300322/4 + video.setAttribute('title', metadata.alt || ""); cb(void 0, video); }, audio: function (metadata, url, content, cfg, cb) { var audio = document.createElement('audio'); audio.setAttribute('src', url); audio.setAttribute('controls', true); + audio.setAttribute('alt', metadata.alt || ""); cb(void 0, audio); }, pdf: function (metadata, url, content, cfg, cb) { + // XXX alt text var iframe = document.createElement('iframe'); if (cfg.pdf.viewer) { // PDFJS var viewerUrl = cfg.pdf.viewer + '?file=' + url; @@ -116,6 +120,7 @@ var factory = function () { download: function (metadata, url, content, cfg, cb) { var btn = document.createElement('button'); btn.setAttribute('class', 'btn btn-default'); + btn.setAttribute('alt', metadata.alt || ""); btn.innerHTML = '' + cfg.download.text + '
' + (metadata.name ? '' + fixHTML(metadata.name) + '' : ''); btn.addEventListener('click', function () { @@ -543,7 +548,7 @@ var factory = function () { // Process var process = function (mediaObject, decrypted, cfg, cb) { - var metadata = decrypted.metadata; + var metadata = decrypted.metadata || {}; var blob = decrypted.content; var mediaType = getType(mediaObject, metadata, cfg); @@ -597,6 +602,15 @@ var factory = function () { }); }; + var initHandlers = function () { + return { + 'progress': [], + 'complete': [], + 'metadata': [], + 'error': [] + }; + }; + // Initialize a media-tag var init = function (el, cfg) { cfg = cfg || {}; @@ -614,13 +628,7 @@ var factory = function () { }; } - var handlers = cfg.handlers || { - 'progress': [], - 'complete': [], - 'metadata': [], - 'error': [] - }; - + var handlers = cfg.handlers || initHandlers(); var mediaObject = el._mediaObject = { handlers: handlers, tag: el @@ -763,6 +771,24 @@ var factory = function () { init.fetchDecryptedMetadata = fetchDecryptedMetadata; + init.preview = function (content, metadata, cfg, cb) { + cfg = cfg || {}; + addMissingConfig(cfg, config); + var handlers = cfg.handlers || initHandlers(); + var el = document.createElement('media-tag'); + var mediaObject = el._mediaObject = { + handlers: handlers, + tag: el, + }; + process(mediaObject, { + metadata: metadata, + content: content + }, cfg, function (err) { + if (err) { return void cb(err); } + cb(void 0, el); + }); + }; + return init; }; diff --git a/www/common/sframe-common-file.js b/www/common/sframe-common-file.js index 1286da14d..a56313444 100644 --- a/www/common/sframe-common-file.js +++ b/www/common/sframe-common-file.js @@ -11,10 +11,12 @@ define([ '/common/hyperscript.js', '/customize/messages.js', '/customize/pages.js', + '/bower_components/nthen/index.js', + '/common/media-tag.js', '/bower_components/file-saver/FileSaver.min.js', '/bower_components/tweetnacl/nacl-fast.min.js', -], function ($, ApiConfig, FileCrypto, MakeBackup, Thumb, UI, UIElements, Util, Hash, h, Messages, Pages) { +], function ($, ApiConfig, FileCrypto, MakeBackup, Thumb, UI, UIElements, Util, Hash, h, Messages, Pages, nThen, MT) { var Nacl = window.nacl; var module = {}; @@ -312,7 +314,11 @@ define([ }); return manualStore; }; - var fileUploadModal = function (defaultFileName, cb) { + + Messages.upload_modal_alt = "Alt text"; // XXX + Messages.upload_addOptionalAlt = "Add descriptive text (optional)"; // XXX + + var fileUploadModal = function (defaultFileName, cb, preview) { var parsedName = /^(\.?.+?)(\.[^.]+)?$/.exec(defaultFileName) || []; var ext = parsedName[2] || ""; @@ -321,9 +327,15 @@ define([ // Ask for name, password and owner var content = h('div', [ h('h4', Messages.upload_modal_title), + (preview? h('div#cp-upload-preview-container', preview): undefined), UIElements.setHTML(h('label', {for: 'cp-upload-name'}), Messages._getKey('upload_modal_filename', [ext])), h('input#cp-upload-name', {type: 'text', placeholder: defaultFileName, value: defaultFileName}), + + h('label', {for: 'cp-upload-alt'}, Messages.upload_addOptionalAlt), // XXX alt text for uploads + h('input#cp-upload-alt', {type: 'text', placeholder: Messages.upload_modal_alt}), + + h('label', {for: 'cp-upload-password'}, Messages.addOptionalPassword), UI.passwordInput({id: 'cp-upload-password'}), h('span', { @@ -335,7 +347,8 @@ define([ manualStore ]); - $(content).find('#cp-upload-owned').on('change', function () { + var $content = $(content); + $content.find('#cp-upload-owned').on('change', function () { var val = Util.isChecked($(content).find('#cp-upload-owned')); if (val) { $(content).find('#cp-upload-store').prop('checked', true).prop('disabled', true); @@ -348,8 +361,9 @@ define([ if (!yes) { return void cb(); } // Get the values - var newName = $(content).find('#cp-upload-name').val(); - var password = $(content).find('#cp-upload-password').val() || undefined; + var newName = $content.find('#cp-upload-name').val(); + var password = $content.find('#cp-upload-password').val() || undefined; + var alt = $content.find('#cp-upload-alt').val() || undefined; var owned = Util.isChecked($(content).find('#cp-upload-owned')); var forceSave = owned || Util.isChecked($(content).find('#cp-upload-store')); @@ -366,7 +380,8 @@ define([ name: newName, password: password, owned: owned, - forceSave: forceSave + forceSave: forceSave, + alt: alt, }); }); }; @@ -437,6 +452,8 @@ define([ } var thumb; + var preview; + var alt; var file_arraybuffer; var name = file.name; var password; @@ -447,6 +464,7 @@ define([ var metadata = { name: name, type: type, + alt: alt, }; if (thumb) { metadata.thumbnail = thumb; } queue.push({ @@ -486,8 +504,9 @@ define([ password = obj.password; owned = obj.owned; forceSave = obj.forceSave; + alt = obj.alt; finish(); - }); + }, preview); } }; @@ -495,11 +514,20 @@ define([ if (e) { console.error(e); } file_arraybuffer = buffer; if (!Thumb.isSupportedType(file)) { return getName(); } - // make a resized thumbnail from the image.. - Thumb.fromBlob(file, function (e, thumb64) { - if (e) { console.error(e); } - if (!thumb64) { return getName(); } - thumb = thumb64; + nThen(function (w) { + // make a resized thumbnail from the image.. + Thumb.fromBlob(file, w(function (e, thumb64) { + if (e) { console.error(e); } + if (!thumb64) { return; } + thumb = thumb64; + })); + MT.preview(buffer, { + type: file.type, + }, void 0, w(function (err, el) { + if (err) { return void console.error(err); } + preview = el; + })); + }).nThen(function () { getName(); }); }); diff --git a/www/profile/inner.js b/www/profile/inner.js index c63b72e99..284b19114 100644 --- a/www/profile/inner.js +++ b/www/profile/inner.js @@ -341,6 +341,8 @@ define([ }); }; + Messages.profile_defaultAlt = "Default profile picture"; // XXX + var displayAvatar = function (val) { var sframeChan = common.getSframeChannel(); var $span = APP.$avatar; @@ -349,7 +351,7 @@ define([ $('', { src: '/customize/images/avatar.png', title: Messages.profile_avatar, - alt: 'Avatar' // XXX translate this "Default profile picture" + alt: Messages.profile_defaultAlt, }).appendTo($span); return; }