frontend_vue -- set up dev/prod container

This commit is contained in:
voje 2019-03-18 23:51:26 +01:00
parent 2d4a6a152a
commit e5b6f02abd
36 changed files with 13121 additions and 0 deletions

1
.gitignore vendored
View File

@ -1,3 +1,4 @@
data/samples/ data/samples/
*egg-info/ *egg-info/
*.pyc *.pyc
src/frontend_vue/node_modules/

View File

@ -0,0 +1,21 @@
# vue_frontend
> Frontend for Valency App.
## Build Setup
``` bash
# install dependencies
npm install
# serve with hot reload at localhost:8080
npm run dev
# build for production with minification
npm run build
# build for production and view the bundle analyzer report
npm run build --report
```
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).

View File

@ -0,0 +1,41 @@
'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'
))
})
})

View File

@ -0,0 +1,54 @@
'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)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

View File

@ -0,0 +1,101 @@
'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')
})
}
}

View File

@ -0,0 +1,22 @@
'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'
}
}

View File

@ -0,0 +1,82 @@
'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)
}
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: [
{
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'
}
}

View File

@ -0,0 +1,95 @@
'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: /.*/, to: path.posix.join(config.dev.assetsPublicPath, 'index.html') },
],
},
hot: true,
contentBase: false, // since we use CopyWebpackPlugin.
compress: true,
host: HOST || config.dev.host,
port: PORT || config.dev.port,
open: config.dev.autoOpenBrowser,
overlay: config.dev.errorOverlay
? { warnings: false, errors: true }
: false,
publicPath: config.dev.assetsPublicPath,
proxy: config.dev.proxyTable,
quiet: true, // necessary for FriendlyErrorsPlugin
watchOptions: {
poll: config.dev.poll,
}
},
plugins: [
new webpack.DefinePlugin({
'process.env': require('../config/dev.env')
}),
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(), // HMR shows correct file names in console on update.
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: 'index.html',
inject: true
}),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.dev.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
module.exports = new Promise((resolve, reject) => {
portfinder.basePort = process.env.PORT || config.dev.port
portfinder.getPort((err, port) => {
if (err) {
reject(err)
} else {
// publish the new Port, necessary for e2e tests
process.env.PORT = port
// add port to devServer config
devWebpackConfig.devServer.port = port
// Add FriendlyErrorsPlugin
devWebpackConfig.plugins.push(new FriendlyErrorsPlugin({
compilationSuccessInfo: {
messages: [`Your application is running here: http://${devWebpackConfig.devServer.host}:${port}`],
},
onErrors: config.dev.notifyOnErrors
? utils.createNotifierCallback()
: undefined
}))
resolve(devWebpackConfig)
}
})
})

View File

@ -0,0 +1,145 @@
'use strict'
const path = require('path')
const utils = require('./utils')
const webpack = require('webpack')
const config = require('../config')
const merge = require('webpack-merge')
const baseWebpackConfig = require('./webpack.base.conf')
const CopyWebpackPlugin = require('copy-webpack-plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const ExtractTextPlugin = require('extract-text-webpack-plugin')
const OptimizeCSSPlugin = require('optimize-css-assets-webpack-plugin')
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
const env = require('../config/prod.env')
const webpackConfig = merge(baseWebpackConfig, {
module: {
rules: utils.styleLoaders({
sourceMap: config.build.productionSourceMap,
extract: true,
usePostCSS: true
})
},
devtool: config.build.productionSourceMap ? config.build.devtool : false,
output: {
path: config.build.assetsRoot,
filename: utils.assetsPath('js/[name].[chunkhash].js'),
chunkFilename: utils.assetsPath('js/[id].[chunkhash].js')
},
plugins: [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({
'process.env': env
}),
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: config.build.productionSourceMap,
parallel: true
}),
// extract css into its own file
new ExtractTextPlugin({
filename: utils.assetsPath('css/[name].[contenthash].css'),
// Setting the following option to `false` will not extract CSS from codesplit chunks.
// Their CSS will instead be inserted dynamically with style-loader when the codesplit chunk has been loaded by webpack.
// It's currently set to `true` because we are seeing that sourcemaps are included in the codesplit bundle as well when it's `false`,
// increasing file size: https://github.com/vuejs-templates/webpack/issues/1110
allChunks: true,
}),
// Compress extracted CSS. We are using this plugin so that possible
// duplicated CSS from different components can be deduped.
new OptimizeCSSPlugin({
cssProcessorOptions: config.build.productionSourceMap
? { safe: true, map: { inline: false } }
: { safe: true }
}),
// generate dist index.html with correct asset hash for caching.
// you can customize output by editing /index.html
// see https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: config.build.index,
template: 'index.html',
inject: true,
minify: {
removeComments: true,
collapseWhitespace: true,
removeAttributeQuotes: true
// more options:
// https://github.com/kangax/html-minifier#options-quick-reference
},
// necessary to consistently work with multiple chunks via CommonsChunkPlugin
chunksSortMode: 'dependency'
}),
// keep module.id stable when vendor modules does not change
new webpack.HashedModuleIdsPlugin(),
// enable scope hoisting
new webpack.optimize.ModuleConcatenationPlugin(),
// split vendor js into its own file
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor',
minChunks (module) {
// any required modules inside node_modules are extracted to vendor
return (
module.resource &&
/\.js$/.test(module.resource) &&
module.resource.indexOf(
path.join(__dirname, '../node_modules')
) === 0
)
}
}),
// extract webpack runtime and module manifest to its own file in order to
// prevent vendor hash from being updated whenever app bundle is updated
new webpack.optimize.CommonsChunkPlugin({
name: 'manifest',
minChunks: Infinity
}),
// This instance extracts shared chunks from code splitted chunks and bundles them
// in a separate chunk, similar to the vendor chunk
// see: https://webpack.js.org/plugins/commons-chunk-plugin/#extra-async-commons-chunk
new webpack.optimize.CommonsChunkPlugin({
name: 'app',
async: 'vendor-async',
children: true,
minChunks: 3
}),
// copy custom static assets
new CopyWebpackPlugin([
{
from: path.resolve(__dirname, '../static'),
to: config.build.assetsSubDirectory,
ignore: ['.*']
}
])
]
})
if (config.build.productionGzip) {
const CompressionWebpackPlugin = require('compression-webpack-plugin')
webpackConfig.plugins.push(
new CompressionWebpackPlugin({
asset: '[path].gz[query]',
algorithm: 'gzip',
test: new RegExp(
'\\.(' +
config.build.productionGzipExtensions.join('|') +
')$'
),
threshold: 10240,
minRatio: 0.8
})
)
}
if (config.build.bundleAnalyzerReport) {
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin
webpackConfig.plugins.push(new BundleAnalyzerPlugin())
}
module.exports = webpackConfig

View File

@ -0,0 +1,3 @@
{
"api_addr": "http://localhost:5004"
}

View File

@ -0,0 +1,3 @@
{
"api_addr": "http://localhost:5004"
}

View File

@ -0,0 +1,3 @@
{
"api_addr": "http://193.2.76.103:5004"
}

View File

@ -0,0 +1,7 @@
'use strict'
const merge = require('webpack-merge')
const prodEnv = require('./prod.env')
module.exports = merge(prodEnv, {
NODE_ENV: '"development"'
})

View File

@ -0,0 +1,69 @@
'use strict'
// Template version: 1.3.1
// see http://vuejs-templates.github.io/webpack for documentation.
const path = require('path')
module.exports = {
dev: {
// Paths
assetsSubDirectory: 'static',
assetsPublicPath: '/',
proxyTable: {},
// Various Dev Server settings
host: 'localhost', // can be overwritten by process.env.HOST
port: 8080, // can be overwritten by process.env.PORT, if port is in use, a free one will be determined
autoOpenBrowser: false,
errorOverlay: true,
notifyOnErrors: true,
poll: false, // https://webpack.js.org/configuration/dev-server/#devserver-watchoptions-
/**
* Source Maps
*/
// https://webpack.js.org/configuration/devtool/#development
devtool: 'cheap-module-eval-source-map',
// If you have problems debugging vue-files in devtools,
// set this to false - it *may* help
// https://vue-loader.vuejs.org/en/options.html#cachebusting
cacheBusting: true,
cssSourceMap: true
},
build: {
// Template for index.html
index: path.resolve(__dirname, '../dist/index.html'),
// Paths
assetsRoot: path.resolve(__dirname, '../dist'),
assetsSubDirectory: 'static',
assetsPublicPath: '/',
/**
* Source Maps
*/
productionSourceMap: true,
// https://webpack.js.org/configuration/devtool/#production
devtool: '#source-map',
// Gzip off by default as many popular static hosts such as
// Surge or Netlify already gzip all static assets for you.
// Before setting to `true`, make sure to:
// npm install --save-dev compression-webpack-plugin
productionGzip: false,
productionGzipExtensions: ['js', 'css'],
// Run the build command with an extra argument to
// View the bundle analyzer report after build finishes:
// `npm run build --report`
// Set to `true` or `false` to always turn it on or off
bundleAnalyzerReport: process.env.npm_config_report
}
}

View File

@ -0,0 +1,4 @@
'use strict'
module.exports = {
NODE_ENV: '"production"'
}

View File

@ -0,0 +1,12 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<title>vue_frontend</title>
</head>
<body>
<div id="app"></div>
<!-- built files will be auto injected -->
</body>
</html>

10853
src/frontend_vue/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,67 @@
{
"name": "vue_frontend",
"version": "1.0.0",
"description": "Frontend for Valency App.",
"author": "voje <kristjan.voje@gmail.com>",
"private": true,
"scripts": {
"dev": "webpack-dev-server --inline --progress --config build/webpack.dev.conf.js",
"start": "npm run dev",
"build": "node build/build.js"
},
"dependencies": {
"axios": "^0.18.0",
"bootstrap-vue": "^2.0.0-rc.11",
"sha256": "^0.2.0",
"vue": "^2.5.2",
"vue-cookies": "^1.5.6",
"vue-router": "^3.0.1",
"vue-spinner": "^1.0.3"
},
"devDependencies": {
"autoprefixer": "^7.1.2",
"babel-core": "^6.22.1",
"babel-helper-vue-jsx-merge-props": "^2.0.3",
"babel-loader": "^7.1.1",
"babel-plugin-syntax-jsx": "^6.18.0",
"babel-plugin-transform-runtime": "^6.22.0",
"babel-plugin-transform-vue-jsx": "^3.5.0",
"babel-preset-env": "^1.3.2",
"babel-preset-stage-2": "^6.22.0",
"chalk": "^2.0.1",
"copy-webpack-plugin": "^4.0.1",
"css-loader": "^0.28.0",
"extract-text-webpack-plugin": "^3.0.0",
"file-loader": "^1.1.4",
"friendly-errors-webpack-plugin": "^1.6.1",
"html-webpack-plugin": "^2.30.1",
"node-notifier": "^5.1.2",
"optimize-css-assets-webpack-plugin": "^3.2.0",
"ora": "^1.2.0",
"portfinder": "^1.0.13",
"postcss-import": "^11.0.0",
"postcss-loader": "^2.0.8",
"postcss-url": "^7.2.1",
"rimraf": "^2.6.0",
"semver": "^5.3.0",
"shelljs": "^0.7.6",
"uglifyjs-webpack-plugin": "^1.1.1",
"url-loader": "^0.5.8",
"vue-loader": "^13.3.0",
"vue-style-loader": "^3.0.1",
"vue-template-compiler": "^2.5.2",
"webpack": "^3.6.0",
"webpack-bundle-analyzer": "^2.9.0",
"webpack-dev-server": "^2.9.1",
"webpack-merge": "^4.1.0"
},
"engines": {
"node": ">= 6.0.0",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 8"
]
}

View File

@ -0,0 +1 @@
module.exports = {};

View File

@ -0,0 +1,9 @@
<template>
<router-view/>
</template>
<script>
export default {
name: 'App',
}
</script>

View File

@ -0,0 +1,206 @@
<template>
<div class="container-fluid">
<div class="row">
<div class="col-sm-12">
<p class="pb-0 mb-0">Urejanje pomenov za besedo: <b>{{ hw }}</b>.</p>
<p><small>
Z miško kliknite na poved, nato kliknite na pomen, ki ga želite dodeliti povedi. Par poved&#8210;pomen bo obarvan z modro. Pare lahko shranite s klikom na gumb "Shrani". Možno je dodajanje poljubnih pomenov.
</small></p>
<button v-on:click="cancel_all">Prekliči</button>
<button v-on:click="save_all">Shrani</button>
</div>
</div>
<div class="row">
<!-- left column: sentences -->
<div class="my-sent col-sm-6">
<div
v-for="(sentence, ssj_id) in sentences"
v-on:click="pick_ssj_id(ssj_id)"
class="border rounded my-sentences my-pointer"
v-bind:class="{
'border-primary': ssj_id === picked_ssj_id
}"
>
<div>
<span
v-for="(word, index) in sentence.words"
v-bind:class="{
'text-primary': index === parseInt(sentence.hw_idx)
}"
>
<span v-if="$root.mkspace(index, word)">&nbsp;</span>{{ word }}
</span>
</div>
<hr>
<div class="col-sm-12"><small>
<div v-if="ssj_id in local_sense_map">
<Sense v-bind:sense="local_sense_map[ssj_id].sense"></Sense>
</div>
<div v-else>
<Sense v-bind:sense="undefined"></Sense>
</div>
</small></div>
</div>
</div>
<!-- right column: senses -->
<div class="col-sm-6 border rounded my-div-scroll sticky-top">
<div
v-for="sense in local_senses"
class="my-pointer"
v-on:click="picked_sense_id = sense.sense_id"
v-bind:class="{
'text-primary': sense.sense_id === picked_sense_id
}"
>
<Sense v-bind:sense="sense"></Sense>
</div>
<div class="row">
<div class="col-sm-12">
<textarea class="my-textarea" v-model="new_sense_desc"></textarea>
</div>
<div class="col-sm-12">
<button v-on:click="new_sense">Dodaj pomen</button>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
import Sense from "./Sense"
export default {
name: "EditSenses",
props: ["hw", "sentences", "sens"],
components: {
Sense: Sense
},
data () { return {
picked_ssj_id: null,
picked_sense_id: null,
local_senses: [], // make changes on a local copy
local_sense_map: {}, // make changes on a local copy
new_sense_desc: "",
new_senses: [], // only send changes to server
delta_sense_map: {}, // only send changes to server
}},
created: function() {
// not sure if needed, maybe move to data()
this.local_senses = this.sens.senses
var json = JSON.stringify(this.sens.sense_map)
this.local_sense_map = JSON.parse(json)
for (var ssj_id in this.local_sense_map) {
this.local_sense_map[ssj_id].sense = this.sense_id_to_sense(
this.local_sense_map[ssj_id].sense_id)
}
},
watch: {
picked_ssj_id: function() {
this.new_link()
},
picked_sense_id: function() {
this.new_link()
}
},
methods: {
pick_ssj_id: function(ssj_id) {
this.picked_ssj_id = ssj_id
if (ssj_id in this.local_sense_map) {
this.picked_sense_id = this.local_sense_map[ssj_id].sense_id
}
},
new_link: function() {
if (this.picked_ssj_id === null ||
this.picked_sense_id === null) { return }
this.local_sense_map[this.picked_ssj_id] = {
sense_id: this.picked_sense_id,
sense: this.sense_id_to_sense(this.picked_sense_id)
}
this.delta_sense_map[this.picked_ssj_id] = { sense_id: this.picked_sense_id }
},
new_sense: function(sense_id) {
if (this.new_sense_desc === "") {
return
}
var new_sense = {
hw: this.hw,
author: this.$root.store.username,
desc: this.new_sense_desc,
sense_id: "tmp_sense_id" + (new Date().getTime()),
}
this.local_senses.push(new_sense)
this.new_senses.push(new_sense)
this.new_sense_desc = ""
},
sense_id_to_sense: function(sense_id) {
for (var i=0; i<this.local_senses.length; i++) {
if (this.local_senses[i].sense_id === sense_id) {
return this.local_senses[i]
}
}
return undefined
},
cancel_all: function() {
this.$parent.state = "normal"
},
save_all: function() {
const data = {
token: this.$root.store.token,
hw: this.hw,
sense_map: this.delta_sense_map,
new_senses: this.new_senses,
}
var component = this
function exit_edit(component) {
component.$parent.state = "normal"
component.$parent.request_reload = true
}
// don't update if there are no changes
if (
Object.keys(data.sense_map).length === 0 &&
data.new_senses.length === 0
) { exit_edit(component); return }
// exit after update
this.$http.post(
this.$root.store.api_addr + "/api/senses/update",
data,
{ headers: {
'Content-type': 'application/json',
}}
).then(function () {
exit_edit(component)
})
},
}
}
</script>
<style scoped>
.my-div-scroll {
margin-top: 5px;
height: 90vh;
overflow-y: auto;
padding-top: 5px;
}
.my-pointer {
cursor: pointer;
}
.my-textarea {
width: 100%;
}
.my-sentences {
margin: 5px 0px 20px 0px;
padding: 5px;
}
.my-sent {
word-wrap: break-word;
}
.my-sent span {
display: inline-block;
}
</style>

View File

@ -0,0 +1,208 @@
<template>
<!-- clicking on empty space clears highlights -->
<div v-on:click="clearOnClick" class="container-fluid">
<hr>
<div class="row">
<div class="col-sm-7">
<div class="row">
<div class="col-sm-12">
št. povedi: {{ frameData.sentences.length }}
</div>
</div>
<!--frame slots-->
<div class="row my-frames">
<div class="col-sm-12">
<span v-for="(slot, key) in frameData.slots">
<span
v-bind:class="{
'my-pointer text-danger': hasHoverTid(idx=key),
'my-underline text-danger': hasSelTid(idx=key)
}"
v-on:mouseover="setHid(idx=key)"
v-on:mouseleave="setHid()"
v-on:click="setSid(idx=key)"
>{{ slot.functor }}</span>&nbsp;&nbsp;
</span>
</div>
</div>
</div>
<!--sense information-->
<div v-if="$root.store.radio === 'three'" class="col-sm-5">
<Sense v-bind:sense="getSense()"></Sense>
</div>
</div>
<!--sentences-->
<div class="row">
<!-- fmode: prikaz->udelezenske vloge (drugacno razvrscanje povedi) -->
<div v-if="fmode" class="col-sm-12">
<div v-for="hw in getAggrHws()">
<blockquote v-for="sentence in getAggrSent(hw)">
<span class="text-secondary">&nbsp;{{ hw }}</span><br>
<span
v-for="(token, index) in sentence"
v-bind:class="{
'my-pointer text-danger': hasHoverTid(idx=null, tid=token[0]), 'my-underline text-danger': hasSelTid(idx=null, tid=token[0]),
'text-primary': isHw(token[0]),
}"
v-on:mouseover="setHid(idx=null, tid=token[0])"
v-on:mouseleave="setHid()"
v-on:click="setSid(idx=null, tid=token[0])"
v-bind:title="token[1].msd"
><span v-if="$root.mkspace(index, token[1].word)">&nbsp;</span>{{ token[1].word }}</span>
</blockquote>
</div>
</div>
<div v-else class="col-sm-12">
<blockquote v-for="sentence in frameData.sentences">
<span
v-for="(token, index) in sentence"
v-bind:class="{
'my-pointer text-danger': hasHoverTid(idx=null, tid=token[0]), 'my-underline text-danger': hasSelTid(idx=null, tid=token[0]),
'text-primary': isHw(token[0]),
}"
v-on:mouseover="setHid(idx=null, tid=token[0])"
v-on:mouseleave="setHid()"
v-on:click="setSid(idx=null, tid=token[0])"
v-bind:title="token[1].msd"
><span v-if="$root.mkspace(index, token[1].word)">&nbsp;</span>{{ token[1].word }}</span>
</blockquote>
</div>
</div>
<br>
</div>
</template>
<script>
import Sense from "./Sense"
export default {
name: "Frame",
props: {
frameData: {},
sensData: {},
fmode: {
default: false,
type: Boolean,
},
},
data() { return {
hid: null, // hover functor index
sid: null, // select functor index (click)
}},
components: {
Sense: Sense
},
watch: {
frameData: function () {
this.hid = null,
this.sid = null
}
},
methods: {
setHid: function (idx=null, tid=null) {
// calling this functoin without parameters
// resets hid
if (tid === null) {
this.hid = idx
return
}
for (var i=0; i<this.frameData.slots.length; i++) {
if (this.frameData.slots[i].tids.includes(tid)) {
this.hid = i
return
}
}
},
clearOnClick: function (event) {
if (event.target.tagName !== "SPAN") {
this.sid = null
}
},
setSid: function (idx=null, tid=null) {
this.sid = null
if (tid === null) {
this.sid = idx
return
}
for (var i=0; i<this.frameData.slots.length; i++) {
if (this.frameData.slots[i].tids.includes(tid)) {
this.sid = i
return
}
}
},
hasHoverTid: function(idx=null, tid=null) {
if (this.hid === null) {
return false
}
if (tid === null) {
if (idx == this.hid) {
return true
}
return false
}
return this.frameData.slots[this.hid].tids.includes(tid)
},
hasSelTid: function (idx=null, tid=null) {
if (this.sid === null) {
return false
}
if (tid === null) {
if (idx == this.sid) {
return true
}
return false
}
return this.frameData.slots[this.sid].tids.includes(tid)
},
isHw: function (tid) {
return this.frameData.tids.includes(tid)
},
getSense: function () {
for (var i in this.sensData.senses) {
if (this.sensData.senses[i].sense_id === this.frameData.sense_info.sense_id) {
return this.sensData.senses[i]
}
}
return undefined
},
getAggrHws: function() {
return (Object.keys(this.frameData.aggr_sent)).sort()
},
getAggrSent: function(hw) {
var sentences = []
for (var i=0; i<this.frameData.aggr_sent[hw].length; i++) {
sentences.push(
this.frameData.sentences[this.frameData.aggr_sent[hw][i]]
)
}
return sentences
},
}
}
</script>
<style scoped>
.my-pointer {
cursor: pointer;
}
.my-underline {
text-decoration: underline;
}
.my-frames {
margin-top: 10px;
margin-bottom: 2px;
}
blockquote {
background: #ffffff;
border-left: 4px solid #ccc;
margin: 10px 0px 10px 10px;
padding: 0px 0px 0px 5px;
word-wrap: break-word;
}
blockquote span {
display: inline-block;
}
</style>

View File

@ -0,0 +1,50 @@
<template>
<div>
<p
v-if="this.$root.store.api_error !== null"
class="text-warning"
>
api_error: {{ this.$root.store.api_error }}
</p>
<Nav></Nav>
<div class="my-home container-fluid">
<div class="row">
<div id="serach" class="col-sm-2 border-right fill">
<LWords v-if="navSS()"></LWords>
<LFunctors v-else></LFunctors>
</div>
<div class="col-sm-10">
<router-view></router-view>
</div>
</div>
</div>
</div>
</template>
<script>
import Nav from "./Nav"
import LWords from "./LWords"
import LFunctors from "./LFunctors"
import MainDispl from "./MainDispl"
export default {
name: 'Home',
components: {
Nav: Nav,
LWords: LWords,
LFunctors: LFunctors,
MainDispl: MainDispl,
},
methods: {
navSS: function () {
return this.$root.storeGet("navSS") === "words"
}
}
}
</script>
<style scoped>
.my-home {
padding-top: 10px;
}
</style>

View File

@ -0,0 +1,51 @@
<template>
<div>
<table>
<tr v-for="functor in functors">
<td><a href="#" v-on:click="selectFunctor(functor)">{{ functor[0] }}</a></td>
<td>({{ functor[1] }})</td>
</tr>
</table>
</div>
</template>
<script>
export default {
name: "LWords",
props: ["appState"],
data() {return {
functors: []
}},
methods: {
apiGetFunctors: function () {
var component = this
this.$http.get(this.$root.store.api_addr + "/api/functors")
.then(function(response) {
component.$root.store.api_error = null
component.functors = response.data
})
.catch(function(error) {
component.$root.store.api_error = error
})
},
selectFunctor: function (functor) {
this.$router.push({
name: "MainDispl",
params: {
hw: functor[0],
fmode: true
}
})
}
},
mounted: function() {
this.apiGetFunctors()
}
}
</script>
<style>
table {
width: 100%;
}
</style>

View File

@ -0,0 +1,80 @@
<template>
<div>
<select v-model="selectedLetter">
<option v-for="letter in alphabet" :value="letter">
{{ letter.toUpperCase() }} ({{ getNumWords(letter) }})
</option>
</select>
<table>
<tr v-for="word in getWords()">
<td><a href="#" v-on:click="selectHw(word)">{{ word[0] }}
<span v-if="$root.store.has_se.includes(word[0])">se</span>
</a></td>
<td>({{ word[1] }})</td>
</tr>
</table>
</div>
</template>
<script>
export default {
name: "LWords",
data() {return {
alphabet: "abcčdefghijklmnoprsštuvzž",
letters: {},
selectedLetter: "a"
}},
methods: {
apiGetWords: function() {
var component = this
this.$http.get(this.$root.storeGet("api_addr") + "/api/words")
.then(function(response) {
component.$root.store.api_error = null
component.$root.store.has_se = response.data["has_se"]
component.letters = response.data["sorted_words"]
})
.catch(function(error) {
component.$root.store.api_error = error
})
},
getNumWords: function(letter) {
var entry = this.letters[letter]
if (entry) {
return entry.length
} else {
return 0
}
},
getWords: function() {
var entry = this.letters[this.selectedLetter]
if (entry) {
return entry
} else {
return []
}
},
selectHw: function(word) {
this.$router.push({
name: "MainDispl",
params: {
hw: word[0],
fmode: false
}
})
}
},
mounted: function() {
this.apiGetWords()
}
}
</script>
<style scoped>
table {
width: 100%;
}
select {
width: 100%;
}
</style>

View File

@ -0,0 +1,120 @@
<template>
<div>
<div class="col-sm-2">
<a href="#" v-on:click="this.$root.routeBack">Nazaj</a>
</div>
<div class="ev-login col-sm-4 offset-sm-4">
<div class="alert alert-danger" v-if="error">
<p>{{ error }}</p>
</div>
<div class="form-group">
<input
type="text"
data-id="login.username"
class="form-control js-login__username"
placeholder="Uporabnik"
v-model="credentials.username"
>
</div>
<div class="form-group">
<input
type="password"
class="form-control js-login__password "
placeholder="Geslo"
v-model="credentials.password"
>
</div>
<button
data-id="login.submit"
class="btn btn-primary solid blank js-login__submit"
@click="submit()"
>
Prijava<i class="fa fa-arrow-circle-o-right"></i>
</button>
<br>
<br>
<br>
<router-link to="/new_pass">Ste pozabili geslo?</router-link>
<br>
<br>
Nov uporabnik?
<br>
<router-link to="/register">Ustvarite nov račun.</router-link>
</div>
</div>
</template>
<script>
export default {
name: 'Login',
data () {
return {
credentials: {
username: '',
password: ''
},
loggingIn: false,
error: ''
}
},
methods: {
submit () {
this.error = ""
//this.loggingIn = true
// Auth.login() returns a promise. A redirect will happen on success.
// For errors, use .then() to capture the response to output
// error_description (if exists) as shown below:
/*
this.$auth.login(credentials, 'dashboard').then((response) => {
this.loggingIn = false
this.error = utils.getError(response)
})
*/
if ( this.credentials.username === "" ||
this.credentials.password === ""
) {
this.error = "Izpolnite vsa polja."
return
}
var data = {
username: this.credentials.username,
password: this.credentials.password
}
var component = this
this.$http.post(this.$root.storeGet("api_addr") + "/api/login",
data, // the data to post
{ headers: {
'Content-type': 'application/x-www-form-urlencoded',
}
})
.then(function (response) {
component.$root.store.api_error = null
var token = response.data.token
if (token === null) {
component.error = "Napačno uporabniško ime ali geslo."
} else {
// set cookies (if the page reloads)
component.$root.store.username = component.credentials.username
component.$root.store.token = token
component.$router.go(-1)
component.$cookies.set("valency_token", token, 60*60*48)
}
})
.catch(function (err) {
component.$root.store.api_error = err
})
},
},
}
</script>
<style scoped>
.ev-login {
margin-top: 100px;
}
</style>

View File

@ -0,0 +1,256 @@
<template>
<!--load mode-->
<div v-if="show_loader">
<pulse-loader :color="loader_color"></pulse-loader>
</div>
<!--edit mode (button: razvrsti po pomenih)-->
<div v-else-if="state === 'editing'" class="container-fluid">
<EditSenses
v-bind:hw="hw"
v-bind:sentences="sentences"
v-bind:sens="sens"
></EditSenses>
</div>
<!--normal mode-->
<div v-else class="container-fluid" id="head">
<!--header (verb/adjective, radio buttons)-->
<div class="row">
<div class="col-sm-4">
<table>
<tr><h4 id="main-displ-hw">{{ hw }}
<span v-if="$root.store.has_se.includes(hw)">se</span>
</h4></tr>
<tr>{{ calcPos() }}</tr>
</table>
</div>
<div class="col-sm-8">
<table>
<tr>Združevanje vezljivostnih vzorcev:</tr>
<tr>
<label class="radio-inline"><input value="one" v-model="$root.store.radio" v-on:change="reload()" checked="" type="radio" name="optradio">posamezne povedi</label>&nbsp;&nbsp;
<label class="radio-inline"><input value="two" v-model="$root.store.radio" v-on:change="reload()" type="radio" name="optradio">skupne udeleženske vloge</label>&nbsp;&nbsp;
<label v-if="this.$root.store.navSS === 'words'" class="radio-inline"><input value="three" v-model="$root.store.radio" v-on:change="reload()" type="radio" name="optradio">po meri</label>
</tr>
</table>
</div>
</div>
<!--frames-->
<div v-if="$root.store.radio === 'three'" class="row">
<div class="col-sm-4">
<button v-on:click="userEdit">razvrsti po pomenih</button>
</div>
</div>
<div class="row" v-for="frame in frames">
<Frame
v-bind:frameData="frame"
v-bind:sensData="sens"
v-bind:fmode="fmode">
</Frame>
</div>
</div>
</template>
<script>
import Frame from "./Frame"
import EditSenses from "./EditSenses"
import PulseLoader from 'vue-spinner/src/PulseLoader.vue'
export default {
name: "MainDispl",
components: {
Frame: Frame,
EditSenses: EditSenses,
PulseLoader: PulseLoader,
},
props: ["hw", "fmode"],
data () { return {
frames: [],
sentences: {},
sens: {
senses: [],
sense_map: {},
},
state: "loading", // editing, normal
request_reload: false,
loader_color: "#007bff",
}},
created: function () {
this.reload()
},
computed: {
show_loader: function () {
return this.state === "loading" && this.$root.store.api_error !== null
}
},
watch: {
hw: function () {
this.reload()
},
frames: function () {
this.buildSentences()
},
request_reload: function () {
if (this.request_reload) {
this.request_reload = false
this.reload()
}
},
},
methods: {
getFFrames: function(functor, reduce_fun=null) {
// get frames in functor mode
if (functor === null || functor === undefined) return
if (reduce_fun === null) {
switch (this.$root.store.radio) {
case "one":
reduce_fun = "reduce_0"
break
case "two":
reduce_fun = "reduce_1"
break
default:
reduce_fun = "reduce_0"
break
}
}
var component = this
this.$http.get(
this.$root.storeGet("api_addr") + "/api/functor-frames" +
"?functor=" + functor + "&rf=" + reduce_fun)
.then(function (response) {
component.$root.store.api_error = null
component.frames = response.data.frames
component.state = "normal"
})
.catch(function(error) {
component.$root.store.api_error = error
})
},
getFrames: function (hw, reduce_fun=null) {
if (hw === null || hw === undefined) return
if (reduce_fun === null) {
switch (this.$root.store.radio) {
case "one":
reduce_fun = "reduce_0"
break
case "two":
reduce_fun = "reduce_1"
break
case "three":
reduce_fun = "reduce_5"
break
}
}
var component = this
this.$http.get(
this.$root.storeGet("api_addr") + "/api/frames" +
"?hw=" + hw + "&rf=" + reduce_fun)
.then(function (response) {
component.$root.store.api_error = null
component.frames = response.data.frames
component.state = "normal"
})
.catch(function(error) {
component.$root.store.api_error = error
})
},
buildSentences: function () {
if (this.frames.length == 0) {
return
}
this.sentences = {}
for (var fi in this.frames) {
for (var si in this.frames[fi].sentences) {
var sentence = this.frames[fi].sentences[si]
// get ssj_id without .t123
var ssj_id = sentence[0][0].split(".")
ssj_id.splice(-1, 1) // removes last element
ssj_id = ssj_id.join(".")
var words = []
var hw_idx = -1
var tmp_hw = this.hw
if (tmp_hw[tmp_hw.length - 1] === "_") {
tmp_hw = tmp_hw.substr(0, tmp_hw.length - 1)
}
for (var i in sentence) {
words.push(sentence[i][1].word)
if (sentence[i][1].lemma === tmp_hw && hw_idx == -1) {
hw_idx = i
}
}
this.sentences[ssj_id] = {
hw_idx: hw_idx,
words: words
}
}
}
},
getSenses: function (hw, callback) {
if (hw === null || hw === undefined) {
return
}
var component = this
this.$http.get(
this.$root.store.api_addr + "/api/senses/get" + "?hw=" + hw)
.then(function(response) {
// console.log(response.data)
component.sens.senses = response.data.senses
component.sens.sense_map = response.data.sense_map
callback()
})
},
reload: function () {
this.state = "loading"
this.sentences = {}
if (this.$root.store.navSS === "functors") this.getFFrames(this.hw)
else {
this.getFrames(this.hw)
if (this.$root.store.radio === "three") {
this.getSenses(this.hw, this.sortBySense)
}
}
this.calcPos()
},
userEdit: function () {
// authenticate the user for this
var tthis = this
this.$root.checkToken()
.then(function (response) {tthis.state = "editing"})
.catch(function (err) {alert("Za urejanje je potrebna prijava.")}
)
},
calcPos: function() {
var bfmode = this.fmode
if (typeof(bfmode) === "string") {
bfmode = (bfmode === "true")
}
if (bfmode) return "udeleženska vloga"
else if (this.hw.substr(this.hw.length-1) === "_") return "pridevnik"
return "glagol"
},
sortBySense: function() {
// frames with defined senses on top
var undefFrames = []
var defFrames = []
//console.log(Object.keys(this.sens.sense_map))
for (var i=0; i<this.frames.length; i++) {
var sense_id = this.frames[i].sense_info.sense_id
if (sense_id === "nedefinirano") undefFrames.push(this.frames[i])
else defFrames.push(this.frames[i])
}
this.frames = defFrames.concat(undefFrames)
}
}
}
</script>
<style scoped>
#main-displ-hw {
margin: 0px;
}
</style>

View File

@ -0,0 +1,74 @@
<template>
<nav>
<b-navbar toggleable="md" type="light" variant="light">
<b-navbar-toggle target="nav_collapse"></b-navbar-toggle>
<b-navbar-brand>Vezljivostni vzorci slovenskih glagolov</b-navbar-brand>
<b-collapse is-nav id="nav_collapse">
<b-navbar-nav>
<b-nav-item-dropdown text="Prikaz" right>
<b-dropdown-item v-for="option in search_options"
:value="option.val"
:key="option.val"
v-on:click="setNavSS(option.val)">
{{ option.key }}
</b-dropdown-item>
</b-nav-item-dropdown>
</b-navbar-nav>
<!-- Right aligned nav items -->
<b-navbar-nav class="ml-auto" right v-if="this.loggedIn()">
<b-nav-item>
Uporabnik: {{ this.$root.store.username }}
<a href="#" v-on:click="logOut()">(odjava)</a>
</b-nav-item>
</b-navbar-nav>
<b-navbar-nav class="ml-auto" right v-else>
<b-nav-item>
<router-link to="/register">
Registracija
</router-link>
</b-nav-item>
<b-nav-item>
<router-link to="/login">
Prijava
</router-link>
</b-nav-item>
</b-navbar-nav>
</b-collapse>
</b-navbar>
</nav>
</template>
<script>
export default {
name: "Nav",
props: ["appState"],
data() {return {
search_options: [
{key: "besede", val: "words"},
{key: "udeleženske vloge", val: "functors"},
],
}},
methods: {
setNavSS(val) {
this.$root.store.radio = "one"
this.$root.store.navSS = val
this.$router.push({
name: "Home"
})
},
loggedIn() {
return (this.$root.store.token !== null)
},
logOut() {
this.$root.store.token = null
this.$root.store.username = null
this.$router.push({
name: "Home"
})
}
}
}
</script>

View File

@ -0,0 +1,106 @@
<template>
<div>
<div class="col-sm-2">
<a href="#" v-on:click="this.$root.routeBack">Nazaj</a>
</div>
<div class="ev-login col-sm-4 offset-sm-4">
<div class="alert alert-danger" v-if="error">
<p>{{ error }}</p>
</div>
<div class="form-group">
<input
type="text"
data-id="login.username"
class="form-control js-login__username"
placeholder="Uporabnik"
v-model="credentials.username"
>
</div>
<div class="form-group">
<input
type="email"
class="form-control"
placeholder="e-pošta"
v-model="credentials.email"
>
</div>
<div>
<p>Novo geslo bo poslano na vaš e-poštni naslov.</p>
</div>
<button
data-id="new_pass.submit"
class="btn btn-primary solid blank js-login__submit"
@click="submit()"
>
Novo geslo<i class="fa fa-arrow-circle-o-right"></i>
</button>
</div>
</div>
</template>
<script>
export default {
name: 'NewPass',
data () {
return {
credentials: {
username: '',
email: ''
},
error: ''
}
},
methods: {
submit () {
this.error = ""
//this.loggingIn = true
// Auth.login() returns a promise. A redirect will happen on success.
// For errors, use .then() to capture the response to output
// error_description (if exists) as shown below:
/*
this.$auth.login(credentials, 'dashboard').then((response) => {
this.loggingIn = false
this.error = utils.getError(response)
})
*/
if ( this.credentials.username === "" ||
this.credentials.email === ""
) {
this.error = "Izpolnite vsa polja."
return
}
var data = {
username: this.credentials.username,
email: this.credentials.email
}
var component = this
this.$http.post(this.$root.storeGet("api_addr") + "/api/new_pass",
data, // the data to post
{ headers: {
'Content-type': 'application/x-www-form-urlencoded',
}
})
.then(function (response) {
component.$root.store.api_error = null
var confirmation = response.data.confirmation
component.$router.push({
name: "Home"
})
})
.catch(function (err) {
component.$root.store.api_error = err
})
},
},
}
</script>
<style scoped>
.ev-login {
margin-top: 100px;
}
</style>

View File

@ -0,0 +1,138 @@
<template>
<div>
<div class="col-sm-2">
<a href="#" v-on:click="this.$root.routeBack">Nazaj</a>
</div>
<div class="ev-login col-sm-4 offset-sm-4">
<div class="alert alert-danger" v-if="error">
<p>{{ error }}</p>
</div>
<div class="form-group">
<input
type="text"
class="form-control js-login__username"
placeholder="Uporabnik"
v-model="credentials.username"
autocomplete="off"
>
</div>
<div class="form-group">
<input
type="email"
class="form-control"
placeholder="e-pošta"
v-model="credentials.email"
autocomplete="off"
>
</div>
<div class="form-group">
<input
type="password"
class="form-control js-login__password "
placeholder="Geslo"
v-model="credentials.password"
autocomplete="off"
>
</div>
<div class="form-group">
<input
type="password"
class="form-control js-login__password "
placeholder="Ponovite geslo."
v-model="credentials.snd_password"
autocomplete="off"
>
</div>
<button
class="btn btn-primary solid blank js-login__submit"
@click="submit()"
>
Registracija<i class="fa fa-arrow-circle-o-right"></i>
</button>
</div>
</div>
</template>
<script>
export default {
name: 'Register',
data () { return {
credentials: {
username: "",
password: "",
snd_password: "",
email: ""
},
error: ""
}},
methods: {
clearFields () {
for (var key in this.credentials) {
this.credentials[key] = ""
}
},
checkEmail () {
// check? ... todo
return true
},
submit () {
//console.log(this.credentials.password)
//console.log(this.credentials.snd_password)
const credentials = {
username: this.credentials.username,
password: this.credentials.password
}
// check if fields are full
for (var key in this.credentials) {
if (credentials[key] === "") {
this.error = "Izpolnite vsa polja."
return
}
}
// check e-mail
if (!this.checkEmail(this.credentials.email)) {
this.error = "Preverite e-poštni naslov."
return
}
// check passwords
if (this.credentials.password !== this.credentials.snd_password) {
this.error = "Gesli se ne ujemata."
this.credentials.password = ""
this.credentials.snd_password = ""
return
}
var component = this
const post_data = {
username: this.credentials.username,
password: this.credentials.password,
email: this.credentials.email,
}
this.$http.post(this.$root.storeGet("api_addr") + "/api/register",
post_data, // the data to post
{ headers: {
'Content-type': 'application/json',
}
})
.then(function (response) {
component.$router.push({
name: "Home"
})
})
.catch(function (err) {
component.$root.store.api_error = err
component.error = "Registracija ni uspela."
})
}
}
}
</script>
<style scoped>
.ev-login {
margin-top: 100px;
}
</style>

View File

@ -0,0 +1,48 @@
<template>
<div class="pb-3">
<div v-if="sense === undefined">
pomen ni definiran
</div>
<div v-else>
<span>{{ sense.desc }}</span>
<br>
&nbsp;&nbsp;
<small class="text-secondary">
- {{ sense.author }}
{{ gen_id() }}
</small>
</div>
</div>
</template>
<script>
export default {
name: "Sense",
props: ["sense"],
methods: {
gen_id: function() {
var id_arr = this.sense.sense_id.split("-")
var ret = ""
if (this.sense.author === "SSKJ") {
if (id_arr[1] !== "0") {
ret += ("[" + id_arr[1] + ["] "])
}
if (id_arr[2] !== "0") {
ret += ("pomen " + id_arr[2])
if (id_arr[3] !== "0") {
ret += ("." + id_arr[3])
}
}
if (id_arr[4] === "sopo") {
ret += " (sopomenka)"
}
}
// ret = this.sense.sense_id //debugging
if (ret.length > 0) {
ret = ": " + ret
}
return ret
}
}
}
</script>

View File

@ -0,0 +1,140 @@
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'
import VueCookies from "vue-cookies"
// bootstrap
import BootstrapVue from "bootstrap-vue"
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
// ajax
import axios from "axios"
// config
import config_data from "../config/config.json"
// console.log(config_data)
Vue.config.productionTip = false
// cokies
Vue.use(VueCookies)
// bootstrap
Vue.use(BootstrapVue)
// CORS
// Vue.$http.headers.common['Access-Control-Allow-Origin'] = true
Vue.prototype.$http = axios
// hand-made global storage
const store = {
api_error: null,
api_addr: config_data.api_addr,
// api_addr: "http://localhost:5004", // development (webpack)
// api_addr: "http://193.2.76.103:5004", // production
token: null,
username: null,
navSS: "words",
radio: "one",
has_se: [], // used for appending (se) to certain verbs
}
const store_methods = {
storeSet: function(key, val) {
store[key] = val
},
storeGet: function(key) {
// returns undefined if not in dict.
// check if (variable)
return store[key]
}
}
const login_methods = {
checkToken: function () {
var tthis = this
return new Promise(function (resolve, reject) {
if (tthis.store.token === null) {
tthis.store.username = null
reject(false)
}
var data = {
token: tthis.store.token,
user: tthis.store.username
}
tthis.$http.post(tthis.store.api_addr + "/api/token", data,
{ headers: {
'Content-type': 'application/x-www-form-urlencoded',
}}
)
.then(function (response) {
tthis.store.api_error = null
if (response.data.confirmation) {
resolve(true)
} else {
tthis.store.username = null
tthis.store.token = null
reject(false)
}
})
.catch(function (err) {
tthis.store.api_error = err
reject(err)
})
})
}
}
const other_methods = {
routeBack: function() {
this.$router.go(-1)
},
mkspace: function (idx, word) {
var stopwords = [".", ",", ":", ";"]
if (stopwords.includes(word)) return false
return true
}
}
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
components: { App },
template: '<App/>',
data() { return {
store: store,
}},
methods: Object.assign(store_methods, login_methods, other_methods),
beforeCreate: function() {
document.title = "Vezljivostni vzorci"
if (this.$cookies.isKey("valency_token")) {
var cookie_token = this.$cookies.get("valency_token")
var data = {
token: cookie_token,
}
var component = this
this.$http.post(store.api_addr + "/api/token",
data, // the data to post
{ headers: {
'Content-type': 'application/x-www-form-urlencoded',
}
})
.then(function (response) {
if (response.data.confirmation) {
store.username = response.data.username
store.token = cookie_token
} else {
this.$cookies.remove("valency_token")
}
})
.catch(function (err) {
store.api_error = err
})
}
}
})

View File

@ -0,0 +1,48 @@
import Vue from 'vue'
import Router from 'vue-router'
import Home from "@/components/Home"
import Login from "@/components/Login"
import Register from "@/components/Register"
import NewPass from "@/components/NewPass"
import MainDispl from "@/components/MainDispl"
import EditSenses from "@/components/EditSenses"
Vue.use(Router)
export default new Router({
mode: "history",
routes: [
{
path: '/',
redirect: "/home"
},
{
path: "/home",
name: "Home",
component: Home,
children: [
{
path: "words/:hw",
name: "MainDispl",
component: MainDispl,
props: true,
},
]
},
{
path: '/login',
name: 'Login',
component: Login
},
{
path: '/register',
name: 'Register',
component: Register
},
{
path: '/new_pass',
name: 'NewPass',
component: NewPass
}
]
})

View File

View File

@ -0,0 +1,3 @@
proxy: {
'*': 'http://localhost:8080'
}