diff --git a/dist/main.css b/dist/main.css index 2c174bd..e2ed91e 100644 Binary files a/dist/main.css and b/dist/main.css differ diff --git a/dist/main.js b/dist/main.js index 3ca47e1..124f86c 100644 Binary files a/dist/main.js and b/dist/main.js differ diff --git a/src/bower.json b/src/bower.json index 12cfb2d..708452c 100644 --- a/src/bower.json +++ b/src/bower.json @@ -5,6 +5,7 @@ "jQuery": "~2.1.3", "js-md5": "~1.1.0", "mousetrap": "~1.4.6", - "basicContext": "~2.0.2" + "basicContext": "~2.0.2", + "basicModal": "~2.0.0" } } diff --git a/src/gulpfile.js b/src/gulpfile.js index 79d1444..b2a4411 100644 --- a/src/gulpfile.js +++ b/src/gulpfile.js @@ -74,6 +74,7 @@ paths.main = { 'bower_components/mousetrap/mousetrap.min.js', 'bower_components/mousetrap/plugins/global-bind/mousetrap-global-bind.min.js', 'bower_components/basicContext/dist/basicContext.min.js', + 'bower_components/basicModal/dist/basicModal.min.js', '../src/scripts/*.js' ], coffee: [ @@ -88,6 +89,7 @@ paths.main = { ], styles: [ 'bower_components/basicContext/src/styles/main.scss', + 'bower_components/basicModal/src/styles/main.scss', '../src/styles/main.scss' ] } diff --git a/src/scripts/album.js b/src/scripts/album.js index cd4a161..3b28656 100644 --- a/src/scripts/album.js +++ b/src/scripts/album.js @@ -99,116 +99,138 @@ album.parse = function() { album.add = function() { - var title, - params, - buttons, - isNumber = function(n) { return !isNaN(parseFloat(n)) && isFinite(n) }; + var action; - buttons = [ - ['Create Album', function() { + action = function(data) { - title = $('.message input.text').val(); + var params, + isNumber = function(n) { return !isNaN(parseFloat(n)) && isFinite(n) }; - if (title.length===0) title = 'Untitled'; + basicModal.close(); - modal.close(); + if (data.title.length===0) data.title = 'Untitled'; - params = 'addAlbum&title=' + escape(encodeURI(title)); - lychee.api(params, function(data) { + params = 'addAlbum&title=' + escape(encodeURI(data.title)); + lychee.api(params, function(data) { - // Avoid first album to be true - if (data===true) data = 1; + // Avoid first album to be true + if (data===true) data = 1; - if (data!==false&&isNumber(data)) { - albums.refresh(); - lychee.goto(data); - } else { - lychee.error(null, params, data); - } + if (data!==false&&isNumber(data)) { + albums.refresh(); + lychee.goto(data); + } else { + lychee.error(null, params, data); + } - }); + }); - }], - ['Cancel', function() {}] - ]; + } - modal.show('New Album', "Enter a title for this album: ", buttons); + basicModal.show({ + body: "

Enter a title for the new album:

", + buttons: { + action: { + title: 'Create Album', + fn: action + }, + cancel: { + title: 'Cancel', + fn: basicModal.close + } + } + }); } album.delete = function(albumIDs) { - var params, - buttons, - albumTitle; + var action = {}, + cancel = {}, + msg = '', + albumTitle = ''; if (!albumIDs) return false; if (albumIDs instanceof Array===false) albumIDs = [albumIDs]; - buttons = [ - ['', function() { + action.fn = function() { - params = 'deleteAlbum&albumIDs=' + albumIDs; - lychee.api(params, function(data) { + var params; - if (visible.albums()) { + basicModal.close(); - albumIDs.forEach(function(id) { - albums.json.num--; - view.albums.content.delete(id); - delete albums.json.content[id]; - }); + params = 'deleteAlbum&albumIDs=' + albumIDs; + lychee.api(params, function(data) { - } else { + if (visible.albums()) { - albums.refresh(); - lychee.goto(''); + albumIDs.forEach(function(id) { + albums.json.num--; + view.albums.content.delete(id); + delete albums.json.content[id]; + }); - } + } else { - if (data!==true) lychee.error(null, params, data); + albums.refresh(); + lychee.goto(''); - }); + } - }], - ['', function() {}] - ]; + if (data!==true) lychee.error(null, params, data); + + }); + + } if (albumIDs.toString()==='0') { - buttons[0][0] = 'Clear Unsorted'; - buttons[1][0] = 'Keep Unsorted'; + action.title = 'Clear Unsorted'; + cancel.title = 'Keep Unsorted'; - modal.show('Clear Unsorted', "Are you sure you want to delete all photos from 'Unsorted'?
This action can't be undone!", buttons); + msg = "

Are you sure you want to delete all photos from 'Unsorted'?
This action can't be undone!

"; } else if (albumIDs.length===1) { - buttons[0][0] = 'Delete Album and Photos'; - buttons[1][0] = 'Keep Album'; + action.title = 'Delete Album and Photos'; + cancel.title = 'Keep Album'; // Get title if (album.json) albumTitle = album.json.title; else if (albums.json) albumTitle = albums.json.content[albumIDs].title; - modal.show('Delete Album', "Are you sure you want to delete the album '" + albumTitle + "' and all of the photos it contains? This action can't be undone!", buttons); + msg = "

Are you sure you want to delete the album '" + albumTitle + "' and all of the photos it contains? This action can't be undone!

"; } else { - buttons[0][0] = 'Delete Albums and Photos'; - buttons[1][0] = 'Keep Albums'; + action.title = 'Delete Albums and Photos'; + cancel.title = 'Keep Albums'; - modal.show('Delete Albums', "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!", buttons); + 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!

"; } + basicModal.show({ + body: msg, + buttons: { + action: { + title: action.title, + fn: action.fn, + class: 'red' + }, + cancel: { + title: cancel.title, + fn: basicModal.close + } + } + }); + } album.setTitle = function(albumIDs) { var oldTitle = '', - newTitle, - params, - buttons; + input; if (!albumIDs) return false; if (albumIDs instanceof Array===false) albumIDs = [albumIDs]; @@ -224,50 +246,68 @@ album.setTitle = function(albumIDs) { } - buttons = [ - ['Set Title', function() { + action = function(data) { - // Get input - newTitle = $('.message input.text').val(); + var params, + newTitle; - // Remove html from input - newTitle = lychee.removeHTML(newTitle); + basicModal.close(); - // Set to Untitled when empty - newTitle = (newTitle==='') ? 'Untitled' : newTitle; + // Get input + newTitle = data.title; - if (visible.album()) { + // Remove html from input + newTitle = lychee.removeHTML(newTitle); - album.json.title = newTitle; - view.album.title(); + // Set to Untitled when empty + newTitle = (newTitle==='') ? 'Untitled' : newTitle; - if (albums.json) { - var id = albumIDs[0]; - albums.json.content[id].title = newTitle; - } + if (visible.album()) { - } else if (visible.albums()) { - - albumIDs.forEach(function(id) { - albums.json.content[id].title = newTitle; - view.albums.content.title(id); - }); + album.json.title = newTitle; + view.album.title(); + if (albums.json) { + var id = albumIDs[0]; + albums.json.content[id].title = newTitle; } - params = 'setAlbumTitle&albumIDs=' + albumIDs + '&title=' + escape(encodeURI(newTitle)); - lychee.api(params, function(data) { - - if (data!==true) lychee.error(null, params, data); + } else if (visible.albums()) { + albumIDs.forEach(function(id) { + albums.json.content[id].title = newTitle; + view.albums.content.title(id); }); - }], - ['Cancel', function() {}] - ]; + } - if (albumIDs.length===1) modal.show('Set Title', "Enter a new title for this album: ", buttons); - else modal.show('Set Titles', "Enter a title for all " + albumIDs.length + " selected album: ", buttons); + params = 'setAlbumTitle&albumIDs=' + albumIDs + '&title=' + escape(encodeURI(newTitle)); + lychee.api(params, function(data) { + + if (data!==true) lychee.error(null, params, data); + + }); + + } + + input = ""; + + if (albumIDs.length===1) msg = "

Enter a new title for this album: " + msg + "

"; + else msg = "

Enter a title for all " + albumIDs.length + " selected albums: " + msg +"

"; + + basicModal.show({ + body: msg, + buttons: { + action: { + title: 'Set title', + fn: action + }, + cancel: { + title: 'Cancel', + fn: basicModal.close + } + } + }); } diff --git a/src/scripts/init.js b/src/scripts/init.js index 64d09d8..611e752 100755 --- a/src/scripts/init.js +++ b/src/scripts/init.js @@ -71,8 +71,8 @@ $(document).ready(function() { Mousetrap .bind('left', function() { if (visible.photo()) $('#imageview a#previous').click() }) .bind('right', function() { if (visible.photo()) $('#imageview a#next').click() }) - .bind(['u', 'ctrl+u'], function() { $('#upload_files').click() }) - .bind(['s', 'ctrl+s', 'f', 'ctrl+f'], function(e) { + .bind('u', function() { $('#upload_files').click() }) + .bind(['s', 'f'], function(e) { if (visible.photo()) { header.dom('#button_star').click(); } else if (visible.albums()) { @@ -80,23 +80,23 @@ $(document).ready(function() { header.dom('#search').focus(); } }) - .bind(['r', 'ctrl+r'], function(e) { + .bind('r', function(e) { e.preventDefault(); if (visible.album()) album.setTitle(album.getID()); else if (visible.photo()) photo.setTitle([photo.getID()]); }) - .bind(['d', 'ctrl+d'], function(e) { + .bind('d', function(e) { e.preventDefault(); if (visible.photo()) photo.setDescription(photo.getID()); else if (visible.album()) album.setDescription(album.getID()); }) - .bind(['t', 'ctrl+t'], function(e) { + .bind('t', function(e) { if (visible.photo()) { e.preventDefault(); photo.editTags([photo.getID()]); } }) - .bind(['i', 'ctrl+i'], function() { + .bind('i', function() { if (visible.infobox()) view.infobox.hide(); else if (visible.multiselect()) return false; else if (visible.infoboxbutton()) view.infobox.show(); @@ -111,12 +111,12 @@ $(document).ready(function() { }); Mousetrap.bindGlobal('enter', function() { - if ($('.message .button.active').length) $('.message .button.active').addClass('pressed').click() + if (basicModal.visible()===true) basicModal.action(); }); Mousetrap.bindGlobal(['esc', 'command+up'], function(e) { e.preventDefault(); - if (visible.message()&&$('.message .close').length>0) modal.close(); + if (basicModal.visible()===true) basicModal.cancel(); else if (visible.contextMenu()) contextMenu.close(); else if (visible.infobox()) view.infobox.hide(); else if (visible.photo()) lychee.goto(album.getID()); diff --git a/src/scripts/lychee.js b/src/scripts/lychee.js index bbbbb0e..4349ccf 100644 --- a/src/scripts/lychee.js +++ b/src/scripts/lychee.js @@ -274,12 +274,12 @@ lychee.setMode = function(mode) { .off('drop'); Mousetrap - .unbind(['u', 'ctrl+u']) - .unbind(['s', 'ctrl+s']) - .unbind(['f', 'ctrl+f']) - .unbind(['r', 'ctrl+r']) - .unbind(['d', 'ctrl+d']) - .unbind(['t', 'ctrl+t']) + .unbind('u') + .unbind('s') + .unbind('f') + .unbind('r') + .unbind('d') + .unbind('t') .unbind(['command+backspace', 'ctrl+backspace']) .unbind(['command+a', 'ctrl+a']); @@ -290,7 +290,7 @@ lychee.setMode = function(mode) { } else if (mode==='view') { - Mousetrap.unbind('esc'); + Mousetrap.unbind(['esc', 'command+up']); $('#button_back, a#next, a#previous').remove(); $('.no_content').remove(); diff --git a/src/scripts/upload.js b/src/scripts/upload.js index fbed445..2f8b1e4 100755 --- a/src/scripts/upload.js +++ b/src/scripts/upload.js @@ -229,86 +229,36 @@ upload.start = { url: function() { var albumID = album.getID(), - params, - extension, - buttons, - link, - files = []; + action; if (albumID===false) albumID = 0; - buttons = [ - ['Import', function() { + action = function(data) { - link = $('.message input.text').val(); + var params, + extension, + files = []; - if (link&&link.length>3) { + basicModal.close(); - extension = link.split('.').pop(); - if (extension!=='jpeg'&&extension!=='jpg'&&extension!=='png'&&extension!=='gif'&&extension!=='webp') { - loadingBar.show('error', 'The file format of this link is not supported.'); - return false; - } + if (data.link&&data.link.length>3) { - files[0] = { - name: link, - supported: true - } - - upload.show('Importing URL', files, function() { - $('.upload_message .rows .row .status').html('Importing'); - }); - - params = 'importUrl&url=' + escape(encodeURI(link)) + '&albumID=' + albumID; - lychee.api(params, function(data) { - - upload.close(); - upload.notify('Import complete'); - - albums.refresh(); - - if (album.getID()===false) lychee.goto('0'); - else album.load(albumID); - - if (data!==true) lychee.error(null, params, data); - - }); - - } else loadingBar.show('error', 'Link to short or too long. Please try another one!'); - - }], - ['Cancel', function() {}] - ]; - - modal.show('Import from Link', "Please enter the direct link to a photo to import it: ", buttons); - - }, - - server: function() { - - var albumID = album.getID(), - params, - buttons, - files = [], - path; - - if (albumID===false) albumID = 0; - - buttons = [ - ['Import', function() { - - path = $('.message input.text').val(); + extension = data.link.split('.').pop(); + if (extension!=='jpeg'&&extension!=='jpg'&&extension!=='png'&&extension!=='gif'&&extension!=='webp') { + loadingBar.show('error', 'The file format of this link is not supported.'); + return false; + } files[0] = { - name: path, + name: data.link, supported: true - }; + } - upload.show('Importing from server', files, function() { + upload.show('Importing URL', files, function() { $('.upload_message .rows .row .status').html('Importing'); }); - params = 'importServer&albumID=' + albumID + '&path=' + escape(encodeURI(path)); + params = 'importUrl&url=' + escape(encodeURI(data.link)) + '&albumID=' + albumID; lychee.api(params, function(data) { upload.close(); @@ -316,24 +266,90 @@ upload.start = { albums.refresh(); - if (data==='Notice: Import only contains albums!') { - if (visible.albums()) lychee.load(); - else lychee.goto(''); - } - else if (album.getID()===false) lychee.goto('0'); + if (album.getID()===false) lychee.goto('0'); else album.load(albumID); - if (data==='Notice: Import only contains albums!') return true; - else if (data==='Warning: Folder empty!') lychee.error('Folder empty. No photos imported!', params, data); - else if (data!==true) lychee.error(null, params, data); + if (data!==true) lychee.error(null, params, data); }); - }], - ['Cancel', function() {}] - ]; + } else loadingBar.show('error', 'Link to short or too long. Please try another one!'); - modal.show('Import from Server', "This action will import all photos, folders and sub-folders which are located in the following directory. The original files will be deleted after the import when possible. ", buttons); + } + + basicModal.show({ + body: "

Please enter the direct link to a photo to import it:

", + buttons: { + action: { + title: 'Import', + fn: action + }, + cancel: { + title: 'Cancel', + fn: basicModal.close + } + } + }); + + }, + + server: function() { + + var albumID = album.getID(), + action; + + if (albumID===false) albumID = 0; + + action = function(data) { + + var params, + files = []; + + files[0] = { + name: data.path, + supported: true + }; + + upload.show('Importing from server', files, function() { + $('.upload_message .rows .row .status').html('Importing'); + }); + + params = 'importServer&albumID=' + albumID + '&path=' + escape(encodeURI(data.path)); + lychee.api(params, function(data) { + + upload.close(); + upload.notify('Import complete'); + + albums.refresh(); + + if (data==='Notice: Import only contains albums!') { + if (visible.albums()) lychee.load(); + else lychee.goto(''); + } + else if (album.getID()===false) lychee.goto('0'); + else album.load(albumID); + + if (data==='Notice: Import only contains albums!') return true; + else if (data==='Warning: Folder empty!') lychee.error('Folder empty. No photos imported!', params, data); + else if (data!==true) lychee.error(null, params, data); + + }); + + } + + basicModal.show({ + body: "

This action will import all photos, folders and sub-folders which are located in the following directory. The original files will be deleted after the import when possible.

", + buttons: { + action: { + title: 'Import', + fn: action + }, + cancel: { + title: 'Cancel', + fn: basicModal.close + } + } + }); }, diff --git a/src/styles/_content.scss b/src/styles/_content.scss index 185717b..b33ab7d 100644 --- a/src/styles/_content.scss +++ b/src/styles/_content.scss @@ -136,7 +136,7 @@ .iconic { fill: #fff; width: 21px; - filter: drop-shadow( 0 -1px 0 black(.1) ); + filter: drop-shadow($shadowLight); } } @@ -148,7 +148,7 @@ width: 100%; opacity: 0; border-top: 1px solid white(.02); - box-shadow: 0 -1px 0 0 black(.2); + box-shadow: $shadow; &:first-child { margin-top: 10px; @@ -162,7 +162,7 @@ color: white(.6); font-size: 14px; font-weight: bold; - text-shadow: 0 -1px 0 black(.1); + text-shadow: $shadow; } } diff --git a/src/styles/_contextmenu.scss b/src/styles/_contextmenu.scss index ce1e003..3c1d705 100644 --- a/src/styles/_contextmenu.scss +++ b/src/styles/_contextmenu.scss @@ -15,9 +15,9 @@ /* Item ------------------------------------------------*/ tr { margin-bottom: 2px; - color: #eee; + color: white(.9); font-size: 14px; - text-shadow: 0 -1px 0px black(.1); + text-shadow: $shadowLight; &.separator { background: black(.2); @@ -28,7 +28,7 @@ tr td { padding: 7px 25px 6px 12px; min-width: auto; - color: #fff; + color: white(1); border-radius: 0; transition: none; diff --git a/src/styles/_etc.scss b/src/styles/_etc.scss index fffa722..7b0fbb9 100644 --- a/src/styles/_etc.scss +++ b/src/styles/_etc.scss @@ -12,9 +12,13 @@ } /* Vars ------------------------------------------------*/ +// Properties +$shadowLight: 0 -1px 0 black(.1); +$shadow: 0 -1px 0 black(.2); + // Colors $colorBlue: #2293EC; -$colorRed: #d02a32; +$colorRed: #d92c34; // Animations $timing: cubic-bezier(.51, .92, .24, 1); diff --git a/src/styles/_header.scss b/src/styles/_header.scss index bd15322..7c0508f 100644 --- a/src/styles/_header.scss +++ b/src/styles/_header.scss @@ -42,7 +42,7 @@ header { font-size: 16px; font-weight: bold; text-align: center; - text-shadow: 0 -1px 0 black(.2); + text-shadow: $shadow; z-index: 1; .iconic { @@ -50,7 +50,7 @@ header { margin: 0 0 0 10px; width: 10px; fill: white(.5); - filter: drop-shadow(0 -1px 0 black(.2)); + filter: drop-shadow($shadow); transition: fill .2s ease-out; } @@ -84,7 +84,7 @@ header { .iconic { fill: white(.5); - filter: drop-shadow(0 -1px 0 black(.2)); + filter: drop-shadow($shadow); transition: fill .2s ease-out; } @@ -149,7 +149,7 @@ header { margin: 13px 9px; color: #888; font-size: 13px; - text-shadow: 0 -1px 0 black(.2); + text-shadow: $shadow; border-radius: 100px; display: none; cursor: pointer; diff --git a/src/styles/_infobox.scss b/src/styles/_infobox.scss index b3d8f0f..ece2eb7 100644 --- a/src/styles/_infobox.scss +++ b/src/styles/_infobox.scss @@ -43,7 +43,7 @@ .iconic { fill: white(.5); - filter: drop-shadow(0 -1px 0 black(.2)); + filter: drop-shadow($shadow); transition: fill .2s ease-out; } @@ -73,7 +73,7 @@ font-size: 16px; font-weight: bold; text-align: center; - text-shadow: 0 -1px 0 black(.2); + text-shadow: $shadow; } .header .close { @@ -84,7 +84,7 @@ .iconic { fill: #888; - filter: drop-shadow(0 -1px 0 black(.2)); + filter: drop-shadow($shadow); transition: fill .2s ease-out; } @@ -97,7 +97,7 @@ padding: 12px 0 8px; width: 100%; border-top: 1px solid white(.02); - box-shadow: 0 -1px 0 0 black(.2); + box-shadow: $shadow; h1 { margin: 0 0 0 20px; diff --git a/src/styles/_message.scss b/src/styles/_message.scss index a5b0695..26076d7 100644 --- a/src/styles/_message.scss +++ b/src/styles/_message.scss @@ -2,6 +2,80 @@ * @copyright 2014 by Tobias Reich */ +.basicModalContainer { + background-color: black(.85); +} + +.basicModal { + + background: linear-gradient(to bottom, #444, #333); + + border: 1px solid black(.7); + border-bottom: 1px solid black(.8); + box-shadow: 0 1px 4px black(.2), inset 0 1px 0 white(.05); + + p { + display: block; + padding: 42px 30px 40px; + color: white(.9); + font-size: 14px; + text-align: left; + text-shadow: $shadow; + line-height: 20px; + + b { + font-weight: bold; + color: white(1); + } + + a { + color: white(.9); + text-decoration: none; + border-bottom: 1px dashed #888; + } + } + + .basicModal__button { + padding: 13px 0 15px; + background: black(.02); + color: white(.5); + text-shadow: $shadow; + border-top: 1px solid black(.2); + box-shadow: inset 0 1px 0 white(.02); + + &:hover { background: white(.02); } + + &:active, + &--active { background: black(.1); } + + &#basicModal__action { + color: $colorBlue; + box-shadow: inset 0 1px 0 white(.02), inset 1px 0 0 black(.2); + } + + &#basicModal__action.red { color: $colorRed; } + + } + + /* Input ------------------------------------------------*/ + input.text { + width: calc(100% - 4px); + padding: 9px 2px; + margin: 10px 0; + background-color: transparent; + color: #fff; + text-shadow: $shadow; + border: none; + border-bottom: 1px solid black(.4); + border-radius: 0; + box-shadow: 0 1px 0 white(.08); + outline: none; + + &:focus { border-bottom-color: $colorBlue; } + } + +} + .message_overlay { position: fixed; width: 100%; @@ -112,7 +186,7 @@ margin-top: 10px; background-color: transparent; color: #fff; - text-shadow: 0 -1px 0 black(.3); + text-shadow: $shadow; border: none; box-shadow: 0 1px 0 white(.1); border-bottom: 1px solid #222;