Are you sure you want to delete the album ${ albumTitle } and all of the photos it contains? This action can't be undone!
` + msg = lychee.html`Are you sure you want to delete the album '$${ albumTitle }' and all of the photos it contains? This action can't be undone!
` } else { action.title = 'Delete Albums and Photos' cancel.title = 'Keep Albums' - msg = `Are you sure you want to delete all ${ albumIDs.length } selected albums and all of the photos they contain? This action can't be undone!
` + msg = lychee.html`Are you sure you want to delete all $${ albumIDs.length } selected albums and all of the photos they contain? This action can't be undone!
` } @@ -247,7 +247,6 @@ album.setTitle = function(albumIDs) { else if (albums.json) oldTitle = albums.getByID(albumIDs).title if (!oldTitle) oldTitle = '' - oldTitle = oldTitle.replace(/'/g, ''') } @@ -257,9 +256,6 @@ album.setTitle = function(albumIDs) { basicModal.close() - // Remove html from input - newTitle = lychee.removeHTML(newTitle) - // Set title to Untitled when empty newTitle = (newTitle==='') ? 'Untitled' : newTitle @@ -296,10 +292,10 @@ album.setTitle = function(albumIDs) { } - let input = `` + let input = lychee.html`` - if (albumIDs.length===1) msg = `Enter a new title for this album: ${ input }
` - else msg = `Enter a title for all ${ albumIDs.length } selected albums: ${ input }
` + if (albumIDs.length===1) msg = lychee.html`Enter a new title for this album: ${ input }
` + else msg = lychee.html`Enter a title for all $${ albumIDs.length } selected albums: ${ input }
` basicModal.show({ body: msg, @@ -327,9 +323,6 @@ album.setDescription = function(albumID) { basicModal.close() - // Remove html from input - description = lychee.removeHTML(description) - if (visible.album()) { album.json.description = description view.album.description() @@ -349,7 +342,7 @@ album.setDescription = function(albumID) { } basicModal.show({ - body: `Please enter a description for this album:
`, + body: lychee.html`Please enter a description for this album:
`, buttons: { action: { title: 'Set Description', @@ -552,7 +545,7 @@ album.getArchive = function(albumID) { if (location.href.indexOf('index.html')>0) link = location.href.replace(location.hash, '').replace('index.html', url) else link = location.href.replace(location.hash, '') + url - if (lychee.publicMode===true) link += `&password=${ password.value }` + if (lychee.publicMode===true) link += `&password=${ encodeURIComponent(password.value) }` location.href = link @@ -581,11 +574,11 @@ album.merge = function(albumIDs) { if (!sTitle) sTitle = '' sTitle = sTitle.replace(/'/g, ''') - msg = `Are you sure you want to merge the album '${ sTitle }' into the album '${ title }'?
` + msg = lychee.html`Are you sure you want to merge the album '$${ sTitle }' into the album '$${ title }'?
` } else { - msg = `Are you sure you want to merge all selected albums into the album '${ title }'?
` + msg = lychee.html`Are you sure you want to merge all selected albums into the album '$${ title }'?
` } diff --git a/src/scripts/build.js b/src/scripts/build.js index 1da00dc..3e25391 100644 --- a/src/scripts/build.js +++ b/src/scripts/build.js @@ -7,54 +7,66 @@ build = {} build.iconic = function(icon, classes = '') { - return `` + let html = '' + + html += lychee.html`` + + return html } build.divider = function(title) { - return `No results
' + html += `No results
` break case 'eye': - html += 'No public albums
' + html += `No public albums
` break case 'cog': - html += 'No configuration
' + html += `No configuration
` break case 'question-mark': - html += 'Photo not found
' + html += `Photo not found
` break } - html += '-
Lychee ${ lychee.version } – Update available!
+Lychee $${ lychee.version } – Update available!
` basicModal.show({ @@ -312,15 +312,6 @@ lychee.animate = function(obj, animation) { } -lychee.escapeHTML = function(s) { - - return s.replace(/&/g, '&') - .replace(/"/g, '"') - .replace(//g, '>') - -} - lychee.retinize = function(path = '') { let pixelRatio = window.devicePixelRatio, @@ -385,14 +376,54 @@ lychee.getEventName = function() { } -lychee.removeHTML = function(html = '') { +lychee.escapeHTML = function(html = '') { - if (html==='') return html + // Ensure that html is a string + html += '' - let tmp = document.createElement('DIV') - tmp.innerHTML = html + // Escape all critical characters + html = html.replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"') + .replace(/'/g, ''') + .replace(/`/g, '`') - return (tmp.textContent || tmp.innerText) + return html + +} + +lychee.html = function(literalSections, ...substs) { + + // Use raw literal sections: we don’t want + // backslashes (\n etc.) to be interpreted + let raw = literalSections.raw, + result = '' + + substs.forEach((subst, i) => { + + // Retrieve the literal section preceding + // the current substitution + let lit = raw[i] + + // If the substitution is preceded by a dollar sign, + // we escape special characters in it + if (lit.slice(-1)==='$') { + subst = lychee.escapeHTML(subst) + lit = lit.slice(0, -1) + } + + result += lit + result += subst + + }) + + // Take care of last literal section + // (Never fails, because an empty template string + // produces one literal section, an empty string) + result += raw[raw.length-1] + + return result } diff --git a/src/scripts/password.js b/src/scripts/password.js index 7187d1f..6b28601 100644 --- a/src/scripts/password.js +++ b/src/scripts/password.js @@ -55,8 +55,10 @@ password.getDialog = function(albumID, callback) { const action = (data) => password.get(albumID, callback, data.password) const cancel = () => { + basicModal.close() if (visible.albums()===false) lychee.goto() + } let msg = ` diff --git a/src/scripts/photo.js b/src/scripts/photo.js index 4ebdc26..5770b5c 100644 --- a/src/scripts/photo.js +++ b/src/scripts/photo.js @@ -244,14 +244,14 @@ photo.delete = function(photoIDs) { action.title = 'Delete Photo' cancel.title = 'Keep Photo' - msg = `Are you sure you want to delete the photo '${ photoTitle }'? This action can't be undone!
` + msg = lychee.html`Are you sure you want to delete the photo '$${ photoTitle }'? This action can't be undone!
` } else { action.title = 'Delete Photo' cancel.title = 'Keep Photo' - msg = `Are you sure you want to delete all ${ photoIDs.length } selected photo? This action can't be undone!
` + msg = lychee.html`Are you sure you want to delete all $${ photoIDs.length } selected photo? This action can't be undone!
` } @@ -285,7 +285,6 @@ photo.setTitle = function(photoIDs) { // Get old title if only one photo is selected if (photo.json) oldTitle = photo.json.title else if (album.json) oldTitle = album.json.content[photoIDs].title - oldTitle = oldTitle.replace(/'/g, ''') } @@ -295,9 +294,6 @@ photo.setTitle = function(photoIDs) { let newTitle = data.title - // Remove html from input - newTitle = lychee.removeHTML(newTitle) - if (visible.photo()) { photo.json.title = (newTitle==='' ? 'Untitled' : newTitle) view.photo.title() @@ -321,10 +317,10 @@ photo.setTitle = function(photoIDs) { } - let input = `` + let input = lychee.html`` - if (photoIDs.length===1) msg = `Enter a new title for this photo: ${ input }
` - else msg = `Enter a title for all ${ photoIDs.length } selected photos: ${ input }
` + if (photoIDs.length===1) msg = lychee.html`Enter a new title for this photo: ${ input }
` + else msg = lychee.html`Enter a title for all $${ photoIDs.length } selected photos: ${ input }
` basicModal.show({ body: msg, @@ -465,7 +461,7 @@ photo.setPublic = function(photoID, e) { photo.setDescription = function(photoID) { - let oldDescription = photo.json.description.replace(/'/g, ''') + let oldDescription = photo.json.description const action = function(data) { @@ -473,9 +469,6 @@ photo.setDescription = function(photoID) { let description = data.description - // Remove html from input - description = lychee.removeHTML(description) - if (visible.photo()) { photo.json.description = description view.photo.description() @@ -495,7 +488,7 @@ photo.setDescription = function(photoID) { } basicModal.show({ - body: `Enter a description for this photo:
`, + body: lychee.html`Enter a description for this photo:
`, buttons: { action: { title: 'Set Description', @@ -541,10 +534,10 @@ photo.editTags = function(photoIDs) { } - let input = `` + let input = lychee.html`` - if (photoIDs.length===1) msg = `Enter your tags for this photo. You can add multiple tags by separating them with a comma: ${ input }
` - else msg = `Enter your tags for all ${ photoIDs.length } selected photos. Existing tags will be overwritten. You can add multiple tags by separating them with a comma: ${ input }
` + if (photoIDs.length===1) msg = lychee.html`Enter your tags for this photo. You can add multiple tags by separating them with a comma: ${ input }
` + else msg = lychee.html`Enter your tags for all $${ photoIDs.length } selected photos. Existing tags will be overwritten. You can add multiple tags by separating them with a comma: ${ input }
` basicModal.show({ body: msg, @@ -571,9 +564,6 @@ photo.setTags = function(photoIDs, tags) { tags = tags.replace(/(\ ,\ )|(\ ,)|(,\ )|(,{1,}\ {0,})|(,$|^,)/g, ',') tags = tags.replace(/,$|^,|(\ ){0,}$/g, '') - // Remove html from input - tags = lychee.removeHTML(tags) - if (visible.photo()) { photo.json.tags = tags view.photo.tags() @@ -684,7 +674,7 @@ photo.getArchive = function(photoID) { if (location.href.indexOf('index.html')>0) link = location.href.replace(location.hash, '').replace('index.html', url) else link = location.href.replace(location.hash, '') + url - if (lychee.publicMode===true) link += '&password=' + password.value + if (lychee.publicMode===true) link += `&password=${ encodeURIComponent(password.value) }` location.href = link diff --git a/src/scripts/settings.js b/src/scripts/settings.js index 89e054c..876ef8b 100644 --- a/src/scripts/settings.js +++ b/src/scripts/settings.js @@ -404,10 +404,10 @@ settings.setDropboxKey = function(callback) { } - let msg = ` + let msg = lychee.html`In order to import photos from your Dropbox, you need a valid drop-ins app key from their website. Generate yourself a personal key and enter it below: - +
` diff --git a/src/scripts/sidebar.js b/src/scripts/sidebar.js index 9cbd473..d301ef6 100644 --- a/src/scripts/sidebar.js +++ b/src/scripts/sidebar.js @@ -93,13 +93,17 @@ sidebar.setSelectable = function(selectable = true) { } -sidebar.changeAttr = function(attr, value = '-') { +sidebar.changeAttr = function(attr, value = '-', dangerouslySetInnerHTML = false) { if (attr==null || attr==='') return false // Set a default for the value if (value==null || value==='') value = '-' + // Escape value + if (dangerouslySetInnerHTML===false) value = lychee.escapeHTML(value) + + // Set new value sidebar.dom('.attr_' + attr).html(value) return true @@ -339,14 +343,14 @@ sidebar.render = function(structure) { if (value==='' || value==null) value = '-' // Wrap span-element around value for easier selecting on change - value = `${ value }` + value = lychee.html`$${ value }` // Add edit-icon to the value when editable if (row.editable===true) value += ' ' + build.editIcon('edit_' + row.title.toLowerCase()) - _html += ` + _html += lychee.html`