Commit aa0f5fc6 authored by Leonard Marschke's avatar Leonard Marschke

initial import from html-training-resource

parents
Pipeline #7224 passed with stages
in 13 minutes and 27 seconds
.DS_Store
node_modules/
dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
yarn.lock
package-lock.json
test/unit/coverage
test/e2e/reports
selenium-debug.log
# Editor directories and files
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
image: node:latest
variables:
DOCKER_DRIVER: overlay
GIT_SSH_COMMAND: "ssh -i .ssh/id_rsa -o UserKnownHostsFile=.ssh/known_hosts"
stages:
- install
- test
- build
install-dependencies:
stage: install
script:
- cd vue
- yarn
artifacts:
paths:
- vue/node_modules
expire_in: 2 days
vue-e2e:
stage: test
script:
- cd vue
- yarn run e2e
vue-unit:
stage: test
script:
- cd vue
- yarn run unit
dependencies:
- install-dependencies
plain-html-check:
stage: test
script:
- cd plain
- yarn global add html5-validator
- html5v index.html
dependencies: []
build-vue:
stage: build
script:
- cd vue
- yarn run build
dependencies:
- install-dependencies
artifacts:
paths:
- vue/dist
expire_in: 1 month
# HTML Training
Welcome to this assignment! We hope you had a lot of fun with building the API in your last assignment.
This week you will build two different user interfaces on top of your API (or one of ours if yours is not fully working).
One user interface will be in plain web languages like HTML5 and JavaScript (without any libraries).
The other user interface you will build on top of the [Vue.js](https://vuejs.org/) framework.
## Tasks (28 Points in total)
We recommend you to solve the tasks in the following order:
1. Solve the `plain` solution (see below for more information).
1. Try to get the Vue application running (see section `Vue.js task`).
1. Look at the `/home` page of the Vue app for more tasks.
1. Hand in your solution, the hand-in requirements are (1 Point):
* ZIP the complete source code directly from the root directory (so no useless subdirs in the ZIP please) but without the folder `node_modules`!
* Make an own solution, show us that this is your solution by adding comments where they are needed to understand your source code
* Upload the ZIP file to our Moodle
* You allowed to work in teams of 2, please write matriculation number(s) where it is asked for:
* [plain/index.html](plain/index.html)
* [vue/src/components/API.vue](vue/src/components/API.vue)
* [vue/src/components/Questions.vue](vue/src/components/Questions.vue)
* Write your answers in German or English, in code please write all English
* Do not change any file names if not really needed (and then please document).
* Set all backend URLs to `http://localhost:5000`
## General recommendations
We recommend that you use a Web IDE like WebStorm for this assignment.
If you want to use other editors like Atom it should be fine too, but sometimes it is easier to use a fully featured IDE (especially for Vue app development).
We recommend you to use your own API you developed in the last assignment.
In case it is not working properly, we provide you our reference API at this URL: https://flask-training-api.www-technologien.marschke.me/
Please note: Our API has some rate limits applied (1 Request per Second) to provide the same quality of service to every student, so please be patient when testing with it.
If you are not familiar with, you should check out the dev tools of your browser [Firefox](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_are_browser_developer_tools), [Chrome](https://developers.google.com/web/tools/chrome-devtools/).
## Plain task
In this task you will use the API with plain HTML and JavaScript (no CSS). The only source file is [plain/index.html](plain/index.html).
You do not need to build this file in any way. Simply open it with a recent web browser (Chrome(ium) or Firefox).
Most of the stuff you need we explained in the source file directly.
For all other things you will find plenty information on the web, to prevent confusions make sure the examples are using plain JavaScript (NO libraries) and HTML5.
### Expected result
For `index.html` we expect something like this as result:
![alt text](docs/expected-result-plain.png)
## Vue.js task
The more interesting task is the Vue task ;)
You will have to slightly modify some source files. These files are:
* [vue/src/components/API.vue](vue/src/components/API.vue)
* [vue/src/components/Questions.vue](vue/src/components/Questions.vue)
* [vue/src/constants.js](vue/src/constants.js) (For setting the backend URL)
Nevertheless you should make sure you understand (at least know what it is generally doing) the rest of the source code in [vue/src](vue/src) before starting to code.
This should make the coding easier for you. If you have any questions you can't answer yourself don't hesitate to ask us.
To get started with the Vue framework we recommend the Vue [documentation](https://vuejs.org/v2/guide/) and this [short (5 minutes long) video](https://player.vimeo.com/video/247494684).
### Development setup
To use the Vue framework you need to have [node installed](https://nodejs.org/en/download/). We recommend the LTS version.
As our package manager we are using [yarn](https://yarnpkg.com/). This software you can [install here](https://yarnpkg.com/lang/en/docs/install/).
After installing all these packages, you can `cd` into the root directory of the Vue app ([vue](vue)). Then execute
```bash
yarn install
```
This should run for a while (approximately a minute). Afterwards you can start the development server by executing
```bash
yarn run dev
```
The first compilation will take a little bit longer.
After finishing compiling you will see the development's server address in your terminal.
Every time you edit a file, it will get compiled and hot reloaded - so you will not have to restart the dev server every time you change something.
But be aware that this feature is not super reliable. So when you have an issue you can't explain, please try to stop the development server and start it again.
### Used libraries
Beside Vue.js itself we are using several other libraries, which makes the coding a little bit easier.
You don't need to understand these libraries, we just want to make sure you are not confused because of them.
First of all we are using [bootstrap-vue](https://bootstrap-vue.js.org/), so all HTML tags beginning with `b-` are provided and documented by them.
Because we are using FontAwesome at some places, we are using [vue-awesome](https://www.npmjs.com/package/vue-awesome) as well.
### Expected result
For the API page we expect something like this as result:
![alt text](docs/expected-result-vue.png)
## Ok, I really do not know what I am supposed to do
If you have any questions left or found a bug in our stub code, please mail us at `www-coding@lists.myhpi.de`.
We will be happy to help you.
<!DOCTYPE html>
<html>
<head>
<title>Plain JavaScript implementation (8 Points)</title>
</head>
<body>
<h1>Image view</h1>
<p>
Matriculation number(s): ??????, ?????? <br>
It took me ?, ? hours to solve this assignment (only the plain version)<br>
You are not allowed to use any libraries. Please provide a working solution for the latest Firefox and Chrome versions.<br>
You do not need to care about error handling for this task.
</p>
<h2>Task list</h2>
<ul>
<li>Add a form field to get an image id from the user to display (1 Point)</li>
<li>
Send a request to your API to get a list of images (or if it is not working, use our API at
<a href="https://flask-training-api.www-technologien.marschke.me/">https://flask-training-api.www-technologien.marschke.me/</a>)
(1 Point image request with fetch, 1 Point json object generation)
</li>
<li>
Display result in a div with an id (we added an example how a single entry should look like)
(0.5 Points Replacing ID in table, 0.5 Points replacing src in Image, 2 Points replacing captions)
</li>
<li>Make use of the <code>API_URL</code> variable in your JavaScript code (1 Point)</li>
<li>Do not write "obfuscated" code ;) and fill out ? above. (1 Point)</li>
</ul>
<h2>Generated content</h2>
<div id="content">
<table>
<thead>
<tr>
<th>ID</th>
<th>Picture</th>
<th>Captions</th>
</tr>
</thead>
<tbody>
<tr>
<th id="image-id">1</th>
<td><img id="image-image-tag" src="https://flask-training-api.www-technologien.marschke.me/v1/images/1/bitmap" alt="Image described by captions"></td>
<td>
<ul id="image-caption-list">
<li>One jet lands at an airport while another takes off next to it.</li>
<li>Two airplanes parked in an airport.</li>
<li>Two jets taxi past each other.</li>
<li>Two parked jet airplanes facing opposite directions.</li>
<li>two passenger planes on a grassy plain</li>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
<script type="text/javascript">
API_URL = 'http://localhost:5000'
/* Some implementation notes:
* You should check out fetch calls in JavaScript
* You can access DOM elements (like your form field) by document.getElementById('image-id-input')
* You can access properties of DOM elements by accessing them like document.getElementById('image-id-input').value
* You should check out the onsubmit event of the form tag
*/
// Write your JavaScript code here
</script>
</body>
</html>
{
"presets": [
["env", {
"modules": false,
"targets": {
"browsers": ["> 1%", "last 2 versions", "not ie <= 8"]
}
}],
"stage-2"
],
"plugins": ["transform-vue-jsx", "transform-runtime"],
"env": {
"test": {
"presets": ["env", "stage-2"],
"plugins": ["transform-vue-jsx", "istanbul"]
}
}
}
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
/build/
/config/
/dist/
/*.js
/test/unit/coverage/
// https://eslint.org/docs/user-guide/configuring
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint'
},
env: {
browser: true,
},
extends: [
// https://github.com/vuejs/eslint-plugin-vue#priority-a-essential-error-prevention
// consider switching to `plugin:vue/strongly-recommended` or `plugin:vue/recommended` for stricter rules.
'plugin:vue/essential',
// https://github.com/standard/standard/blob/master/docs/RULES-en.md
'standard'
],
// required to lint *.vue files
plugins: [
'vue'
],
// add your custom rules here
rules: {
// allow async-await
'generator-star-spacing': 'off',
// allow debugger during development
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off'
}
}
.DS_Store
node_modules/
/dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
/test/unit/coverage/
/test/e2e/reports/
selenium-debug.log
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
yarn.lock
// https://github.com/michael-ciniawsky/postcss-load-config
module.exports = {
"plugins": {
"postcss-import": {},
"postcss-url": {},
// to edit target browsers: use "browserslist" field in package.json
"autoprefixer": {}
}
}
# html-training-vue
> Just another vue "education" project
## Build Setup
``` bash
# install dependencies
yarn install
# serve with hot reload at localhost:8080
yarn run dev
# build for production with minification
yarn run build
# build for production and view the bundle analyzer report
yarn run build --report
# run unit tests -- we do not have unit tests in this project
yarn run unit
# run e2e tests -- we do not have end to end tests in this project
yarn run e2e
# run all tests -- we do not have tests at all :(
yarn test
```
For a detailed explanation on how things work, check out the [guide](http://vuejs-templates.github.io/webpack/) and [docs for vue-loader](http://vuejs.github.io/vue-loader).
'use strict'
require('./check-versions')()
process.env.NODE_ENV = 'production'
const ora = require('ora')
const rm = require('rimraf')
const path = require('path')
const chalk = require('chalk')
const webpack = require('webpack')
const config = require('../config')
const webpackConfig = require('./webpack.prod.conf')
const spinner = ora('building for production...')
spinner.start()
rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), err => {
if (err) throw err
webpack(webpackConfig, (err, stats) => {
spinner.stop()
if (err) throw err
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false, // If you are using ts-loader, setting this to true will make TypeScript errors show up during build.
chunks: false,
chunkModules: false
}) + '\n\n')
if (stats.hasErrors()) {
console.log(chalk.red(' Build failed with errors.\n'))
process.exit(1)
}
console.log(chalk.cyan(' Build complete.\n'))
console.log(chalk.yellow(
' Tip: built files are meant to be served over an HTTP server.\n' +
' Opening index.html over file:// won\'t work.\n'
))
})
})
'use strict'
const chalk = require('chalk')
const semver = require('semver')
const packageConfig = require('../package.json')
const shell = require('shelljs')
function exec (cmd) {
return require('child_process').execSync(cmd).toString().trim()
}
const versionRequirements = [
{
name: 'node',
currentVersion: semver.clean(process.version),
versionRequirement: packageConfig.engines.node
}
]
if (shell.which('npm')) {
versionRequirements.push({
name: 'npm',
currentVersion: exec('npm --version'),
versionRequirement: packageConfig.engines.npm
})
}
module.exports = function () {
const warnings = []
for (let i = 0; i < versionRequirements.length; i++) {
const mod = versionRequirements[i]
if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) {
warnings.push(mod.name + ': ' +
chalk.red(mod.currentVersion) + ' should be ' +
chalk.green(mod.versionRequirement)
)
}
}
if (warnings.length) {
console.log('')
console.log(chalk.yellow('To use this template, you must update following to modules:'))
console.log()
for (let i = 0; i < warnings.length; i++) {
const warning = warnings[i]
console.log(' ' + warning)
}
console.log()
process.exit(1)
}
}
'use strict'
const path = require('path')
const config = require('../config')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const packageConfig = require('../package.json')
exports.assetsPath = function (_path) {
const assetsSubDirectory = process.env.NODE_ENV === 'production'
? config.build.assetsSubDirectory
: config.dev.assetsSubDirectory
return path.posix.join(assetsSubDirectory, _path)
}
exports.cssLoaders = function (options) {
options = options || {}
const cssLoader = {
loader: 'css-loader',
options: {
sourceMap: options.sourceMap
}
}
const postcssLoader = {
loader: 'postcss-loader',
options: {
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
const loaders = options.usePostCSS ? [cssLoader, postcssLoader] : [cssLoader]
if (loader) {
loaders.push({
loader: loader + '-loader',
options: Object.assign({}, loaderOptions, {
sourceMap: options.sourceMap
})
})
}
// Extract CSS when that option is specified
// (which is the case during production build)
if (options.extract) {
return ExtractTextPlugin.extract({
use: loaders,
fallback: 'vue-style-loader'
})
} else {
return ['vue-style-loader'].concat(loaders)
}
}
// https://vue-loader.vuejs.org/en/configurations/extract-css.html
return {
css: generateLoaders(),
postcss: generateLoaders(),
less: generateLoaders('less'),
sass: generateLoaders('sass', { indentedSyntax: true }),
scss: generateLoaders('sass'),
stylus: generateLoaders('stylus'),
styl: generateLoaders('stylus')
}
}
// Generate loaders for standalone style files (outside of .vue)
exports.styleLoaders = function (options) {
const output = []
const loaders = exports.cssLoaders(options)
for (const extension in loaders) {
const loader = loaders[extension]
output.push({
test: new RegExp('\\.' + extension + '$'),
use: loader
})
}
return output
}
exports.createNotifierCallback = () => {
const notifier = require('node-notifier')
return (severity, errors) => {
if (severity !== 'error') return
const error = errors[0]
const filename = error.file && error.file.split('!').pop()
notifier.notify({
title: packageConfig.name,
message: severity + ': ' + error.name,
subtitle: filename || '',
icon: path.join(__dirname, 'logo.png')
})
}
}
'use strict'
const utils = require('./utils')
const config = require('../config')
const isProduction = process.env.NODE_ENV === 'production'
const sourceMapEnabled = isProduction
? config.build.productionSourceMap
: config.dev.cssSourceMap
module.exports = {
loaders: utils.cssLoaders({
sourceMap: sourceMapEnabled,
extract: isProduction
}),
cssSourceMap: sourceMapEnabled,
cacheBusting: config.dev.cacheBusting,
transformToRequire: {
video: ['src', 'poster'],
source: 'src',
img: 'src',
image: 'xlink:href'
}
}
'use strict'
const path = require('path')
const utils = require('./utils')
const config = require('../config')
const vueLoaderConfig = require('./vue-loader.conf')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
const createLintingRule = () => ({
test: /\.(js|vue)$/,
loader: 'eslint-loader',
enforce: 'pre',
include: [resolve('src'), resolve('test')],
options: {
formatter: require('eslint-friendly-formatter'),
emitWarning: !config.dev.showEslintErrorsInOverlay
}
})
module.exports = {
context: path.resolve(__dirname, '../'),
entry: {
app: './src/main.js'
},
output: {
path: config.build.assetsRoot,
filename: '[name].js',
publicPath: process.env.NODE_ENV === 'production'
? config.build.assetsPublicPath
: config.dev.assetsPublicPath
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src'),
}
},
module: {
rules: [
...(config.dev.useEslint ? [createLintingRule()] : []),
{
test: /\.vue$/,
loader: 'vue-loader',
options: vueLoaderConfig
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test'), resolve('node_modules/webpack-dev-server/client')]
},
{
test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('img/[name].[hash:7].[ext]')
}
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('media/[name].[hash:7].[ext]')
}
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/,
loader: 'url-loader',
options: {
limit: 10000,
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}
]
},
node: {
// prevent webpack from injecting useless setImmediate polyfill because Vue
// source contains it (although only uses it if it's native).
setImmediate: false,
// prevent webpack from injecting mocks to Node native modules
// that does not make sense for the client
dgram: 'empty',
fs: 'empty',
net: 'empty',
tls: 'empty',
child_process: 'empty'
}
}
'use strict'
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const path = require('path')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
const portfinder = require('portfinder')
const HOST = process.env.HOST
const PORT = process.env.PORT && Number(process.env.PORT)
const devWebpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap, usePostCSS: true })
},
// cheap-module-eval-source-map is faster for development
devtool: config.dev.devtool,
// these devServer options should be customized in /config/index.js
devServer: {
clientLogLevel: 'warning',
historyApiFallback: {
rewrites: [
{ from<