diff --git a/.bowerrc b/.bowerrc
index 24dc4049b8..7638f18b38 100644
--- a/.bowerrc
+++ b/.bowerrc
@@ -1,4 +1,8 @@
{
- "registry": "https://registry.bower.io",
- "directory": "res/bower_components"
+ "registry": "http://registry.bower.io",
+ "directory": "/tmp/build/bower_modules",
+ "allow_root": true,
+ "off_proxy": "http://127.0.0.1:8080",
+ "off_https-proxy": "http://127.0.0.1:8080",
+ "strict-ssl": false
}
diff --git a/.dockerignore b/.dockerignore
index 868d0d8a23..59c5a4c098 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -12,3 +12,4 @@ rethinkdb_data/
temp/
tmp/
.eslintcache
+Dockerfile
diff --git a/Dockerfile b/Dockerfile
index 7bc80eea38..15fc86681c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,74 +1,117 @@
-FROM ubuntu:16.04
+FROM ubuntu:18.04 AS nodebase
-# Sneak the stf executable into $PATH.
-ENV PATH /app/bin:$PATH
+# Install base packages
+RUN export DEBIAN_FRONTEND=noninteractive && \
+ apt-get update && \
+ apt-get -y --no-install-recommends install curl wget libxml-bare-perl libzmq3-dev libprotobuf-dev graphicsmagick ca-certificates openjdk-8-jdk
-# Work in app dir by default.
-WORKDIR /app
+# Install node
+RUN export DEBIAN_FRONTEND=noninteractive && \
+ curl -sL -o /tmp/install_node.sh https://deb.nodesource.com/setup_8.x && \
+ /bin/bash /tmp/install_node.sh && \
+ apt install --no-install-recommends -y nodejs
-# Export default app port, not enough for all processes but it should do
-# for now.
-EXPOSE 3000
+RUN useradd --system --create-home --shell /usr/sbin/nologin stf
+
+FROM nodebase as with_packages
-# Install app requirements. Trying to optimize push speed for dependant apps
-# by reducing layers as much as possible. Note that one of the final steps
-# installs development files for node-gyp so that npm install won't have to
-# wait for them on the first native module installation.
+# Install additional packages for building things
RUN export DEBIAN_FRONTEND=noninteractive && \
- useradd --system \
- --create-home \
- --shell /usr/sbin/nologin \
- stf-build && \
- useradd --system \
- --create-home \
- --shell /usr/sbin/nologin \
- stf && \
- sed -i'' 's@http://archive.ubuntu.com/ubuntu/@mirror://mirrors.ubuntu.com/mirrors.txt@' /etc/apt/sources.list && \
- apt-get update && \
- apt-get -y install wget python build-essential && \
- cd /tmp && \
- wget --progress=dot:mega \
- https://nodejs.org/dist/v8.9.3/node-v8.9.3-linux-x64.tar.xz && \
- tar -xJf node-v*.tar.xz --strip-components 1 -C /usr/local && \
- rm node-v*.tar.xz && \
- su stf-build -s /bin/bash -c '/usr/local/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js install' && \
- apt-get -y install libzmq3-dev libprotobuf-dev git graphicsmagick openjdk-8-jdk yasm && \
- apt-get clean && \
- rm -rf /var/cache/apt/* /var/lib/apt/lists/* && \
- mkdir /tmp/bundletool && \
- cd /tmp/bundletool && \
- wget --progress=dot:mega \
- https://github.com/google/bundletool/releases/download/1.2.0/bundletool-all-1.2.0.jar && \
- mv bundletool-all-1.2.0.jar bundletool.jar
-
-# Copy app source.
-COPY . /tmp/build/
-
-# Give permissions to our build user.
-RUN mkdir -p /app && \
- chown -R stf-build:stf-build /tmp/build /tmp/bundletool /app
-
-# Switch over to the build user.
-USER stf-build
-
-# Run the build.
+ apt-get -y --no-install-recommends install build-essential git yasm jq python vim
+
+# Install node-gyp ahead of time to avoid installation on native module install
+# RUN /bin/bash -c '/usr/lib/node_modules/npm/node_modules/node-gyp/bin/node-gyp.js install'
+
+# Install just the package dependencies before copying in the full source
+RUN mkdir -p /tmp/build/res/build
+COPY ./package*.json /tmp/build/
+WORKDIR /tmp/build
RUN set -x && \
- cd /tmp/build && \
export PATH=$PWD/node_modules/.bin:$PATH && \
npm install --loglevel http && \
- npm pack && \
- tar xzf devicefarmer-stf-*.tgz --strip-components 1 -C /app && \
- bower cache clean && \
- npm prune --production && \
- mv node_modules /app && \
- rm -rf ~/.node-gyp && \
- mkdir /app/bundletool && \
- mv /tmp/bundletool/* /app/bundletool && \
- cd /app && \
- find /tmp -mindepth 1 ! -regex '^/tmp/hsperfdata_root\(/.*\)?' -delete
-
-# Switch to the app user.
-USER stf
+ curl -sf https://gobinaries.com/tj/node-prune | sh
+
+wget --progress=dot:mega \
+ https://github.com/google/bundletool/releases/download/1.2.0/bundletool-all-1.2.0.jar \
+ -O /tmp/bundletool.jar
+
+# ********* FRONTEND **********
+
+FROM with_packages as frontend
+
+# Install bower dependencies
+WORKDIR /tmp/build
+COPY ./bower.json /tmp/build/
+COPY ./.bowerrc /tmp/build/
+RUN mkdir bower_modules && \
+ ./node_modules/.bin/bower install
+
+# Copy the app ( res ) in
+COPY ./bower.json /tmp/build/
+COPY ./gulpfile.js /tmp/build/
+COPY ./webpack.config.js /tmp/build/
+COPY ./res /tmp/build/res
+COPY ./lib/util /tmp/build/lib/util
+
+RUN ./node_modules/.bin/gulp build
+
+# ********* BACKEND **********
+
+FROM with_packages as backend
+
+COPY ./lib /tmp/build/lib
+
+# Package and cleanup
+WORKDIR /tmp/build
+RUN npm pack 2>&1 | grep -v "npm notice [1-9]" && \
+ mv devicefarmer-stf-$(jq .version package.json -j).tgz stf.tgz
+#npm prune --production && \
+# node-prune && \
+
+FROM alpine as app
+
+RUN mkdir -p /app
+COPY --from=backend /tmp/build/stf.tgz /tmp/stf.tgz
+RUN tar xf /tmp/stf.tgz --strip-components 1 -C /app
+
+# ********* RUNTIME **********
+
+FROM nodebase as runtime
+
+EXPOSE 3000
+
+# Setup user
+RUN mkdir -p /app/res && mkdir -p /app/bundletool && chown stf:stf /app && chown stf:stf /app/*
+
+WORKDIR /app
+
+# Copy in node_modules and prune them
+COPY --from=with_packages --chown=stf:stf /tmp/build/node_modules /app/node_modules
+COPY --from=with_packages --chown=stf:stf /tmp/build/package.json /app/package.json
+RUN npm prune --production
+
+# Copy in resources needed by backend
+COPY --chown=stf:stf ./res/common /app/res/common
+COPY --chown=stf:stf ./res/app/views /app/res/app/views
+COPY --chown=stf:stf ./res/auth/mock/views /app/res/auth/mock/views
+
+# Copy in the backend
+COPY --from=app --chown=stf:stf /app /app
+
+# Copy in the frontend
+COPY --from=frontend --chown=stf:stf /tmp/build/res/build /app/res/build
+
+# Copy in bundletool
+COPY --from=with_packages --chown=stf:stf /tmp/bundletool.jar /app/bundletool/bundletool.jar
+
+COPY ./webpackserver.config.js /app/
+
+#USER root
+#RUN apt-get -y --no-install-recommends install ncdu
+
+# Add stf executable dir into $PATH
+ENV PATH /app/bin:$PATH
+
# Show help by default.
CMD stf --help
diff --git a/bower.json b/bower.json
index 9fea32c57f..be308c999b 100644
--- a/bower.json
+++ b/bower.json
@@ -2,48 +2,49 @@
"name": "stf",
"version": "0.1.0",
"dependencies": {
- "angular": "~1.5.0-rc.2",
- "angular-cookies": "~1.5.0-rc.2",
- "angular-route": "~1.5.0-rc.2",
- "angular-sanitize": "~1.5.0-rc.2",
- "angular-animate": "~1.5.0-rc.2",
- "angular-touch": "~1.5.0-rc.2",
"lodash": "~3.10.1",
- "oboe": "~2.1.2",
- "ng-table": "~1.0.0-beta.9",
- "angular-gettext": "~2.2.0",
- "angular-ui-ace": "~0.2.3",
- "angular-dialog-service": "~5.2.11",
+ "oboe": "2.1.2",
+ "ng-table": "~1.0.0",
"ng-file-upload": "~2.0.5",
- "angular-growl-v2": "JanStevens/angular-growl-2#~0.7.9",
"underscore.string": "~3.2.3",
"bootstrap": "~3.3.6",
- "font-lato-2-subset": "~0.4.0",
"packery": "~1.4.3",
- "draggabilly": "~1.2.4",
- "angular-elastic": "~2.5.1",
- "angular-hotkeys": "chieffancypants/angular-hotkeys#~1.6.0",
- "angular-borderlayout": "git://github.com/filearts/angular-borderlayout.git#7c9716aebd9260763f798561ca49d6fbfd4a5c67",
- "angular-ui-bootstrap": "~1.1.1",
"ng-context-menu": "AdiDahan/ng-context-menu#~1.0.5",
"components-font-awesome": "~4.5.0",
- "epoch": "~0.8.4",
- "ng-epoch": "~1.0.7",
"eventEmitter": "~4.3.0",
- "angular-ladda": "~0.3.1",
- "d3": "~3.5.14",
- "spin.js": "~2.3.2",
- "angular-xeditable": "~0.1.9"
+
+ "angular": "~1.8.0",
+ "angular-cookies": "~1.8.0",
+ "angular-route": "~1.8.0",
+ "angular-sanitize": "~1.8.0",
+ "angular-animate": "~1.8.0",
+ "angular-touch": "~1.8.0",
+ "angular-ladda": "~0.4.3",
+ "spin.js": "~4.1.0",
+ "angular-xeditable": "~0.1.9",
+ "angular-elastic": "~2.5.1",
+ "angular-ui-bootstrap": "~1.1.1",
+ "angular-gettext": "~2.4.1",
+ "angular-ui-ace": "~0.2.3",
+ "angular-dialog-service": "~5.2.11",
+ "angular-growl-v2": "JanStevens/angular-growl-2#~0.7.9",
+ "angular-borderlayout": "https://github.com/nanoscopic/angular-borderlayout.git#0.9.2",
+ "angular-hotkeys": "https://github.com/nanoscopic/angular-hotkeys.git#2.0.29",
+ "mousetrap": "https://github.com/nanoscopic/mousetrap.git#1.7.19",
+
+ "epoch": "~0.8.4",
+ "ng-epoch": "~2.0.1"
},
"private": true,
"devDependencies": {
"angular-mocks": "~1.5.0-rc.2"
},
"resolutions": {
- "angular": "~1.5.0-rc.2",
"d3": "~3.5.5",
- "spin.js": "~2.3.2",
"eventEmitter": "~4.3.0",
- "epoch": "~0.8.4"
+ "epoch": "~0.8.4",
+ "get-size": "~2.0.3",
+ "angular": "~1.8.0",
+ "oboe": "2.1.2"
}
}
diff --git a/gulpfile.js b/gulpfile.js
index efc1f91874..895967c4e3 100644
--- a/gulpfile.js
+++ b/gulpfile.js
@@ -6,7 +6,7 @@ var jsonlint = require('gulp-jsonlint')
var eslint = require('gulp-eslint')
var EslintCLIEngine = require('eslint').CLIEngine
var webpack = require('webpack')
-var webpackConfig = require('./webpack.config').webpack
+var webpackConfig = require('./webpack.config')
var webpackStatusConfig = require('./res/common/status/webpack.config')
var gettext = require('gulp-angular-gettext')
var pug = require('gulp-pug')
@@ -35,7 +35,7 @@ gulp.task('eslint', function() {
return gulp.src([
'lib/**/*.js'
, 'res/**/*.js'
- , '!res/bower_components/**'
+ , '!cache/bower/**'
, '*.js'
])
// eslint() attaches the lint output to the "eslint" property
@@ -75,16 +75,55 @@ gulp.task('eslint-cli', function(done) {
}
})
+gulp.task('clean', function(done) {
+ gutil.log("clean")
+ del.sync([
+ './tmp'
+ //, './res/build'
+ , '.eslintcache'
+ ])
+ done()
+} )
-gulp.task('lint', ['jsonlint', 'eslint-cli'])
-gulp.task('test', ['lint', 'run:checkversion'])
-gulp.task('build', ['clean', 'webpack:build'])
-
-gulp.task('run:checkversion', function() {
+gulp.task('check_stf_version', function() {
gutil.log('Checking STF version...')
return run('./bin/stf -V').exec()
})
+gulp.task('webpack:build', function(callback) {
+ gutil.log('webpack:build')
+ var myConfig = webpackConfig
+ myConfig.plugins = myConfig.plugins.concat(
+ new webpack.DefinePlugin({
+ 'process.env': {
+ NODE_ENV: JSON.stringify('production')
+ }
+ })
+ )
+ myConfig.devtool = false
+
+ webpack(myConfig, function(err, stats) {
+ if (err) {
+ throw new gutil.PluginError('webpack:build', err)
+ }
+
+ gutil.log('[webpack:build]', stats.toString({
+ colors: true
+ }))
+
+ // Save stats to a json file
+ // Can be analyzed in http://webpack.github.io/analyse/
+ fromString('stats.json', JSON.stringify(stats.toJson()))
+ .pipe(gulp.dest('./tmp/'))
+
+ callback()
+ })
+} )
+
+gulp.task('lint', gulp.series( 'jsonlint', 'eslint-cli' ) )
+gulp.task('test', gulp.series( 'lint', 'check_stf_version' ) )
+gulp.task('build', gulp.series( 'clean', 'webpack:build' ) )
+
gulp.task('karma_ci', function(done) {
karma.start({
configFile: path.join(__dirname, karmaConfig)
@@ -113,7 +152,7 @@ gulp.task('protractor-explorer', function(callback) {
}, callback)
})
-gulp.task('protractor', ['webdriver-update'], function(callback) {
+gulp.task('protractor', gulp.series( 'webdriver-update', function(callback) {
gulp.src(['./res/test/e2e/**/*.js'])
.pipe(protractor.protractor({
configFile: protractorConfig
@@ -126,7 +165,7 @@ gulp.task('protractor', ['webdriver-update'], function(callback) {
/* eslint no-console: 0 */
})
.on('end', callback)
-})
+} ) )
// For piping strings
function fromString(filename, string) {
@@ -143,37 +182,6 @@ function fromString(filename, string) {
return src
}
-
-// For production
-gulp.task('webpack:build', function(callback) {
- var myConfig = Object.create(webpackConfig)
- myConfig.plugins = myConfig.plugins.concat(
- new webpack.DefinePlugin({
- 'process.env': {
- NODE_ENV: JSON.stringify('production')
- }
- })
- )
- myConfig.devtool = false
-
- webpack(myConfig, function(err, stats) {
- if (err) {
- throw new gutil.PluginError('webpack:build', err)
- }
-
- gutil.log('[webpack:build]', stats.toString({
- colors: true
- }))
-
- // Save stats to a json file
- // Can be analyzed in http://webpack.github.io/analyse/
- fromString('stats.json', JSON.stringify(stats.toJson()))
- .pipe(gulp.dest('./tmp/'))
-
- callback()
- })
-})
-
gulp.task('webpack:others', function(callback) {
var myConfig = Object.create(webpackStatusConfig)
myConfig.plugins = myConfig.plugins.concat(
@@ -197,17 +205,10 @@ gulp.task('webpack:others', function(callback) {
})
})
-gulp.task('translate', [
- 'translate:extract'
-, 'translate:push'
-, 'translate:pull'
-, 'translate:compile'
-])
-
gulp.task('pug', function() {
return gulp.src([
'./res/**/*.pug'
- , '!./res/bower_components/**'
+ , '!./bower_modules/**'
])
.pipe(pug({
locals: {
@@ -221,16 +222,17 @@ gulp.task('pug', function() {
.pipe(gulp.dest('./tmp/html/'))
})
-gulp.task('translate:extract', ['pug'], function() {
+gulp.task('translate:extract', gulp.series( 'pug', function(done) {
return gulp.src([
'./tmp/html/**/*.html'
, './res/**/*.js'
- , '!./res/bower_components/**'
+ , '!./bower_modules/**'
, '!./res/build/**'
])
.pipe(gettext.extract('stf.pot'))
.pipe(gulp.dest('./res/common/lang/po/'))
-})
+ done()
+}))
gulp.task('translate:compile', function() {
return gulp.src('./res/common/lang/po/**/*.po')
@@ -250,10 +252,9 @@ gulp.task('translate:pull', function() {
return run('tx pull').exec()
})
-gulp.task('clean', function(cb) {
- del([
- './tmp'
- , './res/build'
- , '.eslintcache'
- ], cb)
-})
+gulp.task('translate', gulp.series(
+ 'translate:extract'
+, 'translate:push'
+, 'translate:pull'
+, 'translate:compile'
+))
\ No newline at end of file
diff --git a/lib/cli/storage-temp/index.js b/lib/cli/storage-temp/index.js
index 12afcb09f2..33139c536a 100644
--- a/lib/cli/storage-temp/index.js
+++ b/lib/cli/storage-temp/index.js
@@ -79,19 +79,19 @@ module.exports.builder = function(yargs) {
module.exports.handler = function(argv) {
return require('../../units/storage/temp')({
- port: argv.port
- , saveDir: argv.saveDir
- , maxFileSize: argv.maxFileSize
- , bundletoolPath: argv.bundletoolPath
- , keystore: {
- ksPath: `/tmp/${argv.ks}.keystore`
- , ksKeyAlias: argv.ksKeyAlias
- , ksPass: argv.ksPass
- , ksKeyPass: argv.ksKeyPass
- , ksKeyalg: argv.ksKeyalg
- , ksValidity: argv.ksValidity
- , ksKeysize: argv.ksKeysize
- , ksDname: argv.ksDname
+ port: argv.port,
+ saveDir: argv.saveDir,
+ maxFileSize: argv.maxFileSize,
+ bundletoolPath: argv.bundletoolPath,
+ keystore: {
+ ksPath: `/tmp/${argv.ks}.keystore`,
+ ksKeyAlias: argv.ksKeyAlias,
+ ksPass: argv.ksPass,
+ ksKeyPass: argv.ksKeyPass,
+ ksKeyalg: argv.ksKeyalg,
+ ksValidity: argv.ksValidity,
+ ksKeysize: argv.ksKeysize,
+ ksDname: argv.ksDname
}
})
}
diff --git a/lib/units/app/index.js b/lib/units/app/index.js
index ebd92063f1..a52dad95ae 100644
--- a/lib/units/app/index.js
+++ b/lib/units/app/index.js
@@ -3,6 +3,7 @@ var url = require('url')
var fs = require('fs')
var express = require('express')
+var path = require('path')
var validator = require('express-validator')
var cookieSession = require('cookie-session')
var bodyParser = require('body-parser')
@@ -23,9 +24,32 @@ var markdownServe = require('markdown-serve')
module.exports = function(options) {
var log = logger.createLogger('app')
+
+ express.static.mime.define({'application/javascript': ['js']});
+
var app = express()
+
var server = http.createServer(app)
-
+
+ /*app.use(function (req, res, next) {
+ var filename = path.basename(req.url);
+ var extension = path.extname(filename);
+ if (extension === '.css')
+ console.log("Request: " + filename);
+ next();
+ });*/
+ app.use(express.static('/static', {
+ index: false,
+ setHeaders: (response, file_path, file_stats) => {
+ // This function is called when “serve-static” makes a response.
+ // Note that `file_path` is an absolute path.
+
+ // Logging work
+ //const relative_path = path.join(asset_dir_path, path.relative(asset_dir_path, file_path));
+ console.info(`@${Date.now()}`, "GAVE\t\t", file_path);
+ }
+ }));
+
app.use('/static/wiki', markdownServe.middleware({
rootDirectory: pathutil.root('node_modules/@devicefarmer/stf-wiki')
, view: 'docs'
@@ -50,7 +74,7 @@ module.exports = function(options) {
log.info('Using webpack')
// Keep webpack-related requires here, as our prebuilt package won't
// have them at all.
- var webpackServerConfig = require('./../../../webpack.config').webpackServer
+ var webpackServerConfig = require('./../../../webpackserver.config').webpackServer
app.use('/static/app/build',
require('./middleware/webpack')(webpackServerConfig))
}
diff --git a/lib/units/app/middleware/webpack.js b/lib/units/app/middleware/webpack.js
index e8d4a68249..6a22f9b73d 100644
--- a/lib/units/app/middleware/webpack.js
+++ b/lib/units/app/middleware/webpack.js
@@ -9,7 +9,7 @@ var MemoryFileSystem = require('memory-fs')
var logger = require('../../../util/logger')
var lifecycle = require('../../../util/lifecycle')
-var globalOptions = require('../../../../webpack.config').webpack
+var globalOptions = require('../../../../webpack.config')
// Similar to webpack-dev-middleware, but integrates with our custom
// lifecycle, behaves more like normal express middleware, and removes
diff --git a/lib/units/storage/plugins/apk/index.js b/lib/units/storage/plugins/apk/index.js
index 9ed22bca0e..daf8193e20 100644
--- a/lib/units/storage/plugins/apk/index.js
+++ b/lib/units/storage/plugins/apk/index.js
@@ -3,7 +3,7 @@ var url = require('url')
var util = require('util')
var express = require('express')
-var request = require('request')
+var request = require('request').defaults({ rejectUnauthorized: false })
var logger = require('../../../../util/logger')
var download = require('../../../../util/download')
diff --git a/lib/units/storage/plugins/apk/task/manifest.js b/lib/units/storage/plugins/apk/task/manifest.js
index c6d3faf4db..3cfa3a167c 100644
--- a/lib/units/storage/plugins/apk/task/manifest.js
+++ b/lib/units/storage/plugins/apk/task/manifest.js
@@ -1,7 +1,16 @@
var ApkReader = require('@devicefarmer/adbkit-apkreader')
+var IpaReader = require('../../../../../util/ipareader')
module.exports = function(file) {
- return ApkReader.open(file.path).then(function(reader) {
- return reader.readManifest()
- })
+ if(file.path.endsWith('.apk')){
+ return ApkReader.open(file.path).then(function(reader) {
+ return reader.readManifest()
+ })
+ }
+ else if(file.path.endsWith('.ipa')){
+ var ipaReader = new IpaReader(file.path)
+ return ipaReader.ReadInfoPlist().then(function(res){
+ return res
+ })
+ }
}
diff --git a/lib/units/storage/temp.js b/lib/units/storage/temp.js
index 7e770f52d4..aafdc0e2bf 100644
--- a/lib/units/storage/temp.js
+++ b/lib/units/storage/temp.js
@@ -83,6 +83,11 @@ module.exports = function(options) {
})
})
})
+
+ function get_file_extension(source) {
+ var typestr = source.toLowerCase().substring(source.lastIndexOf('.') + 1)
+ return typestr
+ }
app.post('/s/upload/:plugin', function(req, res) {
var form = new formidable.IncomingForm({
@@ -96,7 +101,9 @@ module.exports = function(options) {
file.isAab = true
}
var md5 = crypto.createHash('md5')
- file.name = md5.update(file.name).digest('hex')
+ var extension = get_file_extension(file.name)
+ file.name = md5.update(file.name).digest('hex') + '.' + extension
+ log.info("Saving with extension ", file.name )
})
Promise.promisify(form.parse, form)(req)
.spread(function(fields, files) {
@@ -104,22 +111,25 @@ module.exports = function(options) {
var file = files[field]
log.info('Uploaded "%s" to "%s"', file.name, file.path)
return {
- field: field
- , id: storage.store(file)
- , name: file.name
- , path: file.path
- , isAab: file.isAab
+ field: field,
+ id: storage.store(file),
+ name: file.name,
+ path: file.path,
+ isAab: ( file.isAab || false )
}
})
})
.then(function(storedFiles) {
- return Promise.all(storedFiles.map(function(file) {
- return bundletool({
- bundletoolPath: options.bundletoolPath
- , keystore: options.keystore
- , file: file
- })
- })
+ return Promise.all(
+ storedFiles.map(
+ function(file) {
+ return bundletool( {
+ bundletoolPath: options.bundletoolPath,
+ keystore: options.keystore,
+ file: file
+ } )
+ }
+ )
)
})
.then(function(storedFiles) {
diff --git a/lib/units/websocket/index.js b/lib/units/websocket/index.js
index b7071d034d..d82aa30d8b 100644
--- a/lib/units/websocket/index.js
+++ b/lib/units/websocket/index.js
@@ -1001,6 +1001,56 @@ module.exports = function(options) {
)
])
})
+ .on('alert.text', function(channel, responseChannel) {
+ joinChannel(responseChannel)
+ push.send([
+ channel
+ , wireutil.transaction(
+ responseChannel
+ , new wire.AlertTextMessage()
+ )
+ ])
+ })
+ .on('alert.buttons', function(channel, responseChannel) {
+ joinChannel(responseChannel)
+ push.send([
+ channel
+ , wireutil.transaction(
+ responseChannel
+ , new wire.AlertButtonsMessage()
+ )
+ ])
+ })
+ .on('alert.accept', function(channel, responseChannel, data) {
+ joinChannel(responseChannel)
+ push.send([
+ channel
+ , wireutil.transaction(
+ responseChannel
+ , new wire.AlertAcceptMessage(data)
+ )
+ ])
+ })
+ .on('alert.dismiss', function(channel, responseChannel, data) {
+ joinChannel(responseChannel)
+ push.send([
+ channel
+ , wireutil.transaction(
+ responseChannel
+ , new wire.AlertDismissMessage(data)
+ )
+ ])
+ })
+ .on('input.wheel', function(channel, responseChannel, data) {
+ joinChannel(responseChannel)
+ push.send([
+ channel
+ , wireutil.transaction(
+ responseChannel
+ , new wire.InputWheelMessage(data)
+ )
+ ])
+ })
})
.finally(function() {
// Clean up all listeners and subscriptions
diff --git a/lib/util/download.js b/lib/util/download.js
index 372345e82b..83663e4692 100644
--- a/lib/util/download.js
+++ b/lib/util/download.js
@@ -1,18 +1,27 @@
var fs = require('fs')
var Promise = require('bluebird')
-var request = require('request')
+var request = require('request').defaults({ rejectUnauthorized: false })
var progress = require('request-progress')
var temp = require('temp')
+var logger = require('./logger')
+var log = logger.createLogger('util:download')
module.exports = function download(url, options) {
var resolver = Promise.defer()
var path = temp.path(options)
+ if(url.endsWith('.apk')){
+ path += '.apk'
+ }
+ else if(url.endsWith('.ipa')){
+ path += '.ipa'
+ }
+ log.info("Downloading " + url + " to " + path )
function errorListener(err) {
resolver.reject(err)
}
-
+
function progressListener(state) {
if (state.total !== null) {
resolver.progress({
diff --git a/lib/util/ipareader.js b/lib/util/ipareader.js
new file mode 100644
index 0000000000..bcf876693b
--- /dev/null
+++ b/lib/util/ipareader.js
@@ -0,0 +1,90 @@
+'use strict'
+
+const Promise = require('bluebird')
+var bplist = require('bplist')
+var plist = require('plist')
+var fs = require('fs')
+var unzipper = require('unzipper')
+var os = require('os')
+var util = require('util')
+var execSync = require('child_process').execSync;
+var EventEmitter = require('eventemitter3')
+var logger = require('./logger')
+var log = logger.createLogger('util:ipareader')
+
+function IpaReader(filepath) {
+ EventEmitter.call(this)
+ this.file = filepath
+ this.cachedir = os.tmpdir()+'/ipa'
+}
+
+util.inherits(IpaReader, EventEmitter)
+
+IpaReader.prototype.parsePlist = function(){
+ var manifest = {}
+
+ var destdir = this.cachedir+'/Payload'
+ var dirs = fs.readdirSync(destdir)
+ destdir = util.format("%s/%s",destdir,dirs[0])
+ var destfile = destdir+'/Info.plist'
+ var content = fs.readFileSync(destfile)
+ return new Promise((resolve,reject)=>{
+ var process = function(err,result){
+ if(err){
+ return reject(err)
+ }
+ manifest = result[0]
+ manifest.package = result[0].CFBundleIdentifier
+ manifest.versionCode = parseInt(result[0].CFBundleInfoDictionaryVersion)
+ manifest.versionName = result[0].CFBundleShortVersionString
+ return resolve(manifest)
+ };
+
+ console.log( typeof( content ) );
+ var firstSix = content.toString( 'ascii', 0, 6 );
+ if( firstSix == "bplist" ) {
+ bplist.parseBuffer( content, process )
+ }
+ else {
+ var data = plist.parse( content.toString('utf-8') );
+ process( 0, [data] );
+ }
+ } )
+}
+
+IpaReader.prototype.UnzipIpa = function(){
+ this.cachedir = os.tmpdir()+'/ipa'
+ if(fs.existsSync(this.cachedir)){
+ var cmd = 'rm -rf '+this.cachedir
+ console.log(cmd)
+ execSync(cmd,{});
+ }
+ fs.mkdirSync(this.cachedir)
+
+ log.info("Extracting " + this.file + " to " + this.cachedir );
+
+ return new Promise((resolve,reject)=>{
+ var extractor = unzipper.Extract({
+ path: this.cachedir
+ });
+ extractor.on('error', function(err) {
+ throw err;
+ });
+ extractor.promise().then(function() {
+ return resolve()
+ });
+ fs.createReadStream( this.file ).pipe( extractor )
+ })
+}
+
+IpaReader.prototype.ReadInfoPlist = function(){
+ return new Promise((resolve,reject)=>{
+ this.UnzipIpa().then(()=>{
+ this.parsePlist().then(function(res){
+ return resolve(res)
+ })
+ })
+ })
+}
+
+module.exports = IpaReader
\ No newline at end of file
diff --git a/lib/wire/wire.proto b/lib/wire/wire.proto
index 3bc8cdf5d1..ceceffe6ab 100644
--- a/lib/wire/wire.proto
+++ b/lib/wire/wire.proto
@@ -90,6 +90,11 @@ enum MessageType {
GroupChangeMessage = 1205;
UserChangeMessage = 1206;
DeviceChangeMessage = 1207;
+ AlertTextMessage = 1300;
+ AlertButtonsMessage = 1301;
+ AlertAcceptMessage = 1302;
+ AlertDismissMessage = 1303;
+ InputWheelMessage = 1304;
}
message UpdateAccessTokenMessage {
@@ -749,3 +754,21 @@ message RotationEvent {
required string serial = 1;
required int32 rotation = 2;
}
+
+message AlertTextMessage {
+}
+
+message AlertButtonsMessage {
+}
+
+message AlertAcceptMessage {
+ required string name = 1;
+}
+
+message AlertDismissMessage {
+ required string name = 1;
+}
+
+message InputWheelMessage {
+ required int32 dist = 1;
+}
diff --git a/package.json b/package.json
index a6b2cbcad3..4959f1b758 100644
--- a/package.json
+++ b/package.json
@@ -29,49 +29,64 @@
"duplicate-arguments-array": false
},
"scripts": {
- "test": "gulp test",
- "prepublish": "bower install && not-in-install && gulp build || in-install"
+ "test": "gulp test"
},
"dependencies": {
"@slack/client": "^3.5.4",
"@devicefarmer/adbkit": "^2.11.2",
"@devicefarmer/adbkit-apkreader": "^3.2.1",
"@devicefarmer/adbkit-monkey": "^1.0.1",
+ "@devicefarmer/minicap-prebuilt": "^2.4.0",
+ "@devicefarmer/minitouch-prebuilt": "^1.3.0",
+ "@devicefarmer/stf-appstore-db": "^1.0.0",
+ "@devicefarmer/stf-browser-db": "^1.0.2",
+ "@devicefarmer/stf-device-db": "^1.2.0",
+ "@devicefarmer/stf-syrup": "^1.0.1",
+ "@devicefarmer/stf-wiki": "^1.0.0",
+ "@julusian/jpeg-turbo": "^0.5.4",
+ "@slack/client": "^3.5.4",
"android-device-list": "^1.2.1",
"aws-sdk": "^2.4.13",
"basic-auth": "^1.0.3",
"bluebird": "^2.10.1",
"body-parser": "^1.13.3",
+ "bplist": "0.0.4",
"bufferutil": "^1.2.1",
"chalk": "~1.1.1",
"compression": "^1.5.2",
"cookie-session": "^2.0.0-alpha.1",
"csurf": "^1.7.0",
+ "d3": "~5.16.0",
"debug": "^2.2.0",
+ "dom-cascade": "git+https://github.com/nanoscopic/DomCascade.git#1.0.3",
+ "draggabilly": "~2.3.0",
+ "epoch-charting": "~0.8.4",
"eventemitter3": "^1.2.0",
"express": "^4.14.0",
"express-validator": "^2.20.8",
"file-saver": "1.3.3",
+ "fontsource-lato": "~2.1.4",
"formidable": "^1.2.0",
"gm": "^1.23.0",
"hipchatter": "^0.3.1",
"http-proxy": "^1.11.2",
"in-publish": "^2.0.0",
- "@julusian/jpeg-turbo": "^0.5.4",
"jws": "^3.1.0",
"ldapjs": "^1.0.0",
"lodash": "^4.14.2",
"markdown-serve": "^0.8.0",
+ "jquery": "~3.5.1",
+ "micromodal": "~0.4.6",
"mime": "^1.3.4",
- "@devicefarmer/minicap-prebuilt": "^2.4.0",
"minimatch": "^3.0.3",
- "@devicefarmer/minitouch-prebuilt": "^1.3.0",
+ "minlib": "git+https://github.com/nanoscopic/minlib.git#1.0.2",
"my-local-ip": "^1.0.0",
"openid": "^2.0.1",
"passport": "^0.4.1",
"passport-oauth2": "^1.1.2",
"passport-saml": "^0.15.0",
"please-update-dependencies": "^2.0.0",
+ "plist": "3.0.1",
"protobufjs": "^3.8.2",
"proxy-addr": "^1.0.10",
"pug": "^2.0.0-beta4",
@@ -82,16 +97,13 @@
"serve-favicon": "^2.2.0",
"serve-static": "^1.9.2",
"socket.io": "^2.0.3",
+ "spin.js": "~2.0.2",
"split": "^1.0.0",
- "@devicefarmer/stf-appstore-db": "^1.0.0",
- "@devicefarmer/stf-browser-db": "^1.0.2",
- "@devicefarmer/stf-device-db": "^1.2.0",
- "@devicefarmer/stf-syrup": "^1.0.1",
- "@devicefarmer/stf-wiki": "^1.0.0",
"swagger-express-mw": "^0.7.0",
"swagger-tools": "^0.10.3",
"temp": "^0.8.1",
"transliteration": "^1.1.6",
+ "unzipper": "~0.10.11",
"url-join": "1.1.0",
"utf-8-validate": "^1.2.1",
"uuid": "^3.0.0",
@@ -105,27 +117,25 @@
"bower": "^1.8.8",
"chai": "^3.4.1",
"css-loader": "^0.23.1",
- "del": "^2.0.1",
+ "del": "^5.1.0",
"eslint": "^3.2.2",
"event-stream": "^3.3.2",
"exports-loader": "^0.7.0",
- "extract-text-webpack-plugin": "^1.0.1",
- "file-loader": "^0.9.0",
+ "extract-text-webpack-plugin": "^3.0.2",
"fs-extra": "^8.1.0",
- "gulp": "^3.8.11",
+ "gulp": "^4.0.2",
"gulp-angular-gettext": "^2.1.0",
- "gulp-eslint": "^3.0.1",
- "gulp-jsonlint": "^1.0.2",
- "gulp-protractor": "^3.0.0",
- "gulp-pug": "^3.0.4",
- "gulp-run": "^1.6.12",
+ "gulp-eslint": "^6.0.0",
+ "gulp-jsonlint": "^1.3.2",
+ "gulp-protractor": "^4.1.1",
+ "gulp-pug": "^4.0.1",
+ "gulp-run": "^1.7.1",
"gulp-util": "^3.0.7",
"html-loader": "^0.5.5",
"http-https": "^1.0.0",
"imports-loader": "^0.8.0",
"jasmine-core": "^2.4.1",
"jasmine-reporters": "^2.3.2",
- "json-loader": "^0.5.4",
"karma": "^1.7.1",
"karma-chrome-launcher": "^2.2.0",
"karma-firefox-launcher": "^1.0.0",
@@ -137,15 +147,13 @@
"karma-safari-launcher": "^1.0.0",
"karma-webpack": "^1.8.0",
"less": "^2.4.0",
- "less-loader": "^2.2.2",
"memory-fs": "^0.3.0",
"node-libs-browser": "^1.0.0",
- "node-sass": "^4.13.1",
+ "node-sass": "^4.14.1",
"phantomjs-prebuilt": "^2.1.11",
"protractor": "^5.4.1",
"protractor-html-reporter-2": "1.0.4",
"raw-loader": "^0.5.1",
- "sass-loader": "^4.0.0",
"script-loader": "^0.7.0",
"sinon": "^1.17.2",
"sinon-chai": "^2.7.0",
@@ -153,9 +161,26 @@
"style-loader": "^0.13.0",
"template-html-loader": "^0.0.3",
"then-jade": "^2.4.1",
- "url-loader": "^0.5.7",
- "webpack": "^1.12.11",
- "webpack-dev-server": "^3.1.11"
+ "webpack": "^4.43.0",
+ "webpack-dev-server": "^3.11.0",
+ "file-loader": "^6.0.0",
+ "less-loader": "5.x.x",
+ "sass-loader": "^9.0.2",
+ "url-loader": "^4.1.0",
+ "typescript": "^3.9.7",
+ "ts-loader": "^8.0.1",
+ "node-unzip-2": "0.2.8",
+ "ng-annotate-patched": "1.12.0",
+ "ng-annotate": "1.2.2",
+ "ng-annotate-loader": "0.7.0",
+ "@babel/cli": "7.10.5",
+ "@babel/core": "7.10.5",
+ "@babel/node": "7.10.5",
+ "@babel/preset-env": "7.10.4",
+ "@babel/register": "7.10.5",
+ "@babel/plugin-proposal-class-properties": "7.10.4",
+ "@babel/plugin-proposal-private-methods": "7.10.4",
+ "babel-loader": "8.1.0"
},
"engines": {
"node": ">= 6.9"
diff --git a/res/app/app.js b/res/app/app.js
index f5073847e3..bcfcf53efb 100644
--- a/res/app/app.js
+++ b/res/app/app.js
@@ -1,17 +1,18 @@
-/**
-* Copyright © 2019 contains code contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
-**/
+// Copyright © 2019 contains code contributed by Orange SA, authors: Denis Barbaron - Licensed under the Apache license 2.0
+
+import * as d3 from "d3";
require.ensure([], function(require) {
require('angular')
require('angular-route')
require('angular-touch')
-
+ require('angular-hotkeys')
+
angular.module('app', [
'ngRoute',
'ngTouch',
require('gettext').name,
- require('angular-hotkeys').name,
+ 'cfp.hotkeys',
require('./layout').name,
require('./device-list').name,
require('./group-list').name,
diff --git a/res/app/components/stf/common-ui/table/index.js b/res/app/components/stf/common-ui/table/index.js
index eb1056f67b..7c71079df9 100644
--- a/res/app/components/stf/common-ui/table/index.js
+++ b/res/app/components/stf/common-ui/table/index.js
@@ -1,5 +1,5 @@
require('./table.css')
-require('script!ng-table/dist/ng-table')
+require('script-loader!ng-table/dist/ng-table')
module.exports = angular.module('stf/common-ui/table', [
'ngTable'
diff --git a/res/app/components/stf/control/control-service.js b/res/app/components/stf/control/control-service.js
index a274030a79..c0285096eb 100644
--- a/res/app/components/stf/control/control-service.js
+++ b/res/app/components/stf/control/control-service.js
@@ -3,6 +3,7 @@ module.exports = function ControlServiceFactory(
, $http
, socket
, TransactionService
+, TransactionError
, $rootScope
, gettext
, KeycodesMapped
@@ -12,10 +13,12 @@ module.exports = function ControlServiceFactory(
function ControlService(target, channel) {
function sendOneWay(action, data) {
+ console.log("sendOneWay",action,data);
socket.emit(action, channel, data)
}
function sendTwoWay(action, data) {
+ console.log("sendTwoWay",action,data);
var tx = TransactionService.create(target)
socket.emit(action, channel, tx.channel, data)
return tx.promise
@@ -292,7 +295,51 @@ module.exports = function ControlServiceFactory(
this.getWifiStatus = function() {
return sendTwoWay('wifi.get')
}
-
+
+ this.getAlertInfo = function() {
+ return new Promise( function( resolve, reject ) {
+ sendTwoWay('alert.text',{}).then( function( res ) {
+ console.log( 'alert.text result', res );
+ sendTwoWay('alert.buttons',{}).then( function( res2 ) {
+ console.log( 'alert.buttons results', res2 );
+ resolve( [ res, res2 ] );
+ } )
+ .catch(TransactionError, function() {
+ throw new Error('alert.buttons failure')
+ });
+ } )
+ .catch(TransactionError, function() {
+ throw new Error('alert.text failure')
+ });
+ } );
+ }
+
+ this.alertAccept = function( name ) {
+ return new Promise( function( resolve, reject ) {
+ sendTwoWay('alert.accept',{ name: name })
+ .then( function( res ) {
+ console.log( 'alert.accept result', res );
+ resolve( res );
+ } )
+ .catch(TransactionError, function() {
+ reject();
+ });
+ } );
+ }
+
+ this.wheel = function( dist ) {
+ return new Promise( function( resolve, reject ) {
+ sendTwoWay('input.wheel',{ dist: dist })
+ .then( function( res ) {
+ console.log( 'input.wheel result', res );
+ resolve( res );
+ } )
+ .catch(TransactionError, function() {
+ reject();
+ });
+ } );
+ }
+
window.cc = this
}
diff --git a/res/app/components/stf/install/install-service.js b/res/app/components/stf/install/install-service.js
index 3c7cd98ee9..bee0d87d54 100644
--- a/res/app/components/stf/install/install-service.js
+++ b/res/app/components/stf/install/install-service.js
@@ -92,7 +92,7 @@ module.exports = function InstallService(
$rootScope.$broadcast('installation', installation)
return StorageService.storeFile('apk', $files, {
filter: function(file) {
- return /\.(apk|aab)$/i.test(file.name)
+ return /\.(apk|aab|ipa)$/i.test(file.name)
}
})
.progressed(function(e) {
diff --git a/res/app/components/stf/screen/screen-directive.js b/res/app/components/stf/screen/screen-directive.js
index 47e0f296ba..cfe12cefaf 100644
--- a/res/app/components/stf/screen/screen-directive.js
+++ b/res/app/components/stf/screen/screen-directive.js
@@ -3,21 +3,21 @@ var rotator = require('./rotator')
var ImagePool = require('./imagepool')
module.exports = function DeviceScreenDirective(
- $document
-, ScalingService
-, VendorUtil
-, PageVisibilityService
-, $timeout
-, $window
+ $document,
+ ScalingService,
+ VendorUtil,
+ PageVisibilityService,
+ $timeout,
+ $window
) {
return {
- restrict: 'E'
- , template: require('./screen.pug')
- , scope: {
- control: '&'
- , device: '&'
- }
- , link: function(scope, element) {
+ restrict: 'E',
+ template: require('./screen.pug'),
+ scope: {
+ control: '&',
+ device: '&'
+ },
+ link: function(scope, element) {
var URL = window.URL || window.webkitURL
var BLANK_IMG =
'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw=='
@@ -29,19 +29,11 @@ module.exports = function DeviceScreenDirective(
var input = element.find('input')
var screen = scope.screen = {
- rotation: 0
- , bounds: {
- x: 0
- , y: 0
- , w: 0
- , h: 0
- }
+ rotation: 0,
+ bounds: { x: 0, y: 0, w: 0, h: 0 }
}
-
- var scaler = ScalingService.coordinator(
- device.display.width
- , device.display.height
- )
+
+ var scaler = ScalingService.coordinator( device.display.width, device.display.height )
/**
* SCREEN HANDLING
@@ -90,9 +82,9 @@ module.exports = function DeviceScreenDirective(
var frontBackRatio = devicePixelRatio / backingStoreRatio
var options = {
- autoScaleForRetina: true
- , density: Math.max(1, Math.min(1.5, devicePixelRatio || 1))
- , minscale: 0.36
+ autoScaleForRetina: true,
+ density: Math.max(1, Math.min(1.5, devicePixelRatio || 1)),
+ minscale: 0.36
}
var adjustedBoundSize
@@ -114,10 +106,7 @@ module.exports = function DeviceScreenDirective(
sh *= f / sh
}
- return {
- w: Math.ceil(sw)
- , h: Math.ceil(sh)
- }
+ return { w: Math.ceil(sw), h: Math.ceil(sh) }
}
// FIXME: element is an object HTMLUnknownElement in IE9
@@ -205,13 +194,8 @@ module.exports = function DeviceScreenDirective(
ws.onmessage = (function() {
var cachedScreen = {
- rotation: 0
- , bounds: {
- x: 0
- , y: 0
- , w: 0
- , h: 0
- }
+ rotation: 0,
+ bounds: { x: 0, y: 0, w: 0, h: 0 }
}
var cachedImageWidth = 0
@@ -222,7 +206,9 @@ module.exports = function DeviceScreenDirective(
function applyQuirks(banner) {
element[0].classList.toggle(
- 'quirk-always-upright', alwaysUpright = banner.quirks.alwaysUpright)
+ 'quirk-always-upright',
+ alwaysUpright = banner.quirks.alwaysUpright
+ )
}
function hasImageAreaChanged(img) {
@@ -411,16 +397,16 @@ module.exports = function DeviceScreenDirective(
// Chrome/Safari/Opera
if (
// Mac | Kinesis keyboard | Karabiner | Latin key, Kana key
- e.keyCode === 0 && e.keyIdentifier === 'U+0010' ||
+ e.keyCode === 0 && e.keyIdentifier === 'U+0010' ||
// Mac | MacBook Pro keyboard | Latin key, Kana key
- e.keyCode === 0 && e.keyIdentifier === 'U+0020' ||
+ e.keyCode === 0 && e.keyIdentifier === 'U+0020' ||
// Win | Lenovo X230 keyboard | Alt+Latin key
- e.keyCode === 246 && e.keyIdentifier === 'U+00F6' ||
+ e.keyCode === 246 && e.keyIdentifier === 'U+00F6' ||
// Win | Lenovo X230 keyboard | Convert key
- e.keyCode === 28 && e.keyIdentifier === 'U+001C'
+ e.keyCode === 28 && e.keyIdentifier === 'U+001C'
) {
return true
}
@@ -573,6 +559,11 @@ module.exports = function DeviceScreenDirective(
}
}
+ function mouseWheelListener(event) {
+ event.preventDefault();
+ control.wheel( event.deltaY );
+ }
+
function mouseDownListener(event) {
var e = event
if (e.originalEvent) {
@@ -593,20 +584,28 @@ module.exports = function DeviceScreenDirective(
var x = e.pageX - screen.bounds.x
var y = e.pageY - screen.bounds.y
- var pressure = 0.5
+
+ var pressure = 0.5;
+ if( e.shiftKey ) pressure = 1;
+
var scaled = scaler.coords(
- screen.bounds.w
- , screen.bounds.h
- , x
- , y
- , screen.rotation
- )
+ screen.bounds.w,
+ screen.bounds.h,
+ x,
+ y,
+ screen.rotation
+ )
control.touchDown(nextSeq(), 0, scaled.xP, scaled.yP, pressure)
if (fakePinch) {
- control.touchDown(nextSeq(), 1, 1 - scaled.xP, 1 - scaled.yP,
- pressure)
+ control.touchDown(
+ nextSeq(),
+ 1,
+ 1 - scaled.xP,
+ 1 - scaled.yP,
+ pressure
+ )
}
control.touchCommit(nextSeq())
@@ -614,16 +613,22 @@ module.exports = function DeviceScreenDirective(
activateFinger(0, x, y, pressure)
if (fakePinch) {
- activateFinger(1, -e.pageX + screen.bounds.x + screen.bounds.w,
- -e.pageY + screen.bounds.y + screen.bounds.h, pressure)
+ activateFinger(
+ 1,
+ -e.pageX + screen.bounds.x + screen.bounds.w,
+ -e.pageY + screen.bounds.y + screen.bounds.h,
+ pressure
+ )
}
element.bind('mousemove', mouseMoveListener)
$document.bind('mouseup', mouseUpListener)
$document.bind('mouseleave', mouseUpListener)
- if (lastPossiblyBuggyMouseUpEvent &&
- lastPossiblyBuggyMouseUpEvent.timeStamp > e.timeStamp) {
+ if (
+ lastPossiblyBuggyMouseUpEvent &&
+ lastPossiblyBuggyMouseUpEvent.timeStamp > e.timeStamp
+ ) {
// We got mouseup before mousedown. See mouseUpBugWorkaroundListener
// for details.
mouseUpListener(lastPossiblyBuggyMouseUpEvent)
@@ -654,23 +659,35 @@ module.exports = function DeviceScreenDirective(
var y = e.pageY - screen.bounds.y
var pressure = 0.5
var scaled = scaler.coords(
- screen.bounds.w
- , screen.bounds.h
- , x
- , y
- , screen.rotation
- )
+ screen.bounds.w,
+ screen.bounds.h,
+ x,
+ y,
+ screen.rotation
+ )
control.touchMove(nextSeq(), 0, scaled.xP, scaled.yP, pressure)
if (addGhostFinger) {
- control.touchDown(nextSeq(), 1, 1 - scaled.xP, 1 - scaled.yP, pressure)
+ control.touchDown(
+ nextSeq(),
+ 1,
+ 1 - scaled.xP,
+ 1 - scaled.yP,
+ pressure
+ )
}
else if (deleteGhostFinger) {
control.touchUp(nextSeq(), 1)
}
else if (fakePinch) {
- control.touchMove(nextSeq(), 1, 1 - scaled.xP, 1 - scaled.yP, pressure)
+ control.touchMove(
+ nextSeq(),
+ 1,
+ 1 - scaled.xP,
+ 1 - scaled.yP,
+ pressure
+ )
}
control.touchCommit(nextSeq())
@@ -681,8 +698,12 @@ module.exports = function DeviceScreenDirective(
deactivateFinger(1)
}
else if (fakePinch) {
- activateFinger(1, -e.pageX + screen.bounds.x + screen.bounds.w,
- -e.pageY + screen.bounds.y + screen.bounds.h, pressure)
+ activateFinger(
+ 1,
+ -e.pageX + screen.bounds.x + screen.bounds.w,
+ -e.pageY + screen.bounds.y + screen.bounds.h,
+ pressure
+ )
}
}
@@ -834,12 +855,12 @@ module.exports = function DeviceScreenDirective(
var y = touch.pageY - screen.bounds.y
var pressure = touch.force || 0.5
var scaled = scaler.coords(
- screen.bounds.w
- , screen.bounds.h
- , x
- , y
- , screen.rotation
- )
+ screen.bounds.w,
+ screen.bounds.h,
+ x,
+ y,
+ screen.rotation
+ )
slotted[touch.identifier] = slot
control.touchDown(nextSeq(), slot, scaled.xP, scaled.yP, pressure)
@@ -868,12 +889,12 @@ module.exports = function DeviceScreenDirective(
var y = touch.pageY - screen.bounds.y
var pressure = touch.force || 0.5
var scaled = scaler.coords(
- screen.bounds.w
- , screen.bounds.h
- , x
- , y
- , screen.rotation
- )
+ screen.bounds.w,
+ screen.bounds.h,
+ x,
+ y,
+ screen.rotation
+ )
control.touchMove(nextSeq(), slot, scaled.xP, scaled.yP, pressure)
activateFinger(slot, x, y, pressure)
@@ -928,6 +949,7 @@ module.exports = function DeviceScreenDirective(
element.on('touchstart', touchStartListener)
element.on('mousedown', mouseDownListener)
element.on('mouseup', mouseUpBugWorkaroundListener)
+ element.on('wheel', mouseWheelListener)
createSlots()
})()
diff --git a/res/app/components/stf/storage/storage-service.js b/res/app/components/stf/storage/storage-service.js
index 7c3a1f76ba..97ebe8de95 100644
--- a/res/app/components/stf/storage/storage-service.js
+++ b/res/app/components/stf/storage/storage-service.js
@@ -18,6 +18,9 @@ module.exports = function StorageServiceFactory($http, $upload) {
var input = options.filter ? files.filter(options.filter) : files
if (input.length) {
+ if(input.indexOf('.ipa')!=-1)
+ type='ipa'
+
$upload.upload({
url: '/s/upload/' + type
, method: 'POST'
diff --git a/res/app/control-panes/advanced/advanced.pug b/res/app/control-panes/advanced/advanced.pug
index b277b7b382..9d548a47ed 100644
--- a/res/app/control-panes/advanced/advanced.pug
+++ b/res/app/control-panes/advanced/advanced.pug
@@ -11,3 +11,5 @@
.col-md-6
div(ng-include='"control-panes/advanced/maintenance/maintenance.pug"')
+
+div(ng-include='"control-panes/advanced/input/alert-dialog.pug"')
diff --git a/res/app/control-panes/advanced/index.js b/res/app/control-panes/advanced/index.js
index 78dc569a15..7a79dff49c 100644
--- a/res/app/control-panes/advanced/index.js
+++ b/res/app/control-panes/advanced/index.js
@@ -1,4 +1,5 @@
require('./advanced.css')
+require('./input/alert-dialog.css')
module.exports = angular.module('stf.advanced', [
require('./input').name,
diff --git a/res/app/control-panes/advanced/input/alert-dialog.css b/res/app/control-panes/advanced/input/alert-dialog.css
new file mode 100644
index 0000000000..dd5bb09bae
--- /dev/null
+++ b/res/app/control-panes/advanced/input/alert-dialog.css
@@ -0,0 +1,106 @@
+/**************************\
+ Basic Modal Styles
+\**************************/
+
+
+.modal {
+ display: none;
+}
+
+.modal.is-open {
+ display: block;
+}
+
+.modal {
+ font-family: -apple-system,BlinkMacSystemFont,avenir next,avenir,helvetica neue,helvetica,ubuntu,roboto,noto,segoe ui,arial,sans-serif;
+}
+
+.modal__overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0,0,0,0.6);
+ display: flex;
+ justify-content: center;
+ align-items: center;
+}
+
+.modal__container {
+ background-color: #fff;
+ padding: 30px;
+ max-width: 500px;
+ max-height: 100vh;
+ border-radius: 4px;
+ overflow-y: auto;
+ box-sizing: border-box;
+}
+
+.modal__header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.modal__title {
+ margin-top: 0;
+ margin-bottom: 0;
+ font-weight: 600;
+ font-size: 1.25rem;
+ line-height: 1.25;
+ color: #00449e;
+ box-sizing: border-box;
+}
+
+.modal__close {
+ background: transparent;
+ border: 0;
+}
+
+.modal__header .modal__close:before { content: "\2715"; }
+
+.modal__content {
+ margin-top: 2rem;
+ margin-bottom: 2rem;
+ line-height: 1.5;
+ color: rgba(0,0,0,.8);
+}
+
+.modal__btn {
+ font-size: .875rem;
+ padding-left: 1rem;
+ padding-right: 1rem;
+ padding-top: .5rem;
+ padding-bottom: .5rem;
+ background-color: #e6e6e6;
+ color: rgba(0,0,0,.8);
+ border-radius: .25rem;
+ border-style: none;
+ border-width: 0;
+ cursor: pointer;
+ -webkit-appearance: button;
+ text-transform: none;
+ overflow: visible;
+ line-height: 1.15;
+ margin: 0;
+ will-change: transform;
+ -moz-osx-font-smoothing: grayscale;
+ -webkit-backface-visibility: hidden;
+ backface-visibility: hidden;
+ -webkit-transform: translateZ(0);
+ transform: translateZ(0);
+ transition: -webkit-transform .25s ease-out;
+ transition: transform .25s ease-out;
+ transition: transform .25s ease-out,-webkit-transform .25s ease-out;
+}
+
+.modal__btn:focus, .modal__btn:hover {
+ -webkit-transform: scale(1.05);
+ transform: scale(1.05);
+}
+
+.modal__btn-primary {
+ background-color: #00449e;
+ color: #fff;
+}
\ No newline at end of file
diff --git a/res/app/control-panes/advanced/input/alert-dialog.pug b/res/app/control-panes/advanced/input/alert-dialog.pug
new file mode 100644
index 0000000000..9316da1634
--- /dev/null
+++ b/res/app/control-panes/advanced/input/alert-dialog.pug
@@ -0,0 +1,17 @@
+#alertModal.modal.micromodal-slide(aria-hidden="true")
+ .modal__overlay(tabindex="-1" data-micromodal-close="")
+ .modal__container(role="dialog" aria-modal="true" aria-labelledby="modal-1-title")
+ header.modal__header
+ h2#alertModal-title.modal__title
+ | Micromodal
+ button.modal__close(aria-label="Close modal" data-micromodal-close="")
+ main#alertModal-content.modal__content
+ p
+ | Try hitting the
+ code tab
+ | key and notice how the focus stays within the modal itself. Also,
+ code esc
+ | to close modal.
+ footer#alertModal-footer.modal__footer
+ button.modal__btn.modal__btn-primary Continue
+ button.modal__btn(data-micromodal-close="" aria-label="Close this dialog window") Close
diff --git a/res/app/control-panes/advanced/input/index.js b/res/app/control-panes/advanced/input/index.js
index e6ecd193ef..cff753abde 100644
--- a/res/app/control-panes/advanced/input/index.js
+++ b/res/app/control-panes/advanced/input/index.js
@@ -6,5 +6,8 @@ module.exports = angular.module('stf.advanced.input', [
$templateCache.put('control-panes/advanced/input/input.pug',
require('./input.pug')
)
+ $templateCache.put('control-panes/advanced/input/alert-dialog.pug',
+ require('./alert-dialog.pug')
+ )
}])
- .controller('InputAdvancedCtrl', require('./input-controller'))
+ .controller('InputAdvancedCtrl', require('./input-controller').default)
diff --git a/res/app/control-panes/advanced/input/input-controller.js b/res/app/control-panes/advanced/input/input-controller.js
index d941424bb8..ff38e9e654 100644
--- a/res/app/control-panes/advanced/input/input-controller.js
+++ b/res/app/control-panes/advanced/input/input-controller.js
@@ -1,6 +1,56 @@
-module.exports = function InputCtrl($scope) {
+import MicroModal from 'micromodal';//require('micromodal');
+import DomCascade from 'dom-cascade';
+import MinLib from 'minlib';
+export default function InputCtrl($scope) {
$scope.press = function(key) {
$scope.control.keyPress(key)
}
+
+ $scope.handle_alert = function() {
+ MicroModal.init();
+
+ var dc = new DomCascade();
+ var ml = new MinLib();
+
+ $scope.control.getAlertInfo().then( function( arr ) {
+ //
+ //
+
+ var text = arr[0].body.value;
+ var parts = text.split('\n');
+ var title = parts.shift();
+ ml.gel('alertModal-title').innerHTML = title;
+
+ var dcParts = [];
+ for( var i=0;i= 1 ? 'complete' : 'unknown'
- }
- log.info('Build progress %d%% (%s)', Math.floor(progress * 100), msg)
}
- , 1000
- ))
+ },
+ {test: /\.pug$/, loader: 'template-html-loader?engine=jade'}
+ , {test: /\.html$/, loader: 'html-loader'}
+ , {test: /\/angular\.js$/, loader: 'exports-loader?angular'}
+ , {test: /angular-cookies\.js$/, loader: 'imports-loader?angular=angular'}
+ , {test: /angular-route\.js$/, loader: 'imports-loader?angular=angular'}
+ , {test: /angular-touch\.js$/, loader: 'imports-loader?angular=angular'}
+ , {test: /angular-animate\.js$/, loader: 'imports-loader?angular=angular'}
+ , {test: /angular-growl\.js$/, loader: 'imports-loader?angular=angular'}
+ , {test: /angular-gettext\.js$/, loader: 'imports-loader?angular=angular'}
+ , {test: /dialogs\.js$/, loader: 'script-loader'}
+ , {test: /epoch\.js$/, loader: 'imports-loader?d3=d3'}
+ //, {test: /\.ts$/, loader: 'ng-annotate-loader?ngAnnotate=ng-annotate-patched!ts-loader`
+ , {
+ test: /\.js$/,
+ use: {
+ loader: 'ng-annotate-loader',
+ options: {
+ ngAnnotate: 'ng-annotate-patched',
+ es6: true,
+ explicityOnly: false
+ }
+ },
+ exclude: /node_modules/
+ }
+ , {
+ test: /\.ts$/,
+ use: {
+ loader: 'ts-loader'
+ },
+ exclude: /node_modules/
+ }
+ , {
+ test: /\.jsx$/,
+ use: {
+ loader: 'babel-loader',
+ options: {
+ "presets": [
+ "@babel/preset-env"
+ ],
+ "plugins": [
+ "@babel/plugin-proposal-class-properties",
+ "@babel/plugin-proposal-private-methods"
+ ]
+ }
+ },
+ exclude: /node_modules/
+ }
+ ],
+ // TODO: enable when its sane
+ // preLoaders: [
+ // {
+ // test: /\.js$/,
+ // exclude: /node_modules|bower_components/,
+ // loader: 'eslint-loader'
+ // }
+ // ],
+ noParse: [
+ pathutil.resource('bower_modules')
]
- }
- , webpackServer: {
- debug: true
- , devtool: 'eval'
- , stats: {
- colors: true
- }
- }
+ },
+ plugins: [
+ new ProgressPlugin(_.throttle(
+ function(progress, message) {
+ var msg
+ if (message) {
+ msg = message
+ }
+ else {
+ msg = progress >= 1 ? 'complete' : 'unknown'
+ }
+ log.info('Build progress %d%% (%s)', Math.floor(progress * 100), msg)
+ }
+ , 1000
+ ))
+ ]
}
diff --git a/webpackserver.config.js b/webpackserver.config.js
new file mode 100644
index 0000000000..adce56a221
--- /dev/null
+++ b/webpackserver.config.js
@@ -0,0 +1,9 @@
+module.exports = {
+ webpackServer: {
+ debug: true,
+ devtool: 'eval',
+ stats: {
+ colors: true
+ }
+ }
+}