HW7: done ex3
This commit is contained in:
parent
243b1f5f6a
commit
ef9219d436
15 changed files with 5788 additions and 86 deletions
|
@ -4,6 +4,7 @@
|
|||
"description": "Exercise 7 of Web Atelier. Express.js and Mongoose",
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
"dustc": "cd ./views; dustc *.dust partials/*.dust -o ../public/js/views.js",
|
||||
"start": "DEBUG='canvas-server' nodemon ./bin/www",
|
||||
"test": "npm run test-mocha",
|
||||
"test-mocha": "./node_modules/mocha/bin/mocha -R spec ./test/routes ./test/hypermedia"
|
||||
|
|
19
hw7/Claudio_Maggioni/public/dustjs-linkedin/LICENSE
Normal file
19
hw7/Claudio_Maggioni/public/dustjs-linkedin/LICENSE
Normal file
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2010 Aleksander Williams
|
||||
|
||||
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.
|
1157
hw7/Claudio_Maggioni/public/dustjs-linkedin/dust-core.js
Normal file
1157
hw7/Claudio_Maggioni/public/dustjs-linkedin/dust-core.js
Normal file
File diff suppressed because it is too large
Load diff
4
hw7/Claudio_Maggioni/public/dustjs-linkedin/dust-core.min.js
vendored
Normal file
4
hw7/Claudio_Maggioni/public/dustjs-linkedin/dust-core.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
4359
hw7/Claudio_Maggioni/public/dustjs-linkedin/dust-full.js
Normal file
4359
hw7/Claudio_Maggioni/public/dustjs-linkedin/dust-full.js
Normal file
File diff suppressed because it is too large
Load diff
5
hw7/Claudio_Maggioni/public/dustjs-linkedin/dust-full.min.js
vendored
Normal file
5
hw7/Claudio_Maggioni/public/dustjs-linkedin/dust-full.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
|
@ -81,6 +81,7 @@ function doJSONRequest(method, url, headers, data){
|
|||
headers['Content-Type'] = JSON_MIME;
|
||||
}
|
||||
|
||||
return doFetchRequest(method, url, headers, data).then(res => res.json());
|
||||
return doFetchRequest(method, url, headers, data)
|
||||
.then(res => res.status == 204 ? {} : res.json());
|
||||
}
|
||||
|
||||
|
|
5
hw7/Claudio_Maggioni/public/js/views.js
Normal file
5
hw7/Claudio_Maggioni/public/js/views.js
Normal file
|
@ -0,0 +1,5 @@
|
|||
(function(dust){dust.register("favourites",body_0);function body_0(chk,ctx){return chk.w("<html><head><meta charset=\"utf-8\">").x(ctx.get(["bookmarked"], false),ctx,{"else":body_1,"block":body_2},{}).w("</head><body>").x(ctx.get(["bookmarked"], false),ctx,{"else":body_3,"block":body_4},{}).s(ctx.get(["favs"], false),ctx,{"else":body_5,"block":body_6},{}).w("</body></html>");}body_0.__dustBody=!0;function body_1(chk,ctx){return chk.w("<title>Favourites</title>");}body_1.__dustBody=!0;function body_2(chk,ctx){return chk.w("<title>Bookmarked</title>");}body_2.__dustBody=!0;function body_3(chk,ctx){return chk.w("<h1>Favourites</h1>");}body_3.__dustBody=!0;function body_4(chk,ctx){return chk.w("<h1>Bookmarked</h1>");}body_4.__dustBody=!0;function body_5(chk,ctx){return chk.w("<strong>No favourites.</strong>");}body_5.__dustBody=!0;function body_6(chk,ctx){return chk.w("<div>").p("favourite_partial",ctx,ctx,{"name":ctx.get(["name"], false),"dataURL":ctx.get(["dataURL"], false),"_id":ctx.get(["_id"], false),"bookmarked":ctx.get(["bookmarked"], false),"details":"true"}).w("</div>");}body_6.__dustBody=!0;return body_0}(dust));
|
||||
(function(dust){dust.register("500",body_0);function body_0(chk,ctx){return chk.w("<html><head><meta charset='utf-8'></head><body><h1>Error 500</h1><pre>").f(ctx.get(["err"], false),ctx,"h").w("</pre></body></html>");}body_0.__dustBody=!0;return body_0}(dust));
|
||||
(function(dust){dust.register("index",body_0);function body_0(chk,ctx){return chk.w("<!DOCTYPE html><html><head><meta charset=\"utf-8\"><title>OO-JS Exercise - Web Atelier 2017</title><link rel=\"stylesheet\" href=\"https://maxcdn.bootstrapcdn.com/font-awesome/4.6.3/css/font-awesome.min.css\"><link rel=\"stylesheet\" href=\"style.css\"></head><body onload=\"init()\"><h1>OO-JS Exercise: Canvas</h1><div id=\"app\"><div id=\"left-toolbar\" class=\"toolbar\"><button id=\"clear-btn\">Clear</button><button id=\"undo-btn\">Undo</button><button id=\"camera-btn\"><i class=\"fa fa-camera\" aria-hidden=\"true\"></i></button></div><canvas id=\"canvas\" width=\"600\" height=\"400\"></canvas><div id=\"brush-toolbar\" class=\"toolbar\"><!-- Brushes buttons go here (programmatically). Each button should be a <button> element --></div></div><div id=\"favourites\"></div><script src=\"dustjs-linkedin/dust-full.min.js\"></script><script src=\"js/views.js\"></script><script src=\"js/fetch.js\"></script><script src=\"scripts/brushes.js\"></script><script src=\"scripts/undo.js\"></script><!-- <script src=\"scripts/clock.js\"></script> --><script src=\"scripts/app.js\"></script><script src=\"main.js\"></script></body></html>");}body_0.__dustBody=!0;return body_0}(dust));
|
||||
(function(dust){dust.register("favourite_partial",body_0);function body_0(chk,ctx){return chk.w("<h3>").f(ctx.get(["name"], false),ctx,"h").w("</h3><img src=\"").f(ctx.get(["dataURL"], false),ctx,"h").w("\" alt=\"").f(ctx.get(["name"], false),ctx,"h").w("\">").x(ctx.get(["b"], false),ctx,{"block":body_1},{}).x(ctx.get(["details"], false),ctx,{"else":body_2,"block":body_5},{});}body_0.__dustBody=!0;function body_1(chk,ctx){return chk.w("<p><strong>Bookmarked</strong></p>");}body_1.__dustBody=!0;function body_2(chk,ctx){return chk.w("<form id=\"favourite").f(ctx.get(["_id"], false),ctx,"h").w("\" method=\"POST\" action=\"/favorites/").f(ctx.get(["_id"], false),ctx,"h").w("?_method=PUT\"><input type=\"hidden\" name=\"dataURL\" value=\"").f(ctx.get(["dataURL"], false),ctx,"h").w("\"><label for=\"name\">Name:</label><input type=\"text\" name=\"name\" placeholder=\"Name\" value=\"").f(ctx.get(["name"], false),ctx,"h").w("\"><br><button data-update>Update</button><br><button data-delete formaction=\"/favorites/").f(ctx.get(["_id"], false),ctx,"h").w("?_method=DELETE\">Delete</button><br>").x(ctx.get(["bookmarked"], false),ctx,{"else":body_3,"block":body_4},{}).w("</form>");}body_2.__dustBody=!0;function body_3(chk,ctx){return chk.w("<button data-bookmark name=\"bookmarked\" value=\"true\"formaction=\"/favorites/").f(ctx.get(["_id"], false),ctx,"h").w("/bookmarked?_method=PUT\">Add bookmark</button>");}body_3.__dustBody=!0;function body_4(chk,ctx){return chk.w("<button data-del-bookmark name=\"bookmarked\" value=\"false\"formaction=\"/favorites/").f(ctx.get(["_id"], false),ctx,"h").w("/bookmarked?_method=PUT\">Remove bookmark</button>");}body_4.__dustBody=!0;function body_5(chk,ctx){return chk.w("<a href=\"/favorites/").f(ctx.get(["_id"], false),ctx,"h").w("\">Details</a>");}body_5.__dustBody=!0;return body_0}(dust));
|
||||
(function(dust){dust.register("favourite",body_0);function body_0(chk,ctx){return chk.w("<html><head><meta charset=\"utf-8\"><title>").f(ctx.get(["name"], false),ctx,"h").w("</title></head><body>").p("favourite_partial",ctx,ctx,{}).w("<a href=\"/favorites\">Favourites list</a></body></html>");}body_0.__dustBody=!0;return body_0}(dust));
|
|
@ -8,6 +8,149 @@ class App {
|
|||
};
|
||||
}
|
||||
|
||||
render_timestamp = 0;
|
||||
render_last = null;
|
||||
render_timeout = null;
|
||||
|
||||
registerFavoriteEvents(_id, dom) {
|
||||
const form = dom.querySelector('form');
|
||||
|
||||
|
||||
const delay = (f, ...args) => {
|
||||
const execute = (f, ...args) => {
|
||||
console.log("don't delay, solve Carzaniga today", ...args);
|
||||
this.render_timestamp = new Date().getTime();
|
||||
this.render_timeout = null;
|
||||
this.render_last = null;
|
||||
f(...args);
|
||||
};
|
||||
|
||||
if (new Date().getTime() - this.render_timestamp < 100000) {
|
||||
console.log("delaying", ...args);
|
||||
this.render_last = () => execute(f, ...args);
|
||||
if (this.render_timeout === null) {
|
||||
this.render_timeout = setTimeout(() => this.render_last(), 100);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
execute(f, ...args);
|
||||
}
|
||||
|
||||
form.querySelector('input[name=name]').onkeyup =
|
||||
() => delay(submit, 'data-update', false);
|
||||
|
||||
const submit = (action, update = true) => {
|
||||
let p;
|
||||
|
||||
const formData = new FormData(form);
|
||||
const name = formData.get('name');
|
||||
const dataURL = formData.get('dataURL');
|
||||
|
||||
switch (action) {
|
||||
case 'data-update':
|
||||
p = doJSONRequest('PUT', '/favorites/' + _id, {},
|
||||
{ name, dataURL });
|
||||
break;
|
||||
case 'data-delete':
|
||||
p = doJSONRequest('DELETE', '/favorites/' + _id);
|
||||
break;
|
||||
case 'data-bookmark':
|
||||
p = doJSONRequest('PUT', `/favorites/${_id}/bookmarked`,
|
||||
{}, { bookmarked: true });
|
||||
break;
|
||||
case 'data-del-bookmark':
|
||||
p = doJSONRequest('PUT', `/favorites/${_id}/bookmarked`,
|
||||
{}, { bookmarked: false });
|
||||
break;
|
||||
default:
|
||||
console.warn(action, ' is not a valid action');
|
||||
return;
|
||||
}
|
||||
|
||||
p.catch(console.error);
|
||||
|
||||
if (action == 'data-delete' && update) {
|
||||
p.then(_ => dom.remove());
|
||||
} else if (update) {
|
||||
p.then(e => this.renderFavorite(e, dom));
|
||||
}
|
||||
|
||||
if (!update) {
|
||||
// updating via javascript to not disrupt cursor in name when
|
||||
// replacing html
|
||||
dom.querySelector('h3').innerText = name;
|
||||
}
|
||||
};
|
||||
|
||||
form.onsubmit = e => {
|
||||
e.preventDefault();
|
||||
|
||||
const formData = new FormData(form);
|
||||
const name = formData.get('name');
|
||||
const dataURL = formData.get('dataURL');
|
||||
|
||||
const action = e.explicitOriginalTarget.getAttributeNames()
|
||||
.filter(e => e.startsWith('data-'))[0];
|
||||
|
||||
submit(action);
|
||||
};
|
||||
}
|
||||
|
||||
renderFavorite(data, div) {
|
||||
data.bookmarked = data.bookmarked === true || data.bookmarked === 'true';
|
||||
dust.render('favourite_partial',
|
||||
data,
|
||||
(err, out) => {
|
||||
if (err) {
|
||||
console.error(err);
|
||||
} else {
|
||||
let dom;
|
||||
|
||||
if (div === 'top' || !div) {
|
||||
dom = document.createElement('div');
|
||||
dom.id = 'favorite_' + data._id;
|
||||
dom.innerHTML = out;
|
||||
|
||||
if (div === 'top') {
|
||||
this.favourites.prepend(dom);
|
||||
} else {
|
||||
this.favourites.appendChild(dom);
|
||||
}
|
||||
} else {
|
||||
div.innerHTML = out;
|
||||
dom = div;
|
||||
}
|
||||
dom.className = data.bookmarked ? 'bookmarked' : '';
|
||||
|
||||
this.registerFavoriteEvents(data._id, dom);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getFavorites(nameFilter, onlyBookmarked = false) {
|
||||
let url = '/favorites/search?ajax=true';
|
||||
|
||||
if (nameFilter) {
|
||||
url += '&name=' + encodeURIComponent(nameFilter);
|
||||
}
|
||||
|
||||
if (onlyBookmarked) {
|
||||
url += '&bookmarked=true';
|
||||
}
|
||||
|
||||
doFetchRequest('GET', url, { 'Accept': 'text/html' })
|
||||
.then(res => res.text())
|
||||
.then(html => {
|
||||
this.favourites.innerHTML = html;
|
||||
this.favourites.childNodes.forEach(e => {
|
||||
const id = e.id.substring('favorite_'.length);
|
||||
this.registerFavoriteEvents(id, e);
|
||||
});
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
|
||||
constructor(conf) {
|
||||
if (!(typeof conf === 'object' && conf)) {
|
||||
throw new Error('Argument conf different from specification');
|
||||
|
@ -21,6 +164,23 @@ class App {
|
|||
this.ctx = this.canvas.getContext('2d');
|
||||
this.favourites = document.querySelector('div#favourites');
|
||||
|
||||
this.getFavorites(false);
|
||||
|
||||
const nameFilter = document.getElementById('name-filter');
|
||||
const bookmarkFilter = document.getElementById('bookmarked-filter');
|
||||
|
||||
const onSearch = () => {
|
||||
const name = nameFilter.value;
|
||||
const bookmark = bookmarkFilter.checked;
|
||||
|
||||
this.getFavorites(name, bookmark);
|
||||
}
|
||||
|
||||
nameFilter.onkeyup = onSearch;
|
||||
bookmarkFilter.onclick = onSearch;
|
||||
|
||||
|
||||
|
||||
if (typeof conf.buttons === 'object' && conf.buttons) {
|
||||
this.buttons = {}
|
||||
for (const b of ['clear', 'undo', 'camera'])
|
||||
|
@ -44,37 +204,16 @@ class App {
|
|||
if (this.buttons.camera) {
|
||||
this.buttons.camera.addEventListener('click', () => {
|
||||
const base64 = this.canvas.toDataURL();
|
||||
const img = document.createElement('img');
|
||||
img.src = base64;
|
||||
|
||||
const form = document.createElement('form');
|
||||
form.method = 'POST';
|
||||
form.action = '/favorites';
|
||||
const data = {
|
||||
dataURL: base64,
|
||||
name: 'My New Masterpiece',
|
||||
bookmarked: false
|
||||
};
|
||||
|
||||
const image = document.createElement('input');
|
||||
image.type = 'hidden';
|
||||
image.name = 'dataURL';
|
||||
image.value = base64;
|
||||
form.appendChild(image);
|
||||
|
||||
const lblName = document.createElement('label');
|
||||
lblName.setAttribute('for', 'name');
|
||||
lblName.innerText = 'Name:';
|
||||
form.appendChild(lblName);
|
||||
|
||||
const name = document.createElement('input');
|
||||
name.type = 'text';
|
||||
name.name = 'name';
|
||||
name.placeholder = 'Name';
|
||||
name.value = 'New Name';
|
||||
form.appendChild(name);
|
||||
|
||||
const submit = document.createElement('button');
|
||||
submit.innerText = 'Save';
|
||||
form.appendChild(submit);
|
||||
|
||||
this.favourites.appendChild(img);
|
||||
this.favourites.appendChild(form);
|
||||
doJSONRequest("POST", "/favorites", {}, data)
|
||||
.then(data => this.renderFavorite(data, 'top'))
|
||||
.catch(console.error);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -85,17 +224,6 @@ class App {
|
|||
const imgCanvas = document.createElement('canvas');
|
||||
imgCanvas.width = this.canvas.width;
|
||||
imgCanvas.height = this.canvas.height;
|
||||
const imgCtx = imgCanvas.getContext('2d');
|
||||
imgCtx.drawImage(player, 0, 0, canvas.width, canvas.height);
|
||||
const imgData = imgCtx.getImageData(0, 0, imgCanvas.width,
|
||||
imgCanvas.height);
|
||||
|
||||
for (let i = 0; i < data.length; i += 4) {
|
||||
const pixel = (data[i] + 2 * data[i+1] + data[i+2]) / 4;
|
||||
data[i] = data[i+1] = data[i+2] = pixel;
|
||||
}
|
||||
imgCtx.putImageData(imgData, 0, 0);
|
||||
|
||||
const img = document.createElement('img');
|
||||
img.src = imgCanvas.toDataURL();
|
||||
img.addEventListener('load', () => {
|
||||
|
|
|
@ -13,6 +13,9 @@ const { error, catchErrs, renderFav, parseId, notFound } = require('../utils');
|
|||
|
||||
async function findAndRender(filter, req, res) {
|
||||
try {
|
||||
if (filter.name) {
|
||||
filter.name = { $regex: filter.name, $options: 'i' };
|
||||
}
|
||||
const favs = await Favorite.find(filter);
|
||||
renderFav(req, res, favs);
|
||||
} catch(e) {
|
||||
|
@ -57,6 +60,7 @@ router.get('/search', async (req, res) => {
|
|||
const filter = Object.assign({}, req.query);
|
||||
delete filter['dataURL'];
|
||||
delete filter['_method'];
|
||||
delete filter['ajax'];
|
||||
|
||||
await findAndRender(filter, req, res);
|
||||
});
|
||||
|
@ -110,6 +114,7 @@ function handleUpdate(partial = false) {
|
|||
}
|
||||
|
||||
res.status = 200;
|
||||
Object.assign(fav, edit);
|
||||
renderFav(req, res, fav, false);
|
||||
} catch (e) {
|
||||
error(e, res);
|
||||
|
@ -139,24 +144,22 @@ router.delete('/:id', async (req, res) => {
|
|||
}
|
||||
});
|
||||
|
||||
router.put('/:id/bookmarked', (req, res) => {
|
||||
router.put('/:id/bookmarked', async (req, res) => {
|
||||
if (req.body.bookmarked !== true && req.body.bookmarked !== false &&
|
||||
req.body.bookmarked !== "true" && req.body.bookmarked !== "false") {
|
||||
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
||||
res.end('Bad PUT bookmark form parameters');
|
||||
}
|
||||
|
||||
try {
|
||||
const fav = Favorite.findByIdAndUpdate(parseId(req), {
|
||||
$set: { bookmarked: req.body.bookmarked }
|
||||
}, { new: true });
|
||||
const fav = await Favorite.findById(parseId(req));
|
||||
fav.bookmarked = req.body.bookmarked === true ||
|
||||
req.body.bookmarked === "true";
|
||||
fav.save();
|
||||
|
||||
if (notFound(fav, res)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!req.body.bookmarked) {
|
||||
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
||||
res.end('Bad PUT bookmark form parameters');
|
||||
} else {
|
||||
renderFav(req, res, fav, false);
|
||||
}
|
||||
renderFav(req, res, fav, false);
|
||||
} catch (e) {
|
||||
error(e, res);
|
||||
notFound(null, res);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -28,8 +28,13 @@ function renderFav(req, res, favs, list = true) {
|
|||
};
|
||||
|
||||
if (req.accepts('html')) {
|
||||
res.render(list ? 'favourites.dust' : 'favourite.dust',
|
||||
list ? { favs } : favs);
|
||||
if (req.query.ajax === 'true' && list) {
|
||||
res.render('server_side.dust', { favs });
|
||||
} else if (list) {
|
||||
res.render('favourites.dust', { favs });
|
||||
} else {
|
||||
res.render('favorite.dust', favs);
|
||||
}
|
||||
} else if (req.accepts('json')) {
|
||||
if (list) {
|
||||
favs = favs.map(makeTestsPass);
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
{! vim: set ts=2 sw=2 et tw=120: !}
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{name}</title>
|
||||
</head>
|
||||
<body>
|
||||
{>"favourite_partial" /}
|
||||
</body>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>{name}</title>
|
||||
</head>
|
||||
<body>
|
||||
{>"favourite_partial" /}
|
||||
<a href="/favorites">Favourites list</a>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
|
|
|
@ -4,25 +4,24 @@
|
|||
<img src="{dataURL}" alt="{name}">
|
||||
{?b}
|
||||
<p>
|
||||
<strong>Bookmarked</strong>
|
||||
<strong>Bookmarked</strong>
|
||||
</p>
|
||||
{/b}
|
||||
{?details}
|
||||
<a href="/favorites/{_id}">Details</a>
|
||||
{:else}
|
||||
<form method="POST" action="/favorites/{_id}?_method=PUT">
|
||||
<input type="hidden" name="dataURL" value="{dataURL}">
|
||||
<label for="name">Name:</label>
|
||||
<input type="text" name="name" placeholder="Name" value="{name}"><br>
|
||||
<button>Update</button><br>
|
||||
<button formaction="/favorites/{_id}?_method=DELETE">Delete</button><br>
|
||||
{?bookmarked}
|
||||
<button name="bookmarked" value="false"
|
||||
formaction="/favorites/{_id}/bookmarked?_method=PUT">Remove bookmark</button>
|
||||
{:else}
|
||||
<button name="bookmarked" value="true"
|
||||
formaction="/favorites/{_id}/bookmarked?_method=PUT">Add bookmark</button>
|
||||
{/bookmarked}
|
||||
<form id="favourite{_id}" method="POST" action="/favorites/{_id}?_method=PUT">
|
||||
<input type="hidden" name="dataURL" value="{dataURL}">
|
||||
<label for="name">Name:</label>
|
||||
<input type="text" name="name" placeholder="Name" value="{name}"><br>
|
||||
<button data-update>Update</button><br>
|
||||
<button data-delete formaction="/favorites/{_id}?_method=DELETE">Delete</button><br>
|
||||
{?bookmarked}
|
||||
<button data-del-bookmark name="bookmarked" value="false"
|
||||
formaction="/favorites/{_id}/bookmarked?_method=PUT">Remove bookmark</button>
|
||||
{:else}
|
||||
<button data-bookmark name="bookmarked" value="true"
|
||||
formaction="/favorites/{_id}/bookmarked?_method=PUT">Add bookmark</button>
|
||||
{/bookmarked}
|
||||
</form>
|
||||
<a href="/favorites">Favourites list</a>
|
||||
{/details}
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>OO-JS Exercise - Web Atelier 2017</title>
|
||||
|
@ -25,12 +24,19 @@
|
|||
<!-- Brushes buttons go here (programmatically). Each button should be a <button> element -->
|
||||
</div>
|
||||
</div>
|
||||
<h2>Favourites</h2>
|
||||
<div id="favourites">
|
||||
{#favs}
|
||||
{>"favourite_partial" name=name dataURL=dataURL _id=_id bookmarked=bookmarked details="true" /}
|
||||
{/favs}
|
||||
<br>
|
||||
<div>
|
||||
<label for="name-filter">By name search:</label>
|
||||
<input id="name-filter" type="text" placeholder="Search by name">
|
||||
<br>
|
||||
<label for="bookmarked-filter">Only bookmarked</label>
|
||||
<input id="bookmarked-filter" type="checkbox">
|
||||
</div>
|
||||
<div id="favourites">
|
||||
</div>
|
||||
<script src="dustjs-linkedin/dust-full.min.js"></script>
|
||||
<script src="js/views.js"></script>
|
||||
<script src="js/fetch.js"></script>
|
||||
<script src="scripts/brushes.js"></script>
|
||||
<script src="scripts/undo.js"></script>
|
||||
<!-- <script src="scripts/clock.js"></script> -->
|
||||
|
|
9
hw7/Claudio_Maggioni/views/server_side.dust
Normal file
9
hw7/Claudio_Maggioni/views/server_side.dust
Normal file
|
@ -0,0 +1,9 @@
|
|||
{! vim: set ts=2 sw=2 et tw=120: !}
|
||||
|
||||
{#favs}
|
||||
<div id="favorite_{_id}" class="{?bookmarked}bookmarked{/bookmarked}">
|
||||
{>"favourite_partial" name=name dataURL=dataURL _id=_id bookmarked=bookmarked /}
|
||||
</div>
|
||||
{/favs}
|
||||
|
||||
|
Reference in a new issue