From 2961d5150fec2daa543785b478166a720d03241f Mon Sep 17 00:00:00 2001 From: Chris Houseknecht Date: Fri, 18 Jul 2014 19:59:28 -0400 Subject: [PATCH] Jobs and Sockets Job submission now removes the spinner once the job is started. It is also 'socket aware'. In the event the socket connection is unavailable on SCM updates and inventory sync runs it prompts the user with a dialog letting them know to hit the refresh button. Came up with a way to 'monitor' socket connections. On the main connection and on the Jobs page connecgtions we now check the connection every 3 seconds. If the connection status is error, or if the status returns 'connnecting' 2x in a row, we kill and restart the connections. --- awx/ui/static/js/app.js | 54 ++- awx/ui/static/js/config.js | 2 +- awx/ui/static/js/controllers/Jobs.js | 402 ++---------------- awx/ui/static/js/controllers/Projects.js | 4 +- awx/ui/static/js/helpers/JobSubmission.js | 26 +- awx/ui/static/lib/ansible/Socket.js | 3 - awx/ui/static/lib/ansible/Utilities.js | 40 +- .../lib/socket.io-client/dist/socket.io.js | 68 +-- awx/ui/templates/ui/index.html | 2 +- 9 files changed, 152 insertions(+), 449 deletions(-) diff --git a/awx/ui/static/js/app.js b/awx/ui/static/js/app.js index 29100b2547..9887476c5f 100644 --- a/awx/ui/static/js/app.js +++ b/awx/ui/static/js/app.js @@ -411,7 +411,7 @@ angular.module('Tower', [ function ($compile, $cookieStore, $rootScope, $log, CheckLicense, $location, Authorization, LoadBasePaths, ViewLicense, Timer, ClearScope, HideStream, Socket) { - var e, html, sock; + var e, html, sock, checkCount; LoadBasePaths(); @@ -468,13 +468,17 @@ angular.module('Tower', [ HideStream(); } + // remove any lingering intervals if ($rootScope.jobDetailInterval) { window.clearInterval($rootScope.jobDetailInterval); } - if ($rootScope.jobStdOutInterval) { window.clearInterval($rootScope.jobStdOutInterval); } + if ($rootScope.checkSocketConnectionInterval) { + // use to monitor and restart socket connections + window.clearInterval($rootScope.checkSocketConnectionInterval); + } // On each navigation request, check that the user is logged in if (!/^\/(login|logout)/.test($location.path())) { @@ -541,33 +545,37 @@ angular.module('Tower', [ function openSocket() { sock = Socket({ scope: $rootScope, endpoint: "jobs" }); sock.init(); - setTimeout(function() { - $rootScope.$apply(function() { - sock.checkStatus(); - $log.debug('socket status: ' + $rootScope.socketStatus); - }); - },2000); sock.on("status_changed", function(data) { $log.debug('Job ' + data.unified_job_id + ' status changed to ' + data.status); $rootScope.$emit('JobStatusChange', data); }); } + openSocket(); - /* - $rootScope.socketToggle = function() { - switch($rootScope.socketStatus) { - case 'ok': - case 'connecting': - sock = null; - $rootScope.socketStatus = 'error'; - $rootScope.socketTip = 'Disconnected. Click to connect.'; - break; - case 'error': - sock = null; - $rootScope.socketStatus = ''; - $rootScope.socketTip = ''; - setTimeout(openSocket, 500); + + setTimeout(function() { + $rootScope.$apply(function() { + sock.checkStatus(); + $log.debug('socket status: ' + $rootScope.socketStatus); + }); + },2000); + + // monitor socket status + checkCount = 0; + setInterval(function() { + if (sock.checkStatus() === 'error' || checkCount > 2) { + // there's an error or we're stuck in a 'connecting' state. attempt to reconnect + sock = null; + $log.debug('attempting new socket connection'); + openSocket(); + checkCount = 0; } - };*/ + else if (sock.checkStatus() === 'connecting') { + checkCount++; + } + else { + checkCount = 0; + } + }, 3000); } ]); diff --git a/awx/ui/static/js/config.js b/awx/ui/static/js/config.js index 66255cf4da..8b82bc3da5 100644 --- a/awx/ui/static/js/config.js +++ b/awx/ui/static/js/config.js @@ -14,7 +14,7 @@ var $AnsibleConfig = { tooltip_delay: {show: 500, hide: 100}, // Default number of milliseconds to delay displaying/hiding tooltips - debug_mode: false, // Enable console logging messages + debug_mode: true, // Enable console logging messages password_strength: 45, // User password strength. Integer between 0 and 100, 100 being impossibly strong. // This value controls progress bar colors: diff --git a/awx/ui/static/js/controllers/Jobs.js b/awx/ui/static/js/controllers/Jobs.js index 5d41f5b9f5..b94018505c 100644 --- a/awx/ui/static/js/controllers/Jobs.js +++ b/awx/ui/static/js/controllers/Jobs.js @@ -10,8 +10,8 @@ 'use strict'; -function JobsListController ($log, $scope, $compile, $routeParams, ClearScope, Breadcrumbs, LoadBreadCrumbs, LoadSchedulesScope, LoadJobsScope, RunningJobsList, CompletedJobsList, QueuedJobsList, - ScheduledJobsList, GetChoices, GetBasePath, Wait, Socket) { +function JobsListController ($rootScope, $log, $scope, $compile, $routeParams, ClearScope, Breadcrumbs, LoadBreadCrumbs, LoadSchedulesScope, + LoadJobsScope, RunningJobsList, CompletedJobsList, QueuedJobsList, ScheduledJobsList, GetChoices, GetBasePath, Wait, Socket) { ClearScope(); @@ -21,104 +21,70 @@ function JobsListController ($log, $scope, $compile, $routeParams, ClearScope, B api_complete = false, schedule_socket, job_socket, - event_queue = [], - expecting = 0, - max_rows; + max_rows, checkCount; - job_socket = Socket({ - scope: $scope, - endpoint: "jobs" - }); + function openSockets() { + job_socket = Socket({ + scope: $scope, + endpoint: "jobs" + }); + job_socket.init(); + job_socket.on("status_changed", function(data) { + if (api_complete) { + processEvent(data); + } + }); + schedule_socket = Socket({ + scope: $scope, + endpoint: "schedules" + }); + schedule_socket.init(); + schedule_socket.on("schedule_changed", function() { + if (api_complete) { + scheduled_scope.search('schedule'); + } + }); + } + openSockets(); - job_socket.init(); - - job_socket.on("status_changed", function(data) { - if (api_complete) { - processEvent(data); + $rootScope.checkSocketConnectionInterval = setInterval(function() { + if (job_socket.checkStatus() === 'error' || checkCount > 2) { + // there's an error or we're stuck in a 'connecting' state. attempt to reconnect + $log.debug('jobs page: initializing and restarting socket connections'); + job_socket = null; + schedule_socket = null; + openSockets(); + checkCount = 0; + } + else if (job_socket.checkStatus() === 'connecting') { + checkCount++; } else { - event_queue.push(data); + checkCount = 0; } - }); - - schedule_socket = Socket({ - scope: $scope, - endpoint: "schedules" - }); - - schedule_socket.init(); - schedule_socket.on("status_change", function() { - if (api_complete) { - scheduled_scope.search('schedule'); - } - }); + }, 3000); function processEvent(event) { - expecting = 0; switch(event.status) { case 'running': - if (!inList(running_scope[RunningJobsList.name], event.unified_job_id)) { - expecting = 2; - running_scope.search('running_job'); - queued_scope.search('queued_job'); - } + running_scope.search('running_job'); + queued_scope.search('queued_job'); break; case 'new': case 'pending': case 'waiting': - if (!inList(queued_scope[QueuedJobsList.name], event.unified_job_id)) { - expecting = 1; - queued_scope.search('queued_job'); - } + queued_scope.search('queued_job'); break; case 'successful': case 'failed': case 'error': case 'canceled': - if (!inList(completed_scope[CompletedJobsList.name], event.unified_job_id)) { - expecting = 2; - completed_scope.search('completed_job'); - running_scope.search('running_job'); - } + completed_scope.search('completed_job'); + running_scope.search('running_job'); break; } } - function inList(list, id) { - var found = false; - list.every( function(row) { - if (row.id === id) { - found = true; - return false; - } - return true; - }); - return found; - } - - - if ($scope.removeProcessQueue) { - $scope.removeProcessQueue(); - } - $scope.removeProcessQueue = $scope.$on('ProcessQueue', function() { - var event; - listCount=0; - if (event_queue.length > 0) { - event = event_queue[0]; - processEvent(event); - event_queue.splice(0,1); - if ($scope.removeListLoaded) { - $scope.removeListLoaded(); - } - $scope.removeListLoaded = $scope.$on('listLoaded', function() { - listCount++; - if (listCount === expecting) { - $scope.$emit('ProcessQueue'); - } - }); - } - }); - LoadBreadCrumbs(); if ($scope.removeListLoaded) { @@ -128,7 +94,6 @@ function JobsListController ($log, $scope, $compile, $routeParams, ClearScope, B listCount++; if (listCount === 4) { api_complete = true; - $scope.$emit('ProcessQueue'); } }); @@ -286,280 +251,5 @@ function JobsListController ($log, $scope, $compile, $routeParams, ClearScope, B } } -JobsListController.$inject = [ '$log', '$scope', '$compile', '$routeParams', 'ClearScope', 'Breadcrumbs', 'LoadBreadCrumbs', 'LoadSchedulesScope', 'LoadJobsScope', 'RunningJobsList', 'CompletedJobsList', - 'QueuedJobsList', 'ScheduledJobsList', 'GetChoices', 'GetBasePath', 'Wait', 'Socket']; - -function JobsEdit($scope, $rootScope, $compile, $location, $log, $routeParams, JobForm, JobTemplateForm, GenerateForm, Rest, - Alert, ProcessErrors, LoadBreadCrumbs, RelatedSearchInit, RelatedPaginateInit, ReturnToCaller, ClearScope, InventoryList, - CredentialList, ProjectList, LookUpInit, PromptPasswords, GetBasePath, md5Setup, FormatDate, JobStatusToolTip, Wait, Empty, - ParseVariableString, GetChoices) { - - ClearScope(); - - var defaultUrl = GetBasePath('jobs'), - generator = GenerateForm, - id = $routeParams.id, - loadingFinishedCount = 0, - templateForm = {}, - choicesCount = 0; - - generator.inject(JobForm, { mode: 'edit', related: true, scope: $scope }); - - $scope.job_id = id; - $scope.parseType = 'yaml'; - $scope.statusSearchSpin = false; - $scope.disableParseSelection = true; - - function getPlaybooks(project, playbook) { - if (!Empty(project)) { - var url = GetBasePath('projects') + project + '/playbooks/'; - Rest.setUrl(url); - Rest.get() - .success(function (data) { - var i; - $scope.playbook_options = []; - for (i = 0; i < data.length; i++) { - $scope.playbook_options.push(data[i]); - } - for (i = 0; i < $scope.playbook_options.length; i++) { - if ($scope.playbook_options[i] === playbook) { - $scope.playbook = $scope.playbook_options[i]; - } - } - $scope.$emit('jobTemplateLoadFinished'); - }) - .error(function () { - $scope.$emit('jobTemplateLoadFinished'); - }); - } else { - $scope.$emit('jobTemplateLoadFinished'); - } - } - - - // Retrieve each related set and populate the playbook list - if ($scope.jobLoadedRemove) { - $scope.jobLoadedRemove(); - } - $scope.jobLoadedRemove = $scope.$on('jobLoaded', function (e, related_cloud_credential, project, playbook) { - - getPlaybooks(project, playbook); - if (related_cloud_credential) { - //Get the name of the cloud credential - Rest.setUrl(related_cloud_credential); - Rest.get() - .success(function (data) { - $scope.cloud_credential_name = data.name; - $scope.$emit('jobTemplateLoadFinished'); - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', - msg: 'Failed to related cloud credential. GET returned status: ' + status }); - }); - } else { - $scope.$emit('jobTemplateLoadFinished'); - } - - }); - - // Turn off 'Wait' after both cloud credential and playbook list come back - if ($scope.removeJobTemplateLoadFinished) { - $scope.removeJobTemplateLoadFinished(); - } - $scope.removeJobTemplateLoadFinished = $scope.$on('jobTemplateLoadFinished', function () { - loadingFinishedCount++; - if (loadingFinishedCount >= 2) { - // The initial template load finished. Now load related jobs, which - // will turn off the 'working' spinner. - Wait('stop'); - } - }); - - $scope.verbosity_options = [{ - value: 0, - label: 'Default' - }, { - value: 1, - label: 'Verbose' - }, { - value: 3, - label: 'Debug' - }]; - - $scope.playbook_options = null; - $scope.playbook = null; - - function calcRows(content) { - var n = content.match(/\n/g), - rows = (n) ? n.length : 1; - return (rows > 15) ? 15 : rows; - } - - if ($scope.removeLoadJobTemplate) { - $scope.removeLoadJobTemplate(); - } - $scope.removeLoadJobTemplate = $scope.$on('loadJobTemplate', function() { - // Retrieve the job detail record and prepopulate the form - Rest.setUrl(defaultUrl + ':id/'); - Rest.get({ params: { id: id } }) - .success(function (data) { - - var i, fld; - - LoadBreadCrumbs(); - - $scope.status = data.status; - $scope.created = FormatDate(data.created); - $scope.modified = FormatDate(data.modified); - $scope.result_stdout = data.result_stdout; - $scope.result_traceback = data.result_traceback; - $scope.stdout_rows = calcRows($scope.result_stdout); - $scope.traceback_rows = calcRows($scope.result_traceback); - $scope.job_explanation = data.job_explanation || 'Things may have ended badly or gone swimingly well'; - - // Now load the job template form - templateForm.addTitle = 'Create Job Templates'; - templateForm.editTitle = '{{ name }}'; - templateForm.name = 'job_templates'; - templateForm.twoColumns = true; - templateForm.fields = angular.copy(JobTemplateForm.fields); - for (fld in templateForm.fields) { - templateForm.fields[fld].readonly = true; - } - - if (data.type === "playbook_run") { - $('#ui-accordion-jobs-collapse-0-panel-1').empty(); - generator.inject(templateForm, { - mode: 'edit', - id: 'ui-accordion-jobs-collapse-0-panel-1', - related: false, - scope: $scope, - breadCrumbs: false - }); - } - else { - $('#ui-accordion-jobs-collapse-0-header-1').hide(); - $('#ui-accordion-jobs-collapse-0-panel-1').empty().hide(); - $('#jobs-collapse-0').accordion( "option", "collapsible", false ); - } - - for (fld in templateForm.fields) { - if (fld !== 'variables' && data[fld] !== null && data[fld] !== undefined) { - if (JobTemplateForm.fields[fld].type === 'select') { - if ($scope[fld + '_options'] && $scope[fld + '_options'].length > 0) { - for (i = 0; i < $scope[fld + '_options'].length; i++) { - if (data[fld] === $scope[fld + '_options'][i].value) { - $scope[fld] = $scope[fld + '_options'][i]; - } - } - } else { - $scope[fld] = data[fld]; - } - } else { - $scope[fld] = data[fld]; - } - } - if (fld === 'variables') { - $scope.variables = ParseVariableString(data.extra_vars); - } - if (JobTemplateForm.fields[fld].type === 'lookup' && data.summary_fields[JobTemplateForm.fields[fld].sourceModel]) { - $scope[JobTemplateForm.fields[fld].sourceModel + '_' + JobTemplateForm.fields[fld].sourceField] = - data.summary_fields[JobTemplateForm.fields[fld].sourceModel][JobTemplateForm.fields[fld].sourceField]; - } - } - - $scope.id = data.id; - $scope.name = (data.summary_fields && data.summary_fields.job_template) ? data.summary_fields.job_template.name : ''; - $scope.statusToolTip = JobStatusToolTip(data.status); - $scope.url = data.url; - $scope.project = data.project; - $scope.launch_type = data.launch_type; - - // set the type - data.type = 'playbook_run'; //temporary - $scope.type_choices.every( function(choice) { - if (choice.value === data.type) { - $scope.type = choice.label; - return false; - } - return true; - }); - - $scope.$emit('jobLoaded', data.related.cloud_credential, data.project, data.playbook); - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', - msg: 'Failed to retrieve job: ' + $routeParams.id + '. GET status: ' + status }); - }); - }); - - Wait('start'); - - if ($scope.removeChoicesReady) { - $scope.removeChoicesReady(); - } - $scope.removeChoicesReady = $scope.$on('choicesReady', function() { - choicesCount++; - if (choicesCount === 2) { - $scope.$emit('loadJobTemplate'); - } - }); - - GetChoices({ - scope: $scope, - url: GetBasePath('unified_jobs'), - field: 'job_type', - variable: 'job_type_options', - callback: 'choicesReady' - }); - - /*GetChoices({ - scope: $scope, - url: GetBasePath('jobs'), - field: 'status', - variable: 'status_choices', - callback: 'choicesReady' - });*/ - - GetChoices({ - scope: $scope, - url: GetBasePath('unified_jobs'), //'/static/sample/data/types/data.json' - field: 'type', - variable: 'type_choices', - callback: 'choicesReady' - }); - - $scope.refresh = function () { - Wait('start'); - Rest.setUrl(defaultUrl + id + '/'); - Rest.get() - .success(function (data) { - $scope.status = data.status; - $scope.result_stdout = data.result_stdout; - $scope.result_traceback = data.result_traceback; - $scope.stdout_rows = calcRows($scope.result_stdout); - $scope.traceback_rows = calcRows($scope.result_traceback); - Wait('stop'); - }) - .error(function (data, status) { - ProcessErrors($scope, data, status, null, { hdr: 'Error!', - msg: 'Attempt to load job failed. GET returned status: ' + status }); - }); - }; - - $scope.jobSummary = function () { - $location.path('/jobs/' + id + '/job_host_summaries'); - }; - - $scope.jobEvents = function () { - $location.path('/jobs/' + id + '/job_events'); - }; -} - -JobsEdit.$inject = ['$scope', '$rootScope', '$compile', '$location', '$log', '$routeParams', 'JobForm', 'JobTemplateForm', - 'GenerateForm', 'Rest', 'Alert', 'ProcessErrors', 'LoadBreadCrumbs', 'RelatedSearchInit', 'RelatedPaginateInit', - 'ReturnToCaller', 'ClearScope', 'InventoryList', 'CredentialList', 'ProjectList', 'LookUpInit', 'PromptPasswords', - 'GetBasePath', 'md5Setup', 'FormatDate', 'JobStatusToolTip', 'Wait', 'Empty', 'ParseVariableString', 'GetChoices', - 'LoadDialogPartial' -]; - +JobsListController.$inject = ['$rootScope', '$log', '$scope', '$compile', '$routeParams', 'ClearScope', 'Breadcrumbs', 'LoadBreadCrumbs', 'LoadSchedulesScope', 'LoadJobsScope', 'RunningJobsList', 'CompletedJobsList', + 'QueuedJobsList', 'ScheduledJobsList', 'GetChoices', 'GetBasePath', 'Wait', 'Socket']; \ No newline at end of file diff --git a/awx/ui/static/js/controllers/Projects.js b/awx/ui/static/js/controllers/Projects.js index ba881e5744..b1619b515a 100644 --- a/awx/ui/static/js/controllers/Projects.js +++ b/awx/ui/static/js/controllers/Projects.js @@ -63,7 +63,7 @@ function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest, if (type.value === project.scm_type) { $scope.projects[i].scm_type = type.label; if (type.label === 'Manual') { - $scope.projects[i].scm_update_tooltip = 'Manaul projects do not require an SCM update'; + $scope.projects[i].scm_update_tooltip = 'Manual projects do not require an SCM update'; $scope.projects[i].scm_schedule_tooltip = 'Manual projects do not require a schedule'; $scope.projects[i].scm_type_class = 'btn-disabled'; $scope.projects[i].statusTip = 'Not configured for SCM'; @@ -284,7 +284,7 @@ function ProjectsList ($scope, $rootScope, $location, $log, $routeParams, Rest, Rest.setUrl(url); Rest.post() .success(function () { - Alert('SCM Update Cancel', 'Your request to cancel the update was submitted to the task maanger.', 'alert-info'); + Alert('SCM Update Cancel', 'Your request to cancel the update was submitted to the task manager.', 'alert-info'); $scope.refresh(); }) .error(function (data, status) { diff --git a/awx/ui/static/js/helpers/JobSubmission.js b/awx/ui/static/js/helpers/JobSubmission.js index c829467826..3e0670bb66 100644 --- a/awx/ui/static/js/helpers/JobSubmission.js +++ b/awx/ui/static/js/helpers/JobSubmission.js @@ -551,15 +551,14 @@ function($location, Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialLi } scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function() { // Refresh the project list after update request submitted - if (scope.refreshJobs) { - Wait('stop'); + Wait('stop'); + if (scope.socketStatus === 'error') { Alert('Update Started', 'The request to start the SCM update process was submitted. ' + - 'To monitor the update status, refresh the page by clicking the button.', 'alert-info'); - scope.refreshJobs(); + 'To monitor the update status, refresh the page by clicking the button.', 'alert-info'); + if (scope.refresh) { + scope.refresh(); + } } - //else if (scope.refresh) { - // scope.refresh(); - //} }); if (scope.removePromptForPasswords) { @@ -619,8 +618,17 @@ function($location, Wait, GetBasePath, LookUpInit, JobTemplateForm, CredentialLi } scope.removeUpdateSubmitted = scope.$on('UpdateSubmitted', function () { Wait('stop'); - // No need to do anything else. The caller should be connected to the socket server and - // handling real-time updates + if (scope.socketStatus === 'error') { + Alert('Sync Started', 'The request to start the inventory sync process was submitted. ' + + 'To monitor the status refresh the page by clicking the button.', 'alert-info'); + if (scope.refreshGroups) { + // inventory detail page + scope.refreshGroups(); + } + else if (scope.refresh) { + scope.refresh(); + } + } }); if (scope.removePromptForPasswords) { diff --git a/awx/ui/static/lib/ansible/Socket.js b/awx/ui/static/lib/ansible/Socket.js index 02c6fc2393..cb550f38fd 100644 --- a/awx/ui/static/lib/ansible/Socket.js +++ b/awx/ui/static/lib/ansible/Socket.js @@ -127,15 +127,12 @@ angular.module('SocketIO', ['AuthService', 'Utilities']) // Check connection status var self = this; if (self.socket.socket.connected) { - $log.debug('Socket connected'); self.scope.socketStatus = 'ok'; } else if (self.socket.socket.connecting || self.socket.socket.reconnecting) { - $log.debug('Socket connecting...'); self.scope.socketStatus = 'connecting'; } else { - $log.debug('Socket error: connection refused'); self.scope.socketStatus = 'error'; } self.scope.socketTip = getSocketTip(self.scope.socketStatus); diff --git a/awx/ui/static/lib/ansible/Utilities.js b/awx/ui/static/lib/ansible/Utilities.js index 5afd01eaa3..e2ec43225c 100644 --- a/awx/ui/static/lib/ansible/Utilities.js +++ b/awx/ui/static/lib/ansible/Utilities.js @@ -16,30 +16,30 @@ angular.module('Utilities', ['RestServices', 'Utilities']) * Place to remove things that might be lingering from a prior tab or view. * This used to destroy the scope, but that causes issues in angular 1.2.x */ -.factory('ClearScope', [ - function () { - return function () { +.factory('ClearScope', [ '$rootScope', function ($rootScope) { + return function () { - $('#form-modal .modal-body').empty(); - $('#form-modal2 .modal-body').empty(); + $rootScope.flashMessage = null; - $('.tooltip').each(function () { - $(this).remove(); - }); + $('#form-modal .modal-body').empty(); + $('#form-modal2 .modal-body').empty(); - $('.popover').each(function () { - $(this).remove(); - }); + $('.tooltip').each(function () { + $(this).remove(); + }); - try { - $('#help-modal').dialog('close'); - } catch (e) { - // ignore - } - $(window).unbind('resize'); - }; - } -]) + $('.popover').each(function () { + $(this).remove(); + }); + + try { + $('#help-modal').dialog('close'); + } catch (e) { + // ignore + } + $(window).unbind('resize'); + }; +}]) /* Empty() diff --git a/awx/ui/static/lib/socket.io-client/dist/socket.io.js b/awx/ui/static/lib/socket.io-client/dist/socket.io.js index 9e11e3ef92..88ac893599 100644 --- a/awx/ui/static/lib/socket.io-client/dist/socket.io.js +++ b/awx/ui/static/lib/socket.io-client/dist/socket.io.js @@ -1651,7 +1651,7 @@ var io = ('undefined' === typeof module ? {} : module.exports); } else if (xhr.status == 403) { self.onError(xhr.responseText); } else { - self.connecting = false; + self.connecting = false; !self.reconnecting && self.onError(xhr.responseText); } } @@ -1695,7 +1695,7 @@ var io = ('undefined' === typeof module ? {} : module.exports); var self = this; self.connecting = true; - + this.handshake(function (sid, heartbeat, close, transports) { self.sessionid = sid; self.closeTimeout = close * 1000; @@ -1817,7 +1817,7 @@ var io = ('undefined' === typeof module ? {} : module.exports); this.transport.payload(this.buffer); this.buffer = []; }; - + /** * Disconnect the established connect. @@ -1877,7 +1877,7 @@ var io = ('undefined' === typeof module ? {} : module.exports); var port = global.location.port || ('https:' == global.location.protocol ? 443 : 80); - return this.options.host !== global.location.hostname + return this.options.host !== global.location.hostname || this.options.port != port; }; @@ -2158,7 +2158,7 @@ var io = ('undefined' === typeof module ? {} : module.exports); * * @api public */ - + SocketNamespace.prototype.emit = function (name) { var args = Array.prototype.slice.call(arguments, 1) , lastArg = args[args.length - 1] @@ -2396,8 +2396,8 @@ var io = ('undefined' === typeof module ? {} : module.exports); * @api public */ - // Do to a bug in the current IDevices browser, we need to wrap the send in a - // setTimeout, when they resume from sleeping the browser will crash if + // Do to a bug in the current IDevices browser, we need to wrap the send in a + // setTimeout, when they resume from sleeping the browser will crash if // we don't allow the browser time to detect the socket has been closed if (io.util.ua.iDevice) { WS.prototype.send = function (data) { @@ -2543,8 +2543,8 @@ var io = ('undefined' === typeof module ? {} : module.exports); Flashsocket.prototype.name = 'flashsocket'; /** - * Disconnect the established `FlashSocket` connection. This is done by adding a - * new task to the FlashSocket. The rest will be handled off by the `WebSocket` + * Disconnect the established `FlashSocket` connection. This is done by adding a + * new task to the FlashSocket. The rest will be handled off by the `WebSocket` * transport. * * @returns {Transport} @@ -2560,10 +2560,10 @@ var io = ('undefined' === typeof module ? {} : module.exports); }); return this; }; - + /** * Sends a message to the Socket.IO server. This is done by adding a new - * task to the FlashSocket. The rest will be handled off by the `WebSocket` + * task to the FlashSocket. The rest will be handled off by the `WebSocket` * transport. * * @returns {Transport} @@ -2657,7 +2657,7 @@ var io = ('undefined' === typeof module ? {} : module.exports); }; /** - * Check if the FlashSocket transport can be used as cross domain / cross origin + * Check if the FlashSocket transport can be used as cross domain / cross origin * transport. Because we can't see which type (secure or insecure) of .swf is used * we will just return true. * @@ -2688,8 +2688,8 @@ var io = ('undefined' === typeof module ? {} : module.exports); 'undefined' != typeof io ? io.Transport : module.exports , 'undefined' != typeof io ? io : module.parent.exports ); -/* SWFObject v2.2 - is released under the MIT License +/* SWFObject v2.2 + is released under the MIT License */ if ('undefined' != typeof window) { var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="ShockwaveFlash.ShockwaveFlash",q="application/x-shockwave-flash",R="SWFObjectExprInst",x="onreadystatechange",O=window,j=document,t=navigator,T=false,U=[h],o=[],N=[],I=[],l,Q,E,B,J=false,a=false,n,G,m=true,M=function(){var aa=typeof j.getElementById!=D&&typeof j.getElementsByTagName!=D&&typeof j.createElement!=D,ah=t.userAgent.toLowerCase(),Y=t.platform.toLowerCase(),ae=Y?/win/.test(Y):/win/.test(ah),ac=Y?/mac/.test(Y):/mac/.test(ah),af=/webkit/.test(ah)?parseFloat(ah.replace(/^.*webkit\/(\d+(\.\d+)?).*$/,"$1")):false,X=!+"\v1",ag=[0,0,0],ab=null;if(typeof t.plugins!=D&&typeof t.plugins[S]==r){ab=t.plugins[S].description;if(ab&&!(typeof t.mimeTypes!=D&&t.mimeTypes[q]&&!t.mimeTypes[q].enabledPlugin)){T=true;X=false;ab=ab.replace(/^.*\s+(\S+\s+\S+$)/,"$1");ag[0]=parseInt(ab.replace(/^(.*)\..*$/,"$1"),10);ag[1]=parseInt(ab.replace(/^.*\.(.*)\s.*$/,"$1"),10);ag[2]=/[a-zA-Z]/.test(ab)?parseInt(ab.replace(/^.*[a-zA-Z]+(.*)$/,"$1"),10):0}}else{if(typeof O[(['Active'].concat('Object').join('X'))]!=D){try{var ad=new window[(['Active'].concat('Object').join('X'))](W);if(ad){ab=ad.GetVariable("$version");if(ab){X=true;ab=ab.split(" ")[1].split(",");ag=[parseInt(ab[0],10),parseInt(ab[1],10),parseInt(ab[2],10)]}}}catch(Z){}}}return{w3:aa,pv:ag,wk:af,ie:X,win:ae,mac:ac}}(),k=function(){if(!M.w3){return}if((typeof j.readyState!=D&&j.readyState=="complete")||(typeof j.readyState==D&&(j.getElementsByTagName("body")[0]||j.body))){f()}if(!J){if(typeof j.addEventListener!=D){j.addEventListener("DOMContentLoaded",f,false)}if(M.ie&&M.win){j.attachEvent(x,function(){if(j.readyState=="complete"){j.detachEvent(x,arguments.callee);f()}});if(O==top){(function(){if(J){return}try{j.documentElement.doScroll("left")}catch(X){setTimeout(arguments.callee,0);return}f()})()}}if(M.wk){(function(){if(J){return}if(!/loaded|complete/.test(j.readyState)){setTimeout(arguments.callee,0);return}f()})()}s(f)}}();function f(){if(J){return}try{var Z=j.getElementsByTagName("body")[0].appendChild(C("span"));Z.parentNode.removeChild(Z)}catch(aa){return}J=true;var X=U.length;for(var Y=0;Y0){for(var af=0;af0){var ae=c(Y);if(ae){if(F(o[af].swfVersion)&&!(M.wk&&M.wk<312)){w(Y,true);if(ab){aa.success=true;aa.ref=z(Y);ab(aa)}}else{if(o[af].expressInstall&&A()){var ai={};ai.data=o[af].expressInstall;ai.width=ae.getAttribute("width")||"0";ai.height=ae.getAttribute("height")||"0";if(ae.getAttribute("class")){ai.styleclass=ae.getAttribute("class")}if(ae.getAttribute("align")){ai.align=ae.getAttribute("align")}var ah={};var X=ae.getElementsByTagName("param");var ac=X.length;for(var ad=0;ad'}}aa.outerHTML='"+af+"";N[N.length]=ai.id;X=c(ai.id)}else{var Z=C(r);Z.setAttribute("type",q);for(var ac in ai){if(ai[ac]!=Object.prototype[ac]){if(ac.toLowerCase()=="styleclass"){Z.setAttribute("class",ai[ac])}else{if(ac.toLowerCase()!="classid"){Z.setAttribute(ac,ai[ac])}}}}for(var ab in ag){if(ag[ab]!=Object.prototype[ab]&&ab.toLowerCase()!="movie"){e(Z,ab,ag[ab])}}aa.parentNode.replaceChild(Z,aa);X=Z}}return X}function e(Z,X,Y){var aa=C("param");aa.setAttribute("name",X);aa.setAttribute("value",Y);Z.appendChild(aa)}function y(Y){var X=c(Y);if(X&&X.nodeName=="OBJECT"){if(M.ie&&M.win){X.style.display="none";(function(){if(X.readyState==4){b(Y)}else{setTimeout(arguments.callee,10)}})()}else{X.parentNode.removeChild(X)}}}function b(Z){var Y=c(Z);if(Y){for(var X in Y){if(typeof Y[X]=="function"){Y[X]=null}}Y.parentNode.removeChild(Y)}}function c(Z){var X=null;try{X=j.getElementById(Z)}catch(Y){}return X}function C(X){return j.createElement(X)}function i(Z,X,Y){Z.attachEvent(X,Y);I[I.length]=[Z,X,Y]}function F(Z){var Y=M.pv,X=Z.split(".");X[0]=parseInt(X[0],10);X[1]=parseInt(X[1],10)||0;X[2]=parseInt(X[2],10)||0;return(Y[0]>X[0]||(Y[0]==X[0]&&Y[1]>X[1])||(Y[0]==X[0]&&Y[1]==X[1]&&Y[2]>=X[2]))?true:false}function v(ac,Y,ad,ab){if(M.ie&&M.mac){return}var aa=j.getElementsByTagName("head")[0];if(!aa){return}var X=(ad&&typeof ad=="string")?ad:"screen";if(ab){n=null;G=null}if(!n||G!=X){var Z=C("style");Z.setAttribute("type","text/css");Z.setAttribute("media",X);n=aa.appendChild(Z);if(M.ie&&M.win&&typeof j.styleSheets!=D&&j.styleSheets.length>0){n=j.styleSheets[j.styleSheets.length-1]}G=X}if(M.ie&&M.win){if(n&&typeof n.addRule==r){n.addRule(ac,Y)}}else{if(n&&typeof j.createTextNode!=D){n.appendChild(j.createTextNode(ac+" {"+Y+"}"))}}}function w(Z,X){if(!m){return}var Y=X?"visible":"hidden";if(J&&c(Z)){c(Z).style.visibility=Y}else{v("#"+Z,"visibility:"+Y)}}function L(Y){var Z=/[\\\"<>\.;]/;var X=Z.exec(Y)!=null;return X&&typeof encodeURIComponent!=D?encodeURIComponent(Y):Y}var d=function(){if(M.ie&&M.win){window.attachEvent("onunload",function(){var ac=I.length;for(var ab=0;ab= 10.0.0 is required."); return; @@ -2846,7 +2846,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho if ("protocol" in flashEvent) { this.protocol = flashEvent.protocol; } - + var jsEvent; if (flashEvent.type == "open" || flashEvent.type == "error") { jsEvent = this.__createSimpleEvent(flashEvent.type); @@ -2859,10 +2859,10 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho } else { throw "unknown event type: " + flashEvent.type; } - + this.dispatchEvent(jsEvent); }; - + WebSocket.prototype.__createSimpleEvent = function(type) { if (document.createEvent && window.Event) { var event = document.createEvent("Event"); @@ -2872,7 +2872,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho return {type: type, bubbles: false, cancelable: false}; } }; - + WebSocket.prototype.__createMessageEvent = function(type, data) { if (document.createEvent && window.MessageEvent && !window.opera) { var event = document.createEvent("MessageEvent"); @@ -2883,7 +2883,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho return {type: type, data: data, bubbles: false, cancelable: false}; } }; - + /** * Define the WebSocket readyState enumeration. */ @@ -2896,7 +2896,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho WebSocket.__instances = {}; WebSocket.__tasks = []; WebSocket.__nextId = 0; - + /** * Load a new flash security policy file. * @param {string} url @@ -2912,7 +2912,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho */ WebSocket.__initialize = function() { if (WebSocket.__flash) return; - + if (WebSocket.__swfLocation) { // For backword compatibility. window.WEB_SOCKET_SWF_LOCATION = WebSocket.__swfLocation; @@ -2958,7 +2958,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho } }); }; - + /** * Called by Flash to notify JS that it's fully loaded and ready * for communication. @@ -2976,7 +2976,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho WebSocket.__tasks = []; }, 0); }; - + /** * Called by Flash to notify WebSockets events are fired. */ @@ -2996,17 +2996,17 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho }, 0); return true; }; - + // Called by Flash. WebSocket.__log = function(message) { console.log(decodeURIComponent(message)); }; - + // Called by Flash. WebSocket.__error = function(message) { console.error(decodeURIComponent(message)); }; - + WebSocket.__addTask = function(task) { if (WebSocket.__flash) { task(); @@ -3014,7 +3014,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho WebSocket.__tasks.push(task); } }; - + /** * Test if the browser is running flash lite. * @return {boolean} True if flash lite is running, false otherwise. @@ -3029,7 +3029,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho } return mimeType.enabledPlugin.filename.match(/flashlite/i) ? true : false; }; - + if (!window.WEB_SOCKET_DISABLE_AUTO_INITIALIZATION) { if (window.addEventListener) { window.addEventListener("load", function(){ @@ -3041,7 +3041,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho }); } } - + })(); /** @@ -3276,7 +3276,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho /** * The HTMLFile transport creates a `forever iframe` based transport - * for Internet Explorer. Regular forever iframe implementations will + * for Internet Explorer. Regular forever iframe implementations will * continuously trigger the browsers buzy indicators. If the forever iframe * is created inside a `htmlfile` these indicators will not be trigged. * @@ -3490,7 +3490,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho return false; }; - /** + /** * Establish a connection, for iPhone and Android this will be done once the page * is loaded. * @@ -3542,7 +3542,7 @@ var swfobject=function(){var D="undefined",r="object",S="Shockwave Flash",W="Sho function onerror () { self.retryCounter ++; if(!self.retryCounter || self.retryCounter > 3) { - self.onClose(); + self.onClose(); } else { self.get(); } diff --git a/awx/ui/templates/ui/index.html b/awx/ui/templates/ui/index.html index 70922a6a98..bb77e5a131 100644 --- a/awx/ui/templates/ui/index.html +++ b/awx/ui/templates/ui/index.html @@ -436,7 +436,7 @@ - +