Merge pull request #49 from gconsidine/ui/clean-build

Update UI build system
This commit is contained in:
Greg Considine 2017-09-08 15:49:02 -04:00 committed by GitHub
commit ccbf7af7f2
84 changed files with 1166 additions and 7921 deletions

View File

@ -17,38 +17,4 @@ module.exports = function(grunt) {
grunt.initConfig(configs);
grunt.loadNpmTasks('grunt-newer');
grunt.loadNpmTasks('grunt-angular-gettext');
// writes environment variables for development. current manages:
// browser-sync + websocket proxy
grunt.registerTask('sync', [
'browserSync:http',
'concurrent:watch'
]);
grunt.registerTask('dev', [
'clean:tmp',
'clean:static',
'concurrent:dev',
'concat:css',
'webpack:dev',
'sync'
]);
grunt.registerTask('devNoSync', [
'clean:tmp',
'clean:static',
'concurrent:devNoSync',
'concat:css'
]);
grunt.registerTask('release', [
'clean:tmp',
'clean:static',
'concurrent:prod',
'webpack:prod',
'concat:css',
'cssmin:vendor',
'cssmin:source'
]);
};

View File

@ -0,0 +1,198 @@
const path = require('path');
const webpack = require('webpack');
const CleanWebpackPlugin = require('clean-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CLIENT_PATH = path.resolve(__dirname, '../client');
const LIB_PATH = path.join(CLIENT_PATH, 'lib');
const UI_PATH = path.resolve(__dirname, '..');
const ASSETS_PATH = path.join(CLIENT_PATH, 'assets');
const COMPONENTS_PATH = path.join(LIB_PATH, 'components');
const COVERAGE_PATH = path.join(UI_PATH, 'coverage');
const FEATURES_PATH = path.join(CLIENT_PATH, 'features');
const LANGUAGES_PATH = path.join(CLIENT_PATH, 'languages');
const MODELS_PATH = path.join(LIB_PATH, 'models');
const NODE_MODULES_PATH = path.join(UI_PATH, 'node_modules');
const SERVICES_PATH = path.join(LIB_PATH, 'services');
const SOURCE_PATH = path.join(CLIENT_PATH, 'src');
const STATIC_PATH = path.join(UI_PATH, 'static');
const APP_ENTRY = path.join(SOURCE_PATH, 'app.js');
const VENDOR_ENTRY = path.join(SOURCE_PATH, 'vendor.js');
const INDEX_ENTRY = path.join(CLIENT_PATH, 'index.template.ejs');
const INDEX_OUTPUT = path.join(UI_PATH, 'templates/ui/index.html');
const THEME_ENTRY = path.join(LIB_PATH, 'theme', 'index.less');
const OUTPUT = 'js/[name].[hash].js';
const CHUNKS = ['vendor', 'app'];
const VENDOR = VENDOR_ENTRY;
const APP = [THEME_ENTRY, APP_ENTRY];
let base = {
entry: {
vendor: VENDOR,
app: APP
},
output: {
path: STATIC_PATH,
publicPath: '',
filename: OUTPUT
},
module: {
rules: [
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.css$/,
use: ExtractTextPlugin.extract({
use: {
loader: 'css-loader',
options: {
url: false
}
}
})
},
{
test: /\lib\/theme\/index.less$/,
use: ExtractTextPlugin.extract({
use: ['css-loader', 'less-loader']
})
},
{
test: require.resolve('jquery'),
loader: 'expose-loader?$!expose-loader?jQuery!expose-loader?jquery'
},
{
loader: 'script-loader',
test: [
/node_modules\/javascript-detect-element-resize\/jquery.resize\.js$/,
/node_modules\/d3\/d3\.min.js$/
]
},
{
test: /\.js$/,
use: 'imports-loader?define=>false'
},
{
test: /\.html$/,
use: ['ngtemplate-loader', 'html-loader'],
include: [
/lib\/components\//,
/features\//
]
},
{
test: /\.json$/,
loader: 'json-loader',
exclude: /node_modules/
}
]
},
plugins: [
new webpack.ProvidePlugin({
jsyaml: 'js-yaml',
CodeMirror: 'codemirror',
jsonlint: 'codemirror.jsonlint',
_: 'lodash'
}),
new ExtractTextPlugin('css/[name].[hash].css'),
new CleanWebpackPlugin([STATIC_PATH, COVERAGE_PATH], {
root: UI_PATH,
}),
new CopyWebpackPlugin([
{
from: path.join(ASSETS_PATH, 'fontcustom/**/*'),
to: path.join(STATIC_PATH, 'fonts/'),
flatten: true
},
{
from: path.join(NODE_MODULES_PATH, 'components-font-awesome/fonts/*'),
to: path.join(STATIC_PATH, 'fonts/'),
flatten: true
},
{
from: path.join(ASSETS_PATH, 'custom-theme/images.new/*'),
to: path.join(STATIC_PATH, 'images/'),
flatten: true
},
{
from: path.join(LANGUAGES_PATH, '*'),
to: path.join(STATIC_PATH, 'languages'),
flatten: true
},
{
from: ASSETS_PATH,
to: path.join(STATIC_PATH, 'assets')
},
{
from: path.join(NODE_MODULES_PATH, 'angular-scheduler/lib/*.html'),
to: path.join(STATIC_PATH, 'lib'),
context: NODE_MODULES_PATH
},
{
from: path.join(NODE_MODULES_PATH, 'angular-tz-extensions/tz/data/*'),
to: path.join(STATIC_PATH, 'lib/'),
context: NODE_MODULES_PATH
},
{
from: path.join(SOURCE_PATH, '**/*.partial.html'),
to: path.join(STATIC_PATH, 'partials/'),
context: SOURCE_PATH
},
{
from: path.join(SOURCE_PATH, 'partials', '*.html'),
to: STATIC_PATH,
context: SOURCE_PATH
},
{
from: path.join(SOURCE_PATH, '*config.js'),
to: STATIC_PATH,
flatten: true
}
]),
new HtmlWebpackPlugin({
alwaysWriteToDisk: true,
template: INDEX_ENTRY,
filename: INDEX_OUTPUT,
inject: false,
chunks: CHUNKS,
chunksSortMode: (moduleA, moduleB) => {
moduleA.files.sort((fileA, fileB) => fileA.includes('js') ? -1 : 1)
moduleB.files.sort((fileA, fileB) => fileA.includes('js') ? -1 : 1)
return moduleA.names[0] === 'vendor' ? -1 : 1
}
})
],
resolve: {
alias: {
'@features': FEATURES_PATH,
'@models': MODELS_PATH,
'@services': SERVICES_PATH,
'@components': COMPONENTS_PATH,
'@modules': NODE_MODULES_PATH,
'@assets': ASSETS_PATH,
'd3$': '@modules/d3/d3.min.js',
'codemirror.jsonlint$': '@modules/codemirror/addon/lint/json-lint.js',
'jquery-resize$': '@modules/javascript-detect-element-resize/jquery.resize.js',
'select2$': '@modules/select2/dist/js/select2.full.min.js',
'js-yaml$': '@modules/js-yaml/dist/js-yaml.min.js',
'lr-infinite-scroll$': '@modules/lr-infinite-scroll/lrInfiniteScroll.js',
'angular-ui-router$': '@modules/angular-ui-router/release/angular-ui-router.js',
'angular-ui-router-state-events$': '@modules/angular-ui-router/release/stateEvents.js',
'ng-toast-provider$': '@modules/ng-toast/src/scripts/provider.js',
'ng-toast-directives$': '@modules/ng-toast/src/scripts/directives.js',
'ng-toast$': '@modules/ng-toast/src/scripts/module.js'
}
}
};
module.exports = base;

View File

@ -0,0 +1,11 @@
const path = require('path');
const _ = require('lodash');
let base = require('./webpack.base');
let development = {
devtool: 'cheap-source-map'
};
module.exports = _.merge(base, development);

View File

@ -0,0 +1,19 @@
const _ = require('lodash');
const webpack = require('webpack');
const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
let base = require('./webpack.base');
let production = {
plugins: [
new UglifyJSPlugin({
compress: true,
mangle: false
})
]
};
production.plugins = base.plugins.concat(production.plugins)
module.exports = _.merge(base, production);

View File

@ -0,0 +1,21 @@
const path = require('path');
const _ = require('lodash');
const webpack = require('webpack');
const STATIC_URL = '/static/';
let development = require('./webpack.development');
let test = {
plugins: [
new webpack.DefinePlugin({
$basePath: STATIC_URL
})
]
};
test.plugins = development.plugins.concat(test.plugins)
module.exports = _.merge(development, test);

View File

@ -0,0 +1,42 @@
const path = require('path');
const _ = require('lodash');
const webpack = require('webpack');
const HtmlWebpackHarddiskPlugin = require('html-webpack-harddisk-plugin');
const TARGET_PORT = _.get(process.env, 'npm_package_config_django_port', 8043);
const TARGET_HOST = _.get(process.env, 'npm_package_config_django_host', 'https://localhost');
const TARGET = `https://${TARGET_HOST}:${TARGET_PORT}`;
let development = require('./webpack.development');
let watch = {
plugins: [
new HtmlWebpackHarddiskPlugin(),
new webpack.HotModuleReplacementPlugin()
],
devServer: {
contentBase: path.resolve(__dirname, '..', 'static'),
clientLogLevel: 'info',
publicPath: '/static/',
host: '127.0.0.1',
port: 3000,
proxy: {
'/': {
target: TARGET,
secure: false,
ws: false
},
'/websocket': {
target: TARGET,
secure: false,
ws: true
}
}
}
};
watch.plugins = development.plugins.concat(watch.plugins)
module.exports = _.merge(development, watch);

View File

@ -3,6 +3,8 @@ import AddController from './add-credentials.controller';
import EditController from './edit-credentials.controller';
import CredentialsStrings from './credentials.strings'
const addEditTemplate = require('@features/credentials/add-edit-credentials.view.html');
function CredentialsResolve ($q, $stateParams, Me, Credential, CredentialType, Organization) {
let id = $stateParams.credential_id;
@ -51,8 +53,7 @@ CredentialsResolve.$inject = [
'OrganizationModel'
];
function CredentialsConfig ($stateExtenderProvider, legacyProvider, pathProvider, stringProvider) {
let path = pathProvider.$get();
function CredentialsConfig ($stateExtenderProvider, legacyProvider, stringProvider) {
let stateExtender = $stateExtenderProvider.$get();
let legacy = legacyProvider.$get();
let strings = stringProvider.$get();
@ -69,7 +70,7 @@ function CredentialsConfig ($stateExtenderProvider, legacyProvider, pathProvider
},
views: {
'add@credentials': {
templateUrl: path.getViewPath('credentials/add-edit-credentials'),
templateUrl: addEditTemplate,
controller: AddController,
controllerAs: 'vm'
}
@ -92,7 +93,7 @@ function CredentialsConfig ($stateExtenderProvider, legacyProvider, pathProvider
},
views: {
'edit@credentials': {
templateUrl: path.getViewPath('credentials/add-edit-credentials'),
templateUrl: addEditTemplate,
controller: EditController,
controllerAs: 'vm'
}
@ -114,7 +115,6 @@ function CredentialsConfig ($stateExtenderProvider, legacyProvider, pathProvider
CredentialsConfig.$inject = [
'$stateExtenderProvider',
'LegacyCredentialsServiceProvider',
'PathServiceProvider',
'CredentialsStringsProvider'
];

View File

@ -5,7 +5,9 @@ import OrganizationList from '../../src/organizations/organizations.list';
import ListController from '../../src/credentials/list/credentials-list.controller';
import { N_ } from '../../src/i18n';
function LegacyCredentialsService (pathService) {
const indexTemplate = require('@features/credentials/index.view.html');
function LegacyCredentialsService () {
this.list = {
name: 'credentials',
route: '/credentials',
@ -18,7 +20,7 @@ function LegacyCredentialsService (pathService) {
},
views: {
'@': {
templateUrl: pathService.getViewPath('credentials/index')
templateUrl: indexTemplate
},
'list@credentials': {
templateProvider: function(CredentialList, generateList) {
@ -361,8 +363,4 @@ function LegacyCredentialsService (pathService) {
};
}
LegacyCredentialsService.$inject = [
'PathService'
];
export default LegacyCredentialsService;

View File

@ -0,0 +1,159 @@
<!DOCTYPE html>
<html lang="en" ng-app="awApp">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="shortcut icon" href="{{ STATIC_URL }}assets/favicon.ico?v={{version}}" />
<title ng-bind="tabTitle"></title>
<script>var $basePath = '{{ STATIC_URL }}'</script>
<% htmlWebpackPlugin.files.css.forEach(file => {%>
<link href="{{ STATIC_URL }}<%= file %>" rel="stylesheet" />
<% }) %>
<% htmlWebpackPlugin.files.js.forEach(file => {%>
<script src="{{ STATIC_URL }}<%= file %>"></script>
<% }) %>
</head>
<body data-user-agent="{{userAgent}}">
<at-layout>
<bread-crumb></bread-crumb>
<toast></toast>
<div class="container-fluid" id="content-container">
<div class="row">
<div class="col-lg-12" ui-view>
</div>
</div>
<!-- Password Dialog -->
<div id="password-modal" style="display: none;"></div>
<div id="idle-modal" style="display:none" translate>Your session will expire in <span id="remaining_seconds" class="IdleModal-remainingSeconds">60</span> seconds, would you like to continue?</div>
<stream-detail-modal></stream-detail-modal>
<!-- Confirmation Dialog -->
<div id="prompt-modal" class="modal fade">
<div class="modal-dialog">
<div class="Modal-content modal-content">
<div class="Modal-header">
<div class="Modal-title" ng-bind="promptHeader" id="prompt-header"></div>
<div class="Modal-exitHolder">
<button class="close Modal-exit" data-target="#prompt-modal" data-dismiss="modal" aria-hidden="true"><i class="fa fa-times-circle"></i></button>
</div>
</div>
<div class="Modal-body" ng-bind-html="promptBody" id="prompt-body">
</div>
<div class="Modal-footer">
<a href="#" data-target="#prompt-modal" data-dismiss="modal" id="prompt_cancel_btn" class="btn Modal-defaultButton Modal-footerButton" translate>CANCEL</a>
<a href="" ng-class="promptActionBtnClass" ng-click="promptAction()" id="prompt_action_btn" class="btn Modal-footerButton" ng-bind="promptActionText"></a>
</div>
</div>
<!-- modal-content -->
</div>
<!-- modal-dialog -->
</div>
<!-- modal -->
<!-- Alerts/error handling dialogs -->
<div id="alert-modal" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" ng-hide="disableButtons" data-target="#alert-modal" data-dismiss="modal" class="modal" aria-hidden="true"><i class="fa fa-times-circle"></i></button>
<h3 id="alertHeader" ng-bind="alertHeader"></h3>
</div>
<div class="modal-body">
<div id="alert-modal-msg" class="alert" ng-bind-html="alertBody"></div>
</div>
<div class="modal-footer">
<a href="#" ng-hide="disableButtons" data-target="#form-modal" data-dismiss="modal" id="alert_ok_btn" class="btn btn-default" translate>OK</a>
</div>
</div>
<!-- modal-content -->
</div>
<!-- modal-dialog -->
</div>
<!-- modal -->
<div id="alert-modal2" class="modal fade">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-target="#alert-modal2" data-dismiss="modal" ng-hide="disableButtons2" aria-hidden="true">&times;</button>
<h3 id="alertHeader2" ng-bind="alertHeader2"></h3>
</div>
<div class="modal-body">
<div id="alert2-modal-msg" class="alert" ng-bind-html="alertBody2"></div>
</div>
<div class="modal-footer">
<a href="#" ng-hide="disableButtons2" data-target="#form-modal2" data-dismiss="modal" id="alert2_ok_btn" class="btn btn-primary">OK</a>
</div>
</div>
<!-- modal-content -->
</div>
<!-- modal-dialog -->
</div>
<!-- modal -->
<div id="login-modal-dialog" style="display: none;"></div>
<div id="help-modal-dialog" style="display: none;"></div>
<div id="prompt-for-days" style="display:none">
<form name="prompt_for_days_form" id="prompt_for_days_form" class="MgmtCards-promptText">
<span translate>Set how many days of data should be retained.</span>
<br>
<input type="integer" id="days_to_keep" name="days_to_keep" ng-model="days_to_keep" ng-required="true" class="form-control Form-textInput" min=0 max=9999 style="margin-top:10px;" integer>
<div class="error" ng-show="prompt_for_days_form.days_to_keep.$dirty && (prompt_for_days_form.days_to_keep.$error.number || prompt_for_days_form.days_to_keep.$error.integer ||
prompt_for_days_form.days_to_keep.$error.required ||
prompt_for_days_form.days_to_keep.$error.min ||
prompt_for_days_form.days_to_keep.$error.max)" translate>Please enter an integer<span ng-show="prompt_for_days_form.days_to_keep.$dirty && prompt_for_days_form.days_to_keep.$error.min"> that is not negative</span><span ng-show="prompt_for_days_form.days_to_keep.$dirty && prompt_for_days_form.days_to_keep.$error.max"> that is lower than 9999</span>.</div>
</form>
</div>
<div id="prompt-for-days-facts" style="display:none">
<form name="prompt_for_days_facts_form" id="prompt_for_days_facts_form" class="MgmtCards-promptText">
<div style="padding-bottom:15px;"><span translate>For facts collected older than the time period specified, save one fact scan (snapshot) per time window (frequency). For example, facts older than 30 days are purged, while one weekly fact scan is kept.</span>
<br>
<br>
<span translate>CAUTION: Setting both numerical variables to "0" will delete all facts.</span>
<br>
<br>
</div>
<div class="form-group">
<label for="description">
<span class="label-text" translate>Select a time period after which to remove old facts</span>
</label>
<div class="row">
<div class="col-xs-6">
<input type="integer" id="keep_amount" name="keep_amount" ng-model="keep_amount" ng-required="true" class="form-control Form-textInput MgmtCards-card--promptElements" min=0 max=9999 integer></input>
</div>
<div class="col-xs-6">
<select id="keep_unit" name="keep_unit" ng-model="keep_unit" ng-options="type.label for type in keep_unit_choices track by type.value" ng-required="true" class="form-control MgmtCards-card--promptElements"></select>
</div>
</div>
<div class="error" ng-show="prompt_for_days_facts_form.keep_amount.$dirty && (prompt_for_days_facts_form.keep_amount.$error.number || prompt_for_days_facts_form.keep_amount.$error.integer ||
prompt_for_days_facts_form.keep_amount.$error.required ||
prompt_for_days_facts_form.keep_amount.$error.min ||
prompt_for_days_facts_form.keep_amount.$error.max)" translate>Please enter an integer<span ng-show="prompt_for_days_facts_form.keep_amount.$dirty && prompt_for_days_facts_form.keep_amount.$error.min"> that is not negative</span><span ng-show="prompt_for_days_facts_form.keep_amount.$dirty && prompt_for_days_facts_form.keep_amount.$error.max"> that is lower than 9999</span>.</div>
</div>
<div class="form-group ">
<label for="description">
<span class="label-text" translate>Select a frequency for snapshot retention</span>
</label>
<div class="row">
<div class="col-xs-6">
<input type="integer" id="granularity_keep_amount" name="granularity_keep_amount" ng-model="granularity_keep_amount" ng-required="true" class="form-control Form-textInput MgmtCards-card--promptElements" min=0 max=9999 integer></input>
</div>
<div class="col-xs-6">
<select id="granularity_keep_unit" name="granularity_keep_unit" ng-model="granularity_keep_unit" ng-options="type.label for type in granularity_keep_unit_choices track by type.value" ng-required="true" class="form-control MgmtCards-card--promptElements"></select>
</div>
</div>
<div class="error" ng-show="prompt_for_days_facts_form.granularity_keep_amount.$dirty && (prompt_for_days_facts_form.granularity_keep_amount.$error.number || prompt_for_days_facts_form.granularity_keep_amount.$error.integer ||
prompt_for_days_facts_form.granularity_keep_amount.$error.required ||
prompt_for_days_facts_form.granularity_keep_amount.$error.min ||
prompt_for_days_facts_form.granularity_keep_amount.$error.max)" translate>Please enter an integer<span ng-show="prompt_for_days_facts_form.granularity_keep_amount.$dirty && prompt_for_days_facts_form.granularity_keep_amount.$error.min"> that is not negative</span><span ng-show="prompt_for_days_facts_form.granularity_keep_amount.$dirty && prompt_for_days_facts_form.granularity_keep_amount.$error.max"> that is lower than 9999</span>.</div>
</div>
</form>
</div>
<div class="overlay"></div>
<div class="spinny"><i class="fa fa-cog fa-spin fa-2x"></i>
<p translate>working...</p>
</div>
</div>
</at-layout>
</body>
</html>

View File

@ -1,9 +1,11 @@
function atActionGroup (pathService) {
const templateUrl = require('@components/action/action-group.partial.html');
function atActionGroup () {
return {
restrict: 'E',
replace: true,
transclude: true,
templateUrl: pathService.getPartialPath('components/action/action-group'),
templateUrl,
scope: {
col: '@',
pos: '@'
@ -11,6 +13,4 @@ function atActionGroup (pathService) {
};
}
atActionGroup.$inject = ['PathService'];
export default atActionGroup;

View File

@ -1,3 +1,5 @@
const templateUrl = require('@components/form/action.partial.html');
function link (scope, element, attrs, controllers) {
let formController = controllers[0];
let actionController = controllers[1];
@ -52,13 +54,13 @@ function atFormActionController ($state, strings) {
atFormActionController.$inject = ['$state', 'ComponentsStrings'];
function atFormAction (pathService) {
function atFormAction () {
return {
restrict: 'E',
transclude: true,
replace: true,
require: ['^^atForm', 'atFormAction'],
templateUrl: pathService.getPartialPath('components/form/action'),
templateUrl,
controller: atFormActionController,
controllerAs: 'vm',
link,
@ -70,6 +72,4 @@ function atFormAction (pathService) {
};
}
atFormAction.$inject = ['PathService'];
export default atFormAction;

View File

@ -1,3 +1,5 @@
const templateUrl = require('@components/form/form.partial.html');
function atFormLink (scope, el, attrs, controllers) {
let formController = controllers[0];
let form = el[0];
@ -195,13 +197,13 @@ function AtFormController (eventService, strings) {
AtFormController.$inject = ['EventService', 'ComponentsStrings'];
function atForm (pathService) {
function atForm () {
return {
restrict: 'E',
replace: true,
transclude: true,
require: ['atForm'],
templateUrl: pathService.getPartialPath('components/form/form'),
templateUrl,
controller: AtFormController,
controllerAs: 'vm',
link: atFormLink,
@ -211,6 +213,4 @@ function atForm (pathService) {
};
}
atForm.$inject = ['PathService'];
export default atForm;

View File

@ -1,39 +1,35 @@
import layout from './layout/layout.directive';
import topNavItem from './layout/top-nav-item.directive';
import sideNav from './layout/side-nav.directive';
import sideNavItem from './layout/side-nav-item.directive';
import actionGroup from './action/action-group.directive';
import divider from './utility/divider.directive';
import form from './form/form.directive';
import formAction from './form/action.directive';
import inputCheckbox from './input/checkbox.directive';
import inputGroup from './input/group.directive';
import inputLabel from './input/label.directive';
import inputLookup from './input/lookup.directive';
import inputMessage from './input/message.directive';
import inputSecret from './input/secret.directive';
import inputSelect from './input/select.directive';
import inputText from './input/text.directive';
import inputTextarea from './input/textarea.directive';
import inputTextareaSecret from './input/textarea-secret.directive';
import modal from './modal/modal.directive';
import panel from './panel/panel.directive';
import panelHeading from './panel/heading.directive';
import panelBody from './panel/body.directive';
import popover from './popover/popover.directive';
import tab from './tabs/tab.directive';
import tabGroup from './tabs/group.directive';
import truncate from './truncate/truncate.directive';
import actionGroup from '@components/action/action-group.directive';
import divider from '@components/utility/divider.directive';
import form from '@components/form/form.directive';
import formAction from '@components/form/action.directive';
import inputCheckbox from '@components/input/checkbox.directive';
import inputGroup from '@components/input/group.directive';
import inputLabel from '@components/input/label.directive';
import inputLookup from '@components/input/lookup.directive';
import inputMessage from '@components/input/message.directive';
import inputSecret from '@components/input/secret.directive';
import inputSelect from '@components/input/select.directive';
import inputText from '@components/input/text.directive';
import inputTextarea from '@components/input/textarea.directive';
import inputTextareaSecret from '@components/input/textarea-secret.directive';
import layout from '@components/layout/layout.directive';
import modal from '@components/modal/modal.directive';
import panel from '@components/panel/panel.directive';
import panelBody from '@components/panel/body.directive';
import panelHeading from '@components/panel/heading.directive';
import popover from '@components/popover/popover.directive';
import sideNav from '@components/layout/side-nav.directive';
import sideNavItem from '@components/layout/side-nav-item.directive';
import tab from '@components/tabs/tab.directive';
import tabGroup from '@components/tabs/group.directive';
import topNavItem from '@components/layout/top-nav-item.directive';
import truncate from '@components/truncate/truncate.directive';
import BaseInputController from './input/base.controller';
import ComponentsStrings from './components.strings';
import BaseInputController from '@components/input/base.controller';
import ComponentsStrings from '@components/components.strings';
angular
.module('at.lib.components', [])
.directive('atLayout', layout)
.directive('atTopNavItem', topNavItem)
.directive('atSideNav', sideNav)
.directive('atSideNavItem', sideNavItem)
.directive('atActionGroup', actionGroup)
.directive('atDivider', divider)
.directive('atForm', form)
@ -48,13 +44,17 @@ angular
.directive('atInputText', inputText)
.directive('atInputTextarea', inputTextarea)
.directive('atInputTextareaSecret', inputTextareaSecret)
.directive('atLayout', layout)
.directive('atModal', modal)
.directive('atPanel', panel)
.directive('atPanelHeading', panelHeading)
.directive('atPanelBody', panelBody)
.directive('atPanelHeading', panelHeading)
.directive('atPopover', popover)
.directive('atSideNav', sideNav)
.directive('atSideNavItem', sideNavItem)
.directive('atTab', tab)
.directive('atTabGroup', tabGroup)
.directive('atTopNavItem', topNavItem)
.directive('atTruncate', truncate)
.service('ComponentsStrings', ComponentsStrings)
.service('BaseInputController', BaseInputController);
.service('BaseInputController', BaseInputController)
.service('ComponentsStrings', ComponentsStrings);

View File

@ -1,3 +1,5 @@
const templateUrl = require('@components/input/checkbox.partial.html');
function atInputCheckboxLink (scope, element, attrs, controllers) {
let formController = controllers[0];
let inputController = controllers[1];
@ -23,13 +25,13 @@ function AtInputCheckboxController (baseInputController) {
AtInputCheckboxController.$inject = ['BaseInputController'];
function atInputCheckbox (pathService) {
function atInputCheckbox () {
return {
restrict: 'E',
transclude: true,
replace: true,
require: ['^^atForm', 'atInputCheckbox'],
templateUrl: pathService.getPartialPath('components/input/checkbox'),
templateUrl,
controller: AtInputCheckboxController,
controllerAs: 'vm',
link: atInputCheckboxLink,
@ -41,6 +43,4 @@ function atInputCheckbox (pathService) {
};
}
atInputCheckbox.$inject = ['PathService'];
export default atInputCheckbox;

View File

@ -1,3 +1,5 @@
const templateUrl = require('@components/input/group.partial.html');
function atInputGroupLink (scope, el, attrs, controllers) {
let groupController = controllers[0];
let formController = controllers[1];
@ -168,13 +170,13 @@ function AtInputGroupController ($scope, $compile) {
AtInputGroupController.$inject = ['$scope', '$compile'];
function atInputGroup (pathService) {
function atInputGroup () {
return {
restrict: 'E',
replace: true,
transclude: true,
require: ['atInputGroup', '^^atForm'],
templateUrl: pathService.getPartialPath('components/input/group'),
templateUrl,
controller: AtInputGroupController,
controllerAs: 'vm',
link: atInputGroupLink,
@ -186,6 +188,4 @@ function atInputGroup (pathService) {
};
}
atInputGroup.$inject = ['PathService'];
export default atInputGroup;

View File

@ -1,11 +1,11 @@
function atInputLabel (pathService) {
const templateUrl = require('@components/input/label.partial.html');
function atInputLabel () {
return {
restrict: 'E',
replace: true,
templateUrl: pathService.getPartialPath('components/input/label')
templateUrl
};
}
atInputLabel.$inject = ['PathService'];
export default atInputLabel;

View File

@ -1,3 +1,5 @@
const templateUrl = require('@components/input/lookup.partial.html');
const DEFAULT_DEBOUNCE = 250;
const DEFAULT_KEY = 'name';
@ -120,13 +122,13 @@ AtInputLookupController.$inject = [
'$stateParams'
];
function atInputLookup (pathService) {
function atInputLookup () {
return {
restrict: 'E',
transclude: true,
replace: true,
require: ['^^atForm', 'atInputLookup'],
templateUrl: pathService.getPartialPath('components/input/lookup'),
templateUrl,
controller: AtInputLookupController,
controllerAs: 'vm',
link: atInputLookupLink,
@ -138,6 +140,4 @@ function atInputLookup (pathService) {
};
}
atInputLookup.$inject = ['PathService'];
export default atInputLookup;

View File

@ -1,11 +1,11 @@
function atInputMessage (pathService) {
const templateUrl = require('@components/input/message.partial.html');
function atInputMessage () {
return {
restrict: 'E',
replace: true,
templateUrl: pathService.getPartialPath('components/input/message'),
templateUrl
};
}
atInputMessage.$inject = ['PathService'];
export default atInputMessage;

View File

@ -1,3 +1,5 @@
const templateUrl = require('@components/input/secret.partial.html');
function atInputSecretLink (scope, element, attrs, controllers) {
let formController = controllers[0];
let inputController = controllers[1];
@ -48,13 +50,13 @@ function AtInputSecretController (baseInputController) {
AtInputSecretController.$inject = ['BaseInputController'];
function atInputSecret (pathService) {
function atInputSecret () {
return {
restrict: 'E',
transclude: true,
replace: true,
require: ['^^atForm', 'atInputSecret'],
templateUrl: pathService.getPartialPath('components/input/secret'),
templateUrl,
controller: AtInputSecretController,
controllerAs: 'vm',
link: atInputSecretLink,
@ -66,6 +68,4 @@ function atInputSecret (pathService) {
};
}
atInputSecret.$inject = ['PathService'];
export default atInputSecret;

View File

@ -1,3 +1,5 @@
const templateUrl = require('@components/input/select.partial.html');
function atInputSelectLink (scope, element, attrs, controllers) {
let formController = controllers[0];
let inputController = controllers[1];
@ -72,13 +74,13 @@ function AtInputSelectController (baseInputController, eventService) {
AtInputSelectController.$inject = ['BaseInputController', 'EventService'];
function atInputSelect (pathService) {
function atInputSelect () {
return {
restrict: 'E',
transclude: true,
replace: true,
require: ['^^at-form', 'atInputSelect'],
templateUrl: pathService.getPartialPath('components/input/select'),
templateUrl,
controller: AtInputSelectController,
controllerAs: 'vm',
link: atInputSelectLink,
@ -90,6 +92,4 @@ function atInputSelect (pathService) {
};
}
atInputSelect.$inject = ['PathService'];
export default atInputSelect;

View File

@ -1,3 +1,5 @@
const templateUrl = require('@components/input/text.partial.html');
function atInputTextLink (scope, element, attrs, controllers) {
let formController = controllers[0];
let inputController = controllers[1];
@ -21,13 +23,13 @@ function AtInputTextController (baseInputController) {
AtInputTextController.$inject = ['BaseInputController'];
function atInputText (pathService) {
function atInputText () {
return {
restrict: 'E',
transclude: true,
replace: true,
require: ['^^atForm', 'atInputText'],
templateUrl: pathService.getPartialPath('components/input/text'),
templateUrl,
controller: AtInputTextController,
controllerAs: 'vm',
link: atInputTextLink,
@ -39,6 +41,4 @@ function atInputText (pathService) {
};
}
atInputText.$inject = ['PathService'];
export default atInputText;

View File

@ -1,3 +1,5 @@
const templateUrl = require('@components/input/textarea-secret.partial.html');
function atInputTextareaSecretLink (scope, element, attrs, controllers) {
let formController = controllers[0];
let inputController = controllers[1];
@ -96,13 +98,13 @@ AtInputTextareaSecretController.$inject = [
'ComponentsStrings'
];
function atInputTextareaSecret (pathService) {
function atInputTextareaSecret () {
return {
restrict: 'E',
transclude: true,
replace: true,
require: ['^^atForm', 'atInputTextareaSecret'],
templateUrl: pathService.getPartialPath('components/input/textarea-secret'),
templateUrl,
controller: AtInputTextareaSecretController,
controllerAs: 'vm',
link: atInputTextareaSecretLink,
@ -114,6 +116,4 @@ function atInputTextareaSecret (pathService) {
};
}
atInputTextareaSecret.$inject = ['PathService'];
export default atInputTextareaSecret;

View File

@ -1,3 +1,5 @@
const templateUrl = require('@components/input/textarea.partial.html');
function atInputTextareaLink (scope, element, attrs, controllers) {
let formController = controllers[0];
let inputController = controllers[1];
@ -21,13 +23,13 @@ function AtInputTextareaController (baseInputController) {
AtInputTextareaController.$inject = ['BaseInputController'];
function atInputTextarea (pathService) {
function atInputTextarea () {
return {
restrict: 'E',
transclude: true,
replace: true,
require: ['^^atForm', 'atInputTextarea'],
templateUrl: pathService.getPartialPath('components/input/textarea'),
templateUrl,
controller: AtInputTextareaController,
controllerAs: 'vm',
link: atInputTextareaLink,
@ -39,6 +41,4 @@ function atInputTextarea (pathService) {
};
}
atInputTextarea.$inject = ['PathService'];
export default atInputTextarea;

View File

@ -1,3 +1,5 @@
const templateUrl = require('@components/layout/layout.partial.html');
function AtLayoutController ($scope, strings) {
let vm = this || {};
@ -34,12 +36,12 @@ function AtLayoutController ($scope, strings) {
AtLayoutController.$inject = ['$scope', 'ComponentsStrings'];
function atLayout (pathService) {
function atLayout () {
return {
restrict: 'E',
replace: true,
transclude: true,
templateUrl: pathService.getPartialPath('components/layout/layout'),
templateUrl,
controller: AtLayoutController,
controllerAs: 'vm',
scope: {
@ -47,6 +49,4 @@ function atLayout (pathService) {
};
}
atLayout.$inject = ['PathService'];
export default atLayout;

View File

@ -1,3 +1,5 @@
const templateUrl = require('@components/layout/side-nav-item.partial.html');
function atSideNavItemLink (scope, element, attrs, ctrl) {
scope.navVm = ctrl[0];
scope.layoutVm = ctrl[1];
@ -29,10 +31,10 @@ function AtSideNavItemController ($state, $scope) {
AtSideNavItemController.$inject = ['$state', '$scope'];
function atSideNavItem (pathService) {
function atSideNavItem () {
return {
restrict: 'E',
templateUrl: pathService.getPartialPath('components/layout/side-nav-item'),
templateUrl,
require: ['^^atSideNav', '^^atLayout'],
controller: AtSideNavItemController,
controllerAs: 'vm',
@ -46,6 +48,4 @@ function atSideNavItem (pathService) {
};
}
atSideNavItem.$inject = ['PathService'];
export default atSideNavItem;

View File

@ -1,3 +1,5 @@
const templateUrl = require('@components/layout/side-nav.partial.html');
function atSideNavLink (scope, element, attrs, ctrl) {
scope.layoutVm = ctrl;
}
@ -12,7 +14,7 @@ function AtSideNavController () {
}
}
function atSideNav (pathService) {
function atSideNav () {
return {
restrict: 'E',
replace: true,
@ -21,12 +23,10 @@ function atSideNav (pathService) {
controllerAs: 'vm',
link: atSideNavLink,
transclude: true,
templateUrl: pathService.getPartialPath('components/layout/side-nav'),
templateUrl,
scope: {
}
};
}
atSideNav.$inject = ['PathService'];
export default atSideNav;

View File

@ -1,3 +1,5 @@
const templateUrl = require('@components/layout/top-nav-item.partial.html');
function atTopNavItemLink (scope, element, attrs, ctrl) {
scope.layoutVm = ctrl;
@ -12,12 +14,12 @@ function atTopNavItemLink (scope, element, attrs, ctrl) {
}
}
function atTopNavItem (pathService) {
function atTopNavItem () {
return {
restrict: 'E',
replace: true,
transclude: true,
templateUrl: pathService.getPartialPath('components/layout/top-nav-item'),
templateUrl,
require: '^^atLayout',
link: atTopNavItemLink,
scope: {
@ -25,6 +27,4 @@ function atTopNavItem (pathService) {
};
}
atTopNavItem.$inject = ['PathService'];
export default atTopNavItem;

View File

@ -1,3 +1,5 @@
const templateUrl = require('@components/modal/modal.partial.html');
const DEFAULT_ANIMATION_DURATION = 150;
function atModalLink (scope, el, attrs, controllers) {
@ -74,13 +76,13 @@ AtModalController.$inject = [
'ComponentsStrings'
]
function atModal (pathService) {
function atModal () {
return {
restrict: 'E',
replace: true,
transclude: true,
require: ['atModal'],
templateUrl: pathService.getPartialPath('components/modal/modal'),
templateUrl,
controller: AtModalController,
controllerAs: 'vm',
link: atModalLink,
@ -88,8 +90,4 @@ function atModal (pathService) {
};
}
atModal.$inject = [
'PathService'
];
export default atModal;

View File

@ -1,15 +1,15 @@
function atPanelBody (pathService) {
const templateUrl = require('@components/panel/body.partial.html');
function atPanelBody () {
return {
restrict: 'E',
replace: true,
transclude: true,
templateUrl: pathService.getPartialPath('components/panel/body'),
templateUrl,
scope: {
state: '='
}
};
}
atPanelBody.$inject = ['PathService'];
export default atPanelBody;

View File

@ -1,18 +1,18 @@
const templateUrl = require('@components/panel/heading.partial.html');
function link (scope, el, attrs, panel) {
panel.use(scope);
}
function atPanelHeading (pathService) {
function atPanelHeading () {
return {
restrict: 'E',
require: '^^atPanel',
replace: true,
transclude: true,
templateUrl: pathService.getPartialPath('components/panel/heading'),
templateUrl,
link
};
}
atPanelHeading.$inject = ['PathService'];
export default atPanelHeading;

View File

@ -1,3 +1,5 @@
const templateUrl = require('@components/panel/panel.partial.html');
function atPanelLink (scope, el, attrs, controllers) {
let panelController = controllers[0];
@ -26,13 +28,13 @@ function AtPanelController ($state) {
AtPanelController.$inject = ['$state'];
function atPanel (pathService, _$animate_) {
function atPanel () {
return {
restrict: 'E',
replace: true,
require: ['atPanel'],
transclude: true,
templateUrl: pathService.getPartialPath('components/panel/panel'),
templateUrl,
controller: AtPanelController,
controllerAs: 'vm',
link: atPanelLink,
@ -43,6 +45,4 @@ function atPanel (pathService, _$animate_) {
};
}
atPanel.$inject = ['PathService'];
export default atPanel;

View File

@ -1,3 +1,5 @@
const templateUrl = require('@components/popover/popover.partial.html');
const DEFAULT_POSITION = 'right';
const DEFAULT_ACTION = 'click';
const DEFAULT_ICON = 'fa fa-question-circle';
@ -201,13 +203,13 @@ function AtPopoverController () {
};
}
function atPopover (pathService) {
function atPopover () {
return {
restrict: 'E',
replace: true,
transclude: true,
require: ['atPopover'],
templateUrl: pathService.getPartialPath('components/popover/popover'),
templateUrl,
controller: AtPopoverController,
controllerAs: 'vm',
link: atPopoverLink,
@ -217,8 +219,4 @@ function atPopover (pathService) {
};
}
atPopover.$inject = [
'PathService'
];
export default atPopover;
export default atPopover;

View File

@ -1,3 +1,5 @@
const templateUrl = require('@components/tabs/group.partial.html');
function atTabGroupLink (scope, el, attrs, controllers) {
let groupController = controllers[0];
@ -26,13 +28,13 @@ function AtTabGroupController ($state) {
AtTabGroupController.$inject = ['$state'];
function atTabGroup (pathService, _$animate_) {
function atTabGroup () {
return {
restrict: 'E',
replace: true,
require: ['atTabGroup'],
transclude: true,
templateUrl: pathService.getPartialPath('components/tabs/group'),
templateUrl,
controller: AtTabGroupController,
controllerAs: 'vm',
link: atTabGroupLink,
@ -42,6 +44,4 @@ function atTabGroup (pathService, _$animate_) {
};
}
atTabGroup.$inject = ['PathService'];
export default atTabGroup;

View File

@ -1,3 +1,5 @@
const templateUrl = require('@components/tabs/tab.partial.html');
function atTabLink (scope, el, attrs, controllers) {
let groupController = controllers[0];
let tabController = controllers[1];
@ -31,13 +33,13 @@ function AtTabController ($state) {
AtTabController.$inject = ['$state'];
function atTab (pathService, _$animate_) {
function atTab () {
return {
restrict: 'E',
replace: true,
transclude: true,
require: ['^^atTabGroup', 'atTab'],
templateUrl: pathService.getPartialPath('components/tabs/tab'),
templateUrl,
controller: AtTabController,
controllerAs: 'vm',
link: atTabLink,
@ -47,6 +49,4 @@ function atTab (pathService, _$animate_) {
};
}
atTab.$inject = ['PathService'];
export default atTab;

View File

@ -1,3 +1,5 @@
const templateUrl = require('@components/truncate/truncate.partial.html');
function atTruncateLink (scope, el, attr, ctrl) {
let truncateController = ctrl;
let string = attr.string;
@ -44,12 +46,12 @@ function AtTruncateController (strings) {
AtTruncateController.$inject = ['ComponentsStrings'];
function atTruncate(pathService) {
function atTruncate() {
return {
restrict: 'E',
replace: true,
transclude: true,
templateUrl: pathService.getPartialPath('components/truncate/truncate'),
templateUrl,
controller: AtTruncateController,
controllerAs: 'vm',
link: atTruncateLink,
@ -61,8 +63,4 @@ function atTruncate(pathService) {
}
}
atTruncate.$inject = [
'PathService'
];
export default atTruncate;

View File

@ -1,12 +1,12 @@
function atPanelBody (pathService) {
const templateUrl = require('@components/utility/divider.partial.html');
function atPanelBody () {
return {
restrict: 'E',
replace: true,
templateUrl: pathService.getPartialPath('components/utility/divider'),
templateUrl,
scope: false
};
}
atPanelBody.$inject = ['PathService'];
export default atPanelBody;

View File

@ -1,9 +1,9 @@
import Base from './Base';
import Config from './Config';
import Credential from './Credential';
import CredentialType from './CredentialType';
import Me from './Me';
import Organization from './Organization';
import Base from '@models/Base';
import Config from '@models/Config';
import Credential from '@models/Credential';
import CredentialType from '@models/CredentialType';
import Me from '@models/Me';
import Organization from '@models/Organization';
angular
.module('at.lib.models', [])

View File

@ -1,4 +1,4 @@
import defaults from '../../assets/default.strings.json';
import defaults from '@assets/default.strings.json';
let i18n;

View File

@ -1,13 +1,11 @@
import CacheService from './cache.service';
import EventService from './event.service';
import PathService from './path.service';
import BaseStringService from './base-string.service';
import AppStrings from './app.strings';
import CacheService from '@services/cache.service';
import EventService from '@services/event.service';
import BaseStringService from '@services/base-string.service';
import AppStrings from '@services/app.strings';
angular
.module('at.lib.services', [])
.service('AppStrings', AppStrings)
.service('BaseStringService', BaseStringService)
.service('CacheService', CacheService)
.service('EventService', EventService)
.service('PathService', PathService);
.service('EventService', EventService);

View File

@ -1,11 +0,0 @@
function PathService () {
this.getPartialPath = path => {
return `/static/partials/${path}.partial.html`;
};
this.getViewPath = path => {
return `/static/views/${path}.view.html`;
}
}
export default PathService;

View File

@ -18,24 +18,24 @@
* NOTE: Styles below are a mix of 3rd-party dependencies and in-house code. For the 3rd-party
* stuff, we'd be better off managing them via npm where possible.
*/
@import '../../legacy-styles/fonts';
@import '../../legacy-styles/animations';
@import '../../legacy-styles/jquery-ui-overrides';
@import '../../legacy-styles/codemirror';
@import '../../legacy-styles/angular-scheduler';
@import '../../legacy-styles/log-viewer';
@import '../../legacy-styles/event-viewer';
@import '../../legacy-styles/job-details';
@import '../../legacy-styles/jobs';
@import '../../legacy-styles/inventory-edit';
@import '../../legacy-styles/stdout';
@import '../../legacy-styles/lists';
@import '../../legacy-styles/forms';
@import '../../legacy-styles/dashboard';
@import '../../legacy-styles/survey-maker';
@import '../../legacy-styles/text-label';
@import '../../legacy-styles/bootstrap-datepicker';
@import '../../legacy-styles/ansible-ui';
@import '../../legacy/styles/fonts';
@import '../../legacy/styles/animations';
@import '../../legacy/styles/jquery-ui-overrides';
@import '../../legacy/styles/codemirror';
@import '../../legacy/styles/angular-scheduler';
@import '../../legacy/styles/log-viewer';
@import '../../legacy/styles/event-viewer';
@import '../../legacy/styles/job-details';
@import '../../legacy/styles/jobs';
@import '../../legacy/styles/inventory-edit';
@import '../../legacy/styles/stdout';
@import '../../legacy/styles/lists';
@import '../../legacy/styles/forms';
@import '../../legacy/styles/dashboard';
@import '../../legacy/styles/survey-maker';
@import '../../legacy/styles/text-label';
@import '../../legacy/styles/bootstrap-datepicker';
@import '../../legacy/styles/ansible-ui';
// Dependency Style Overrides
@import '../../src/shared/bootstrap-settings';

View File

@ -1,39 +1,17 @@
/*************************************************
* Copyright (c) 2016 Ansible, Inc.
*
* All Rights Reserved
*************************************************/
// Vendor dependencies
import 'jquery';
import 'angular';
import 'angular-gettext';
import 'bootstrap';
import 'jquery-ui';
import 'bootstrap-datepicker';
import 'jquery.resize';
import 'codemirror';
import 'js-yaml';
import 'select2';
import uiRouter from 'angular-ui-router';
// backwards compatibility for $stateChange* events
import 'angular-ui-router/release/stateEvents';
// Configuration dependencies
global.$AnsibleConfig = null;
// Provided via Webpack DefinePlugin in webpack.config.js
global.$ENV = {} ;
// ui-router debugging
if ($ENV['route-debug']){
let trace = require('angular-ui-router').trace;
let trace = angular.module('ui.router').trace;
trace.enable();
}
var urlPrefix;
if ($basePath) {
urlPrefix = $basePath;
urlPrefix = `${$basePath}`;
}
// Modules
@ -73,22 +51,21 @@ import '../lib/models';
import '../lib/services';
import '../features';
var awApp = angular.module('awApp', [
// how to add CommonJS / AMD third-party dependencies:
// 1. npm install --save package-name
// 2. add package name to ./grunt-tasks/webpack.vendorFiles
require('angular-breadcrumb'),
require('angular-codemirror'),
require('angular-drag-and-drop-lists'),
require('angular-sanitize'),
require('angular-scheduler').name,
require('angular-tz-extensions'),
require('angular-md5'),
require('lr-infinite-scroll'),
require('ng-toast'),
'gettext',
angular.module('awApp', [
'I18N',
uiRouter,
'AngularCodeMirrorModule',
'angular-duration-format',
'angularMoment',
'AngularScheduler',
'angular-md5',
'dndLists',
'ncy-angular-breadcrumb',
'ngSanitize',
'ngCookies',
'ngToast',
'gettext',
'Timezones',
'ui.router',
'ui.router.state.events',
'lrInfiniteScroll',
@ -134,7 +111,6 @@ var awApp = angular.module('awApp', [
'at.lib.services',
'at.features',
])
.constant('AngularScheduler.partials', urlPrefix + 'lib/angular-scheduler/lib/')
.constant('AngularScheduler.useTimezone', true)
.constant('AngularScheduler.showUTCField', true)
@ -465,5 +441,3 @@ var awApp = angular.module('awApp', [
LoadConfig();
}
]);
export default awApp;

View File

@ -4,9 +4,6 @@
* Options: static height, custom breakpoint
*/
@import "./client/src/shared/branding/colors.default.less";
.OnePlusOne-container(@height: 100%; @breakpoint: 900px){
height: ~"calc(100vh - 150px)";
display: flex;

View File

@ -6,8 +6,6 @@
* Style conventions
* .ModuleName-component--subComponent
*/
@import "./client/src/shared/branding/colors.default.less";
.OnePlusTwo-container(@height: 100%; @breakpoint: 900px){
height: @height;

View File

@ -34,10 +34,10 @@ import orgAdminLookup from './org-admin-lookup/main';
import limitPanels from './limit-panels/main';
import multiSelectPreview from './multi-select-preview/main';
import credentialTypesLookup from './credentialTypesLookup.factory';
import 'angular-duration-format';
export default
angular.module('shared', [listGenerator.name,
angular.module('shared', [
listGenerator.name,
formGenerator.name,
lookupModal.name,
smartSearch.name,
@ -64,7 +64,7 @@ angular.module('shared', [listGenerator.name,
orgAdminLookup.name,
limitPanels.name,
multiSelectPreview.name,
require('angular-cookies'),
'ngCookies',
'angular-duration-format'
])
.factory('stateDefinitions', stateDefinitions)

View File

@ -1,7 +1,7 @@
import newMoment from './moment';
export default
angular.module('moment', [require('angular-moment').name])
angular.module('moment', ['angularMoment'])
.config(function() {
// Remove the global variable for moment
delete window.moment;

View File

@ -1,5 +1,3 @@
@import "./client/src/shared/branding/colors.default.less";
.position-center {
left: 0;
right: 0;
@ -255,4 +253,4 @@
max-width: ~"calc(100vw - 50px)";
}
}
}
}

View File

@ -0,0 +1,39 @@
require('@assets/custom-theme/jquery-ui-1.10.3.custom.min.css');
require('@assets/ansible-bootstrap.min.css');
require('@assets/fontcustom/fontcustom.css');
require('@modules/components-font-awesome/css/font-awesome.min.css');
require('@modules/select2/dist/css/select2.css');
require('@modules/codemirror/lib/codemirror.css');
require('@modules/codemirror/theme/elegant.css');
require('@modules/codemirror/addon/lint/lint.css');
require('@modules/nvd3/build/nv.d3.css');
require('@modules/ng-toast/dist/ngToast.min.css');
require('jquery');
require('jquery-resize');
require('jquery-ui');
require('bootstrap');
require('bootstrap-datepicker');
require('moment');
require('select2');
require('sprintf-js');
require('reconnectingwebsocket');
require('d3');
require('nvd3');
require('angular');
require('angular-cookies');
require('angular-sanitize');
require('angular-breadcrumb');
require('angular-codemirror');
require('angular-drag-and-drop-lists');
require('angular-duration-format');
require('angular-gettext');
require('angular-md5');
require('angular-moment');
require('angular-scheduler');
require('angular-tz-extensions');
require('angular-ui-router');
require('angular-ui-router-state-events');
require('ng-toast-provider');
require('ng-toast-directives');
require('ng-toast');
require('lr-infinite-scroll');

View File

@ -1,30 +0,0 @@
var django_port = process.env.npm_package_config_django_port,
django_host = process.env.npm_package_config_django_host;
module.exports = {
http: {
bsFiles: {
src: [
'static/**/*',
'!static/tower.vendor.js',
'!static/tower.vendor.map.js',
'!static/tower.js.map'
]
},
options: {
proxy: {
target: `https://${django_host}:${django_port}`,
ws: true
},
keepalive: false,
watchTask: true,
reloadDebounce: 1000,
// The browser-sync-client lib will write your current scroll position to window.name
// https://github.com/BrowserSync/browser-sync-client/blob/a2718faa91e11553feca7a3962313bf1ec6ba3e5/dist/index.js#L500
// This strategy is enabled in the core browser-sync lib, and not externally documented as an option. Yay!
// https://github.com/BrowserSync/browser-sync/blob/a522aaf12b6167d5591ed285eb3086f43a4d9ac2/lib/default-config.js#L312
scrollRestoreTechnique: null,
injectChanges: true
}
}
};

View File

@ -1,7 +0,0 @@
module.exports = {
options: { force: true },
static: 'static/*',
coverage: 'coverage/*',
tmp: '../../tmp',
jshint: 'coverage/jshint.xml'
};

View File

@ -1,17 +0,0 @@
module.exports = {
css: {
src: [
'static/assets/custom-theme/jquery-ui-1.10.3.custom.min.css',
'static/assets/ansible-bootstrap.min.css',
'static/assets/fontcustom/fontcustom.css',
'static/lib/components-font-awesome/css/font-awesome.min.css',
'static/lib/select2/dist/css/select2.css',
'static/lib/codemirror/lib/codemirror.css',
'static/lib/codemirror/theme/elegant.css',
'static/lib/codemirror/addon/lint/lint.css',
'static/lib/nvd3/build/nv.d3.css',
'static/lib/ng-toast/dist/ngToast.min.css'
],
dest: 'static/css/app.vendor.css'
}
};

View File

@ -1,61 +0,0 @@
module.exports = {
dev: {
tasks: [
'copy:vendor',
'copy:assets',
'copy:icons',
'copy:fonts',
'copy:images',
'copy:partials',
'copy:views',
'copy:languages',
'copy:config',
'less:dev'
]
},
// This concurrent target is intended for development ui builds that do not require raising browser-sync or filesystem polling
devNoSync: {
tasks: [
'copy:vendor',
'copy:assets',
'copy:icons',
'copy:fonts',
'copy:images',
'copy:partials',
'copy:views',
'copy:languages',
'copy:config',
'less:dev',
'webpack:dev'
]
},
prod: {
tasks: [
'newer:copy:vendor',
'newer:copy:assets',
'newer:copy:icons',
'newer:copy:fonts',
'newer:copy:images',
'newer:copy:partials',
'newer:copy:views',
'newer:copy:languages',
'newer:copy:config',
'newer:less:prod'
]
},
watch: {
tasks: [
'watch:css',
'watch:partials',
'watch:views',
'watch:assets',
[
'watch:config'
]
],
options: {
logConcurrentOutput: true
}
}
};

View File

@ -1,100 +0,0 @@
var staticFiles = ['angular-tz-extensions/tz/data/*',
'angular-scheduler/lib/angular-scheduler-detail.html',
'angular-scheduler/lib/angular-scheduler.html',
'nvd3/build/nv.d3.css',
'ng-toast/dist/ngToast.min.css',
'codemirror/addon/lint/lint.css',
'codemirror/theme/elegant.css',
'codemirror/lib/codemirror.css',
'select2/dist/css/select2.css',
'components-font-awesome/css/font-awesome.min.css',
'components-font-awesome/fonts/fontawesome-webfont.ttf',
'components-font-awesome/fonts/fontawesome-webfont.woff',
'components-font-awesome/fonts/fontawesome-webfont.woff2'
];
module.exports = {
fonts: {
files: [{
cwd: 'client/',
expand: true,
flatten: true,
filter: 'isFile',
src: 'assets/fontcustom/**/*',
dest: 'static/fonts/'
}]
},
icons: {
files: [{
cwd: 'node_modules/',
expand: true,
flatten: true,
filter: 'isFile',
src: 'components-font-awesome/fonts/*',
dest: 'static/fonts/'
}]
},
images: {
files: [{
cwd: 'client/',
expand: true,
flatten: true,
filter: 'isFile',
src: 'assets/custom-theme/images.new/*',
dest: 'static/images/'
}]
},
assets: {
files: [{
cwd: 'client/',
expand: true,
src: 'assets/**/*',
dest: 'static/'
}]
},
vendor: {
files: [{
expand: true,
cwd: 'node_modules/',
src: staticFiles,
dest: 'static/lib/'
}]
},
views: {
files: [{
cwd: 'client/features',
expand: true,
src: ['**/*.view.html'],
dest: 'static/views/'
}]
},
partials: {
files: [{
cwd: 'client/src',
expand: true,
src: ['**/*.partial.html'],
dest: 'static/partials'
}, {
cwd: 'client/src/partials',
expand: true,
src: ['*.html'],
dest: 'static/partials/'
}, {
cwd: 'client/lib/components',
expand: true,
src: ['**/*.partial.html'],
dest: 'static/partials/components/'
}]
},
languages: {
files: [{
cwd: 'client/',
expand: true,
src: 'languages/*.json',
dest: 'static/'
}]
},
config: {
files: { 'static/config.js': ['client/src/config.js'] }
}
};

View File

@ -1,22 +0,0 @@
module.exports = {
vendor: {
files: [
{
expand: true,
src: 'static/css/app.vendor.css',
dest: '.',
ext: '.vendor.css'
}
]
},
source: {
files: [
{
expand: true,
src: 'static/css/app.css',
dest: '.',
ext: '.css'
}
]
}
};

View File

@ -1,27 +0,0 @@
var AutoPrefixer = require('less-plugin-autoprefix');
var autoPrefixer = new AutoPrefixer({
browsers: [ 'last 2 versions' ]
});
module.exports = {
dev: {
files: {
'static/css/app.css': 'client/lib/theme/index.less'
},
options: {
sourceMap: true,
plugins: [ autoPrefixer ]
}
},
prod: {
files: {
'static/css/app.css': 'client/lib/theme/index.less'
},
options: {
compress: true,
sourceMap: false,
plugins: [ autoPrefixer ]
}
}
};

View File

@ -3,13 +3,14 @@ module.exports = {
options: {
format: 'json'
},
files: [ {
files: [{
expand: true,
dot: true,
dest: 'client/languages',
cwd: 'po',
ext: '.json',
src: ['*.po']
} ]
dot: true,
dest: 'client/languages',
cwd: 'po',
ext: '.json',
src: ['*.po']
}]
}
};

View File

@ -1,26 +0,0 @@
module.exports = {
css: {
files: 'client/**/*.less',
tasks: ['less:dev']
},
partials: {
files: [
'client/lib/components/**/*.partial.html',
'client/src/**/*.partial.html',
'client/src/partials/*.html'
],
tasks: ['newer:copy:partials']
},
views: {
files: 'client/features/**/*.view.html',
tasks: ['newer:copy:views']
},
assets: {
files: 'client/assets',
tasks: ['newer:copy:assets']
},
config: {
files: 'client/src/config.js',
tasks: ['newer:copy:config']
}
};

View File

@ -1,5 +0,0 @@
var config = require('../webpack.config.js');
module.exports = {
dev: config.dev,
prod: config.release
};

View File

@ -1,5 +1,4 @@
var path = require('path'),
webpack = require('webpack');
const webpackTestConfig = require('./build/webpack.test.js');
module.exports = function(config) {
config.set({
@ -16,80 +15,20 @@ module.exports = function(config) {
'jasmine',
],
reporters: ['progress', 'coverage', 'junit'],
files: [
files:[
'./client/src/vendor.js',
'./client/src/app.js',
'./node_modules/angular-mocks/angular-mocks.js',
{ pattern: './tests/**/*-test.js' },
'client/src/**/*.html'
],
preprocessors: {
'./client/src/vendor.js': ['webpack', 'sourcemap'],
'./client/src/app.js': ['webpack', 'sourcemap'],
'./tests/**/*-test.js': ['webpack', 'sourcemap'],
'client/src/**/*.html': ['html2js']
},
webpack: {
plugins: [
// Django-provided definitions
new webpack.DefinePlugin({
$basePath: '/static/'
}),
// vendor shims:
// [{expected_local_var : dependency}, ...]
new webpack.ProvidePlugin({
$: 'jquery',
jQuery: 'jquery',
'window.jQuery': 'jquery',
_: 'lodash',
'CodeMirror': 'codemirror',
'$.fn.datepicker': 'bootstrap-datepicker'
})
],
module: {
loaders: [{
test: /\.angular.js$/,
loader: 'expose?angular'
},
{
test: /\.json$/,
loader: 'json-loader',
exclude: '/(node_modules)/'
},
{
test: /\.js$/,
loader: 'babel-loader',
include: [path.resolve() + '/tests/'],
exclude: '/(node_modules)/',
query: {
presets: ['es2015']
}
}, {
test: /\.js$/,
loader: 'babel-loader',
include: [
path.resolve() + '/client/src/',
path.resolve() + '/client/lib/',
path.resolve() + '/client/features/'
],
exclude: '/(node_modules)/',
query: {
presets: ['es2015'],
plugins: ['istanbul']
}
}
]
},
resolve: {
root: [],
modulesDirectory: ['node_modules'],
alias: {
'jquery.resize': path.resolve() + '/node_modules/javascript-detect-element-resize/jquery.resize.js',
'select2': path.resolve() + '/node_modules/select2/dist/js/select2.full.js'
}
},
devtool: 'inline-source-map',
debug: true,
cache: true
},
webpack: webpackTestConfig,
webpackMiddleware: {
stats: {
colors: true

File diff suppressed because it is too large Load Diff

View File

@ -1,65 +1,61 @@
{
"name": "ansible-tower",
"version": "3.1.0",
"name": "awx",
"version": "1.0.0",
"repository": {
"type": "git",
"url": ""
"url": "https://github.com/ansible/awx"
},
"config": {
"django_port": "8043",
"django_host": "localhost"
},
"engines": {
"node": "^6.3.1",
"npm": "^3.10.3"
"node": "^6.11.3",
"npm": "^3.10.10"
},
"scripts": {
"ui-docker-machine": "ip=$(docker-machine ip $DOCKER_MACHINE_NAME); npm set ansible-tower:django_host ${ip}; grunt dev;",
"ui-docker": "grunt dev;",
"build-devel": "grunt devNoSync",
"ui-docker": "npm run watch;",
"build-devel": "npm run dev",
"pot": "grunt nggettext_extract",
"languages": "grunt nggettext_compile",
"build-release": "grunt release",
"pretest": "grunt clean:coverage",
"build-release": "npm run production",
"pretest": "",
"test": "karma start karma.conf.js",
"jshint": "grunt clean:jshint jshint:source --no-color",
"jshint": "grunt jshint:source --no-color",
"test:ci": "npm run test -- --single-run --reporter junit,dots --browsers=PhantomJS",
"lint": "./node_modules/.bin/eslint -c .eslintrc.js .",
"component-test": "./node_modules/.bin/karma start client/test/karma.conf.js",
"lint-dev": "./node_modules/.bin/nodemon --exec \"./node_modules/.bin/eslint -c .eslintrc.js .\" --watch \"client/components/**/*.js\"",
"component-dev": "./node_modules/.bin/karma start client/test/karma.conf.js --auto-watch --no-single-run"
},
"optionalDependencies": {
"browser-sync": "^2.14.0",
"grunt-browser-sync": "^2.2.0",
"grunt-contrib-watch": "^1.0.0",
"webpack-dev-server": "^1.14.1"
"dev": "./node_modules/.bin/webpack --config build/webpack.development.js --progress",
"watch": "./node_modules/.bin/webpack-dev-server --config build/webpack.watch.js --progress",
"production": "./node_modules/.bin/webpack --config build/webpack.production.js"
},
"devDependencies": {
"angular-mocks": "~1.4.14",
"babel-core": "^6.11.4",
"babel-core": "^6.26.0",
"babel-istanbul": "^0.11.0",
"babel-loader": "^6.2.4",
"babel-loader": "^7.1.2",
"babel-plugin-istanbul": "^2.0.0",
"babel-preset-es2015": "^6.9.0",
"babel-preset-es2015": "^6.24.1",
"clean-webpack-plugin": "^0.1.16",
"copy-webpack-plugin": "^4.0.1",
"css-loader": "^0.28.5",
"eslint": "^3.18.0",
"eslint-config-airbnb-base": "^11.1.1",
"eslint-plugin-import": "^2.2.0",
"expose-loader": "^0.7.1",
"expose-loader": "^0.7.3",
"extract-text-webpack-plugin": "^3.0.0",
"grunt": "^1.0.1",
"grunt-angular-gettext": "^2.2.3",
"grunt-cli": "^1.2.0",
"grunt-concurrent": "^2.3.0",
"grunt-contrib-clean": "^1.0.0",
"grunt-contrib-concat": "^1.0.1",
"grunt-contrib-copy": "^1.0.0",
"grunt-contrib-cssmin": "^2.2.0",
"grunt-contrib-jshint": "^1.0.0",
"grunt-contrib-less": "^1.3.0",
"grunt-extract-sourcemap": "^0.1.18",
"grunt-newer": "^1.2.0",
"grunt-webpack": "^1.0.11",
"imports-loader": "^0.6.5",
"html-loader": "^0.5.1",
"html-webpack-harddisk-plugin": "^0.1.0",
"html-webpack-plugin": "^2.30.1",
"imports-loader": "^0.7.1",
"jasmine-core": "^2.5.2",
"jshint": "^2.9.4",
"jshint-loader": "^0.8.3",
@ -77,31 +73,38 @@
"karma-sauce-launcher": "^1.0.0",
"karma-sourcemap-loader": "^0.3.7",
"karma-webpack": "^1.8.0",
"less": "^2.7.2",
"less-loader": "^4.0.5",
"less-plugin-autoprefix": "^1.4.2",
"load-grunt-configs": "^1.0.0",
"load-grunt-tasks": "^3.5.0",
"minimist": "^1.2.0",
"ngtemplate-loader": "^2.0.1",
"phantomjs-prebuilt": "^2.1.12",
"script-loader": "^0.7.0",
"style-loader": "^0.18.2",
"time-grunt": "^1.4.0",
"webpack": "^1.13.1"
"uglifyjs-webpack-plugin": "^0.4.6",
"webpack": "^3.0.0",
"webpack-dev-server": "^2.7.1"
},
"dependencies": {
"angular": "~1.4.14",
"angular-breadcrumb": "github:ansible/angular-breadcrumb#0.4.1",
"angular-codemirror": "github:ansible/angular-codemirror#1.0.4",
"angular-breadcrumb": "git+https://git@github.com/ansible/angular-breadcrumb#0.4.1",
"angular-codemirror": "git+https://git@github.com/ansible/angular-codemirror#1.0.4",
"angular-cookies": "~1.4.14",
"angular-drag-and-drop-lists": "github:ansible/angular-drag-and-drop-lists#1.4.0",
"angular-drag-and-drop-lists": "git+https://git@github.com/ansible/angular-drag-and-drop-lists#1.4.0",
"angular-duration-format": "^1.0.1",
"angular-gettext": "^2.3.5",
"angular-md5": "^0.1.8",
"angular-moment": "^0.10.1",
"angular-resource": "~1.4.14",
"angular-sanitize": "~1.4.14",
"angular-scheduler": "github:ansible/angular-scheduler#0.1.1",
"angular-tz-extensions": "github:ansible/angular-tz-extensions#0.3.13",
"angular-scheduler": "git+https://git@github.com/ansible/angular-scheduler#0.1.1",
"angular-tz-extensions": "git+https://git@github.com/ansible/angular-tz-extensions#0.3.13",
"angular-ui-router": "1.0.0-beta.3",
"bootstrap": "^3.1.1",
"bootstrap-datepicker": "^1.4.0",
"bootstrap": "^3.3.7",
"bootstrap-datepicker": "^1.7.1",
"codemirror": "^5.17.0",
"components-font-awesome": "^4.6.1",
"d3": "~3.3.13",
@ -111,12 +114,12 @@
"js-yaml": "^3.2.7",
"legacy-loader": "0.0.2",
"lodash": "~3.8.0",
"lr-infinite-scroll": "github:lorenzofox3/lrInfiniteScroll",
"lr-infinite-scroll": "git+https://git@github.com/lorenzofox3/lrInfiniteScroll",
"moment": "~2.10.0",
"ng-toast": "github:ansible/ngToast#2.0.1",
"nvd3": "github:ansible/nvd3#1.7.1",
"ng-toast": "git+https://git@github.com/ansible/ngToast#2.0.1",
"nvd3": "git+https://git@github.com/ansible/nvd3#1.7.1",
"reconnectingwebsocket": "^1.0.0",
"rrule": "github:jkbrzt/rrule#4ff63b2f8524fd6d5ba6e80db770953b5cd08a0c",
"rrule": "git+https://git@github.com/jkbrzt/rrule#4ff63b2f8524fd6d5ba6e80db770953b5cd08a0c",
"select2": "^4.0.2",
"sprintf-js": "^1.0.3"
}

File diff suppressed because it is too large Load Diff

View File

@ -1,20 +1,23 @@
<!DOCTYPE html>
{% load i18n %}
<html lang="en" ng-app="awApp">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<title ng-bind="tabTitle"></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="{{ STATIC_URL }}css/app.vendor.css?v={{version}}" type="text/css">
<link rel="stylesheet" href="{{ STATIC_URL }}css/app.css?v={{version}}" type="text/css">
<link rel="shortcut icon" href="{{ STATIC_URL }}assets/favicon.ico?v={{version}}" />
<script>
var $basePath = "{{ STATIC_URL }}";
</script>
<script src="{{ STATIC_URL }}app.vendor.js?v={{version}}"></script>
<script src="{{ STATIC_URL }}app.js?v={{version}}"></script>
<title ng-bind="tabTitle"></title>
<script>var $basePath = '{{ STATIC_URL }}'</script>
<link href="{{ STATIC_URL }}css/vendor.f283b4795bc6921b48e5.css" rel="stylesheet" />
<link href="{{ STATIC_URL }}css/app.f283b4795bc6921b48e5.css" rel="stylesheet" />
<script src="{{ STATIC_URL }}js/vendor.f283b4795bc6921b48e5.js"></script>
<script src="{{ STATIC_URL }}js/app.f283b4795bc6921b48e5.js"></script>
</head>
<body data-user-agent="{{userAgent}}">
@ -28,7 +31,7 @@
</div>
<!-- Password Dialog -->
<div id="password-modal" style="display: none;"></div>
<div id="idle-modal" style="display:none">{% blocktrans %}Your session will expire in <span id="remaining_seconds" class="IdleModal-remainingSeconds">60</span> seconds, would you like to continue?{% endblocktrans %}</div>
<div id="idle-modal" style="display:none" translate>Your session will expire in <span id="remaining_seconds" class="IdleModal-remainingSeconds">60</span> seconds, would you like to continue?</div>
<stream-detail-modal></stream-detail-modal>
<!-- Confirmation Dialog -->
<div id="prompt-modal" class="modal fade">
@ -43,7 +46,7 @@
<div class="Modal-body" ng-bind-html="promptBody" id="prompt-body">
</div>
<div class="Modal-footer">
<a href="#" data-target="#prompt-modal" data-dismiss="modal" id="prompt_cancel_btn" class="btn Modal-defaultButton Modal-footerButton">{% trans 'CANCEL' %}</a>
<a href="#" data-target="#prompt-modal" data-dismiss="modal" id="prompt_cancel_btn" class="btn Modal-defaultButton Modal-footerButton" translate>CANCEL</a>
<a href="" ng-class="promptActionBtnClass" ng-click="promptAction()" id="prompt_action_btn" class="btn Modal-footerButton" ng-bind="promptActionText"></a>
</div>
</div>
@ -64,7 +67,7 @@
<div id="alert-modal-msg" class="alert" ng-bind-html="alertBody"></div>
</div>
<div class="modal-footer">
<a href="#" ng-hide="disableButtons" data-target="#form-modal" data-dismiss="modal" id="alert_ok_btn" class="btn btn-default">{% trans 'OK' %}</a>
<a href="#" ng-hide="disableButtons" data-target="#form-modal" data-dismiss="modal" id="alert_ok_btn" class="btn btn-default" translate>OK</a>
</div>
</div>
<!-- modal-content -->
@ -83,7 +86,7 @@
<div id="alert2-modal-msg" class="alert" ng-bind-html="alertBody2"></div>
</div>
<div class="modal-footer">
<a href="#" ng-hide="disableButtons2" data-target="#form-modal2" data-dismiss="modal" id="alert2_ok_btn" class="btn btn-primary">{% trans 'OK' %}</a>
<a href="#" ng-hide="disableButtons2" data-target="#form-modal2" data-dismiss="modal" id="alert2_ok_btn" class="btn btn-primary">OK</a>
</div>
</div>
<!-- modal-content -->
@ -95,28 +98,27 @@
<div id="help-modal-dialog" style="display: none;"></div>
<div id="prompt-for-days" style="display:none">
<form name="prompt_for_days_form" id="prompt_for_days_form" class="MgmtCards-promptText">
{% trans 'Set how many days of data should be retained.' %}
<span translate>Set how many days of data should be retained.</span>
<br>
<input type="integer" id="days_to_keep" name="days_to_keep" ng-model="days_to_keep" ng-required="true" class="form-control Form-textInput" min=0 max=9999 style="margin-top:10px;" integer>
<div class="error" ng-show="prompt_for_days_form.days_to_keep.$dirty && (prompt_for_days_form.days_to_keep.$error.number || prompt_for_days_form.days_to_keep.$error.integer ||
prompt_for_days_form.days_to_keep.$error.required ||
prompt_for_days_form.days_to_keep.$error.min ||
prompt_for_days_form.days_to_keep.$error.max)">{% blocktrans %}Please enter an integer<span ng-show="prompt_for_days_form.days_to_keep.$dirty && prompt_for_days_form.days_to_keep.$error.min"> that is not negative</span><span ng-show="prompt_for_days_form.days_to_keep.$dirty && prompt_for_days_form.days_to_keep.$error.max"> that is lower than 9999</span>.{% endblocktrans %}</div>
prompt_for_days_form.days_to_keep.$error.max)" translate>Please enter an integer<span ng-show="prompt_for_days_form.days_to_keep.$dirty && prompt_for_days_form.days_to_keep.$error.min"> that is not negative</span><span ng-show="prompt_for_days_form.days_to_keep.$dirty && prompt_for_days_form.days_to_keep.$error.max"> that is lower than 9999</span>.</div>
</form>
</div>
<div id="prompt-for-days-facts" style="display:none">
<form name="prompt_for_days_facts_form" id="prompt_for_days_facts_form" class="MgmtCards-promptText">
<div style="padding-bottom:15px;">{% blocktrans %}For facts collected older than the time period specified, save one fact scan (snapshot) per time window (frequency). For example, facts older than 30 days are purged, while one weekly fact scan is kept.
<div style="padding-bottom:15px;"><span translate>For facts collected older than the time period specified, save one fact scan (snapshot) per time window (frequency). For example, facts older than 30 days are purged, while one weekly fact scan is kept.</span>
<br>
<br>
<span translate>CAUTION: Setting both numerical variables to "0" will delete all facts.</span>
<br>
<br> CAUTION: Setting both numerical variables to "0" will delete all facts.
<br>
<br>{% endblocktrans %}
</div>
<div class="form-group">
<label for="description">
<span class="label-text">
{% trans 'Select a time period after which to remove old facts' %}
</span>
<span class="label-text" translate>Select a time period after which to remove old facts</span>
</label>
<div class="row">
<div class="col-xs-6">
@ -129,13 +131,11 @@
<div class="error" ng-show="prompt_for_days_facts_form.keep_amount.$dirty && (prompt_for_days_facts_form.keep_amount.$error.number || prompt_for_days_facts_form.keep_amount.$error.integer ||
prompt_for_days_facts_form.keep_amount.$error.required ||
prompt_for_days_facts_form.keep_amount.$error.min ||
prompt_for_days_facts_form.keep_amount.$error.max)">{% blocktrans %}Please enter an integer<span ng-show="prompt_for_days_facts_form.keep_amount.$dirty && prompt_for_days_facts_form.keep_amount.$error.min"> that is not negative</span><span ng-show="prompt_for_days_facts_form.keep_amount.$dirty && prompt_for_days_facts_form.keep_amount.$error.max"> that is lower than 9999</span>.{% endblocktrans %}</div>
prompt_for_days_facts_form.keep_amount.$error.max)" translate>Please enter an integer<span ng-show="prompt_for_days_facts_form.keep_amount.$dirty && prompt_for_days_facts_form.keep_amount.$error.min"> that is not negative</span><span ng-show="prompt_for_days_facts_form.keep_amount.$dirty && prompt_for_days_facts_form.keep_amount.$error.max"> that is lower than 9999</span>.</div>
</div>
<div class="form-group ">
<label for="description">
<span class="label-text">
{% trans 'Select a frequency for snapshot retention' %}
</span>
<span class="label-text" translate>Select a frequency for snapshot retention</span>
</label>
<div class="row">
<div class="col-xs-6">
@ -148,13 +148,13 @@
<div class="error" ng-show="prompt_for_days_facts_form.granularity_keep_amount.$dirty && (prompt_for_days_facts_form.granularity_keep_amount.$error.number || prompt_for_days_facts_form.granularity_keep_amount.$error.integer ||
prompt_for_days_facts_form.granularity_keep_amount.$error.required ||
prompt_for_days_facts_form.granularity_keep_amount.$error.min ||
prompt_for_days_facts_form.granularity_keep_amount.$error.max)">{% blocktrans %}Please enter an integer<span ng-show="prompt_for_days_facts_form.granularity_keep_amount.$dirty && prompt_for_days_facts_form.granularity_keep_amount.$error.min"> that is not negative</span><span ng-show="prompt_for_days_facts_form.granularity_keep_amount.$dirty && prompt_for_days_facts_form.granularity_keep_amount.$error.max"> that is lower than 9999</span>.{% endblocktrans %}</div>
prompt_for_days_facts_form.granularity_keep_amount.$error.max)" translate>Please enter an integer<span ng-show="prompt_for_days_facts_form.granularity_keep_amount.$dirty && prompt_for_days_facts_form.granularity_keep_amount.$error.min"> that is not negative</span><span ng-show="prompt_for_days_facts_form.granularity_keep_amount.$dirty && prompt_for_days_facts_form.granularity_keep_amount.$error.max"> that is lower than 9999</span>.</div>
</div>
</form>
</div>
<div class="overlay"></div>
<div class="spinny"><i class="fa fa-cog fa-spin fa-2x"></i>
<p>{% trans 'working...' %}</p>
<p translate>working...</p>
</div>
</div>
</at-layout>

View File

@ -1,126 +0,0 @@
var awx_env,
path = require('path'),
webpack = require('webpack'),
options = require('minimist')(JSON.parse(process.env.npm_config_argv).remain),
merge = require('lodash').merge;
awx_env = {
'proxy': {
'django_host': process.env.npm_package_config_django_host,
'django_port': process.env.npm_package_config_django_port
}
};
merge(awx_env, options);
var vendorPkgs = [
'angular',
'angular-breadcrumb',
'angular-codemirror',
'angular-cookies',
'angular-drag-and-drop-lists',
'angular-duration-format',
'angular-gettext',
'angular-md5',
'angular-moment',
'angular-sanitize',
'angular-scheduler',
'angular-tz-extensions',
'angular-ui-router',
'bootstrap',
'bootstrap-datepicker',
'codemirror',
'd3',
//'javascript-detect-element-resize', // jquery-flavored dist is alias'd below
'jquery',
'jquery-ui',
'js-yaml',
'lodash',
'lr-infinite-scroll',
'moment',
'ng-toast',
'nvd3',
'select2',
'sprintf-js',
'reconnectingwebsocket'
];
var baseConfig = function() {
return {
entry: {
app: './client/src/app.js',
vendor: vendorPkgs
},
output: {
path: './static/',
filename: 'app.js'
},
plugins: [
// vendor shims:
// [{expected_local_var : dependency}, ...]
new webpack.ProvidePlugin({
'$': 'jquery',
'jQuery': 'jquery',
'window.jQuery': 'jquery',
'_': 'lodash',
'CodeMirror': 'codemirror',
'jsyaml': 'js-yaml',
'jsonlint': 'codemirror.jsonlint'
}),
new webpack.optimize.CommonsChunkPlugin('vendor', 'app.vendor.js')
],
module: {
loaders: [
{
// disable AMD loading (broken in this lib) and default to CommonJS (not broken)
test: /\.angular-tz-extensions.js$/,
loader: 'imports?define=>false'
},
{
// es6 -> es5
test: /\.js$/,
loader: 'babel-loader',
exclude: /(node_modules)/,
query: {
presets: ['es2015']
}
},
{
test: /\.json$/,
loader: 'json-loader',
exclude: /(node_modules)/
}
]
},
resolve: {
alias: {
'codemirror.jsonlint': path.resolve() + '/node_modules/codemirror/addon/lint/json-lint.js',
'jquery.resize': path.resolve() + '/node_modules/javascript-detect-element-resize/jquery.resize.js',
'select2': path.resolve() + '/node_modules/select2/dist/js/select2.full.js'
}
}
};
};
var dev = baseConfig();
dev.devtool = 'inline-source-map';
dev.watch = true;
dev.plugins.push(new webpack.DefinePlugin({ $ENV: JSON.stringify(awx_env) }));
dev.module.preLoaders = [
{
test: /\.js?$/,
loader: 'jshint-loader',
exclude: ['/(node_modules)/'],
include: [path.resolve() + '/client/src/'],
jshint: {
emitErrors: true
}
}
];
var release = baseConfig();
release.plugins.push(new webpack.DefinePlugin({ $ENV: {} }));
release.plugins.push(new webpack.optimize.UglifyJsPlugin({ mangle: false }));
module.exports = { dev: dev, release: release };

View File

@ -1,237 +0,0 @@
# UI BUILD SYSTEM
### Table of Contents
1. [Care and Keeping of NodeJS + NPM](#nodejs-and-npm)
1. [Pin NodeJS & NPM versions](#pin-nodejs-npm-versions)
2. [Use NVM to manage multple Node/NPM installations](#use-nvm)
3.. [Add, incremenet, remove a package](#add-upgrade-remove-npm-package)
4. [dependency, devDependency, or optionalDependency?](#npm-dependency-types)
2. [Webpack](#webpack)
1. [What does Webpack handle?](#webpack-what-do)
2. [Add / remove a vendor module](#add-upgrade-remove-vendor-module)
3. [Strategies: loading a module](#loading-modules)
4. [Strategies: exposing a module to application code](#exposing-modules)
3. [Grunt](#grunt)
1. [What does Grunt handle?](#grunt-what-do)
2. [Add / remove a Grunt task](#add-remove-upgrade-grunt-task)
3. [Task concurrency & process lifecycles](#grunt-task-concurrency)
4. [Karma](#karma)
1. [Developer configuration](#karma-developer-config)
2. [CI configuration](#karma-ci-config)
5. [Interfaces & Environments](#interfaces-and-environments)
1. [NPM script targets](#npm-scripts)
2. [Makefile targets](#make-targets)
3. [NPM config variables](#npm-config-variables)
4. [Other environment variables](#environment-variables)
6. References / Resources
# <a name="nodejs-and-npm">Care and Keeping of NodeJS + NPM</a>
## <a name="pin-nodejs-npm-versions">Pin NodeJS & NPM versions</a>
NodeJS began packaging their releases with a bundled version of NPM. Occasionally, the version of NPM that ships with a given NodeJS release can be unstable. For example, the v6 LTS of Node shipped with a version of NPM that introduced a regression that broke module installation for any package with platform-specific dependencies.
For this reason, it's strongly advised to pin development environments, CI, and release pipelines to vetted releases of NodeJS + NPM.
Pinned versions are best expressed through the engine field in `package.json`.
```json
"engines": {
"node": "^6.3.1",
"npm": "=3.10.3"
}
```
## <a name="use-nvm">Use NVM to manage multiple NodeJS + NPM installations</a>
A system installation of Node raises *many* challenges on a development or shared system: user permissions, shared (global) modules, and installation paths for multiple versions. `nvm` is a light executable that addresses all of these issues. In the context of Tower, we use nvm to quickly switch between versions of NodeJS + NPM.
Version support per Tower release
3.0.* - NodeJS v0.12.17 & NPM v2.15.1
3.1.* - NodeJS 6.3.1 * & NPM 3.10.3
* [nvm installation guide](https://github.com/creationix/nvm#installation)
* [additional shell integrations](https://github.com/creationix/nvm#deeper-shell-integration)
```bash
$ nvm install 6.3
$ nvm use 6.3
```
## <a name="add-upgrade-remove-npm-package">Adding, incrementing, removing packages via NPM</a>
The Tower package utilizes an `npm-shrinkwrap.json` file to freeze dependency resolution. When `npm install` runs, it will prefer to resolve dependencies as frozen in the shrinkwrap file before it ingests versions in `package.json`. To update the shrinkwrap file with a new dependency, pass the `--save` argument e.g. `npm install fooify@1.2.3 --save`.
*Do not run `npm shrinkwrap` when add/updating dependencies*. This will re-generate the entire conflict resolution tree, which will churn over time. Instead, depend on `--save`, `--save-dev` and `--save-optional` to create/update the shrinkwrapped package.
## <a name="npm-dependency-types">What's a dependency, devDependency, or optionalDependency</a>
`dependency` - Bundled in the Tower product. Customer-facing.
`devDependency` - Used in the development, build, and release pipelines
`optionalDependency` - Platform-specific dependencies, or dependencies which should not cause `npm install` to exit with an error if these modules fail to install.
# <a name="webpack">Webpack</a>
## <a name="webpack-what-do">What does Webpack handle?</a>
Webpack ingests our vendor and application modules, and outputs bundled code optimized for development or production. Configuration lives in `webpack.config.js`.
Webpack is a highly pluggable framework ([list of vetted plugins](https://webpack.github.io/docs/list-of-plugins.html)) We make use of the following plugins:
* [ProvidePlugin](https://webpack.github.io/docs/list-of-plugins.html#provideplugin) - automatically loads and exports a module into specified namespace. A modular approach to loading modules that you would otherwise have to load into global namespace. Example:
```javascript
// webpack.config.js
plugins: {
new webpack.ProvidePlugin({
'$': 'jquery',
})
}
```
```javascript
// some-application-module.js
// the following code:
$('.my-thingy').click();
// is transformed into:
var $ = require('jquery');
$('.my-thingy').click();
```
* [CommonChunksPlugin](https://webpack.github.io/docs/list-of-plugins.html#commonschunkplugin) - a [chunk](https://webpack.github.io/docs/code-splitting.html#chunk-content) is Webpack's unit of code-splitting. This plugin' chunk consolidation strategy helps us split our bundled vendor code from our bundled application code, which *dramatically reduces* rebuild and browser loading time in development.
Currently, Tower is split into two output bundles: `tower.vendor.js` and `tower.js`. This would be the plugin configuration to update to additionally split application code into more portable components (example: for usage in the Insights UI).
* [DefinePlugin](https://webpack.github.io/docs/list-of-plugins.html#defineplugin) - injects a module that behaves like a global constant, which can be defined/configured as compile time. Tower uses this plugin to allow command-line arguments passed in at build time to be consumed by application code.
* [UglifyJSPlugin](https://webpack.github.io/docs/list-of-plugins.html#uglifyjsplugin) (production-only) - removes whitespace and minifies output. The mangle option can be used for an addiction layer of obfuscation, but it can also cause namespace issues.
## <a name="add-upgrade-remove-vendor-module">Add / remove a vendor module</a>
First, [install the package via npm](#add-upgrade-remove-npm-package). If the package doesn't export its contents as a CommonJS, AMD, or SystemJS module you will need to [write a module loader](https://webpack.github.io/docs/how-to-write-a-loader.html).
Not all packages correctly import their own dependencies. Some packages (notable: most jquery plugins) assume a certain dependency will already be in the global namespace. You will need to shim dependencies into these modules using Webpack's [export loader](https://webpack.github.io/docs/shimming-modules.html#exporting).
To bundle a new dependency in `tower.vendor.js`, add it to the `vendorPkgs` array in `webpack.config.js`.
## <a name="loading-modules">Strategies: loading a module</a>
Webpack ingests code through a concept called a `loader`. [What is a loader?, exactly?](http://webpack.github.io/docs/using-loaders.html#what-are-loaders)
Loaders can be chained together to perform a complex series of transformations and exports, or used in isolation to target a specific module.
The Webpack loaders used by Tower:
[Babel loader](https://github.com/babel/babel-loader) - loads files matching a pattern
[imports loader](https://github.com/webpack/imports-loader) - shims dependency namespace, or constrains our own module loading strategies for this package (e.g. prefer to use CJS because AMD strategy is broken in package)
# <a name="grunt">Grunt</a>
[Grunt](http://gruntjs.com/) is a modular task runner. Functionally, it serves the same purpose as a Makefile or set of bash scripts. Grunt helps normalize the interfaces between disparate pieces of tooling. For purposes of Tower, Grunt also simplifies managing the lifecycle of concurrent child processes.
## <a name="grunt-what-do">What does Grunt handle?</a>a>
Grunt cleans up build artifacts, copies source files, lints, runs 18n text extraction & compilation, and transforms LESS to CSS.
Other development-only Grunt tasks will poll for file changes, run tasks when a subset of files changes, and raise an instance of BrowserSync (reloads browser on built changes) proxied to an instance of the Django API, running in a native Docker container or container inside of a Docker Machine.
Grunt internally uses minimatch [file globbing patterns](http://gruntjs.com/configuring-tasks#globbing-patterns)
## <a name="add-remove-upgrade-grunt-tasks"> Add / change / remove a Grunt task</a>
Grunt tasks live in `awx/ui/grunt-tasks/` and are organized in a file-per-plugin pattern.
The plugin `load-grunt-configs` will automatically load and register tasks read from the configuration files in `awx/ui/grunt-tasks`. This reduces the amount of boilerplate required to write, load, register, each task configuration.
FEach task may be configured with a set of default option, plus additional targets that inherit or override defaults. For example, all tasks in `grunt-tasks/clean.js` run with `-f` or `--force` flag. `grunt-contrib-clean`
```javascript
module.exports = {
options: { force: true },
static: 'static/*',
coverage: 'coverage/*',
tmp: '../../tmp',
jshint: 'coverage/jshint.xml'
};
```
## <a name="grunt-task-concurrency">Grunt task concurrency</a>
By default, Grunt tasks are run in a series. [grunt-concurrent] is a plugin that allows us to parallelize certain tasks, to speed up the overall build process.
Note: certain polling tasks *must always be run on a thread that remains alive*. For example, the `webpack` tasks interact with Webpack's API. Therefor when Webpack's `livereload` option is on, Grunt `webpack` tasks should be spawned in series prior to Grunt `watch` tasks.
# <a name="karma"> Karma </a>
`karma.conf.js` is a generic configuration shared between developer and CI unit test runs.
The base configuration is suitable for live development. Additional command-line arguments supplement this general config to suit CI systems.
An [npm script](#npm-scripts) interface is provided to run either of these configurations: `npm test` (base) `npm test:ci`
# <a name="Interfaces & Environments">Interfaces & Environments</a>
The UI build system is intended for use through [NPM scripts](https://docs.npmjs.com/misc/scripts). NPM scripts are preferable to just running `grunt sometask` because `npm run` will look for `node_modules/.bin` executables, allowing us to manage CI/release executable versions through `package.json`. You would otherwise have to append these to the executor's $PATH.
`npm run` targets are run in a shell, which makes them a flexible way of mixing together Python, Ruby, Node, or other CLI tooling in a project.
## <a name="npm-scripts">Tower's NPM script targets</a>
Below is a reference of what each script target does in human language, and then what you can expect the script to execute.
-------
Builds a development version of the UI with a BrowserSync instance proxied to a Docker Machine
```bash
$ DOCKER_MACHINE_NAME=default npm run build-docker-machine
$ ip=$(docker-machine ip $DOCKER_MACHINE_NAME); npm set ansible-tower:django_host ${ip}; grunt dev;
```
Builds a development version of the UI with a BrowserSync instance proxied to a native Docker container
```bash
$ DOCKER_CID=1a2b3c4d5e npm run build-docker
$ ip=`docker inspect --format '{{ .NetworkSettings.IPAddress }}' $DOCkER_CID` | npm set config ansible-tower:django_host ${ip}; grunt dev;
```
Builds a development version of the UI. No filesystem polling. Re-run after each new revision.
```bash
$ npm run build-devel
```
Builds a production version of the UI.
```bash
$ npm run build-release
```
Launches unit test runners in assorted browsers. Alias for `npm run test`
```bash
$ npm test
```
Launches unit test runners headless in PhantomJS. Alias for `npm run test:ci`
```bash
$ npm test:ci
```
Extracts i18n string markers to a .pot template.
```bash
$ npm run pot
```
Builds i18n language files with regard to .pot.
```bash
$ npm run languages
```