chapter 5: Implement sliders and filters
The application communicates back and forth with JavaScript using ports and subscriptions. It also receives initial data from JavaScript by passing 'flags' to the init function.
This commit is contained in:
parent
5f80082567
commit
43971bd268
6 changed files with 3249 additions and 260 deletions
730
app.js
730
app.js
|
|
@ -80,6 +80,190 @@ function A9(fun, a, b, c, d, e, f, g, h, i) {
|
|||
console.warn('Compiled in DEV mode. Follow the advice at https://elm-lang.org/0.19.1/optimize for better performance and smaller assets.');
|
||||
|
||||
|
||||
// EQUALITY
|
||||
|
||||
function _Utils_eq(x, y)
|
||||
{
|
||||
for (
|
||||
var pair, stack = [], isEqual = _Utils_eqHelp(x, y, 0, stack);
|
||||
isEqual && (pair = stack.pop());
|
||||
isEqual = _Utils_eqHelp(pair.a, pair.b, 0, stack)
|
||||
)
|
||||
{}
|
||||
|
||||
return isEqual;
|
||||
}
|
||||
|
||||
function _Utils_eqHelp(x, y, depth, stack)
|
||||
{
|
||||
if (x === y)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof x !== 'object' || x === null || y === null)
|
||||
{
|
||||
typeof x === 'function' && _Debug_crash(5);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (depth > 100)
|
||||
{
|
||||
stack.push(_Utils_Tuple2(x,y));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**/
|
||||
if (x.$ === 'Set_elm_builtin')
|
||||
{
|
||||
x = $elm$core$Set$toList(x);
|
||||
y = $elm$core$Set$toList(y);
|
||||
}
|
||||
if (x.$ === 'RBNode_elm_builtin' || x.$ === 'RBEmpty_elm_builtin')
|
||||
{
|
||||
x = $elm$core$Dict$toList(x);
|
||||
y = $elm$core$Dict$toList(y);
|
||||
}
|
||||
//*/
|
||||
|
||||
/**_UNUSED/
|
||||
if (x.$ < 0)
|
||||
{
|
||||
x = $elm$core$Dict$toList(x);
|
||||
y = $elm$core$Dict$toList(y);
|
||||
}
|
||||
//*/
|
||||
|
||||
for (var key in x)
|
||||
{
|
||||
if (!_Utils_eqHelp(x[key], y[key], depth + 1, stack))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var _Utils_equal = F2(_Utils_eq);
|
||||
var _Utils_notEqual = F2(function(a, b) { return !_Utils_eq(a,b); });
|
||||
|
||||
|
||||
|
||||
// COMPARISONS
|
||||
|
||||
// Code in Generate/JavaScript.hs, Basics.js, and List.js depends on
|
||||
// the particular integer values assigned to LT, EQ, and GT.
|
||||
|
||||
function _Utils_cmp(x, y, ord)
|
||||
{
|
||||
if (typeof x !== 'object')
|
||||
{
|
||||
return x === y ? /*EQ*/ 0 : x < y ? /*LT*/ -1 : /*GT*/ 1;
|
||||
}
|
||||
|
||||
/**/
|
||||
if (x instanceof String)
|
||||
{
|
||||
var a = x.valueOf();
|
||||
var b = y.valueOf();
|
||||
return a === b ? 0 : a < b ? -1 : 1;
|
||||
}
|
||||
//*/
|
||||
|
||||
/**_UNUSED/
|
||||
if (typeof x.$ === 'undefined')
|
||||
//*/
|
||||
/**/
|
||||
if (x.$[0] === '#')
|
||||
//*/
|
||||
{
|
||||
return (ord = _Utils_cmp(x.a, y.a))
|
||||
? ord
|
||||
: (ord = _Utils_cmp(x.b, y.b))
|
||||
? ord
|
||||
: _Utils_cmp(x.c, y.c);
|
||||
}
|
||||
|
||||
// traverse conses until end of a list or a mismatch
|
||||
for (; x.b && y.b && !(ord = _Utils_cmp(x.a, y.a)); x = x.b, y = y.b) {} // WHILE_CONSES
|
||||
return ord || (x.b ? /*GT*/ 1 : y.b ? /*LT*/ -1 : /*EQ*/ 0);
|
||||
}
|
||||
|
||||
var _Utils_lt = F2(function(a, b) { return _Utils_cmp(a, b) < 0; });
|
||||
var _Utils_le = F2(function(a, b) { return _Utils_cmp(a, b) < 1; });
|
||||
var _Utils_gt = F2(function(a, b) { return _Utils_cmp(a, b) > 0; });
|
||||
var _Utils_ge = F2(function(a, b) { return _Utils_cmp(a, b) >= 0; });
|
||||
|
||||
var _Utils_compare = F2(function(x, y)
|
||||
{
|
||||
var n = _Utils_cmp(x, y);
|
||||
return n < 0 ? $elm$core$Basics$LT : n ? $elm$core$Basics$GT : $elm$core$Basics$EQ;
|
||||
});
|
||||
|
||||
|
||||
// COMMON VALUES
|
||||
|
||||
var _Utils_Tuple0_UNUSED = 0;
|
||||
var _Utils_Tuple0 = { $: '#0' };
|
||||
|
||||
function _Utils_Tuple2_UNUSED(a, b) { return { a: a, b: b }; }
|
||||
function _Utils_Tuple2(a, b) { return { $: '#2', a: a, b: b }; }
|
||||
|
||||
function _Utils_Tuple3_UNUSED(a, b, c) { return { a: a, b: b, c: c }; }
|
||||
function _Utils_Tuple3(a, b, c) { return { $: '#3', a: a, b: b, c: c }; }
|
||||
|
||||
function _Utils_chr_UNUSED(c) { return c; }
|
||||
function _Utils_chr(c) { return new String(c); }
|
||||
|
||||
|
||||
// RECORDS
|
||||
|
||||
function _Utils_update(oldRecord, updatedFields)
|
||||
{
|
||||
var newRecord = {};
|
||||
|
||||
for (var key in oldRecord)
|
||||
{
|
||||
newRecord[key] = oldRecord[key];
|
||||
}
|
||||
|
||||
for (var key in updatedFields)
|
||||
{
|
||||
newRecord[key] = updatedFields[key];
|
||||
}
|
||||
|
||||
return newRecord;
|
||||
}
|
||||
|
||||
|
||||
// APPEND
|
||||
|
||||
var _Utils_append = F2(_Utils_ap);
|
||||
|
||||
function _Utils_ap(xs, ys)
|
||||
{
|
||||
// append Strings
|
||||
if (typeof xs === 'string')
|
||||
{
|
||||
return xs + ys;
|
||||
}
|
||||
|
||||
// append Lists
|
||||
if (!xs.b)
|
||||
{
|
||||
return ys;
|
||||
}
|
||||
var root = _List_Cons(xs.a, ys);
|
||||
xs = xs.b
|
||||
for (var curr = root; xs.b; xs = xs.b) // WHILE_CONS
|
||||
{
|
||||
curr = curr.b = _List_Cons(xs.a, ys);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
|
||||
var _List_Nil_UNUSED = { $: 0 };
|
||||
var _List_Nil = { $: '[]' };
|
||||
|
||||
|
|
@ -609,190 +793,6 @@ function _Debug_regionToString(region)
|
|||
|
||||
|
||||
|
||||
// EQUALITY
|
||||
|
||||
function _Utils_eq(x, y)
|
||||
{
|
||||
for (
|
||||
var pair, stack = [], isEqual = _Utils_eqHelp(x, y, 0, stack);
|
||||
isEqual && (pair = stack.pop());
|
||||
isEqual = _Utils_eqHelp(pair.a, pair.b, 0, stack)
|
||||
)
|
||||
{}
|
||||
|
||||
return isEqual;
|
||||
}
|
||||
|
||||
function _Utils_eqHelp(x, y, depth, stack)
|
||||
{
|
||||
if (x === y)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (typeof x !== 'object' || x === null || y === null)
|
||||
{
|
||||
typeof x === 'function' && _Debug_crash(5);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (depth > 100)
|
||||
{
|
||||
stack.push(_Utils_Tuple2(x,y));
|
||||
return true;
|
||||
}
|
||||
|
||||
/**/
|
||||
if (x.$ === 'Set_elm_builtin')
|
||||
{
|
||||
x = $elm$core$Set$toList(x);
|
||||
y = $elm$core$Set$toList(y);
|
||||
}
|
||||
if (x.$ === 'RBNode_elm_builtin' || x.$ === 'RBEmpty_elm_builtin')
|
||||
{
|
||||
x = $elm$core$Dict$toList(x);
|
||||
y = $elm$core$Dict$toList(y);
|
||||
}
|
||||
//*/
|
||||
|
||||
/**_UNUSED/
|
||||
if (x.$ < 0)
|
||||
{
|
||||
x = $elm$core$Dict$toList(x);
|
||||
y = $elm$core$Dict$toList(y);
|
||||
}
|
||||
//*/
|
||||
|
||||
for (var key in x)
|
||||
{
|
||||
if (!_Utils_eqHelp(x[key], y[key], depth + 1, stack))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
var _Utils_equal = F2(_Utils_eq);
|
||||
var _Utils_notEqual = F2(function(a, b) { return !_Utils_eq(a,b); });
|
||||
|
||||
|
||||
|
||||
// COMPARISONS
|
||||
|
||||
// Code in Generate/JavaScript.hs, Basics.js, and List.js depends on
|
||||
// the particular integer values assigned to LT, EQ, and GT.
|
||||
|
||||
function _Utils_cmp(x, y, ord)
|
||||
{
|
||||
if (typeof x !== 'object')
|
||||
{
|
||||
return x === y ? /*EQ*/ 0 : x < y ? /*LT*/ -1 : /*GT*/ 1;
|
||||
}
|
||||
|
||||
/**/
|
||||
if (x instanceof String)
|
||||
{
|
||||
var a = x.valueOf();
|
||||
var b = y.valueOf();
|
||||
return a === b ? 0 : a < b ? -1 : 1;
|
||||
}
|
||||
//*/
|
||||
|
||||
/**_UNUSED/
|
||||
if (typeof x.$ === 'undefined')
|
||||
//*/
|
||||
/**/
|
||||
if (x.$[0] === '#')
|
||||
//*/
|
||||
{
|
||||
return (ord = _Utils_cmp(x.a, y.a))
|
||||
? ord
|
||||
: (ord = _Utils_cmp(x.b, y.b))
|
||||
? ord
|
||||
: _Utils_cmp(x.c, y.c);
|
||||
}
|
||||
|
||||
// traverse conses until end of a list or a mismatch
|
||||
for (; x.b && y.b && !(ord = _Utils_cmp(x.a, y.a)); x = x.b, y = y.b) {} // WHILE_CONSES
|
||||
return ord || (x.b ? /*GT*/ 1 : y.b ? /*LT*/ -1 : /*EQ*/ 0);
|
||||
}
|
||||
|
||||
var _Utils_lt = F2(function(a, b) { return _Utils_cmp(a, b) < 0; });
|
||||
var _Utils_le = F2(function(a, b) { return _Utils_cmp(a, b) < 1; });
|
||||
var _Utils_gt = F2(function(a, b) { return _Utils_cmp(a, b) > 0; });
|
||||
var _Utils_ge = F2(function(a, b) { return _Utils_cmp(a, b) >= 0; });
|
||||
|
||||
var _Utils_compare = F2(function(x, y)
|
||||
{
|
||||
var n = _Utils_cmp(x, y);
|
||||
return n < 0 ? $elm$core$Basics$LT : n ? $elm$core$Basics$GT : $elm$core$Basics$EQ;
|
||||
});
|
||||
|
||||
|
||||
// COMMON VALUES
|
||||
|
||||
var _Utils_Tuple0_UNUSED = 0;
|
||||
var _Utils_Tuple0 = { $: '#0' };
|
||||
|
||||
function _Utils_Tuple2_UNUSED(a, b) { return { a: a, b: b }; }
|
||||
function _Utils_Tuple2(a, b) { return { $: '#2', a: a, b: b }; }
|
||||
|
||||
function _Utils_Tuple3_UNUSED(a, b, c) { return { a: a, b: b, c: c }; }
|
||||
function _Utils_Tuple3(a, b, c) { return { $: '#3', a: a, b: b, c: c }; }
|
||||
|
||||
function _Utils_chr_UNUSED(c) { return c; }
|
||||
function _Utils_chr(c) { return new String(c); }
|
||||
|
||||
|
||||
// RECORDS
|
||||
|
||||
function _Utils_update(oldRecord, updatedFields)
|
||||
{
|
||||
var newRecord = {};
|
||||
|
||||
for (var key in oldRecord)
|
||||
{
|
||||
newRecord[key] = oldRecord[key];
|
||||
}
|
||||
|
||||
for (var key in updatedFields)
|
||||
{
|
||||
newRecord[key] = updatedFields[key];
|
||||
}
|
||||
|
||||
return newRecord;
|
||||
}
|
||||
|
||||
|
||||
// APPEND
|
||||
|
||||
var _Utils_append = F2(_Utils_ap);
|
||||
|
||||
function _Utils_ap(xs, ys)
|
||||
{
|
||||
// append Strings
|
||||
if (typeof xs === 'string')
|
||||
{
|
||||
return xs + ys;
|
||||
}
|
||||
|
||||
// append Lists
|
||||
if (!xs.b)
|
||||
{
|
||||
return ys;
|
||||
}
|
||||
var root = _List_Cons(xs.a, ys);
|
||||
xs = xs.b
|
||||
for (var curr = root; xs.b; xs = xs.b) // WHILE_CONS
|
||||
{
|
||||
curr = curr.b = _List_Cons(xs.a, ys);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// MATH
|
||||
|
||||
var _Basics_add = F2(function(a, b) { return a + b; });
|
||||
|
|
@ -4629,32 +4629,9 @@ function _Time_getZoneName()
|
|||
});
|
||||
}
|
||||
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;
|
||||
var $elm$core$Elm$JsArray$foldr = _JsArray_foldr;
|
||||
var $elm$core$Array$foldr = F3(
|
||||
function (func, baseCase, _v0) {
|
||||
var tree = _v0.c;
|
||||
var tail = _v0.d;
|
||||
var helper = F2(
|
||||
function (node, acc) {
|
||||
if (node.$ === 'SubTree') {
|
||||
var subTree = node.a;
|
||||
return A3($elm$core$Elm$JsArray$foldr, helper, acc, subTree);
|
||||
} else {
|
||||
var values = node.a;
|
||||
return A3($elm$core$Elm$JsArray$foldr, func, acc, values);
|
||||
}
|
||||
});
|
||||
return A3(
|
||||
$elm$core$Elm$JsArray$foldr,
|
||||
helper,
|
||||
A3($elm$core$Elm$JsArray$foldr, func, baseCase, tail),
|
||||
tree);
|
||||
});
|
||||
var $elm$core$Array$toList = function (array) {
|
||||
return A3($elm$core$Array$foldr, $elm$core$List$cons, _List_Nil, array);
|
||||
};
|
||||
var $elm$core$Dict$foldr = F3(
|
||||
function (func, acc, t) {
|
||||
foldr:
|
||||
|
|
@ -4707,7 +4684,30 @@ var $elm$core$Set$toList = function (_v0) {
|
|||
var dict = _v0.a;
|
||||
return $elm$core$Dict$keys(dict);
|
||||
};
|
||||
var $elm$core$Basics$GT = {$: 'GT'};
|
||||
var $elm$core$Elm$JsArray$foldr = _JsArray_foldr;
|
||||
var $elm$core$Array$foldr = F3(
|
||||
function (func, baseCase, _v0) {
|
||||
var tree = _v0.c;
|
||||
var tail = _v0.d;
|
||||
var helper = F2(
|
||||
function (node, acc) {
|
||||
if (node.$ === 'SubTree') {
|
||||
var subTree = node.a;
|
||||
return A3($elm$core$Elm$JsArray$foldr, helper, acc, subTree);
|
||||
} else {
|
||||
var values = node.a;
|
||||
return A3($elm$core$Elm$JsArray$foldr, func, acc, values);
|
||||
}
|
||||
});
|
||||
return A3(
|
||||
$elm$core$Elm$JsArray$foldr,
|
||||
helper,
|
||||
A3($elm$core$Elm$JsArray$foldr, func, baseCase, tail),
|
||||
tree);
|
||||
});
|
||||
var $elm$core$Array$toList = function (array) {
|
||||
return A3($elm$core$Array$foldr, $elm$core$List$cons, _List_Nil, array);
|
||||
};
|
||||
var $elm$core$Result$Err = function (a) {
|
||||
return {$: 'Err', a: a};
|
||||
};
|
||||
|
|
@ -5417,6 +5417,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};
|
||||
};
|
||||
|
|
@ -6302,11 +6304,29 @@ var $author$project$PhotoGroove$initialCommand = $elm$http$Http$get(
|
|||
$elm$json$Json$Decode$list($author$project$PhotoGroove$photoDecoder)),
|
||||
url: 'list'
|
||||
});
|
||||
var $author$project$PhotoGroove$Large = {$: 'Large'};
|
||||
var $author$project$PhotoGroove$Loading = {$: 'Loading'};
|
||||
var $author$project$PhotoGroove$initialModel = {chosenSize: $author$project$PhotoGroove$Large, status: $author$project$PhotoGroove$Loading};
|
||||
var $elm$core$Platform$Sub$batch = _Platform_batch;
|
||||
var $elm$core$Platform$Sub$none = $elm$core$Platform$Sub$batch(_List_Nil);
|
||||
var $author$project$PhotoGroove$Medium = {$: 'Medium'};
|
||||
var $author$project$PhotoGroove$initialModel = {
|
||||
activity: '',
|
||||
chosenSize: $author$project$PhotoGroove$Medium,
|
||||
filterValues: {hue: 0, noise: 0, ripple: 0},
|
||||
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$initialCommand);
|
||||
};
|
||||
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 (_v0) {
|
||||
return $author$project$PhotoGroove$activityChanges($author$project$PhotoGroove$GotActivity);
|
||||
};
|
||||
var $author$project$PhotoGroove$Errored = function (a) {
|
||||
return {$: 'Errored', a: a};
|
||||
};
|
||||
|
|
@ -6317,6 +6337,107 @@ var $author$project$PhotoGroove$Loaded = F2(
|
|||
function (a, b) {
|
||||
return {$: 'Loaded', a: a, b: b};
|
||||
});
|
||||
var $author$project$PhotoGroove$Hue = {$: 'Hue'};
|
||||
var $author$project$PhotoGroove$Noise = {$: 'Noise'};
|
||||
var $author$project$PhotoGroove$Ripple = {$: 'Ripple'};
|
||||
var $author$project$PhotoGroove$filterTypeToName = function (t) {
|
||||
switch (t.$) {
|
||||
case 'Hue':
|
||||
return 'Hue';
|
||||
case 'Ripple':
|
||||
return 'Ripple';
|
||||
default:
|
||||
return 'Noise';
|
||||
}
|
||||
};
|
||||
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(
|
||||
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 = 'http://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 inPecent = function (v) {
|
||||
return v / 11;
|
||||
};
|
||||
var filters = _List_fromArray(
|
||||
[
|
||||
{
|
||||
amount: inPecent(model.filterValues.hue),
|
||||
name: $author$project$PhotoGroove$filterTypeToName($author$project$PhotoGroove$Hue)
|
||||
},
|
||||
{
|
||||
amount: inPecent(model.filterValues.ripple),
|
||||
name: $author$project$PhotoGroove$filterTypeToName($author$project$PhotoGroove$Ripple)
|
||||
},
|
||||
{
|
||||
amount: inPecent(model.filterValues.noise),
|
||||
name: $author$project$PhotoGroove$filterTypeToName($author$project$PhotoGroove$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:
|
||||
return _Utils_Tuple2(model, $elm$core$Platform$Cmd$none);
|
||||
}
|
||||
};
|
||||
var $elm$random$Random$Generate = function (a) {
|
||||
return {$: 'Generate', a: a};
|
||||
};
|
||||
|
|
@ -6441,8 +6562,6 @@ var $author$project$PhotoGroove$httpErrorToString = function (err) {
|
|||
return 'bad body: ' + msg;
|
||||
}
|
||||
};
|
||||
var $elm$core$Platform$Cmd$batch = _Platform_batch;
|
||||
var $elm$core$Platform$Cmd$none = $elm$core$Platform$Cmd$batch(_List_Nil);
|
||||
var $elm$core$Tuple$pair = F2(
|
||||
function (a, b) {
|
||||
return _Utils_Tuple2(a, b);
|
||||
|
|
@ -6459,6 +6578,23 @@ var $author$project$PhotoGroove$selectUrl = F2(
|
|||
return status;
|
||||
}
|
||||
});
|
||||
var $author$project$PhotoGroove$setFilterValue = F3(
|
||||
function (filterType, val, values) {
|
||||
switch (filterType.$) {
|
||||
case 'Hue':
|
||||
return _Utils_update(
|
||||
values,
|
||||
{hue: val});
|
||||
case 'Ripple':
|
||||
return _Utils_update(
|
||||
values,
|
||||
{ripple: val});
|
||||
default:
|
||||
return _Utils_update(
|
||||
values,
|
||||
{noise: val});
|
||||
}
|
||||
});
|
||||
var $elm$random$Random$addOne = function (value) {
|
||||
return _Utils_Tuple2(1, value);
|
||||
};
|
||||
|
|
@ -6547,13 +6683,12 @@ var $author$project$PhotoGroove$update = F2(
|
|||
switch (msg.$) {
|
||||
case 'ClickedThumbnail':
|
||||
var thumb = msg.a;
|
||||
return _Utils_Tuple2(
|
||||
return $author$project$PhotoGroove$applyFilters(
|
||||
_Utils_update(
|
||||
model,
|
||||
{
|
||||
status: A2($author$project$PhotoGroove$selectUrl, thumb, model.status)
|
||||
}),
|
||||
$elm$core$Platform$Cmd$none);
|
||||
}));
|
||||
case 'ClickedSurpriseMe':
|
||||
var _v1 = model.status;
|
||||
switch (_v1.$) {
|
||||
|
|
@ -6584,27 +6719,34 @@ var $author$project$PhotoGroove$update = F2(
|
|||
model,
|
||||
{chosenSize: size}),
|
||||
$elm$core$Platform$Cmd$none);
|
||||
case 'ChangedFilter':
|
||||
var f = msg.a;
|
||||
var val = msg.b;
|
||||
return $author$project$PhotoGroove$applyFilters(
|
||||
_Utils_update(
|
||||
model,
|
||||
{
|
||||
filterValues: A3($author$project$PhotoGroove$setFilterValue, f, val, model.filterValues)
|
||||
}));
|
||||
case 'GotRandomPhoto':
|
||||
var photo = msg.a;
|
||||
return _Utils_Tuple2(
|
||||
return $author$project$PhotoGroove$applyFilters(
|
||||
_Utils_update(
|
||||
model,
|
||||
{
|
||||
status: A2($author$project$PhotoGroove$selectUrl, photo.url, model.status)
|
||||
}),
|
||||
$elm$core$Platform$Cmd$none);
|
||||
default:
|
||||
}));
|
||||
case 'GotPhotos':
|
||||
if (msg.a.$ === 'Ok') {
|
||||
if (msg.a.a.b) {
|
||||
var photos = msg.a.a;
|
||||
var firstPhoto = photos.a;
|
||||
return _Utils_Tuple2(
|
||||
return $author$project$PhotoGroove$applyFilters(
|
||||
_Utils_update(
|
||||
model,
|
||||
{
|
||||
status: A2($author$project$PhotoGroove$Loaded, photos, firstPhoto.url)
|
||||
}),
|
||||
$elm$core$Platform$Cmd$none);
|
||||
}));
|
||||
} else {
|
||||
return _Utils_Tuple2(
|
||||
_Utils_update(
|
||||
|
|
@ -6625,9 +6767,15 @@ var $author$project$PhotoGroove$update = F2(
|
|||
}),
|
||||
$elm$core$Platform$Cmd$none);
|
||||
}
|
||||
default:
|
||||
var activity = msg.a;
|
||||
return _Utils_Tuple2(
|
||||
_Utils_update(
|
||||
model,
|
||||
{activity: activity}),
|
||||
$elm$core$Platform$Cmd$none);
|
||||
}
|
||||
});
|
||||
var $elm$json$Json$Encode$string = _Json_wrap;
|
||||
var $elm$html$Html$Attributes$stringProperty = F2(
|
||||
function (key, string) {
|
||||
return A2(
|
||||
|
|
@ -6640,9 +6788,10 @@ var $elm$html$Html$div = _VirtualDom_node('div');
|
|||
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$Medium = {$: 'Medium'};
|
||||
var $author$project$PhotoGroove$Large = {$: 'Large'};
|
||||
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');
|
||||
|
|
@ -6680,12 +6829,85 @@ var $elm$html$Html$Attributes$src = function (url) {
|
|||
'src',
|
||||
_VirtualDom_noJavaScriptOrHtmlUri(url));
|
||||
};
|
||||
var $author$project$PhotoGroove$urlPrefix = 'http://elm-in-action.com/';
|
||||
var $author$project$PhotoGroove$ChangedFilter = F2(
|
||||
function (a, b) {
|
||||
return {$: 'ChangedFilter', a: a, b: b};
|
||||
});
|
||||
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) {
|
||||
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)));
|
||||
};
|
||||
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 = $elm$html$Html$node('range-slider');
|
||||
var $author$project$PhotoGroove$viewFilter = F2(
|
||||
function (filterType, magnitude) {
|
||||
return A2(
|
||||
$elm$html$Html$div,
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$Attributes$class('filter-slider')
|
||||
]),
|
||||
_List_fromArray(
|
||||
[
|
||||
A2(
|
||||
$elm$html$Html$label,
|
||||
_List_Nil,
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$text(
|
||||
$author$project$PhotoGroove$filterTypeToName(filterType))
|
||||
])),
|
||||
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(
|
||||
$author$project$PhotoGroove$ChangedFilter(filterType))
|
||||
]),
|
||||
_List_Nil),
|
||||
A2(
|
||||
$elm$html$Html$label,
|
||||
_List_Nil,
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$text(
|
||||
$elm$core$String$fromInt(magnitude))
|
||||
]))
|
||||
]));
|
||||
});
|
||||
var $author$project$PhotoGroove$ClickedSize = function (a) {
|
||||
return {$: 'ClickedSize', a: a};
|
||||
};
|
||||
var $elm$html$Html$input = _VirtualDom_node('input');
|
||||
var $elm$html$Html$label = _VirtualDom_node('label');
|
||||
var $elm$html$Html$Attributes$name = $elm$html$Html$Attributes$stringProperty('name');
|
||||
var $author$project$PhotoGroove$sizeToString = function (size) {
|
||||
switch (size.$) {
|
||||
|
|
@ -6770,7 +6992,7 @@ var $author$project$PhotoGroove$viewThumbnail = F2(
|
|||
_List_Nil);
|
||||
});
|
||||
var $author$project$PhotoGroove$viewLoaded = F3(
|
||||
function (photos, selected, size) {
|
||||
function (model, photos, selected) {
|
||||
return _List_fromArray(
|
||||
[
|
||||
A2(
|
||||
|
|
@ -6791,6 +7013,28 @@ var $author$project$PhotoGroove$viewLoaded = F3(
|
|||
$elm$html$Html$text('Surprise me!')
|
||||
])),
|
||||
A2(
|
||||
$elm$html$Html$div,
|
||||
_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(
|
||||
[
|
||||
A2($author$project$PhotoGroove$viewFilter, $author$project$PhotoGroove$Hue, model.filterValues.hue),
|
||||
A2($author$project$PhotoGroove$viewFilter, $author$project$PhotoGroove$Ripple, model.filterValues.ripple),
|
||||
A2($author$project$PhotoGroove$viewFilter, $author$project$PhotoGroove$Noise, model.filterValues.noise)
|
||||
])),
|
||||
A2(
|
||||
$elm$html$Html$h3,
|
||||
_List_Nil,
|
||||
_List_fromArray(
|
||||
|
|
@ -6814,7 +7058,7 @@ var $author$project$PhotoGroove$viewLoaded = F3(
|
|||
[
|
||||
$elm$html$Html$Attributes$id('thumbnails'),
|
||||
$elm$html$Html$Attributes$class(
|
||||
$author$project$PhotoGroove$sizeToClass(size))
|
||||
$author$project$PhotoGroove$sizeToClass(model.chosenSize))
|
||||
]),
|
||||
A2(
|
||||
$elm$core$List$map,
|
||||
|
|
@ -6827,6 +7071,14 @@ var $author$project$PhotoGroove$viewLoaded = F3(
|
|||
$elm$html$Html$Attributes$class('large'),
|
||||
$elm$html$Html$Attributes$src($author$project$PhotoGroove$urlPrefix + ('large/' + selected))
|
||||
]),
|
||||
_List_Nil),
|
||||
A2(
|
||||
$elm$html$Html$canvas,
|
||||
_List_fromArray(
|
||||
[
|
||||
$elm$html$Html$Attributes$id('canvas-main'),
|
||||
$elm$html$Html$Attributes$class('large')
|
||||
]),
|
||||
_List_Nil)
|
||||
]);
|
||||
});
|
||||
|
|
@ -6845,7 +7097,7 @@ var $author$project$PhotoGroove$view = function (model) {
|
|||
case 'Loaded':
|
||||
var photos = _v0.a;
|
||||
var selected = _v0.b;
|
||||
return A3($author$project$PhotoGroove$viewLoaded, photos, selected, model.chosenSize);
|
||||
return A3($author$project$PhotoGroove$viewLoaded, model, photos, selected);
|
||||
default:
|
||||
var error = _v0.a;
|
||||
return _List_fromArray(
|
||||
|
|
@ -6856,15 +7108,5 @@ var $author$project$PhotoGroove$view = function (model) {
|
|||
}());
|
||||
};
|
||||
var $author$project$PhotoGroove$main = $elm$browser$Browser$element(
|
||||
{
|
||||
init: function (_v0) {
|
||||
return _Utils_Tuple2($author$project$PhotoGroove$initialModel, $author$project$PhotoGroove$initialCommand);
|
||||
},
|
||||
subscriptions: function (_v1) {
|
||||
return $elm$core$Platform$Sub$none;
|
||||
},
|
||||
update: $author$project$PhotoGroove$update,
|
||||
view: $author$project$PhotoGroove$view
|
||||
});
|
||||
_Platform_export({'PhotoGroove':{'init':$author$project$PhotoGroove$main(
|
||||
$elm$json$Json$Decode$succeed(_Utils_Tuple0))(0)}});}(this));
|
||||
{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));
|
||||
39
index.html
39
index.html
|
|
@ -2,12 +2,49 @@
|
|||
<html>
|
||||
<head>
|
||||
<link rel="stylesheet" href="styles.css" />
|
||||
<link rel="stylesheet" href="range-slider.css" />
|
||||
<script src="./range-slider.js"></script>
|
||||
<script>
|
||||
class RangeSlider extends HTMLElement {
|
||||
connectedCallback() {
|
||||
var input = document.createElement("input");
|
||||
this.appendChild(input);
|
||||
|
||||
var jsr = new JSR(input,
|
||||
{ max: this.max,
|
||||
values: [this.val],
|
||||
sliders: 1,
|
||||
grid: false
|
||||
});
|
||||
var rangeSliderNode = this;
|
||||
jsr.addEventListener ("update",
|
||||
function (elem, value) {
|
||||
var event =
|
||||
new CustomEvent ("slide",
|
||||
{detail: {userSlidTo: value}});
|
||||
rangeSliderNode.dispatchEvent(event);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
window.customElements.define("range-slider", RangeSlider);
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="pasta.js"></script>
|
||||
<script src="app.js"></script>
|
||||
<script>
|
||||
Elm.PhotoGroove.init({node: document.getElementById("app")});
|
||||
var app = Elm.PhotoGroove.init({node: document.getElementById("app"), flags: Pasta.version});
|
||||
app.ports.setFilters.subscribe (
|
||||
function(options) {
|
||||
requestAnimationFrame(function() {
|
||||
Pasta.apply(document.getElementById("canvas-main"), options);
|
||||
});}
|
||||
);
|
||||
Pasta.addActivityListener(function(activity) {
|
||||
app.ports.activityChanges.send(activity);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
141
range-slider.css
Normal file
141
range-slider.css
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/* https://github.com/mm-jsr/jsr
|
||||
|
||||
Copyright 2017 Mateusz "Soanvig" Koteja
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
|
||||
.jsr, .jsr_bar, .jsr_label, .jsr_rail,
|
||||
.jsr_rail-outer, .jsr_slider {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.jsr {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
|
||||
display: block;
|
||||
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
|
||||
margin: 20px 0;
|
||||
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
-webkit-touch-callout: none;
|
||||
-khtml-user-select: none;
|
||||
|
||||
font: 14px sans-serif;
|
||||
}
|
||||
|
||||
.jsr_rail-outer {
|
||||
position: relative;
|
||||
padding: 10px 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.jsr_rail {
|
||||
height: 5px;
|
||||
background: #444;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.jsr_bar {
|
||||
position: absolute;
|
||||
height: 5px;
|
||||
background-color: #999;
|
||||
z-index: 2;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.jsr_bar--limit {
|
||||
background-color: #7e7e7e;
|
||||
z-index: 1;
|
||||
cursor: auto;
|
||||
}
|
||||
|
||||
.jsr_slider {
|
||||
position: absolute;
|
||||
top: calc(5px / 2 + 10px);
|
||||
left: 0;
|
||||
|
||||
transform: translate(-50%, -50%);
|
||||
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
|
||||
cursor: col-resize;
|
||||
transition: background 0.1s ease-in-out;
|
||||
|
||||
outline: 0;
|
||||
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
.jsr_slider::before {
|
||||
content: '';
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
background: #999;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.jsr_slider:focus::before {
|
||||
background: #c00;
|
||||
}
|
||||
|
||||
.jsr_label {
|
||||
position: absolute;
|
||||
top: calc(10px + 5px + 15px / 1.5);
|
||||
padding: 0.2em 0.4em;
|
||||
background: #444;
|
||||
color: #fff;
|
||||
font-size: 0.9em;
|
||||
white-space: nowrap;
|
||||
border-radius: 0.3em;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.jsr_label--minmax {
|
||||
z-index: 1;
|
||||
color: #999;
|
||||
background: #333;
|
||||
transition: opacity 0.2s ease-in-out;
|
||||
}
|
||||
|
||||
/* Merged labels */
|
||||
.jsr_label .jsr_label {
|
||||
position: static;
|
||||
display: inline-block;
|
||||
font-size: 1em;
|
||||
padding-top: 0;
|
||||
padding-right: 0;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.jsr_label .jsr_label::before {
|
||||
content: ' - ';
|
||||
}
|
||||
|
||||
.jsr_canvas {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
/* Lock screen for touch */
|
||||
.jsr_lockscreen {
|
||||
overflow: hidden;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
12
range-slider.js
Normal file
12
range-slider.js
Normal file
File diff suppressed because one or more lines are too long
|
|
@ -1,12 +1,13 @@
|
|||
module PhotoGroove exposing (main)
|
||||
port module PhotoGroove exposing (main)
|
||||
|
||||
import Browser
|
||||
import Html exposing (Html, button, div, h1, h3, img, input, label, text)
|
||||
import Html.Attributes exposing (..)
|
||||
import Html.Events exposing (onClick)
|
||||
import Html exposing (Attribute, Html, button, canvas, div, h1, h3, img, input, label, node, text)
|
||||
import Html.Attributes as Attr exposing (..)
|
||||
import Html.Events exposing (on, onClick)
|
||||
import Http
|
||||
import Json.Decode as D exposing (Decoder)
|
||||
import Json.Decode.Pipeline as D
|
||||
import Json.Encode
|
||||
import Random
|
||||
|
||||
|
||||
|
|
@ -16,9 +17,30 @@ type Status
|
|||
| Errored String
|
||||
|
||||
|
||||
type alias FilterValues =
|
||||
{ hue : Int
|
||||
, ripple : Int
|
||||
, noise : Int
|
||||
}
|
||||
|
||||
|
||||
type alias FilterOptions =
|
||||
{ url : String
|
||||
, filters : List { name : String, amount : Float }
|
||||
}
|
||||
|
||||
|
||||
port setFilters : FilterOptions -> Cmd msg
|
||||
|
||||
|
||||
port activityChanges : (String -> msg) -> Sub msg
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ status : Status
|
||||
, chosenSize : ThumbnailSize
|
||||
, filterValues : FilterValues
|
||||
, activity : String
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -46,7 +68,9 @@ type ThumbnailSize
|
|||
initialModel : Model
|
||||
initialModel =
|
||||
{ status = Loading
|
||||
, chosenSize = Large
|
||||
, chosenSize = Medium
|
||||
, filterValues = { hue = 0, ripple = 0, noise = 0 }
|
||||
, activity = ""
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -66,25 +90,32 @@ view model =
|
|||
[]
|
||||
|
||||
Loaded photos selected ->
|
||||
viewLoaded photos selected model.chosenSize
|
||||
viewLoaded model photos selected
|
||||
|
||||
Errored error ->
|
||||
[ text ("Error: " ++ error) ]
|
||||
|
||||
|
||||
viewLoaded : List Photo -> String -> ThumbnailSize -> List (Html Message)
|
||||
viewLoaded photos selected size =
|
||||
viewLoaded : Model -> List Photo -> String -> List (Html Message)
|
||||
viewLoaded model photos selected =
|
||||
[ h1 [] [ text "Photo Groove" ]
|
||||
, button [ onClick ClickedSurpriseMe ] [ text "Surprise me!" ]
|
||||
, div [ class "activity" ] [ text model.activity ]
|
||||
, div [ class "filters" ]
|
||||
[ viewFilter Hue model.filterValues.hue
|
||||
, viewFilter Ripple model.filterValues.ripple
|
||||
, viewFilter Noise model.filterValues.noise
|
||||
]
|
||||
, h3 [] [ text "Thumbnail Size:" ]
|
||||
, div [ id "choose-size" ]
|
||||
(List.map viewSizeChooser [ Small, Medium, Large ])
|
||||
, div
|
||||
[ id "thumbnails"
|
||||
, class (sizeToClass size)
|
||||
, class (sizeToClass model.chosenSize)
|
||||
]
|
||||
(List.map (viewThumbnail selected) photos)
|
||||
, img [ class "large", src (urlPrefix ++ "large/" ++ selected) ] []
|
||||
, canvas [ id "canvas-main", class "large" ] []
|
||||
]
|
||||
|
||||
|
||||
|
|
@ -112,6 +143,22 @@ viewSizeChooser size =
|
|||
]
|
||||
|
||||
|
||||
viewFilter : FilterType -> Int -> Html Message
|
||||
viewFilter filterType magnitude =
|
||||
div
|
||||
[ class "filter-slider"
|
||||
]
|
||||
[ label [] [ text <| filterTypeToName filterType ]
|
||||
, rangeSlider
|
||||
[ Attr.max "11"
|
||||
, property "val" (Json.Encode.int magnitude)
|
||||
, onSlide (ChangedFilter filterType)
|
||||
]
|
||||
[]
|
||||
, label [] [ text (String.fromInt magnitude) ]
|
||||
]
|
||||
|
||||
|
||||
sizeToString : ThumbnailSize -> String
|
||||
sizeToString size =
|
||||
case size of
|
||||
|
|
@ -142,15 +189,36 @@ type Message
|
|||
= ClickedThumbnail String
|
||||
| ClickedSurpriseMe
|
||||
| ClickedSize ThumbnailSize
|
||||
| ChangedFilter FilterType Int
|
||||
| GotRandomPhoto Photo
|
||||
| GotPhotos (Result Http.Error (List Photo))
|
||||
| GotActivity String
|
||||
|
||||
|
||||
type FilterType
|
||||
= Hue
|
||||
| Ripple
|
||||
| Noise
|
||||
|
||||
|
||||
filterTypeToName : FilterType -> String
|
||||
filterTypeToName t =
|
||||
case t of
|
||||
Hue ->
|
||||
"Hue"
|
||||
|
||||
Ripple ->
|
||||
"Ripple"
|
||||
|
||||
Noise ->
|
||||
"Noise"
|
||||
|
||||
|
||||
update : Message -> Model -> ( Model, Cmd Message )
|
||||
update msg model =
|
||||
case msg of
|
||||
ClickedThumbnail thumb ->
|
||||
( { model | status = selectUrl thumb model.status }, Cmd.none )
|
||||
applyFilters { model | status = selectUrl thumb model.status }
|
||||
|
||||
ClickedSurpriseMe ->
|
||||
case model.status of
|
||||
|
|
@ -171,11 +239,14 @@ update msg model =
|
|||
ClickedSize size ->
|
||||
( { model | chosenSize = size }, Cmd.none )
|
||||
|
||||
ChangedFilter f val ->
|
||||
applyFilters { model | filterValues = setFilterValue f val model.filterValues }
|
||||
|
||||
GotRandomPhoto photo ->
|
||||
( { model | status = selectUrl photo.url model.status }, Cmd.none )
|
||||
applyFilters { model | status = selectUrl photo.url model.status }
|
||||
|
||||
GotPhotos (Ok ((firstPhoto :: _) as photos)) ->
|
||||
( { model | status = Loaded photos firstPhoto.url }, Cmd.none )
|
||||
applyFilters { model | status = Loaded photos firstPhoto.url }
|
||||
|
||||
GotPhotos (Ok []) ->
|
||||
( { model | status = Errored "No photos!" }, Cmd.none )
|
||||
|
|
@ -183,6 +254,62 @@ update msg model =
|
|||
GotPhotos (Err httpError) ->
|
||||
( { model | status = Errored <| ("Failed to load photos: " ++ httpErrorToString httpError) }, Cmd.none )
|
||||
|
||||
GotActivity activity ->
|
||||
( { model | activity = activity }, Cmd.none )
|
||||
|
||||
|
||||
subscriptions : Model -> Sub Message
|
||||
subscriptions _ =
|
||||
activityChanges GotActivity
|
||||
|
||||
|
||||
init : Float -> ( Model, Cmd Message )
|
||||
init flags =
|
||||
let
|
||||
activity =
|
||||
"Initializing Pasta v" ++ String.fromFloat flags
|
||||
in
|
||||
( { initialModel | activity = activity }, initialCommand )
|
||||
|
||||
|
||||
setFilterValue : FilterType -> Int -> FilterValues -> FilterValues
|
||||
setFilterValue filterType val values =
|
||||
case filterType of
|
||||
Hue ->
|
||||
{ values | hue = val }
|
||||
|
||||
Ripple ->
|
||||
{ values | ripple = val }
|
||||
|
||||
Noise ->
|
||||
{ values | noise = val }
|
||||
|
||||
|
||||
applyFilters : Model -> ( Model, Cmd msg )
|
||||
applyFilters model =
|
||||
case model.status of
|
||||
Loaded photos selectedUrl ->
|
||||
let
|
||||
inPecent v =
|
||||
toFloat v / 11
|
||||
|
||||
filters =
|
||||
[ { name = filterTypeToName Hue, amount = inPecent model.filterValues.hue }
|
||||
, { name = filterTypeToName Ripple, amount = inPecent model.filterValues.ripple }
|
||||
, { name = filterTypeToName Noise, amount = inPecent model.filterValues.noise }
|
||||
]
|
||||
|
||||
url =
|
||||
urlPrefix ++ "large/" ++ selectedUrl
|
||||
in
|
||||
( model, setFilters { url = url, filters = filters } )
|
||||
|
||||
Loading ->
|
||||
( model, Cmd.none )
|
||||
|
||||
Errored _ ->
|
||||
( model, Cmd.none )
|
||||
|
||||
|
||||
selectUrl : String -> Status -> Status
|
||||
selectUrl url status =
|
||||
|
|
@ -221,11 +348,23 @@ urlPrefix =
|
|||
"http://elm-in-action.com/"
|
||||
|
||||
|
||||
main : Program () Model Message
|
||||
main : Program Float Model Message
|
||||
main =
|
||||
Browser.element
|
||||
{ init = \_ -> ( initialModel, initialCommand )
|
||||
, subscriptions = \_ -> Sub.none
|
||||
{ init = init
|
||||
, subscriptions = subscriptions
|
||||
, view = view
|
||||
, update = update
|
||||
}
|
||||
|
||||
|
||||
rangeSlider : List (Html.Attribute msg) -> List (Html msg) -> Html msg
|
||||
rangeSlider =
|
||||
node "range-slider"
|
||||
|
||||
|
||||
onSlide : (Int -> msg) -> Attribute msg
|
||||
onSlide toMsg =
|
||||
D.at [ "detail", "userSlidTo" ] D.int
|
||||
|> D.map toMsg
|
||||
|> on "slide"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue