diff --git a/PhotoGroove/app.js b/PhotoGroove/app.js index 71960a4..438d3f2 100644 --- a/PhotoGroove/app.js +++ b/PhotoGroove/app.js @@ -4545,90 +4545,7 @@ function _Http_track(router, xhr, tracker) size: event.lengthComputable ? $elm$core$Maybe$Just(event.total) : $elm$core$Maybe$Nothing })))); }); -} - - -var _Bitwise_and = F2(function(a, b) -{ - return a & b; -}); - -var _Bitwise_or = F2(function(a, b) -{ - return a | b; -}); - -var _Bitwise_xor = F2(function(a, b) -{ - return a ^ b; -}); - -function _Bitwise_complement(a) -{ - return ~a; -}; - -var _Bitwise_shiftLeftBy = F2(function(offset, a) -{ - return a << offset; -}); - -var _Bitwise_shiftRightBy = F2(function(offset, a) -{ - return a >> offset; -}); - -var _Bitwise_shiftRightZfBy = F2(function(offset, a) -{ - return a >>> offset; -}); - - - -function _Time_now(millisToPosix) -{ - return _Scheduler_binding(function(callback) - { - callback(_Scheduler_succeed(millisToPosix(Date.now()))); - }); -} - -var _Time_setInterval = F2(function(interval, task) -{ - return _Scheduler_binding(function(callback) - { - var id = setInterval(function() { _Scheduler_rawSpawn(task); }, interval); - return function() { clearInterval(id); }; - }); -}); - -function _Time_here() -{ - return _Scheduler_binding(function(callback) - { - callback(_Scheduler_succeed( - A2($elm$time$Time$customZone, -(new Date().getTimezoneOffset()), _List_Nil) - )); - }); -} - - -function _Time_getZoneName() -{ - return _Scheduler_binding(function(callback) - { - try - { - var name = $elm$time$Time$Name(Intl.DateTimeFormat().resolvedOptions().timeZone); - } - catch (e) - { - var name = $elm$time$Time$Offset(new Date().getTimezoneOffset()); - } - callback(_Scheduler_succeed(name)); - }); -} -var $elm$core$Basics$EQ = {$: 'EQ'}; +}var $elm$core$Basics$EQ = {$: 'EQ'}; var $elm$core$Basics$GT = {$: 'GT'}; var $elm$core$Basics$LT = {$: 'LT'}; var $elm$core$List$cons = _List_cons; @@ -5417,10 +5334,8 @@ var $elm$core$Task$perform = F2( A2($elm$core$Task$map, toMessage, task))); }); var $elm$browser$Browser$element = _Browser_element; -var $elm$json$Json$Decode$float = _Json_decodeFloat; -var $elm$core$String$fromFloat = _String_fromNumber; -var $author$project$PhotoGroove$GotPhotos = function (a) { - return {$: 'GotPhotos', a: a}; +var $author$project$PhotoFolders$GotInitialModel = function (a) { + return {$: 'GotInitialModel', a: a}; }; var $elm$json$Json$Decode$decodeString = _Json_runOnString; var $elm$http$Http$BadStatus_ = F2( @@ -6209,71 +6124,63 @@ var $elm$http$Http$get = function (r) { return $elm$http$Http$request( {body: $elm$http$Http$emptyBody, expect: r.expect, headers: _List_Nil, method: 'GET', timeout: $elm$core$Maybe$Nothing, tracker: $elm$core$Maybe$Nothing, url: r.url}); }; +var $author$project$PhotoFolders$Folder = function (a) { + return {$: 'Folder', a: a}; +}; +var $author$project$PhotoFolders$initialModel = { + photos: $elm$core$Dict$empty, + root: $author$project$PhotoFolders$Folder( + {expanded: true, name: 'Loading...', photoUrls: _List_Nil, subfolders: _List_Nil}), + selectedPhotoUrl: $elm$core$Maybe$Nothing +}; +var $author$project$PhotoFolders$folderFromJson = F3( + function (name, photos, subfolders) { + return $author$project$PhotoFolders$Folder( + { + expanded: true, + name: name, + photoUrls: $elm$core$Dict$keys(photos), + subfolders: subfolders + }); + }); +var $elm$json$Json$Decode$andThen = _Json_andThen; +var $elm$json$Json$Decode$lazy = function (thunk) { + return A2( + $elm$json$Json$Decode$andThen, + thunk, + $elm$json$Json$Decode$succeed(_Utils_Tuple0)); +}; var $elm$json$Json$Decode$list = _Json_decodeList; -var $author$project$PhotoGroove$Photo = F3( - function (url, size, title) { - return {size: size, title: title, url: url}; +var $author$project$PhotoFolders$finishPhoto = function (_v0) { + var url = _v0.a; + var json = _v0.b; + return _Utils_Tuple2( + url, + {relatedUrls: json.relatedUrls, size: json.size, title: json.title, url: url}); +}; +var $elm$core$Dict$fromList = function (assocs) { + return A3( + $elm$core$List$foldl, + F2( + function (_v0, dict) { + var key = _v0.a; + var value = _v0.b; + return A3($elm$core$Dict$insert, key, value, dict); + }), + $elm$core$Dict$empty, + assocs); +}; +var $author$project$PhotoFolders$fromPairs = function (pairs) { + return $elm$core$Dict$fromList( + A2($elm$core$List$map, $author$project$PhotoFolders$finishPhoto, pairs)); +}; +var $author$project$PhotoFolders$JsonPhoto = F3( + function (title, size, relatedUrls) { + return {relatedUrls: relatedUrls, size: size, title: title}; }); var $elm$json$Json$Decode$int = _Json_decodeInt; var $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$custom = $elm$json$Json$Decode$map2($elm$core$Basics$apR); -var $elm$json$Json$Decode$andThen = _Json_andThen; var $elm$json$Json$Decode$field = _Json_decodeField; -var $elm$json$Json$Decode$at = F2( - function (fields, decoder) { - return A3($elm$core$List$foldr, $elm$json$Json$Decode$field, decoder, fields); - }); -var $elm$json$Json$Decode$decodeValue = _Json_run; -var $elm$json$Json$Decode$null = _Json_decodeNull; -var $elm$json$Json$Decode$oneOf = _Json_oneOf; -var $elm$json$Json$Decode$value = _Json_decodeValue; -var $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$optionalDecoder = F3( - function (path, valDecoder, fallback) { - var nullOr = function (decoder) { - return $elm$json$Json$Decode$oneOf( - _List_fromArray( - [ - decoder, - $elm$json$Json$Decode$null(fallback) - ])); - }; - var handleResult = function (input) { - var _v0 = A2( - $elm$json$Json$Decode$decodeValue, - A2($elm$json$Json$Decode$at, path, $elm$json$Json$Decode$value), - input); - if (_v0.$ === 'Ok') { - var rawValue = _v0.a; - var _v1 = A2( - $elm$json$Json$Decode$decodeValue, - nullOr(valDecoder), - rawValue); - if (_v1.$ === 'Ok') { - var finalResult = _v1.a; - return $elm$json$Json$Decode$succeed(finalResult); - } else { - return A2( - $elm$json$Json$Decode$at, - path, - nullOr(valDecoder)); - } - } else { - return $elm$json$Json$Decode$succeed(fallback); - } - }; - return A2($elm$json$Json$Decode$andThen, handleResult, $elm$json$Json$Decode$value); - }); -var $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$optional = F4( - function (key, valDecoder, fallback, decoder) { - return A2( - $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$custom, - A3( - $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$optionalDecoder, - _List_fromArray( - [key]), - valDecoder, - fallback), - decoder); - }); var $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required = F3( function (key, valDecoder, decoder) { return A2( @@ -6282,453 +6189,190 @@ var $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required = F3( decoder); }); var $elm$json$Json$Decode$string = _Json_decodeString; -var $author$project$PhotoGroove$photoDecoder = A4( - $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$optional, - 'title', - $elm$json$Json$Decode$string, - '(untitled)', +var $author$project$PhotoFolders$jsonPhotoDecoder = A3( + $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required, + 'related_photos', + $elm$json$Json$Decode$list($elm$json$Json$Decode$string), A3( $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required, 'size', $elm$json$Json$Decode$int, A3( $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required, - 'url', + 'title', $elm$json$Json$Decode$string, - $elm$json$Json$Decode$succeed($author$project$PhotoGroove$Photo)))); -var $author$project$PhotoGroove$initialCmd = $elm$http$Http$get( - { - expect: A2( - $elm$http$Http$expectJson, - $author$project$PhotoGroove$GotPhotos, - $elm$json$Json$Decode$list($author$project$PhotoGroove$photoDecoder)), - url: 'http://elm-in-action.com/photos/list.json' - }); -var $author$project$PhotoGroove$Loading = {$: 'Loading'}; -var $author$project$PhotoGroove$Medium = {$: 'Medium'}; -var $author$project$PhotoGroove$initialModel = {activity: '', chosenSize: $author$project$PhotoGroove$Medium, hue: 5, noise: 5, ripple: 5, status: $author$project$PhotoGroove$Loading}; -var $author$project$PhotoGroove$init = function (flags) { - var activity = 'Initializing Pasta v' + $elm$core$String$fromFloat(flags); - return _Utils_Tuple2( - _Utils_update( - $author$project$PhotoGroove$initialModel, - {activity: activity}), - $author$project$PhotoGroove$initialCmd); -}; -var $author$project$PhotoGroove$GotActivity = function (a) { - return {$: 'GotActivity', a: a}; -}; -var $author$project$PhotoGroove$activityChanges = _Platform_incomingPort('activityChanges', $elm$json$Json$Decode$string); -var $author$project$PhotoGroove$subscriptions = function (model) { - return $author$project$PhotoGroove$activityChanges($author$project$PhotoGroove$GotActivity); -}; -var $author$project$PhotoGroove$Errored = function (a) { - return {$: 'Errored', a: a}; -}; -var $author$project$PhotoGroove$GotRandomPhoto = function (a) { - return {$: 'GotRandomPhoto', a: a}; -}; -var $author$project$PhotoGroove$Loaded = F2( - function (a, b) { - return {$: 'Loaded', a: a, b: b}; - }); -var $elm$core$Platform$Cmd$batch = _Platform_batch; -var $elm$core$Platform$Cmd$none = $elm$core$Platform$Cmd$batch(_List_Nil); -var $elm$json$Json$Encode$float = _Json_wrap; -var $elm$json$Json$Encode$list = F2( - function (func, entries) { - return _Json_wrap( - A3( - $elm$core$List$foldl, - _Json_addEntry(func), - _Json_emptyArray(_Utils_Tuple0), - entries)); - }); -var $elm$json$Json$Encode$object = function (pairs) { - return _Json_wrap( + $elm$json$Json$Decode$succeed($author$project$PhotoFolders$JsonPhoto)))); +var $elm$json$Json$Decode$keyValuePairs = _Json_decodeKeyValuePairs; +var $author$project$PhotoFolders$photosDecoder = A2( + $elm$json$Json$Decode$map, + $author$project$PhotoFolders$fromPairs, + $elm$json$Json$Decode$keyValuePairs($author$project$PhotoFolders$jsonPhotoDecoder)); +function $author$project$PhotoFolders$cyclic$folderDecoder() { + return A3( + $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required, + 'subfolders', + $elm$json$Json$Decode$lazy( + function (_v0) { + return $elm$json$Json$Decode$list( + $author$project$PhotoFolders$cyclic$folderDecoder()); + }), A3( - $elm$core$List$foldl, - F2( - function (_v0, obj) { - var k = _v0.a; - var v = _v0.b; - return A3(_Json_addField, k, v, obj); - }), - _Json_emptyObject(_Utils_Tuple0), - pairs)); -}; -var $elm$json$Json$Encode$string = _Json_wrap; -var $author$project$PhotoGroove$setFilters = _Platform_outgoingPort( - 'setFilters', - function ($) { - return $elm$json$Json$Encode$object( - _List_fromArray( - [ - _Utils_Tuple2( - 'filters', - $elm$json$Json$Encode$list( - function ($) { - return $elm$json$Json$Encode$object( - _List_fromArray( - [ - _Utils_Tuple2( - 'amount', - $elm$json$Json$Encode$float($.amount)), - _Utils_Tuple2( - 'name', - $elm$json$Json$Encode$string($.name)) - ])); - })($.filters)), - _Utils_Tuple2( - 'url', - $elm$json$Json$Encode$string($.url)) - ])); - }); -var $author$project$PhotoGroove$urlPrefix = 'https://elm-in-action.com/'; -var $author$project$PhotoGroove$applyFilters = function (model) { - var _v0 = model.status; - switch (_v0.$) { - case 'Loaded': - var photos = _v0.a; - var selectedUrl = _v0.b; - var url = $author$project$PhotoGroove$urlPrefix + ('large/' + selectedUrl); - var filters = _List_fromArray( - [ - {amount: model.hue / 11, name: 'Hue'}, - {amount: model.ripple / 11, name: 'Ripple'}, - {amount: model.noise / 11, name: 'Noise'} - ]); - return _Utils_Tuple2( - model, - $author$project$PhotoGroove$setFilters( - {filters: filters, url: url})); - case 'Loading': - return _Utils_Tuple2(model, $elm$core$Platform$Cmd$none); - default: - var errorMessage = _v0.a; - return _Utils_Tuple2(model, $elm$core$Platform$Cmd$none); - } -}; -var $elm$random$Random$Generate = function (a) { - return {$: 'Generate', a: a}; -}; -var $elm$random$Random$Seed = F2( - function (a, b) { - return {$: 'Seed', a: a, b: b}; - }); -var $elm$core$Bitwise$shiftRightZfBy = _Bitwise_shiftRightZfBy; -var $elm$random$Random$next = function (_v0) { - var state0 = _v0.a; - var incr = _v0.b; - return A2($elm$random$Random$Seed, ((state0 * 1664525) + incr) >>> 0, incr); -}; -var $elm$random$Random$initialSeed = function (x) { - var _v0 = $elm$random$Random$next( - A2($elm$random$Random$Seed, 0, 1013904223)); - var state1 = _v0.a; - var incr = _v0.b; - var state2 = (state1 + x) >>> 0; - return $elm$random$Random$next( - A2($elm$random$Random$Seed, state2, incr)); -}; -var $elm$time$Time$Name = function (a) { - return {$: 'Name', a: a}; -}; -var $elm$time$Time$Offset = function (a) { - return {$: 'Offset', a: a}; -}; -var $elm$time$Time$Zone = F2( - function (a, b) { - return {$: 'Zone', a: a, b: b}; - }); -var $elm$time$Time$customZone = $elm$time$Time$Zone; -var $elm$time$Time$Posix = function (a) { - return {$: 'Posix', a: a}; -}; -var $elm$time$Time$millisToPosix = $elm$time$Time$Posix; -var $elm$time$Time$now = _Time_now($elm$time$Time$millisToPosix); -var $elm$time$Time$posixToMillis = function (_v0) { - var millis = _v0.a; - return millis; -}; -var $elm$random$Random$init = A2( - $elm$core$Task$andThen, - function (time) { - return $elm$core$Task$succeed( - $elm$random$Random$initialSeed( - $elm$time$Time$posixToMillis(time))); - }, - $elm$time$Time$now); -var $elm$random$Random$step = F2( - function (_v0, seed) { - var generator = _v0.a; - return generator(seed); - }); -var $elm$random$Random$onEffects = F3( - function (router, commands, seed) { - if (!commands.b) { - return $elm$core$Task$succeed(seed); - } else { - var generator = commands.a.a; - var rest = commands.b; - var _v1 = A2($elm$random$Random$step, generator, seed); - var value = _v1.a; - var newSeed = _v1.b; - return A2( - $elm$core$Task$andThen, - function (_v2) { - return A3($elm$random$Random$onEffects, router, rest, newSeed); - }, - A2($elm$core$Platform$sendToApp, router, value)); - } - }); -var $elm$random$Random$onSelfMsg = F3( - function (_v0, _v1, seed) { - return $elm$core$Task$succeed(seed); - }); -var $elm$random$Random$Generator = function (a) { - return {$: 'Generator', a: a}; -}; -var $elm$random$Random$map = F2( - function (func, _v0) { - var genA = _v0.a; - return $elm$random$Random$Generator( - function (seed0) { - var _v1 = genA(seed0); - var a = _v1.a; - var seed1 = _v1.b; - return _Utils_Tuple2( - func(a), - seed1); - }); - }); -var $elm$random$Random$cmdMap = F2( - function (func, _v0) { - var generator = _v0.a; - return $elm$random$Random$Generate( - A2($elm$random$Random$map, func, generator)); - }); -_Platform_effectManagers['Random'] = _Platform_createManager($elm$random$Random$init, $elm$random$Random$onEffects, $elm$random$Random$onSelfMsg, $elm$random$Random$cmdMap); -var $elm$random$Random$command = _Platform_leaf('Random'); -var $elm$random$Random$generate = F2( - function (tagger, generator) { - return $elm$random$Random$command( - $elm$random$Random$Generate( - A2($elm$random$Random$map, tagger, generator))); - }); -var $elm$core$List$head = function (list) { - if (list.b) { - var x = list.a; - var xs = list.b; - return $elm$core$Maybe$Just(x); - } else { - return $elm$core$Maybe$Nothing; - } -}; -var $author$project$PhotoGroove$selectUrl = F2( - function (url, status) { - switch (status.$) { - case 'Loaded': - var photos = status.a; - return A2($author$project$PhotoGroove$Loaded, photos, url); - case 'Loading': - return status; - default: - var errorMessage = status.a; - return status; - } - }); -var $elm$random$Random$addOne = function (value) { - return _Utils_Tuple2(1, value); -}; -var $elm$core$Basics$negate = function (n) { - return -n; -}; -var $elm$core$Basics$abs = function (n) { - return (n < 0) ? (-n) : n; -}; -var $elm$core$Bitwise$and = _Bitwise_and; -var $elm$core$Bitwise$xor = _Bitwise_xor; -var $elm$random$Random$peel = function (_v0) { - var state = _v0.a; - var word = (state ^ (state >>> ((state >>> 28) + 4))) * 277803737; - return ((word >>> 22) ^ word) >>> 0; -}; -var $elm$random$Random$float = F2( - function (a, b) { - return $elm$random$Random$Generator( - function (seed0) { - var seed1 = $elm$random$Random$next(seed0); - var range = $elm$core$Basics$abs(b - a); - var n1 = $elm$random$Random$peel(seed1); - var n0 = $elm$random$Random$peel(seed0); - var lo = (134217727 & n1) * 1.0; - var hi = (67108863 & n0) * 1.0; - var val = ((hi * 134217728.0) + lo) / 9007199254740992.0; - var scaled = (val * range) + a; - return _Utils_Tuple2( - scaled, - $elm$random$Random$next(seed1)); - }); - }); -var $elm$random$Random$getByWeight = F3( - function (_v0, others, countdown) { - getByWeight: + $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required, + 'photos', + $author$project$PhotoFolders$photosDecoder, + A3( + $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required, + 'name', + $elm$json$Json$Decode$string, + $elm$json$Json$Decode$succeed($author$project$PhotoFolders$folderFromJson)))); +} +try { + var $author$project$PhotoFolders$folderDecoder = $author$project$PhotoFolders$cyclic$folderDecoder(); + $author$project$PhotoFolders$cyclic$folderDecoder = function () { + return $author$project$PhotoFolders$folderDecoder; + }; +} catch ($) { + throw 'Some top-level definitions from `PhotoFolders` are causing infinite recursion:\n\n ┌─────┐\n │ folderDecoder\n └─────┘\n\nThese errors are very tricky, so read https://elm-lang.org/0.19.1/bad-recursion to learn how to fix it!';} +var $elm$core$Dict$foldl = F3( + function (func, acc, dict) { + foldl: while (true) { - var weight = _v0.a; - var value = _v0.b; - if (!others.b) { - return value; + if (dict.$ === 'RBEmpty_elm_builtin') { + return acc; } else { - var second = others.a; - var otherOthers = others.b; - if (_Utils_cmp( - countdown, - $elm$core$Basics$abs(weight)) < 1) { - return value; - } else { - var $temp$_v0 = second, - $temp$others = otherOthers, - $temp$countdown = countdown - $elm$core$Basics$abs(weight); - _v0 = $temp$_v0; - others = $temp$others; - countdown = $temp$countdown; - continue getByWeight; - } + var key = dict.b; + var value = dict.c; + var left = dict.d; + var right = dict.e; + var $temp$func = func, + $temp$acc = A3( + func, + key, + value, + A3($elm$core$Dict$foldl, func, acc, left)), + $temp$dict = right; + func = $temp$func; + acc = $temp$acc; + dict = $temp$dict; + continue foldl; } } }); -var $elm$core$List$sum = function (numbers) { - return A3($elm$core$List$foldl, $elm$core$Basics$add, 0, numbers); +var $elm$core$Dict$union = F2( + function (t1, t2) { + return A3($elm$core$Dict$foldl, $elm$core$Dict$insert, t2, t1); + }); +var $author$project$PhotoFolders$modelPhotosFromJson = F2( + function (folderPhotos, subfolderPhotos) { + return A3($elm$core$List$foldl, $elm$core$Dict$union, folderPhotos, subfolderPhotos); + }); +function $author$project$PhotoFolders$cyclic$modelPhotosDecoder() { + return A3( + $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required, + 'subfolders', + $elm$json$Json$Decode$lazy( + function (_v0) { + return $elm$json$Json$Decode$list( + $author$project$PhotoFolders$cyclic$modelPhotosDecoder()); + }), + A3( + $NoRedInk$elm_json_decode_pipeline$Json$Decode$Pipeline$required, + 'photos', + $author$project$PhotoFolders$photosDecoder, + $elm$json$Json$Decode$succeed($author$project$PhotoFolders$modelPhotosFromJson))); +} +try { + var $author$project$PhotoFolders$modelPhotosDecoder = $author$project$PhotoFolders$cyclic$modelPhotosDecoder(); + $author$project$PhotoFolders$cyclic$modelPhotosDecoder = function () { + return $author$project$PhotoFolders$modelPhotosDecoder; + }; +} catch ($) { + throw 'Some top-level definitions from `PhotoFolders` are causing infinite recursion:\n\n ┌─────┐\n │ modelPhotosDecoder\n └─────┘\n\nThese errors are very tricky, so read https://elm-lang.org/0.19.1/bad-recursion to learn how to fix it!';} +var $author$project$PhotoFolders$modelDecoder = A3( + $elm$json$Json$Decode$map2, + F2( + function (photos, root) { + return {photos: photos, root: root, selectedPhotoUrl: $elm$core$Maybe$Nothing}; + }), + $author$project$PhotoFolders$modelPhotosDecoder, + $author$project$PhotoFolders$folderDecoder); +var $author$project$PhotoFolders$init = function (_v0) { + return _Utils_Tuple2( + $author$project$PhotoFolders$initialModel, + $elm$http$Http$get( + { + expect: A2($elm$http$Http$expectJson, $author$project$PhotoFolders$GotInitialModel, $author$project$PhotoFolders$modelDecoder), + url: 'http://elm-in-action.com/folders/list' + })); }; -var $elm$random$Random$weighted = F2( - function (first, others) { - var normalize = function (_v0) { - var weight = _v0.a; - return $elm$core$Basics$abs(weight); - }; - var total = normalize(first) + $elm$core$List$sum( - A2($elm$core$List$map, normalize, others)); - return A2( - $elm$random$Random$map, - A2($elm$random$Random$getByWeight, first, others), - A2($elm$random$Random$float, 0, total)); +var $elm$core$Platform$Sub$batch = _Platform_batch; +var $elm$core$Platform$Sub$none = $elm$core$Platform$Sub$batch(_List_Nil); +var $elm$core$Platform$Cmd$batch = _Platform_batch; +var $elm$core$Platform$Cmd$none = $elm$core$Platform$Cmd$batch(_List_Nil); +var $elm$core$Basics$not = _Basics_not; +var $author$project$PhotoFolders$toggleExpanded = F2( + function (path, _v0) { + var folder = _v0.a; + if (path.$ === 'End') { + return $author$project$PhotoFolders$Folder( + _Utils_update( + folder, + {expanded: !folder.expanded})); + } else { + var targetIndex = path.a; + var remainingPath = path.b; + var transform = F2( + function (currentIndex, currentSubfolder) { + return _Utils_eq(currentIndex, targetIndex) ? A2($author$project$PhotoFolders$toggleExpanded, remainingPath, currentSubfolder) : currentSubfolder; + }); + var subfolders = A2($elm$core$List$indexedMap, transform, folder.subfolders); + return $author$project$PhotoFolders$Folder( + _Utils_update( + folder, + {subfolders: subfolders})); + } }); -var $elm$random$Random$uniform = F2( - function (value, valueList) { - return A2( - $elm$random$Random$weighted, - $elm$random$Random$addOne(value), - A2($elm$core$List$map, $elm$random$Random$addOne, valueList)); - }); -var $author$project$PhotoGroove$update = F2( +var $author$project$PhotoFolders$update = F2( function (msg, model) { switch (msg.$) { - case 'GotActivity': - var activity = msg.a; + case 'ClickedFolder': + var path = msg.a; return _Utils_Tuple2( _Utils_update( model, - {activity: activity}), + { + root: A2($author$project$PhotoFolders$toggleExpanded, path, model.root) + }), $elm$core$Platform$Cmd$none); - case 'GotPhotos': + case 'ClickedPhoto': + var url = msg.a; + return _Utils_Tuple2( + _Utils_update( + model, + { + selectedPhotoUrl: $elm$core$Maybe$Just(url) + }), + $elm$core$Platform$Cmd$none); + default: if (msg.a.$ === 'Ok') { - var photos = msg.a.a; - if (photos.b) { - var first = photos.a; - var rest = photos.b; - return $author$project$PhotoGroove$applyFilters( - _Utils_update( - model, - { - status: function () { - var _v2 = $elm$core$List$head(photos); - if (_v2.$ === 'Just') { - var photo = _v2.a; - return A2($author$project$PhotoGroove$Loaded, photos, photo.url); - } else { - return A2($author$project$PhotoGroove$Loaded, _List_Nil, ''); - } - }() - })); - } else { - return _Utils_Tuple2( - _Utils_update( - model, - { - status: $author$project$PhotoGroove$Errored('0 photos found') - }), - $elm$core$Platform$Cmd$none); - } + var newModel = msg.a.a; + return _Utils_Tuple2(newModel, $elm$core$Platform$Cmd$none); } else { return _Utils_Tuple2(model, $elm$core$Platform$Cmd$none); } - case 'GotRandomPhoto': - var photo = msg.a; - return $author$project$PhotoGroove$applyFilters( - _Utils_update( - model, - { - status: A2($author$project$PhotoGroove$selectUrl, photo.url, model.status) - })); - case 'ClickedPhoto': - var url = msg.a; - return $author$project$PhotoGroove$applyFilters( - _Utils_update( - model, - { - status: A2($author$project$PhotoGroove$selectUrl, url, model.status) - })); - case 'SetSize': - var size = msg.a; - return _Utils_Tuple2( - _Utils_update( - model, - {chosenSize: size}), - $elm$core$Platform$Cmd$none); - case 'ClickedSurpriseMe': - var _v3 = model.status; - switch (_v3.$) { - case 'Loaded': - if (_v3.a.b) { - var _v4 = _v3.a; - var firstPhoto = _v4.a; - var otherPhotos = _v4.b; - return _Utils_Tuple2( - model, - A2( - $elm$random$Random$generate, - $author$project$PhotoGroove$GotRandomPhoto, - A2($elm$random$Random$uniform, firstPhoto, otherPhotos))); - } else { - return _Utils_Tuple2(model, $elm$core$Platform$Cmd$none); - } - case 'Loading': - return _Utils_Tuple2(model, $elm$core$Platform$Cmd$none); - default: - var errorMessage = _v3.a; - return _Utils_Tuple2(model, $elm$core$Platform$Cmd$none); - } - case 'SlidHue': - var hue = msg.a; - return $author$project$PhotoGroove$applyFilters( - _Utils_update( - model, - {hue: hue})); - case 'SlidRipple': - var ripple = msg.a; - return $author$project$PhotoGroove$applyFilters( - _Utils_update( - model, - {ripple: ripple})); - default: - var noise = msg.a; - return $author$project$PhotoGroove$applyFilters( - _Utils_update( - model, - {noise: noise})); } }); +var $author$project$PhotoFolders$End = {$: 'End'}; +var $elm$core$Maybe$andThen = F2( + function (callback, maybeValue) { + if (maybeValue.$ === 'Just') { + var value = maybeValue.a; + return callback(value); + } else { + return $elm$core$Maybe$Nothing; + } + }); +var $elm$json$Json$Encode$string = _Json_wrap; var $elm$html$Html$Attributes$stringProperty = F2( function (key, string) { return A2( @@ -6738,25 +6382,38 @@ var $elm$html$Html$Attributes$stringProperty = F2( }); var $elm$html$Html$Attributes$class = $elm$html$Html$Attributes$stringProperty('className'); var $elm$html$Html$div = _VirtualDom_node('div'); +var $elm$html$Html$h1 = _VirtualDom_node('h1'); var $elm$virtual_dom$VirtualDom$text = _VirtualDom_text; var $elm$html$Html$text = $elm$virtual_dom$VirtualDom$text; -var $author$project$PhotoGroove$ClickedSurpriseMe = {$: 'ClickedSurpriseMe'}; -var $author$project$PhotoGroove$Large = {$: 'Large'}; -var $author$project$PhotoGroove$SlidHue = function (a) { - return {$: 'SlidHue', a: a}; +var $author$project$PhotoFolders$ClickedFolder = function (a) { + return {$: 'ClickedFolder', a: a}; }; -var $author$project$PhotoGroove$SlidNoise = function (a) { - return {$: 'SlidNoise', a: a}; -}; -var $author$project$PhotoGroove$SlidRipple = function (a) { - return {$: 'SlidRipple', a: a}; -}; -var $author$project$PhotoGroove$Small = {$: 'Small'}; -var $elm$html$Html$button = _VirtualDom_node('button'); -var $elm$html$Html$canvas = _VirtualDom_node('canvas'); -var $elm$html$Html$h1 = _VirtualDom_node('h1'); -var $elm$html$Html$h3 = _VirtualDom_node('h3'); -var $elm$html$Html$Attributes$id = $elm$html$Html$Attributes$stringProperty('id'); +var $elm$core$List$append = F2( + function (xs, ys) { + if (!ys.b) { + return xs; + } else { + return A3($elm$core$List$foldr, $elm$core$List$cons, ys, xs); + } + }); +var $author$project$PhotoFolders$Subfolder = F2( + function (a, b) { + return {$: 'Subfolder', a: a, b: b}; + }); +var $author$project$PhotoFolders$appendIndex = F2( + function (index, path) { + if (path.$ === 'End') { + return A2($author$project$PhotoFolders$Subfolder, index, $author$project$PhotoFolders$End); + } else { + var subfolderIndex = path.a; + var remainingPath = path.b; + return A2( + $author$project$PhotoFolders$Subfolder, + subfolderIndex, + A2($author$project$PhotoFolders$appendIndex, index, remainingPath)); + } + }); +var $elm$html$Html$label = _VirtualDom_node('label'); var $elm$virtual_dom$VirtualDom$Normal = function (a) { return {$: 'Normal', a: a}; }; @@ -6774,280 +6431,203 @@ var $elm$html$Html$Events$onClick = function (msg) { 'click', $elm$json$Json$Decode$succeed(msg)); }; -var $author$project$PhotoGroove$sizeToString = function (size) { - switch (size.$) { - case 'Small': - return 'small'; - case 'Medium': - return 'medium'; - default: - return 'large'; - } +var $author$project$PhotoFolders$ClickedPhoto = function (a) { + return {$: 'ClickedPhoto', a: a}; }; -var $elm$json$Json$Encode$int = _Json_wrap; -var $elm$html$Html$label = _VirtualDom_node('label'); -var $elm$html$Html$Attributes$max = $elm$html$Html$Attributes$stringProperty('max'); -var $author$project$PhotoGroove$onSlide = function (toMsg) { +var $author$project$PhotoFolders$viewPhoto = function (url) { return A2( - $elm$html$Html$Events$on, - 'slide', - A2( - $elm$json$Json$Decode$map, - toMsg, - A2( - $elm$json$Json$Decode$at, - _List_fromArray( - ['detail', 'userSlidTo']), - $elm$json$Json$Decode$int))); + $elm$html$Html$div, + _List_fromArray( + [ + $elm$html$Html$Attributes$class('photo'), + $elm$html$Html$Events$onClick( + $author$project$PhotoFolders$ClickedPhoto(url)) + ]), + _List_fromArray( + [ + $elm$html$Html$text(url) + ])); }; -var $elm$virtual_dom$VirtualDom$property = F2( - function (key, value) { - return A2( - _VirtualDom_property, - _VirtualDom_noInnerHtmlOrFormAction(key), - _VirtualDom_noJavaScriptOrHtmlJson(value)); - }); -var $elm$html$Html$Attributes$property = $elm$virtual_dom$VirtualDom$property; -var $elm$virtual_dom$VirtualDom$node = function (tag) { - return _VirtualDom_node( - _VirtualDom_noScript(tag)); -}; -var $elm$html$Html$node = $elm$virtual_dom$VirtualDom$node; -var $author$project$PhotoGroove$rangeSlider = F2( - function (attributes, children) { - return A3($elm$html$Html$node, 'range-slider', attributes, children); - }); -var $author$project$PhotoGroove$viewFilter = F3( - function (toMsg, name, magnitude) { - return A2( - $elm$html$Html$div, +var $author$project$PhotoFolders$viewFolder = F2( + function (path, _v0) { + var folder = _v0.a; + var viewSubfolder = F2( + function (index, subfolder) { + return A2( + $author$project$PhotoFolders$viewFolder, + A2($author$project$PhotoFolders$appendIndex, index, path), + subfolder); + }); + var folderLabel = A2( + $elm$html$Html$label, _List_fromArray( [ - $elm$html$Html$Attributes$class('filter-slider') + $elm$html$Html$Events$onClick( + $author$project$PhotoFolders$ClickedFolder(path)) ]), _List_fromArray( [ - A2( - $elm$html$Html$label, - _List_Nil, - _List_fromArray( - [ - $elm$html$Html$text(name) - ])), - A2( - $author$project$PhotoGroove$rangeSlider, - _List_fromArray( - [ - $elm$html$Html$Attributes$max('11'), - A2( - $elm$html$Html$Attributes$property, - 'val', - $elm$json$Json$Encode$int(magnitude)), - $author$project$PhotoGroove$onSlide(toMsg) - ]), - _List_Nil), - A2( - $elm$html$Html$label, - _List_Nil, - _List_fromArray( - [ - $elm$html$Html$text( - $elm$core$String$fromInt(magnitude)) - ])) + $elm$html$Html$text(folder.name) ])); - }); -var $author$project$PhotoGroove$SetSize = function (a) { - return {$: 'SetSize', a: a}; -}; -var $elm$html$Html$input = _VirtualDom_node('input'); -var $elm$html$Html$Attributes$name = $elm$html$Html$Attributes$stringProperty('name'); -var $elm$html$Html$Attributes$type_ = $elm$html$Html$Attributes$stringProperty('type'); -var $author$project$PhotoGroove$viewSizeChooser = function (size) { - return A2( - $elm$html$Html$label, - _List_Nil, - _List_fromArray( - [ - A2( - $elm$html$Html$input, + if (folder.expanded) { + var contents = A2( + $elm$core$List$append, + A2($elm$core$List$indexedMap, viewSubfolder, folder.subfolders), + A2($elm$core$List$map, $author$project$PhotoFolders$viewPhoto, folder.photoUrls)); + return A2( + $elm$html$Html$div, _List_fromArray( [ - $elm$html$Html$Attributes$type_('radio'), - $elm$html$Html$Attributes$name('size'), - $elm$html$Html$Events$onClick( - $author$project$PhotoGroove$SetSize(size)) + $elm$html$Html$Attributes$class('folder expanded') ]), - _List_Nil), - $elm$html$Html$text( - $author$project$PhotoGroove$sizeToString(size)) - ])); -}; -var $author$project$PhotoGroove$ClickedPhoto = function (a) { - return {$: 'ClickedPhoto', a: a}; -}; -var $elm$core$List$filter = F2( - function (isGood, list) { - return A3( - $elm$core$List$foldr, - F2( - function (x, xs) { - return isGood(x) ? A2($elm$core$List$cons, x, xs) : xs; - }), - _List_Nil, - list); + _List_fromArray( + [ + folderLabel, + A2( + $elm$html$Html$div, + _List_fromArray( + [ + $elm$html$Html$Attributes$class('contents') + ]), + contents) + ])); + } else { + return A2( + $elm$html$Html$div, + _List_fromArray( + [ + $elm$html$Html$Attributes$class('folder collapsed') + ]), + _List_fromArray( + [folderLabel])); + } }); -var $elm$core$Tuple$second = function (_v0) { - var y = _v0.b; - return y; -}; -var $elm$html$Html$Attributes$classList = function (classes) { - return $elm$html$Html$Attributes$class( - A2( - $elm$core$String$join, - ' ', - A2( - $elm$core$List$map, - $elm$core$Tuple$first, - A2($elm$core$List$filter, $elm$core$Tuple$second, classes)))); -}; +var $elm$html$Html$h2 = _VirtualDom_node('h2'); +var $elm$html$Html$h3 = _VirtualDom_node('h3'); var $elm$html$Html$img = _VirtualDom_node('img'); +var $elm$html$Html$span = _VirtualDom_node('span'); var $elm$html$Html$Attributes$src = function (url) { return A2( $elm$html$Html$Attributes$stringProperty, 'src', _VirtualDom_noJavaScriptOrHtmlUri(url)); }; -var $elm$html$Html$Attributes$title = $elm$html$Html$Attributes$stringProperty('title'); -var $author$project$PhotoGroove$viewThumbnail = F2( - function (selectedUrl, thumb) { - return A2( - $elm$html$Html$img, - _List_fromArray( - [ - $elm$html$Html$Attributes$src( - _Utils_ap($author$project$PhotoGroove$urlPrefix, thumb.url)), - $elm$html$Html$Attributes$title( - thumb.title + (' [' + ($elm$core$String$fromInt(thumb.size) + ' KB]'))), - $elm$html$Html$Attributes$classList( - _List_fromArray( - [ - _Utils_Tuple2( - 'selected', - _Utils_eq(selectedUrl, thumb.url)) - ])), - $elm$html$Html$Events$onClick( - $author$project$PhotoGroove$ClickedPhoto(thumb.url)) - ]), - _List_Nil); - }); -var $author$project$PhotoGroove$viewLoaded = F3( - function (photos, selectedUrl, model) { - return _List_fromArray( +var $author$project$PhotoFolders$urlPrefix = 'http://elm-in-action.com/'; +var $author$project$PhotoFolders$viewRelatedPhoto = function (url) { + return A2( + $elm$html$Html$img, + _List_fromArray( + [ + $elm$html$Html$Attributes$class('related-photo'), + $elm$html$Html$Events$onClick( + $author$project$PhotoFolders$ClickedPhoto(url)), + $elm$html$Html$Attributes$src($author$project$PhotoFolders$urlPrefix + ('photos/' + (url + '/thumb'))) + ]), + _List_Nil); +}; +var $author$project$PhotoFolders$viewSelectedPhoto = function (photo) { + return A2( + $elm$html$Html$div, + _List_fromArray( + [ + $elm$html$Html$Attributes$class('selected-photo') + ]), + _List_fromArray( [ A2( - $elm$html$Html$h1, + $elm$html$Html$h2, _List_Nil, _List_fromArray( [ - $elm$html$Html$text('Photo Groove') + $elm$html$Html$text(photo.title) ])), A2( - $elm$html$Html$button, + $elm$html$Html$img, _List_fromArray( [ - $elm$html$Html$Events$onClick($author$project$PhotoGroove$ClickedSurpriseMe) + $elm$html$Html$Attributes$src($author$project$PhotoFolders$urlPrefix + ('photos/' + (photo.url + '/full'))) ]), - _List_fromArray( - [ - $elm$html$Html$text('Surprise Me!') - ])), + _List_Nil), A2( - $elm$html$Html$div, + $elm$html$Html$span, + _List_Nil, _List_fromArray( [ - $elm$html$Html$Attributes$class('activity') - ]), - _List_fromArray( - [ - $elm$html$Html$text(model.activity) - ])), - A2( - $elm$html$Html$div, - _List_fromArray( - [ - $elm$html$Html$Attributes$class('filters') - ]), - _List_fromArray( - [ - A3($author$project$PhotoGroove$viewFilter, $author$project$PhotoGroove$SlidHue, 'Hue', model.hue), - A3($author$project$PhotoGroove$viewFilter, $author$project$PhotoGroove$SlidRipple, 'Ripple', model.ripple), - A3($author$project$PhotoGroove$viewFilter, $author$project$PhotoGroove$SlidNoise, 'Noise', model.noise) + $elm$html$Html$text( + $elm$core$String$fromInt(photo.size) + 'KB') ])), A2( $elm$html$Html$h3, _List_Nil, _List_fromArray( [ - $elm$html$Html$text('Thumbnail Size:') + $elm$html$Html$text('Related') ])), A2( $elm$html$Html$div, _List_fromArray( [ - $elm$html$Html$Attributes$id('choose-size') + $elm$html$Html$Attributes$class('related-photos') ]), - A2( - $elm$core$List$map, - $author$project$PhotoGroove$viewSizeChooser, - _List_fromArray( - [$author$project$PhotoGroove$Small, $author$project$PhotoGroove$Medium, $author$project$PhotoGroove$Large]))), - A2( - $elm$html$Html$div, - _List_fromArray( - [ - $elm$html$Html$Attributes$id('thumbnails'), - $elm$html$Html$Attributes$class( - $author$project$PhotoGroove$sizeToString(model.chosenSize)) - ]), - A2( - $elm$core$List$map, - $author$project$PhotoGroove$viewThumbnail(selectedUrl), - photos)), - A2( - $elm$html$Html$canvas, - _List_fromArray( - [ - $elm$html$Html$Attributes$id('main-canvas'), - $elm$html$Html$Attributes$class('large') - ]), - _List_Nil) - ]); - }); -var $author$project$PhotoGroove$view = function (model) { + A2($elm$core$List$map, $author$project$PhotoFolders$viewRelatedPhoto, photo.relatedUrls)) + ])); +}; +var $author$project$PhotoFolders$view = function (model) { + var photoByUrl = function (url) { + return A2($elm$core$Dict$get, url, model.photos); + }; + var selectedPhoto = function () { + var _v0 = A2($elm$core$Maybe$andThen, photoByUrl, model.selectedPhotoUrl); + if (_v0.$ === 'Just') { + var photo = _v0.a; + return $author$project$PhotoFolders$viewSelectedPhoto(photo); + } else { + return $elm$html$Html$text(''); + } + }(); return A2( $elm$html$Html$div, _List_fromArray( [ $elm$html$Html$Attributes$class('content') ]), - function () { - var _v0 = model.status; - switch (_v0.$) { - case 'Loaded': - var photos = _v0.a; - var selectedUrl = _v0.b; - return A3($author$project$PhotoGroove$viewLoaded, photos, selectedUrl, model); - case 'Loading': - return _List_Nil; - default: - var errorMessage = _v0.a; - return _List_fromArray( - [ - $elm$html$Html$text('Error: ' + errorMessage) - ]); - } - }()); + _List_fromArray( + [ + A2( + $elm$html$Html$div, + _List_fromArray( + [ + $elm$html$Html$Attributes$class('folders') + ]), + _List_fromArray( + [ + A2( + $elm$html$Html$h1, + _List_Nil, + _List_fromArray( + [ + $elm$html$Html$text('Folders') + ])), + A2($author$project$PhotoFolders$viewFolder, $author$project$PhotoFolders$End, model.root) + ])), + A2( + $elm$html$Html$div, + _List_fromArray( + [ + $elm$html$Html$Attributes$class('selected-photo') + ]), + _List_fromArray( + [selectedPhoto])) + ])); }; -var $author$project$PhotoGroove$main = $elm$browser$Browser$element( - {init: $author$project$PhotoGroove$init, subscriptions: $author$project$PhotoGroove$subscriptions, update: $author$project$PhotoGroove$update, view: $author$project$PhotoGroove$view}); -_Platform_export({'PhotoGroove':{'init':$author$project$PhotoGroove$main($elm$json$Json$Decode$float)(0)}});}(this)); \ No newline at end of file +var $author$project$PhotoFolders$main = $elm$browser$Browser$element( + { + init: $author$project$PhotoFolders$init, + subscriptions: function (_v0) { + return $elm$core$Platform$Sub$none; + }, + update: $author$project$PhotoFolders$update, + view: $author$project$PhotoFolders$view + }); +_Platform_export({'PhotoFolders':{'init':$author$project$PhotoFolders$main( + $elm$json$Json$Decode$succeed(_Utils_Tuple0))(0)}});}(this)); \ No newline at end of file diff --git a/PhotoGroove/index.html b/PhotoGroove/index.html index d5c05d4..d2861cf 100644 --- a/PhotoGroove/index.html +++ b/PhotoGroove/index.html @@ -39,21 +39,7 @@ diff --git a/PhotoGroove/src/PhotoFolders.elm b/PhotoGroove/src/PhotoFolders.elm new file mode 100644 index 0000000..ec7b6e3 --- /dev/null +++ b/PhotoGroove/src/PhotoFolders.elm @@ -0,0 +1,258 @@ +module PhotoFolders exposing (main) + +import Browser +import Dict exposing (Dict) +import Html exposing (..) +import Html.Attributes exposing (class, src) +import Html.Events exposing (onClick) +import Http +import Json.Decode as Decode exposing (Decoder, int, list, string) +import Json.Decode.Pipeline exposing (required) + +type Folder = + Folder + { name : String + , photoUrls : List String + , subfolders : List Folder + , expanded : Bool + } + +type alias Model = + { selectedPhotoUrl : Maybe String + , photos : Dict String Photo + , root : Folder + } + +initialModel : Model +initialModel = + { selectedPhotoUrl = Nothing + , photos = Dict.empty + , root = Folder { name = "Loading...", expanded = True, photoUrls = [], subfolders = [] } + } + +init : () -> ( Model, Cmd Msg ) +init _ = + ( initialModel + , Http.get + { url = "http://elm-in-action.com/folders/list" + , expect = Http.expectJson GotInitialModel modelDecoder + } + ) + +modelDecoder : Decoder Model +modelDecoder = + Decode.map2 + (\photos root -> + { photos = photos, root = root, selectedPhotoUrl = Nothing } + ) + modelPhotosDecoder + folderDecoder + +type Msg + = ClickedPhoto String + | GotInitialModel (Result Http.Error Model) + | ClickedFolder FolderPath + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + ClickedFolder path -> + ( { model | root = toggleExpanded path model.root }, Cmd.none ) + + ClickedPhoto url -> + ( { model | selectedPhotoUrl = Just url }, Cmd.none ) + + GotInitialModel (Ok newModel) -> + ( newModel, Cmd.none ) + + GotInitialModel (Err _) -> + ( model, Cmd.none ) + +view : Model -> Html Msg +view model = + let + photoByUrl : String -> Maybe Photo + photoByUrl url = + Dict.get url model.photos + + selectedPhoto : Html Msg + selectedPhoto = + case Maybe.andThen photoByUrl model.selectedPhotoUrl of + Just photo -> + viewSelectedPhoto photo + + Nothing -> + text "" + in + div [ class "content" ] + [ div [ class "folders" ] + [ h1 [] [ text "Folders" ] + , viewFolder End model.root + ] + , div [ class "selected-photo"] [ selectedPhoto ] + ] + +main : Program () Model Msg +main = + Browser.element + { init = init + , view = view + , update = update + , subscriptions = \_ -> Sub.none + } + +type alias Photo = + { title : String + , size : Int + , relatedUrls : List String + , url : String + } + +viewPhoto : String -> Html Msg +viewPhoto url = + div [ class "photo", onClick (ClickedPhoto url) ] + [ text url ] + +viewSelectedPhoto : Photo -> Html Msg +viewSelectedPhoto photo = + div + [ class "selected-photo" ] + [ h2 [] [ text photo.title] + , img [ src (urlPrefix ++ "photos/" ++ photo.url ++ "/full") ] [] + , span [] [ text (String.fromInt photo.size ++ "KB") ] + , h3 [] [ text "Related" ] + , div [ class "related-photos" ] + (List.map viewRelatedPhoto photo.relatedUrls) + ] + +viewRelatedPhoto : String -> Html Msg +viewRelatedPhoto url = + img + [ class "related-photo" + , onClick (ClickedPhoto url) + , src (urlPrefix ++ "photos/" ++ url ++ "/thumb") + ] + [] + +viewFolder : FolderPath -> Folder -> Html Msg +viewFolder path (Folder folder) = + let + viewSubfolder : Int -> Folder -> Html Msg + viewSubfolder index subfolder = + viewFolder (appendIndex index path) subfolder + + folderLabel = + label [ onClick (ClickedFolder path) ] [ text folder.name ] + in + if folder.expanded then + let + contents = + List.append + (List.indexedMap viewSubfolder folder.subfolders) + (List.map viewPhoto folder.photoUrls) + in + div [ class "folder expanded" ] + [ folderLabel + , div [ class "contents" ] contents + ] + else + div [ class "folder collapsed" ] [ folderLabel ] + +appendIndex : Int -> FolderPath -> FolderPath +appendIndex index path = + case path of + End -> + Subfolder index End + + Subfolder subfolderIndex remainingPath -> + Subfolder subfolderIndex (appendIndex index remainingPath) + +urlPrefix : String +urlPrefix = + "http://elm-in-action.com/" + +type FolderPath + = End + | Subfolder Int FolderPath + +toggleExpanded : FolderPath -> Folder -> Folder +toggleExpanded path (Folder folder) = + case path of + End -> + Folder { folder | expanded = not folder.expanded } + + Subfolder targetIndex remainingPath -> + let + subfolders : List Folder + subfolders = + List.indexedMap transform folder.subfolders + + transform : Int -> Folder -> Folder + transform currentIndex currentSubfolder = + if currentIndex == targetIndex then + toggleExpanded remainingPath currentSubfolder + + else + currentSubfolder + in + Folder { folder | subfolders = subfolders } + +type alias JsonPhoto = + { title : String + , size : Int + , relatedUrls : List String + } + +jsonPhotoDecoder : Decoder JsonPhoto +jsonPhotoDecoder = + Decode.succeed JsonPhoto + |> required "title" string + |> required "size" int + |> required "related_photos" (list string) + +finishPhoto : ( String, JsonPhoto ) -> ( String, Photo ) +finishPhoto ( url, json ) = + ( url + , { url = url + , size = json.size + , title = json.title + , relatedUrls = json.relatedUrls + } + ) + +fromPairs : List ( String, JsonPhoto ) -> Dict String Photo +fromPairs pairs = + pairs + |> List.map finishPhoto + |> Dict.fromList + +photosDecoder : Decoder (Dict String Photo) +photosDecoder = + Decode.keyValuePairs jsonPhotoDecoder + |> Decode.map fromPairs + +folderDecoder : Decoder Folder +folderDecoder = + Decode.succeed folderFromJson + |> required "name" string + |> required "photos" photosDecoder + |> required "subfolders" (Decode.lazy (\_ -> list folderDecoder)) + +folderFromJson : String -> Dict String Photo -> List Folder -> Folder +folderFromJson name photos subfolders = + Folder + { name = name + , expanded = True + , subfolders = subfolders + , photoUrls = Dict.keys photos + } + +modelPhotosDecoder : Decoder (Dict String Photo) +modelPhotosDecoder = + Decode.succeed modelPhotosFromJson + |> required "photos" photosDecoder + |> required "subfolders" (Decode.lazy (\_ -> list modelPhotosDecoder)) + +modelPhotosFromJson : Dict String Photo -> List (Dict String Photo) -> Dict String Photo +modelPhotosFromJson folderPhotos subfolderPhotos = + List.foldl Dict.union folderPhotos subfolderPhotos