mirror of
https://github.com/ansible/awx.git
synced 2024-11-02 01:21:21 +03:00
Moved a significant amount of the tree generation logic out to the workflow service
This commit is contained in:
parent
bcf768e7fc
commit
17b4b1e7a2
@ -12,7 +12,7 @@ export default
|
||||
return {
|
||||
restrict: 'E',
|
||||
scope: false,
|
||||
templateUrl: templateUrl('job-templates/labels/labelsList'),
|
||||
templateUrl: templateUrl('templates/labels/labelsList'),
|
||||
link: function(scope, element, attrs) {
|
||||
scope.showDelete = attrs.showDelete === 'true';
|
||||
scope.seeMoreInactive = true;
|
||||
|
@ -24,7 +24,7 @@ angular.module('templates', [surveyMaker.name, templatesList.name, jobTemplatesA
|
||||
workflowChart.name, workflowMaker.name
|
||||
])
|
||||
.service('TemplatesService', templatesService)
|
||||
.service('WorkflowHelpService', workflowService)
|
||||
.service('WorkflowService', workflowService)
|
||||
.config(['$stateProvider', 'stateDefinitionsProvider', '$stateExtenderProvider',
|
||||
function($stateProvider, stateDefinitionsProvider, $stateExtenderProvider) {
|
||||
let stateTree, addJobTemplate, editJobTemplate, addWorkflow, editWorkflow,
|
||||
|
@ -8,13 +8,13 @@
|
||||
[ '$scope', '$stateParams', 'WorkflowForm', 'GenerateForm', 'Alert', 'ProcessErrors',
|
||||
'ClearScope', 'GetBasePath', '$q', 'ParseTypeChange', 'Wait', 'Empty',
|
||||
'ToJSON', 'initSurvey', '$state', 'CreateSelect2', 'ParseVariableString',
|
||||
'TemplatesService', 'OrganizationList', 'Rest',
|
||||
'TemplatesService', 'OrganizationList', 'Rest', 'WorkflowService',
|
||||
function(
|
||||
$scope, $stateParams, WorkflowForm, GenerateForm, Alert, ProcessErrors,
|
||||
ClearScope, GetBasePath, $q, ParseTypeChange, Wait, Empty,
|
||||
ToJSON, SurveyControllerInit, $state, CreateSelect2, ParseVariableString,
|
||||
TemplatesService, OrganizationList, Rest
|
||||
) {window.state = $state;
|
||||
TemplatesService, OrganizationList, Rest, WorkflowService
|
||||
) {
|
||||
|
||||
ClearScope();
|
||||
|
||||
@ -40,90 +40,6 @@
|
||||
$scope.associateRequests = [];
|
||||
$scope.disassociateRequests = [];
|
||||
|
||||
$scope.workflowTree = {
|
||||
data: {
|
||||
id: 1,
|
||||
canDelete: false,
|
||||
canEdit: false,
|
||||
canAddTo: true,
|
||||
isStartNode: true,
|
||||
unifiedJobTemplate: {
|
||||
name: "Workflow Launch"
|
||||
},
|
||||
children: [],
|
||||
deletedNodes: [],
|
||||
totalNodes: 0
|
||||
},
|
||||
nextIndex: 2
|
||||
};
|
||||
|
||||
function buildBranch(params) {
|
||||
// params.nodeId
|
||||
// params.parentId
|
||||
// params.edgeType
|
||||
// params.nodesObj
|
||||
// params.isRoot
|
||||
|
||||
let treeNode = {
|
||||
children: [],
|
||||
c: "#D7D7D7",
|
||||
id: $scope.workflowTree.nextIndex,
|
||||
nodeId: params.nodeId,
|
||||
canDelete: true,
|
||||
canEdit: true,
|
||||
canAddTo: true,
|
||||
placeholder: false,
|
||||
edgeType: params.edgeType,
|
||||
unifiedJobTemplate: _.clone(params.nodesObj[params.nodeId].summary_fields.unified_job_template),
|
||||
isNew: false,
|
||||
edited: false,
|
||||
originalEdge: params.edgeType,
|
||||
originalNodeObj: _.clone(params.nodesObj[params.nodeId]),
|
||||
promptValues: {},
|
||||
isRoot: params.isRoot ? params.isRoot : false
|
||||
};
|
||||
|
||||
$scope.workflowTree.data.totalNodes++;
|
||||
|
||||
$scope.workflowTree.nextIndex++;
|
||||
|
||||
if(params.parentId) {
|
||||
treeNode.originalParentId = params.parentId;
|
||||
}
|
||||
|
||||
// Loop across the success nodes and add them recursively
|
||||
_.forEach(params.nodesObj[params.nodeId].success_nodes, function(successNodeId) {
|
||||
treeNode.children.push(buildBranch({
|
||||
nodeId: successNodeId,
|
||||
parentId: params.nodeId,
|
||||
edgeType: "success",
|
||||
nodesObj: params.nodesObj
|
||||
}));
|
||||
});
|
||||
|
||||
// failure nodes
|
||||
_.forEach(params.nodesObj[params.nodeId].failure_nodes, function(failureNodesId) {
|
||||
treeNode.children.push(buildBranch({
|
||||
nodeId: failureNodesId,
|
||||
parentId: params.nodeId,
|
||||
edgeType: "failure",
|
||||
nodesObj: params.nodesObj
|
||||
}));
|
||||
});
|
||||
|
||||
// always nodes
|
||||
_.forEach(params.nodesObj[params.nodeId].always_nodes, function(alwaysNodesId) {
|
||||
treeNode.children.push(buildBranch({
|
||||
nodeId: alwaysNodesId,
|
||||
parentId: params.nodeId,
|
||||
edgeType: "always",
|
||||
nodesObj: params.nodesObj
|
||||
}));
|
||||
});
|
||||
|
||||
return treeNode;
|
||||
}
|
||||
|
||||
function init() {
|
||||
|
||||
// Select2-ify the lables input
|
||||
@ -195,40 +111,8 @@
|
||||
TemplatesService.getWorkflowJobTemplateNodes(id)
|
||||
.then(function(data){
|
||||
|
||||
let nodesArray = data.data.results;
|
||||
let nodesObj = {};
|
||||
let nonRootNodeIds = [];
|
||||
let allNodeIds = [];
|
||||
|
||||
// Determine which nodes are root nodes
|
||||
_.forEach(nodesArray, function(node) {
|
||||
nodesObj[node.id] = _.clone(node);
|
||||
|
||||
allNodeIds.push(node.id);
|
||||
|
||||
_.forEach(node.success_nodes, function(nodeId){
|
||||
nonRootNodeIds.push(nodeId);
|
||||
});
|
||||
_.forEach(node.failure_nodes, function(nodeId){
|
||||
nonRootNodeIds.push(nodeId);
|
||||
});
|
||||
_.forEach(node.always_nodes, function(nodeId){
|
||||
nonRootNodeIds.push(nodeId);
|
||||
});
|
||||
});
|
||||
|
||||
let rootNodes = _.difference(allNodeIds, nonRootNodeIds);
|
||||
|
||||
// Loop across the root nodes and re-build the tree
|
||||
_.forEach(rootNodes, function(rootNodeId) {
|
||||
let branch = buildBranch({
|
||||
nodeId: rootNodeId,
|
||||
edgeType: "always",
|
||||
nodesObj: nodesObj,
|
||||
isRoot: true
|
||||
});
|
||||
|
||||
$scope.workflowTree.data.children.push(branch);
|
||||
$scope.workflowTree = WorkflowService.buildTree({
|
||||
workflowNodes: data.data.results
|
||||
});
|
||||
|
||||
// TODO: I think that the workflow chart directive (and eventually d3) is meddling with
|
||||
|
@ -4,11 +4,11 @@
|
||||
* All Rights Reserved
|
||||
*************************************************/
|
||||
|
||||
export default ['$scope', 'WorkflowHelpService', 'generateList', 'TemplateList', 'ProjectList',
|
||||
export default ['$scope', 'WorkflowService', 'generateList', 'TemplateList', 'ProjectList',
|
||||
'GetBasePath', 'Wait', 'TemplatesService', '$state',
|
||||
'ProcessErrors', 'InventorySourcesList', 'CreateSelect2', 'WorkflowMakerForm',
|
||||
'GenerateForm', 'InventoryList', 'CredentialList', '$q',
|
||||
function($scope, WorkflowHelpService, GenerateList, TemplateList, ProjectList,
|
||||
function($scope, WorkflowService, GenerateList, TemplateList, ProjectList,
|
||||
GetBasePath, Wait, TemplatesService, $state,
|
||||
ProcessErrors, InventorySourcesList, CreateSelect2, WorkflowMakerForm,
|
||||
GenerateForm, InventoryList, CredentialList, $q) {
|
||||
@ -78,7 +78,7 @@ export default ['$scope', 'WorkflowHelpService', 'generateList', 'TemplateList',
|
||||
$scope.addParent = parent;
|
||||
$scope.betweenTwoNodes = betweenTwoNodes;
|
||||
|
||||
$scope.placeholderNode = WorkflowHelpService.addPlaceholderNode({
|
||||
$scope.placeholderNode = WorkflowService.addPlaceholderNode({
|
||||
parent: parent,
|
||||
betweenTwoNodes: betweenTwoNodes,
|
||||
tree: $scope.treeData.data,
|
||||
@ -87,7 +87,7 @@ export default ['$scope', 'WorkflowHelpService', 'generateList', 'TemplateList',
|
||||
|
||||
$scope.treeData.nextIndex++;
|
||||
|
||||
let siblingConnectionTypes = WorkflowHelpService.getSiblingConnectionTypes({
|
||||
let siblingConnectionTypes = WorkflowService.getSiblingConnectionTypes({
|
||||
tree: $scope.treeData.data,
|
||||
parentId: betweenTwoNodes ? parent.source.id : parent.id
|
||||
});
|
||||
@ -187,7 +187,7 @@ export default ['$scope', 'WorkflowHelpService', 'generateList', 'TemplateList',
|
||||
$scope.cancelNodeForm = function() {
|
||||
if ($scope.workflowMakerFormConfig.nodeMode === "add") {
|
||||
// Remove the placeholder node from the tree
|
||||
WorkflowHelpService.removeNodeFromTree({
|
||||
WorkflowService.removeNodeFromTree({
|
||||
tree: $scope.treeData.data,
|
||||
nodeToBeDeleted: $scope.placeholderNode
|
||||
});
|
||||
@ -212,12 +212,12 @@ export default ['$scope', 'WorkflowHelpService', 'generateList', 'TemplateList',
|
||||
|
||||
$scope.workflowMakerFormConfig.nodeMode = "edit";
|
||||
|
||||
let parent = WorkflowHelpService.searchTree({
|
||||
let parent = WorkflowService.searchTree({
|
||||
element: $scope.treeData.data,
|
||||
matchingId: nodeToEdit.parent.id
|
||||
});
|
||||
|
||||
$scope.nodeBeingEdited = WorkflowHelpService.searchTree({
|
||||
$scope.nodeBeingEdited = WorkflowService.searchTree({
|
||||
element: parent,
|
||||
matchingId: nodeToEdit.id
|
||||
});
|
||||
@ -427,7 +427,7 @@ export default ['$scope', 'WorkflowHelpService', 'generateList', 'TemplateList',
|
||||
|
||||
// TODO: turn this into a promise so that we can handle errors
|
||||
|
||||
WorkflowHelpService.removeNodeFromTree({
|
||||
WorkflowService.removeNodeFromTree({
|
||||
tree: $scope.treeData.data,
|
||||
nodeToBeDeleted: $scope.nodeToBeDeleted
|
||||
});
|
||||
|
@ -14,7 +14,7 @@ export default ['templateUrl', 'CreateDialog', 'Wait', '$state',
|
||||
canAddWorkflowJobTemplate: '='
|
||||
},
|
||||
restrict: 'E',
|
||||
templateUrl: templateUrl('job-templates/workflow-maker/workflow-maker'),
|
||||
templateUrl: templateUrl('templates/workflows/workflow-maker/workflow-maker'),
|
||||
controller: workflowMakerController,
|
||||
link: function(scope) {
|
||||
CreateDialog({
|
||||
|
@ -117,6 +117,139 @@ export default [function(){
|
||||
}
|
||||
|
||||
return Object.keys(siblingConnectionTypes);
|
||||
},
|
||||
buildTree: function(params) {
|
||||
//params.workflowNodes
|
||||
|
||||
let _this = this;
|
||||
|
||||
let treeData = {
|
||||
data: {
|
||||
id: 1,
|
||||
canDelete: false,
|
||||
canEdit: false,
|
||||
canAddTo: true,
|
||||
isStartNode: true,
|
||||
unifiedJobTemplate: {
|
||||
name: "Workflow Launch"
|
||||
},
|
||||
children: [],
|
||||
deletedNodes: [],
|
||||
totalNodes: 0
|
||||
},
|
||||
nextIndex: 2
|
||||
};
|
||||
|
||||
let nodesArray = params.workflowNodes;
|
||||
let nodesObj = {};
|
||||
let nonRootNodeIds = [];
|
||||
let allNodeIds = [];
|
||||
|
||||
// Determine which nodes are root nodes
|
||||
_.forEach(nodesArray, function(node) {
|
||||
nodesObj[node.id] = _.clone(node);
|
||||
|
||||
allNodeIds.push(node.id);
|
||||
|
||||
_.forEach(node.success_nodes, function(nodeId){
|
||||
nonRootNodeIds.push(nodeId);
|
||||
});
|
||||
_.forEach(node.failure_nodes, function(nodeId){
|
||||
nonRootNodeIds.push(nodeId);
|
||||
});
|
||||
_.forEach(node.always_nodes, function(nodeId){
|
||||
nonRootNodeIds.push(nodeId);
|
||||
});
|
||||
});
|
||||
|
||||
let rootNodes = _.difference(allNodeIds, nonRootNodeIds);
|
||||
|
||||
// Loop across the root nodes and re-build the tree
|
||||
_.forEach(rootNodes, function(rootNodeId) {
|
||||
let branch = _this.buildBranch({
|
||||
nodeId: rootNodeId,
|
||||
edgeType: "always",
|
||||
nodesObj: nodesObj,
|
||||
isRoot: true,
|
||||
treeData: treeData
|
||||
});
|
||||
|
||||
treeData.data.children.push(branch);
|
||||
});
|
||||
|
||||
return treeData;
|
||||
},
|
||||
buildBranch: function(params) {
|
||||
// params.nodeId
|
||||
// params.parentId
|
||||
// params.edgeType
|
||||
// params.nodesObj
|
||||
// params.isRoot
|
||||
// params.treeData
|
||||
|
||||
let _this = this;
|
||||
|
||||
let treeNode = {
|
||||
children: [],
|
||||
c: "#D7D7D7",
|
||||
id: params.treeData.nextIndex,
|
||||
nodeId: params.nodeId,
|
||||
canDelete: true,
|
||||
canEdit: true,
|
||||
canAddTo: true,
|
||||
placeholder: false,
|
||||
edgeType: params.edgeType,
|
||||
unifiedJobTemplate: _.clone(params.nodesObj[params.nodeId].summary_fields.unified_job_template),
|
||||
isNew: false,
|
||||
edited: false,
|
||||
originalEdge: params.edgeType,
|
||||
originalNodeObj: _.clone(params.nodesObj[params.nodeId]),
|
||||
promptValues: {},
|
||||
isRoot: params.isRoot ? params.isRoot : false
|
||||
};
|
||||
|
||||
params.treeData.data.totalNodes++;
|
||||
|
||||
params.treeData.nextIndex++;
|
||||
|
||||
if(params.parentId) {
|
||||
treeNode.originalParentId = params.parentId;
|
||||
}
|
||||
|
||||
// Loop across the success nodes and add them recursively
|
||||
_.forEach(params.nodesObj[params.nodeId].success_nodes, function(successNodeId) {
|
||||
treeNode.children.push(_this.buildBranch({
|
||||
nodeId: successNodeId,
|
||||
parentId: params.nodeId,
|
||||
edgeType: "success",
|
||||
nodesObj: params.nodesObj,
|
||||
treeData: params.treeData
|
||||
}));
|
||||
});
|
||||
|
||||
// failure nodes
|
||||
_.forEach(params.nodesObj[params.nodeId].failure_nodes, function(failureNodesId) {
|
||||
treeNode.children.push(_this.buildBranch({
|
||||
nodeId: failureNodesId,
|
||||
parentId: params.nodeId,
|
||||
edgeType: "failure",
|
||||
nodesObj: params.nodesObj,
|
||||
treeData: params.treeData
|
||||
}));
|
||||
});
|
||||
|
||||
// always nodes
|
||||
_.forEach(params.nodesObj[params.nodeId].always_nodes, function(alwaysNodesId) {
|
||||
treeNode.children.push(_this.buildBranch({
|
||||
nodeId: alwaysNodesId,
|
||||
parentId: params.nodeId,
|
||||
edgeType: "always",
|
||||
nodesObj: params.nodesObj,
|
||||
treeData: params.treeData
|
||||
}));
|
||||
});
|
||||
|
||||
return treeNode;
|
||||
}
|
||||
};
|
||||
}];
|
||||
|
@ -6,6 +6,7 @@ export default ['workflowData',
|
||||
'$scope',
|
||||
'ParseTypeChange',
|
||||
'ParseVariableString',
|
||||
'WorkflowService',
|
||||
function(workflowData,
|
||||
workflowResultsService,
|
||||
workflowDataOptions,
|
||||
@ -13,8 +14,10 @@ export default ['workflowData',
|
||||
workflowNodes,
|
||||
$scope,
|
||||
ParseTypeChange,
|
||||
ParseVariableString
|
||||
ParseVariableString,
|
||||
WorkflowService
|
||||
) {
|
||||
|
||||
var getTowerLinks = function() {
|
||||
var getTowerLink = function(key) {
|
||||
if ($scope.workflow.related[key]) {
|
||||
@ -48,11 +51,7 @@ export default ['workflowData',
|
||||
$scope.verbosity_label = getTowerLabel('verbosity');
|
||||
};
|
||||
|
||||
// var getTotalHostCount = function(count) {
|
||||
// return Object
|
||||
// .keys(count).reduce((acc, i) => acc += count[i], 0);
|
||||
// };
|
||||
|
||||
function init() {
|
||||
// put initially resolved request data on scope
|
||||
$scope.workflow = workflowData;
|
||||
$scope.workflow_nodes = workflowNodes;
|
||||
@ -74,6 +73,30 @@ export default ['workflowData',
|
||||
|
||||
// Click binding for the expand/collapse button on the standard out log
|
||||
$scope.stdoutFullScreen = false;
|
||||
|
||||
$scope.stdoutArr = [];
|
||||
|
||||
$scope.treeData = WorkflowService.buildTree({
|
||||
workflowNodes: workflowNodes
|
||||
});
|
||||
|
||||
// TODO: I think that the workflow chart directive (and eventually d3) is meddling with
|
||||
// this treeData object and removing the children object for some reason (?)
|
||||
// This happens on occasion and I think is a race condition (?)
|
||||
if(!$scope.treeData.data.children) {
|
||||
$scope.treeData.data.children = [];
|
||||
}
|
||||
|
||||
$scope.canAddWorkflowJobTemplate = false;
|
||||
|
||||
}
|
||||
|
||||
// var getTotalHostCount = function(count) {
|
||||
// return Object
|
||||
// .keys(count).reduce((acc, i) => acc += count[i], 0);
|
||||
// };
|
||||
|
||||
|
||||
$scope.toggleStdoutFullscreen = function() {
|
||||
$scope.stdoutFullScreen = !$scope.stdoutFullScreen;
|
||||
};
|
||||
@ -90,138 +113,6 @@ export default ['workflowData',
|
||||
workflowResultsService.relaunchJob($scope);
|
||||
};
|
||||
|
||||
$scope.stdoutArr = [];
|
||||
|
||||
$scope.treeData = {
|
||||
data: {
|
||||
id: 1,
|
||||
canDelete: false,
|
||||
canEdit: false,
|
||||
canAddTo: true,
|
||||
isStartNode: true,
|
||||
unifiedJobTemplate: {
|
||||
name: "Workflow Launch"
|
||||
},
|
||||
children: [],
|
||||
deletedNodes: [],
|
||||
totalNodes: 0
|
||||
},
|
||||
nextIndex: 2
|
||||
};
|
||||
|
||||
function buildBranch(params) {
|
||||
// params.nodeId
|
||||
// params.parentId
|
||||
// params.edgeType
|
||||
// params.nodesObj
|
||||
// params.isRoot
|
||||
|
||||
let treeNode = {
|
||||
children: [],
|
||||
c: "#D7D7D7",
|
||||
id: $scope.treeData.nextIndex,
|
||||
nodeId: params.nodeId,
|
||||
canDelete: true,
|
||||
canEdit: true,
|
||||
canAddTo: true,
|
||||
placeholder: false,
|
||||
edgeType: params.edgeType,
|
||||
unifiedJobTemplate: _.clone(params.nodesObj[params.nodeId].summary_fields.unified_job_template),
|
||||
isNew: false,
|
||||
edited: false,
|
||||
originalEdge: params.edgeType,
|
||||
originalNodeObj: _.clone(params.nodesObj[params.nodeId]),
|
||||
promptValues: {},
|
||||
isRoot: params.isRoot ? params.isRoot : false
|
||||
};
|
||||
|
||||
$scope.treeData.data.totalNodes++;
|
||||
|
||||
$scope.treeData.nextIndex++;
|
||||
|
||||
if(params.parentId) {
|
||||
treeNode.originalParentId = params.parentId;
|
||||
}
|
||||
|
||||
// Loop across the success nodes and add them recursively
|
||||
_.forEach(params.nodesObj[params.nodeId].success_nodes, function(successNodeId) {
|
||||
treeNode.children.push(buildBranch({
|
||||
nodeId: successNodeId,
|
||||
parentId: params.nodeId,
|
||||
edgeType: "success",
|
||||
nodesObj: params.nodesObj
|
||||
}));
|
||||
});
|
||||
|
||||
// failure nodes
|
||||
_.forEach(params.nodesObj[params.nodeId].failure_nodes, function(failureNodesId) {
|
||||
treeNode.children.push(buildBranch({
|
||||
nodeId: failureNodesId,
|
||||
parentId: params.nodeId,
|
||||
edgeType: "failure",
|
||||
nodesObj: params.nodesObj
|
||||
}));
|
||||
});
|
||||
|
||||
// always nodes
|
||||
_.forEach(params.nodesObj[params.nodeId].always_nodes, function(alwaysNodesId) {
|
||||
treeNode.children.push(buildBranch({
|
||||
nodeId: alwaysNodesId,
|
||||
parentId: params.nodeId,
|
||||
edgeType: "always",
|
||||
nodesObj: params.nodesObj
|
||||
}));
|
||||
});
|
||||
|
||||
return treeNode;
|
||||
}
|
||||
|
||||
|
||||
let nodesArray = $scope.workflow_nodes;
|
||||
let nodesObj = {};
|
||||
let nonRootNodeIds = [];
|
||||
let allNodeIds = [];
|
||||
|
||||
// Determine which nodes are root nodes
|
||||
_.forEach(nodesArray, function(node) {
|
||||
nodesObj[node.id] = _.clone(node);
|
||||
|
||||
allNodeIds.push(node.id);
|
||||
|
||||
_.forEach(node.success_nodes, function(nodeId){
|
||||
nonRootNodeIds.push(nodeId);
|
||||
});
|
||||
_.forEach(node.failure_nodes, function(nodeId){
|
||||
nonRootNodeIds.push(nodeId);
|
||||
});
|
||||
_.forEach(node.always_nodes, function(nodeId){
|
||||
nonRootNodeIds.push(nodeId);
|
||||
});
|
||||
});
|
||||
|
||||
let rootNodes = _.difference(allNodeIds, nonRootNodeIds);
|
||||
|
||||
// Loop across the root nodes and re-build the tree
|
||||
_.forEach(rootNodes, function(rootNodeId) {
|
||||
let branch = buildBranch({
|
||||
nodeId: rootNodeId,
|
||||
edgeType: "always",
|
||||
nodesObj: nodesObj,
|
||||
isRoot: true
|
||||
});
|
||||
|
||||
$scope.treeData.data.children.push(branch);
|
||||
});
|
||||
|
||||
// TODO: I think that the workflow chart directive (and eventually d3) is meddling with
|
||||
// this treeData object and removing the children object for some reason (?)
|
||||
// This happens on occasion and I think is a race condition (?)
|
||||
if(!$scope.treeData.data.children) {
|
||||
$scope.treeData.data.children = [];
|
||||
}
|
||||
|
||||
$scope.canAddWorkflowJobTemplate = false;
|
||||
|
||||
// EVENT STUFF BELOW
|
||||
|
||||
// just putting the event queue on scope so it can be inspected in the
|
||||
@ -315,4 +206,6 @@ export default ['workflowData',
|
||||
$scope.workflow.status = data.status;
|
||||
}
|
||||
});
|
||||
|
||||
init();
|
||||
}];
|
||||
|
Loading…
Reference in New Issue
Block a user