2019-10-04 in class

This commit is contained in:
Claudio Maggioni 2019-10-04 17:24:57 +02:00
parent 6388526c01
commit c72515f5ae
13 changed files with 28388 additions and 0 deletions

View file

@ -0,0 +1,42 @@
<!-- DON'T MODIFY THIS FILE -->
<!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="clock">
<div id="progress-bar"></div>
<div id="clock-time"></div>
</div> -->
<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>
<h2>Favourites</h2>
<div id="favourites">
<!-- Favourites will go here (programmatically). Each favourite should be an <img> element -->
</div>
<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>

View file

@ -0,0 +1,15 @@
// vim: set ts=2 sw=2 tw=80 et:
// Enter your initialization code here
function init() {
// Create canvas app
const app = new App({
canvas: 'canvas',
buttons: {
clear: 'clear-btn',
camera: 'camera-btn',
undo: 'undo-btn'
},
brushToolbar: 'brush-toolbar'
});
}

View file

@ -0,0 +1,5 @@
let test = QUnit.test;
let equal = QUnit.assert.equal.bind(QUnit.assert);
let notEqual = QUnit.assert.notEqual.bind(QUnit.assert);
let deepEqual = QUnit.assert.deepEqual.bind(QUnit.assert);
let notDeepEqual = QUnit.assert.notDeepEqual.bind(QUnit.assert);

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,235 @@
/**
* QUnit v1.10.0 - A JavaScript Unit Testing Framework
*
* http://qunitjs.com
*
* Copyright 2012 jQuery Foundation and other contributors
* Released under the MIT license.
* http://jquery.org/license
*/
/** Font Family and Sizes */
#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
}
#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
#qunit-tests { font-size: smaller; }
/** Resets */
#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter {
margin: 0;
padding: 0;
}
/** Header */
#qunit-header {
padding: 0.5em 0 0.5em 1em;
color: #8699a4;
background-color: #0d3349;
font-size: 1.5em;
line-height: 1em;
font-weight: normal;
border-radius: 5px 5px 0 0;
-moz-border-radius: 5px 5px 0 0;
-webkit-border-top-right-radius: 5px;
-webkit-border-top-left-radius: 5px;
}
#qunit-header a {
text-decoration: none;
color: #c2ccd1;
}
#qunit-header a:hover,
#qunit-header a:focus {
color: #fff;
}
#qunit-testrunner-toolbar label {
display: inline-block;
padding: 0 .5em 0 .1em;
}
#qunit-banner {
height: 5px;
}
#qunit-testrunner-toolbar {
padding: 0.5em 0 0.5em 2em;
color: #5E740B;
background-color: #eee;
overflow: hidden;
}
#qunit-userAgent {
padding: 0.5em 0 0.5em 2.5em;
background-color: #2b81af;
color: #fff;
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}
#qunit-modulefilter-container {
float: right;
}
/** Tests: Pass/Fail */
#qunit-tests {
list-style-position: inside;
}
#qunit-tests li {
padding: 0.4em 0.5em 0.4em 2.5em;
border-bottom: 1px solid #fff;
list-style-position: inside;
}
#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running {
display: none;
}
#qunit-tests li strong {
cursor: pointer;
}
#qunit-tests li a {
padding: 0.5em;
color: #c2ccd1;
text-decoration: none;
}
#qunit-tests li a:hover,
#qunit-tests li a:focus {
color: #000;
}
#qunit-tests ol {
margin-top: 0.5em;
padding: 0.5em;
background-color: #fff;
border-radius: 5px;
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
}
#qunit-tests table {
border-collapse: collapse;
margin-top: .2em;
}
#qunit-tests th {
text-align: right;
vertical-align: top;
padding: 0 .5em 0 0;
}
#qunit-tests td {
vertical-align: top;
}
#qunit-tests pre {
margin: 0;
white-space: pre-wrap;
word-wrap: break-word;
}
#qunit-tests del {
background-color: #e0f2be;
color: #374e0c;
text-decoration: none;
}
#qunit-tests ins {
background-color: #ffcaca;
color: #500;
text-decoration: none;
}
/*** Test Counts */
#qunit-tests b.counts { color: black; }
#qunit-tests b.passed { color: #5E740B; }
#qunit-tests b.failed { color: #710909; }
#qunit-tests li li {
padding: 5px;
background-color: #fff;
border-bottom: none;
list-style-position: inside;
}
/*** Passing Styles */
#qunit-tests li li.pass {
color: #3c510c;
background-color: #fff;
border-left: 10px solid #C6E746;
}
#qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
#qunit-tests .pass .test-name { color: #366097; }
#qunit-tests .pass .test-actual,
#qunit-tests .pass .test-expected { color: #999999; }
#qunit-banner.qunit-pass { background-color: #C6E746; }
/*** Failing Styles */
#qunit-tests li li.fail {
color: #710909;
background-color: #fff;
border-left: 10px solid #EE5757;
white-space: pre;
}
#qunit-tests > li:last-child {
border-radius: 0 0 5px 5px;
-moz-border-radius: 0 0 5px 5px;
-webkit-border-bottom-right-radius: 5px;
-webkit-border-bottom-left-radius: 5px;
}
#qunit-tests .fail { color: #000000; background-color: #EE5757; }
#qunit-tests .fail .test-name,
#qunit-tests .fail .module-name { color: #000000; }
#qunit-tests .fail .test-actual { color: #EE5757; }
#qunit-tests .fail .test-expected { color: green; }
#qunit-banner.qunit-fail { background-color: #EE5757; }
/** Result */
#qunit-testresult {
padding: 0.5em 0.5em 0.5em 2.5em;
color: #2b81af;
background-color: #D2E0E6;
border-bottom: 1px solid white;
}
#qunit-testresult .module-name {
font-weight: bold;
}
/** Fixture */
#qunit-fixture {
position: absolute;
top: -10000px;
left: -10000px;
width: 1000px;
height: 1000px;
}

View file

@ -0,0 +1,83 @@
// vim: set ts=2 sw=2 et tw=80:
class App {
constructor(conf) {
if (!(typeof conf === 'object' && conf)) {
throw new Error('Argument conf different from specification');
}
this.canvas = document.getElementById(conf.canvas);
if (!this.canvas || this.canvas.tagName !== 'CANVAS') {
throw new Error(`canvas is not a canvas`);
}
this.ctx = this.canvas.getContext('2d');
this.favourites = document.querySelector('div#favourites');
if (typeof conf.buttons === 'object' && conf.buttons) {
this.buttons = {}
for (const b of ['clear', 'button', 'camera'])
this.buttons[b] = document.getElementById(conf.buttons[b]);
if (this.buttons.clear)
this.buttons.clear.addEventListener('click', () =>
this.ctx.clearRect(0, 0, canvas.width, canvas.height));
if (this.buttons.camera)
this.buttons.camera.addEventListener('click', () => {
const base64 = this.canvas.toDataURL();
const img = document.createElement('img');
img.src = base64;
const span = document.createElement('span');
span.contentEditable = true;
this.favourites.appendChild(img);
this.favourites.appendChild(span);
});
}
this.canvas.addEventListener('mousedown', (e) => {
this.ctx.save();
this.ctx.lineWidth = 1;
this.ctx.moveTo(e.offsetX, e.offsetY);
this.ctx.beginPath();
this.mousedown = true;
});
this.canvas.addEventListener('mousemove', (e) => this.mousedown && this.draw(e));
const endPath = (e) => {
this.ctx.lineTo(e.offsetX, e.offsetY);
this.ctx.stroke();
this.ctx.restore();
this.mousedown = false;
};
this.canvas.addEventListener('mouseup', endPath);
this.canvas.addEventListener('mouseout', endPath);
this.strokeStyle = this.constructor.defaultStrokeStyle;
}
static get defaultStrokeStyle() {
return 'black';
}
get strokeStyle() {
return this.ctx.strokeStyle;
}
set strokeStyle(style) {
if (typeof style !== 'string') {
throw new Error('style is not a string');
}
this.ctx.strokeStyle = style;
}
draw(e) {
console.log(e);
this.ctx.lineTo(e.offsetX, e.offsetY);
this.ctx.stroke();
this.ctx.beginPath();
this.ctx.moveTo(e.offsetX, e.offsetY);
}
}

View file

@ -0,0 +1,19 @@
class PenBrush {
constructor() {
this.opacity = 1;
this.name = "PenBrush";
}
draw(ctx, strokeStyle, x, y) {
ctx.lineJoin = ctx.lineCap = 'round';
ctx.strokeStyle = strokeStyle;
ctx.lineTo(x, y);
ctx.stroke();
}
}
//TODO DiscBrush
//TODO StarBrush

View file

@ -0,0 +1,4 @@
const history = {
}

View file

@ -0,0 +1,136 @@
/* DO NOT MODIFY */
body{
font-family: "Lucida Sans Unicode", "Lucida Grande", sans-serif;
}
#app{
display: flex;
}
#canvas{
border: 1px solid #bbb;
}
.toolbar {
width: 56px;
text-align: center;
}
.toolbar button {
height: 25px;
width: 52px;
margin-bottom: 10px;
cursor: pointer;
}
#brush-toolbar{
width: 84px;
}
#brush-toolbar button{
height: 40px;
width: 80px;
}
#palette{
display: flex;
flex-wrap: wrap;
justify-content: space-around;
width: 53px;
height: 204px;
background-color: buttonface;
cursor: pointer;
}
.toolbar #camera-btn{
font-size: 1.5em;
line-height: 1.5em;
color: #444;
background: none;
border: none;
border-radius: 3px;
height: 40px;
outline: none;
}
.toolbar #camera-btn:hover{
background: #70a0e8;
}
.toolbar #camera-btn:active{
background: #0e57c3;
}
.p-color{
width: 25px;
height: 25px;
}
.black {background-color:rgb(0, 0, 0);}
.dark-gray {background-color:rgb(87, 87, 87);}
.red {background-color:rgb(173, 35, 35);}
.blue {background-color:rgb(42, 75, 215);}
.green {background-color:rgb(29, 105, 20);}
.brown {background-color:rgb(129, 74, 25);}
.purple {background-color:rgb(129, 38, 192);}
.light-gray {background-color:rgb(160, 160, 160);}
.light-green {background-color:rgb(129, 197, 122);}
.light-blue {background-color:rgb(157, 175, 255);}
.cyan {background-color:rgb(41, 208, 208);}
.orange {background-color:rgb(255, 146, 51);}
.yellow {background-color:rgb(255, 238, 51);}
.tan {background-color:rgb(233, 222, 187);}
.pink {background-color:rgb(255, 205, 243);}
.white {background-color:rgb(255, 255, 255);}
#clock {
background-color: #ddd;
width: 360px;
height: 48px;
text-align: center;
position: relative;
font-size: 2.5rem;
margin-top: -10px;
font-weight: bold;
margin-bottom: 20px;
}
#clock-time,
#progress-bar {
width: 100%;
height: 100%;
position: absolute;
top: 0;
left: 0;
}
#clock-time {
z-index: 10;
line-height: 48px; /* same height as #clock so that the time is vertically centered */
}
#progress-bar {
width: 0%;
height: 48px;
background-color: #8DFF80;
text-align: center;
line-height: 30px;
color: white;
}
#favourites div.gallery {
margin: 5px;
float: left;
width: 200px;
}
#favourites div.gallery img {
width: 90%;
height: 100px;
box-shadow: 1px 1px 3px 1px rgba(0, 0, 0, 0.42);
margin-left: 10px;
}
#favourites div.desc {
padding: 15px;
text-align: center;
}

View file

@ -0,0 +1,26 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Exercise 3 - Object-oriented Javascript</title>
<link rel="stylesheet" href="resources/qunit.css">
</head>
<body>
<canvas id="test-canvas" width="600" height="400" style="display: none"></canvas>
<div id="qunit"></div>
<div id="qunit-fixture"></div>
<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="resources/qunit-2.4.0.js"></script>
<script src="resources/lodash.js"></script>
<script src="resources/jsverify.standalone.js"></script>
<script src="qunit-compat.js"></script>
<script src="test.js"></script>
<script>
tests();
</script>
</body>
</html>

View file

@ -0,0 +1,103 @@
function tests() {
test("App class constructors and methods", function(assert) {
// The App class must be defined
equal(typeof App === 'function', true, "The App class must be defined");
equal(/^\s*class\s+/.test(App.toString()), true, "App is a function but it is not defined using the class keyword")
// The App class constructor should throw an error if its argument is undefined
assert.throws(function() {
new App(undefined)
}, "The App class constructor should throw an error if its argument is undefined")
// The App class constructor should throw an error if its argument is not a canvas
assert.throws(function() {
new App("");
}, "The App class constructor should throw an error if its argument is not an object")
assert.throws(function() {
new App(1);
}, "The App class constructor should throw an error if its argument is a number")
assert.throws(function() {
new App([]);
}, "The App class constructor should throw an error if its argument is an array")
assert.throws(function() {
new App(true);
}, "The App class constructor should throw an error if its argument is a boolean")
// The default Stroke Style should be accessible in a static way, and should be equal to "black"
equal(App.defaultStrokeStyle === 'black', true, 'The default Stroke Style should be accessible in a static way, and should be equal to "black"')
assert.throws(function() {
new App({});
}, "The App class constructor should throw an error if its argument options object is not pointing to a canvas element under the 'canvas' property")
const app = new App({canvas: 'test-canvas'})
equal(app.strokeStyle, "black", "Getter for strokeStyle is not defined")
// The draw method must be defined
equal(typeof app.draw === 'function', true, "The draw method must be defined")
});
test("History and Stroke object literals fields and methods", function(assert) {
// The Stroke class must be defined
equal(typeof Stroke === 'function', true, "The Stroke class must be defined");
equal(/^\s*class\s+/.test(Stroke.toString()), true, "Stroke is a function but it is not defined using the class keyword")
equal(function(){
try {
const stroke = new Stroke('square')
} catch (err) {
return false
}
return true
}(), true, "Stroke can be instantiated")
const stroke = new Stroke('square');
const stroke1 = new Stroke('circle');
const stroke2 = new Stroke('triangle');
history.initializeNewPath()
assert.throws(function() {
history.push()
}, "Must pass a Stroke instance when you push in the history")
equal(function() {
try {
history.push(stroke);
} catch (err) {
return false;
}
return true;
}(), true, "History push accepts Stroke instances as a parameter");
equal(history.pop()[0] === stroke, true, "Pop returns an array containing the pushed Stroke instance")
history.initializeNewPath();
history.push(stroke);
history.push(stroke1);
history.push(stroke2);
equal(history.pop().length, 3, "Pop returns an array containing the pushed Stroke instances")
history.initializeNewPath();
equal(history.pop().length, 0, "Pop on an empty history should return an empty array")
history.initializeNewPath(); //simulate mouse down
history.push(stroke); //simulate mouse move
history.push(stroke1); //simulate mouse move
history.initializeNewPath(); //simulate mouse up and down again
history.push(stroke2); //simulate mouse move
equal(history.pop().length, 1, "Pop returns an array containing the most recent path (Expected path with 1 Stroke)")
equal(history.pop().length, 2, "Pop returns an array containing the most recent path (Expected path with 2 Strokes)")
});
}