Commit 99033d7d authored by Patrick's avatar Patrick
Browse files

first commit

parents
#### Getting Started
Requirements: **NodeJS 6 LTS** and **npm 3** in your PATH.
1. Check the folder structure. An exported (compiled) **virtualcityMAP** must be in the _vcm_ path, relative to the root of your project
```
- .
|- build/
|- src/
|- test/
|- vcm/ <- needs to be copied
|- .babelrc
|- package.json
```
2. Install all the needed dependencies using `npm install`
3. To run the examples, execute `npm run examples`
#### Reading the _docs_
Once you have the development server running, you will find the documentation of the **vcm** API
at [http://localhost:8081/doc/index.html](http://localhost:8081/doc/index.html).
Further reading:
- [Vuejs](https://vuejs.org/)
- [vue-router](https://router.vuejs.org/en/)
- [vuex](https://vuex.vuejs.org/en/)
- [openlayers](https://openlayers.org/en/latest/apidoc/index.html)
- [Cesium](https://cesiumjs.org/Cesium/Build/Documentation/index.html)
### Writing your own plugin
Using the examples as a basis, you can start writing your own plugins. Make sure to register your plugin by calling `vcs.ui.registerPlugin(options)`.
To start writing your own plugin, simple use the `src/index.js` as an entry point for your own plugin.
To stop running the examples, and run you own plugin, start the dev server with `npm start`.
It is important to only write _a single plugin_ and only call _vcs.ui.registerPlugin_ **once**.
Otherwise the build process will not properly run through and the
configuration with the **virtualcityPUBLISHER** becomes impossible.
Once you are satisfied with your plugin, you can build it using `npm run build`.
This will create a zip-file containing your applications **bundled code** and **assets**.
To add your plugin to the **virtualcityPUBLISHER**, unzip the contents of this file into
the `root/public/plugins/` folder. The structur of the `public/plugins/` folder must look like this:
```
public/plugins
|- myPlugin/
|- assets/
|- myPlugin.js
|- config.json
|- mySecondPlugin/
|- assets/
|- mySecondPlugin.js
|- noAssetsPlugin/
|- noAssetsPlugin.js
|- config.json
```
Your plugin can now be added to **virtualcityMAP**s.
To ensure the usage of your plugin in a **virtualcityMAP** with a specific map version,
set the `vcm` property in the `package.json`s _engine_ property. The `package.json`
is shipped with your plugin, use it to define the current _version_ of your plugin,
the author and supported **virtualcityMAP** version.
To add your plugin to a deployed map, simply unzip the distribution into a folder
`plugins/`.
Add your plugin to a deployed map, by adding your plugin to the `config.json` under _ui.plugins_
and providing the name of your served plugin as
the key (this is especially useful if you wish to pass configurations to your plugin):
> This only works properly of you maintain the folder structure provided by the build script
> and use the `plugins/` directory on your server.
> If you use the virtualcityPUBLISHER to create your maps, this should not be an issue.
```json
...,
"ui": {
"plugins": {
"myPlugin": {
...
}
}
}
```
#### Adding static data to your plugin
You may wish to add your own icons, images or other forms of static content. For this, use the
`assets/` folder. You can then request your resource with `assets/yourResource`. The build script
will automatically change this URLs to `plugins/myPlugin/assets/yourResource` for ease of deployment
#### Adding configuration to your plugin
Some plugins might need configuring. You can add your configuration to the vcm's `config.json`
in the section _ui.plugins.myPlugin_ as shown above. To allow for a configuration of your
plugin by the **virtualcityPUBLISHER**, you can specify the configurable parameters of your
plugin in the `config.json` found in the root of your application. Add the keys
and their default values to this JSON.
```json
{
"myKey": "defaultValue",
"myNumeric": 123,
"myBoolean": true,
"myArray": [1, 2, 3]
}
```
#### Testing your plugin
Test are written for the `mocha` test framework, using `chai` for assertation and `sinon` for spies, stubs and mocks and `karma` as a test runner.
To run the tests, run `npm test`. In the `test/` directory you will find a small example _spec_.
var http = require("http");
var auth = require("http-auth");
var basic = auth.basic({
authRealm: "Private area",
authFile: __dirname + "/htpasswd",
authType: "basic"
});
var server = http.createServer(function(request, response) {
basic.apply(request, response, function(username) {
response.writeHead(200, {"Content-Type": "text/plain"});
response.write("Hello " + username);
response.end();
});
});
server.listen(80);
console.log("Server is listening");
\ No newline at end of file
/* eslint-disable */
require('eventsource-polyfill')
var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true')
hotClient.subscribe(function (event) {
if (event.action === 'reload') {
window.location.reload()
}
})
// var config = require('../config')
process.env.NODE_ENV = "development";
var opn = require('opn');
var express = require('express');
var webpack = require('webpack');
var webpackConfig = require('./webpack.dev.conf');
var examples = {
buttons: './src/examples/buttons/buttons.js',
content: './src/examples/content/content.js',
search: './src/examples/search/search.js'
};
if (process.argv.length === 3) {
var argEx = process.argv[2];
if (argEx === 'examples') {
for (var ex in examples) {
webpackConfig.entry.app.push(examples[ex]);
}
} else if (examples[argEx]) {
webpackConfig.entry.app.push(examples[argEx]);
}
}
var port = 8081;
var autoOpenBrowser = true;
var app = express();
var compiler = webpack(webpackConfig);
var devMiddleware = require('webpack-dev-middleware')(compiler, {
publicPath: webpackConfig.output.publicPath,
quiet: true
});
var hotMiddleware = require('webpack-hot-middleware')(compiler, {
log: () => {}
});
// force page reload when html-webpack-plugin template changes
compiler.plugin('compilation', function (compilation) {
compilation.plugin('html-webpack-plugin-after-emit', function (data, cb) {
hotMiddleware.publish({ action: 'reload' });
cb();
});
});
app.use(require('connect-history-api-fallback')());
app.use(devMiddleware);
app.use(hotMiddleware);
// serve pure static assets
app.use("/assets", express.static('./assets'));
app.use("/lib", express.static('./vcm/lib'));
app.use("/config.json", express.static('./vcm/config.json'));
app.use("/css", express.static('./vcm/css'));
app.use("/fonts", express.static('./vcm/fonts'));
app.use("/images", express.static('./vcm/images'));
app.use("/img", express.static('./vcm/img'));
app.use("/templates", express.static('./vcm/templates'));
app.use("/examples", express.static('./vcm/examples'));
app.use("/doc", express.static('./vcm/doc'));
app.use("/datasource-data", express.static('./vcm/datasource-data'));
var uri = 'http://localhost:' + port;
devMiddleware.waitUntilValid(function () {
console.log('> Listening at ' + uri + '\n');
});
module.exports = app.listen(port, function (err) {
if (err) {
console.log(err);
return;
}
// when env is testing, don't need open it
if (autoOpenBrowser && process.env.NODE_ENV !== 'testing') {
opn(uri);
}
});
var path = require('path')
var webpack = require('webpack')
var HtmlWebpackPlugin = require('html-webpack-plugin')
var FriendlyErrorsPlugin = require('friendly-errors-webpack-plugin')
var ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
function cssLoaders (options) {
options = options || {}
var cssLoader = {
loader: 'css-loader',
options: {
minimize: process.env.NODE_ENV === 'production',
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
var loaders = [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)
}
}
// http://vuejs.github.io/vue-loader/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')
}
}
module.exports = {
entry: {
app: ['./src/index.js', './build/dev-client']
},
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
chunkFilename: '[name].js',
publicPath: '/'
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': resolve('src'),
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: cssLoaders({
sourceMap: false,
extract: false
})
}
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src')]
}
]
},
devtool: '#cheap-module-eval-source-map',
plugins: [
new webpack.DefinePlugin({
'process.env': { NODE_ENV: '"development"' }
}),
// https://github.com/glenjamin/webpack-hot-middleware#installation--usage
new webpack.HotModuleReplacementPlugin(),
new webpack.NoEmitOnErrorsPlugin(),
// https://github.com/ampedandwired/html-webpack-plugin
new HtmlWebpackPlugin({
filename: 'index.html',
template: './vcm/index.html',
inject: true
}),
new ScriptExtHtmlWebpackPlugin({
defer: 'app.js'
}),
new FriendlyErrorsPlugin()
]
}
process.env.NODE_ENV = 'production'
var path = require('path')
var webpack = require('webpack')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
function cssLoaders (options) {
options = options || {}
var cssLoader = {
loader: 'css-loader',
options: {
minimize: process.env.NODE_ENV === 'production',
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
var loaders = [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)
}
}
// http://vuejs.github.io/vue-loader/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')
}
}
const plugins = [
// http://vuejs.github.io/vue-loader/en/workflow/production.html
new webpack.DefinePlugin({
'process.env': { NODE_ENV: '"production"' }
}),
];
if (!process.env.VUE_CLI_MODERN_BUILD) {
plugins.push(new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
},
sourceMap: true
}));
}
module.exports = {
entry: {
plugin: './src/index.js'
},
output: {
path: path.resolve(__dirname, '..', 'dist'),
filename: process.env.VUE_CLI_MODERN_BUILD ? 'plugin._es6.js' : 'plugin.js',
library: process.env.LIBRARY_NAME,
libraryTarget: process.env.LIBRARY_NAME ? 'umd': undefined,
publicPath: './'
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'@': resolve('src'),
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: cssLoaders({
sourceMap: false,
extract: false
})
}
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src')]
},
{
test: /\.(png|jpe?g|gif)(\?.*)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
name: 'img/[name].[hash:8].[ext]'
}
}
]
},
{
test: /\.(svg)(\?.*)?$/,
use: [
{
loader: 'file-loader',
options: {
name: 'img/[name].[hash:8].[ext]'
}
}
]
},
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
name: 'media/[name].[hash:8].[ext]'
}
}
]
},
{
test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i,
use: [
{
loader: 'url-loader',
options: {
limit: 10000,
name: 'fonts/[name].[hash:8].[ext]'
}
}
]
}
]
},
devtool: false,
plugins,
};
var path = require('path')
var webpack = require('webpack')
function resolve (dir) {
return path.join(__dirname, '..', dir)
}
function cssLoaders (options) {
options = options || {}
var cssLoader = {
loader: 'css-loader',
options: {
minimize: process.env.NODE_ENV === 'production',
sourceMap: options.sourceMap
}
}
// generate loader string to be used with extract text plugin
function generateLoaders (loader, loaderOptions) {
var loaders = [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)
}
}
// http://vuejs.github.io/vue-loader/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')
}
}
module.exports = {
output: {
path: path.resolve(__dirname, 'dist'),
filename: '[name].js',
chunkFilename: '[name].js',
publicPath: '/'
},
resolve: {
extensions: ['.js', '.vue', '.json'],
alias: {
'vue$': 'vue/dist/vue.esm.js',
'@': resolve('src')
}
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: cssLoaders({
sourceMap: false,
extract: false
})
}
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [resolve('src'), resolve('test')]
}
]
},
devtool: '#inline-source-map',
plugins: [
new webpack.DefinePlugin({
'process.env': { NODE_ENV: '"testing"' }
})
]
}
var gulp = require('gulp');
var gutil = require('gulp-util');
var replace = require('gulp-replace');
var gulpif = require('gulp-if');
var rename = require('gulp-rename');
var zip = require('gulp-zip');
var del = require('del');
var vinylPaths = require('vinyl-paths');
var runSequence = require('run-sequence');
var program = require('commander');
var spawn = require('child_process').spawn;
var jeditor = require('gulp-json-editor');
var fs = require('fs');
program
.option('-p, --plugin <name>', 'the name to use for the plugin, overwrites the default')
.option('-l, --library <name>', 'to export the plugin as a library')
.parse(process.argv);
var pluginName = program.plugin;
gulp.task('clean', function() {
return del([
'dist/*'
]);
});
gulp.task('webpack-modern', function(cb) {
const child = spawn('node', ['node_modules/webpack/bin/webpack.js', '--config', 'build/webpack.prod.conf.js'], {
env: Object.assign({
VUE_CLI_MODERN_BUILD: true,
LIBRARY_NAME: program.library,
}, process.env),
});
child.stdout.on('data', (data) => {
gutil.log(data.toString());
});
child.stderr.on('data', (data) => {
gutil.log(data.toString());
});
child.on('close', (code) => {
if (code) {
throw new gutil.PluginError('webpack modern', code);
}
cb();
});
});
gulp.task('webpack-legacy', function(cb) {
const child = spawn('node', ['node_modules/webpack/bin/webpack.js', '--config', 'build/webpack.prod.conf.js'], {
env: Object.assign({
LIBRARY_NAME: program.library,
}, process.env),
});
child.stdout.on('data', (data) => {
gutil.log(data.toString());
});
child.stderr.on('data', (data) => {
gutil.log(data.toString());
});
child.on('close', (code) => {
if (code) {
throw new gutil.PluginError('webpack legacy', code);
}
cb();
});
});
gulp.task('uglify-modern', function(cb) { // TODO replace with terser-webpack-plugin when moving to webpack 4
const child = spawn('node', ['node_modules/terser/bin/uglifyjs', '--mangle', '--output', 'dist/plugin.es6.js', 'dist/plugin._es6.js']);
child.stdout.on('data', (data) => {
gutil.log(data.toString());
});
child.stderr.on('data', (data) => {
gutil.log(data.toString());
});
child.on('close', (code) => {
if (code) {
throw new gutil.PluginError('webpack modern', code);
}
fs.unlink('dist/plugin._es6.js', (err) => {
if (err) {
throw new guitl.PluginError('webpack modern', err);
}
cb();
});
});
});
gulp.task('replace', function() {
return gulp.src(['dist/*.js'])
.pipe(gulpif(!pluginName, replace(/registerPlugin\((.*)\)/, function(match) {
pluginName = match.match(/name:"(\w+)"/)[1];
return match;
})))
.pipe(replace(/\.?\/?(assets|img|fonts|media)\//g, function(match, folder) {
return `plugins/${pluginName}/`;
}))
.pipe(vinylPaths(del))
.pipe(rename(function(path) {
path.basename = /\.es6$/.test(path.basename) ? `${pluginName}.es6` : pluginName;
}))
.pipe(gulp.dest('dist/'));
});
gulp.task('add-esmodule', function() {
if (!fs.existsSync('config.json')) {
fs.writeFileSync('config.json', '{}');
}
return gulp.src('config.json')
.pipe(jeditor({
_esmodule: true,
}))
.pipe(gulp.dest('.'));
});
gulp.task('zip', function() {
return gulp.src(['dist/*.js', 'assets/**/*', 'config.json', 'package.json', 'img/**/*', 'README.md'], { base: process.cwd() })
.pipe(rename(function (path) {
var ext = path.dirname !== 'dist' && path.dirname !== '.' ? path.dirname : '';
path.dirname = pluginName + '/' + ext;
}))
.pipe(zip(pluginName + '.zip'))
.pipe(gulp.dest('dist'));
});
gulp.task('build', function(cb) {
runSequence(
'clean',
'webpack-modern',
'webpack-legacy',
'uglify-modern',
'replace',
'add-esmodule',
'zip',
cb
);
});
gulp.task('default', ['build']);
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../acorn/bin/acorn" "$@"
ret=$?
else
node "$basedir/../acorn/bin/acorn" "$@"
ret=$?
fi
exit $ret
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\acorn\bin\acorn" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\acorn\bin\acorn" %*
)
\ No newline at end of file
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../ansi-html/bin/ansi-html" "$@"
ret=$?
else
node "$basedir/../ansi-html/bin/ansi-html" "$@"
ret=$?
fi
exit $ret
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\ansi-html\bin\ansi-html" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\ansi-html\bin\ansi-html" %*
)
\ No newline at end of file
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../atob/bin/atob.js" "$@"
ret=$?
else
node "$basedir/../atob/bin/atob.js" "$@"
ret=$?
fi
exit $ret
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\atob\bin\atob.js" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\atob\bin\atob.js" %*
)
\ No newline at end of file
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../babylon/bin/babylon.js" "$@"
ret=$?
else
node "$basedir/../babylon/bin/babylon.js" "$@"
ret=$?
fi
exit $ret
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\babylon\bin\babylon.js" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\babylon\bin\babylon.js" %*
)
\ No newline at end of file
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../browserslist/cli.js" "$@"
ret=$?
else
node "$basedir/../browserslist/cli.js" "$@"
ret=$?
fi
exit $ret
@IF EXIST "%~dp0\node.exe" (
"%~dp0\node.exe" "%~dp0\..\browserslist\cli.js" %*
) ELSE (
@SETLOCAL
@SET PATHEXT=%PATHEXT:;.JS;=;%
node "%~dp0\..\browserslist\cli.js" %*
)
\ No newline at end of file
#!/bin/sh
basedir=$(dirname "$(echo "$0" | sed -e 's,\\,/,g')")
case `uname` in
*CYGWIN*) basedir=`cygpath -w "$basedir"`;;
esac
if [ -x "$basedir/node" ]; then
"$basedir/node" "$basedir/../color-support/bin.js" "$@"
ret=$?
else
node "$basedir/../color-support/bin.js" "$@"
ret=$?
fi
exit $ret
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment