1%{--
2  - Copyright 2016 SimplifyOps, Inc. (http://simplifyops.com)
3  -
4  - Licensed under the Apache License, Version 2.0 (the "License");
5  - you may not use this file except in compliance with the License.
6  - You may obtain a copy of the License at
7  -
8  -     http://www.apache.org/licenses/LICENSE-2.0
9  -
10  - Unless required by applicable law or agreed to in writing, software
11  - distributed under the License is distributed on an "AS IS" BASIS,
12  - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  - See the License for the specific language governing permissions and
14  - limitations under the License.
15  --}%
16
17<%@ page import="org.rundeck.core.auth.AuthConstants; grails.util.Environment; rundeck.Execution; rundeck.ScheduledExecution" %>
18
19<html>
20  <head>
21    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
22    <meta name="tabpage" content="events"/>
23    <meta name="layout" content="base" />
24    <meta name="skipPrototypeJs" content="base" />
25
26    <title><g:appTitle/> -
27    %{--      <g:if test="${null==execution?.dateCompleted}"><g:message code="now.running" /> - </g:if>--}%
28      <g:if test="${scheduledExecution}"><g:enc>${scheduledExecution?.jobName}</g:enc> :  </g:if>
29      <g:else><g:message code="execution.type.adhoc.title" /></g:else> <g:message code="execution.at.time.by.user" args="[g.relativeDateString(atDate:execution.dateStarted),execution.user]"/>
30    </title>
31      <g:set var="followmode" value="${params.mode in ['browse','tail','node']?params.mode:'tail'}"/>
32      <g:set var="authKeys" value="${[AuthConstants.ACTION_KILL,
33                                      AuthConstants.ACTION_READ, AuthConstants.ACTION_VIEW, AuthConstants.ACTION_CREATE, AuthConstants.ACTION_RUN]}"/>
34      <g:set var="authChecks" value="${[:]}"/>
35      <g:each in="${authKeys}" var="actionName">
36      <g:if test="${execution.scheduledExecution}">
37          <%-- set auth values --%>
38          %{
39              authChecks[actionName]=auth.jobAllowedTest(job:execution.scheduledExecution,action: actionName)
40          }%
41      </g:if>
42      <g:else>
43          %{
44              authChecks[actionName] = auth.adhocAllowedTest(action: actionName,project:execution.project)
45          }%
46      </g:else>
47      </g:each>
48      <g:set var="adhocRunAllowed" value="${auth.adhocAllowedTest(action: AuthConstants.ACTION_RUN,project:execution.project)}"/>
49      <g:set var="projAdminAuth" value="${auth.resourceAllowedTest(context: AuthConstants.CTX_APPLICATION, type: AuthConstants.TYPE_PROJECT, name: params.project, action: [AuthConstants.ACTION_ADMIN, AuthConstants.ACTION_APP_ADMIN])}"/>
50      <g:set var="deleteExecAuth" value="${auth.resourceAllowedTest(context: AuthConstants.CTX_APPLICATION, type: AuthConstants.TYPE_PROJECT, name: params.project, action: AuthConstants.ACTION_DELETE_EXECUTION) || projAdminAuth}"/>
51
52      <g:set var="defaultLastLines" value="${grailsApplication.config.rundeck.gui.execution.tail.lines.default}"/>
53      <g:set var="maxLastLines" value="${grailsApplication.config.rundeck.gui.execution.tail.lines.max}"/>
54
55      <asset:javascript src="execution/show.js"/>
56
57      <g:embedJSON id="execInfoJSON" data="${[jobId:scheduledExecution?.extid,execId:execution.id]}"/>
58      <g:embedJSON id="jobDetail"
59                   data="${[id: scheduledExecution?.extid, name: scheduledExecution?.jobName, group: scheduledExecution?.groupPath,
60                            project: params.project ?: request.project]}"/>
61      <g:embedJSON id="workflowDataJSON" data="${workflowTree}"/>
62      <g:embedJSON id="nodeStepPluginsJSON" data="${stepPluginDescriptions.node.collectEntries { [(it.key): [title: it.value.title]] }}"/>
63      <g:embedJSON id="wfStepPluginsJSON" data="${stepPluginDescriptions.workflow.collectEntries { [(it.key): [title: it.value.title]] }}"/>
64      <g:if test="${grails.util.Environment.current==grails.util.Environment.DEVELOPMENT}">
65          <asset:javascript src="workflow.test.js"/>
66          <asset:javascript src="util/compactMapList.test.js"/>
67      </g:if>
68      <g:jsMessages codes="['execution.show.mode.Log.title','execution.page.show.tab.Nodes.title']"/>
69
70      <asset:stylesheet href="static/css/pages/project-dashboard.css"/>
71      <g:jsMessages code="jobslist.date.format.ko,select.all,select.none,delete.selected.executions,cancel.bulk.delete,cancel,close,all,bulk.delete,running"/>
72      <g:jsMessages code="search.ellipsis
73jobquery.title.titleFilter
74jobquery.title.jobFilter
75jobquery.title.jobIdFilter
76jobquery.title.userFilter
77jobquery.title.statFilter
78jobquery.title.filter
79jobquery.title.recentFilter
80jobquery.title.startbeforeFilter
81jobquery.title.startafterFilter
82jobquery.title.endbeforeFilter
83jobquery.title.endafterFilter
84saved.filters
85search
86"/>
87      <style type="text/css">
88        #log{
89            margin-bottom:20px;
90        }
91        .padded{
92            padding: 10px;
93        }
94        .errmsg {
95            color: gray;
96        }
97        .executionshow.affix:before,
98        .executionshow.affix:after {
99            content: " ";
100            display: table;
101        }
102        .executionshow.affix:after {
103            clear: both;
104        }
105        .executionshow .runoutput {
106            display: none;
107        }
108        .executionshow.affix .runoutput {
109            display: block;
110        }
111        .executionshow.affix {
112            top: 0;
113            width: 80%;
114            z-index: 1;
115            margin-right: auto;
116            margin-left: auto;
117            padding-left: 15px;
118            padding-right: 15px;
119            padding-top: 20px;
120            padding-bottom: 10px;
121            border-bottom: 1px solid #eeeeee;
122        }
123        .executionshow.affix.panel-heading-affix {
124            background-color: #eeeeee;
125            width: auto;
126            margin: 0 15px;
127            padding: 8px 10px;
128        }
129        .affixed-shown {
130            display: none;
131        }
132        .affix .affixed-shown {
133            display: block;
134            margin-top: 0px;
135            margin-left: 15px;
136        }
137        .affix .affixed-shown.affixed-shown-inline {
138            display: inline;
139        }
140      </style>
141      <g:set var="projectName" value="${execution.project}"/>
142      <g:javascript>
143    var execInfo=loadJsonData('execInfoJSON');
144    window._rundeck = Object.assign(window._rundeck || {}, {
145        data:{
146            projectAdminAuth:${enc(js:projAdminAuth)},
147            deleteExecAuth:${enc(js:deleteExecAuth)},
148            jobslistDateFormatMoment:"${enc(js:g.message(code:'jobslist.date.format.ko'))}",
149            runningDateFormatMoment:"${enc(js:g.message(code:'jobslist.running.format.ko'))}",
150            activityUrl: appLinks.reportsEventsAjax,
151            nowrunningUrl: "${createLink(uri:"/api/${com.dtolabs.rundeck.app.api.ApiVersions.API_CURRENT_VERSION}/project/${projectName}/executions/running")}",
152            bulkDeleteUrl: appLinks.apiExecutionsBulkDelete,
153            activityPageHref:"${enc(js:createLink(controller:'reports',action:'index',params:[project:projectName]))}",
154            sinceUpdatedUrl:"${enc(js:g.createLink(controller:'reports',action: 'since.json', params: [project:projectName]))}",
155            filterListUrl:"${enc(js:g.createLink(controller:'reports',action: 'listFiltersAjax', params: [project:projectName]))}",
156            filterSaveUrl:"${enc(js:g.createLink(controller:'reports',action: 'saveFilterAjax', params: [project:projectName]))}",
157            filterDeleteUrl:"${enc(js:g.createLink(controller:'reports',action: 'deleteFilterAjax', params: [project:projectName]))}",
158            pagination:{
159                max: ${enc(js:params.max?params.int('max',10):10)}
160          },
161          query:{
162              jobIdFilter:execInfo.jobId
163            },
164            filterOpts: {
165                showFilter: false,
166                showRecentFilter: true,
167                showSavedFilter: false
168            },
169            runningOpts: {
170                loadRunning:false,
171                allowAutoRefresh: false
172            }
173    }
174})
175      </g:javascript>
176      <asset:javascript src="static/pages/project-activity.js" defer="defer"/>
177
178      <asset:stylesheet href="static/css/chunk-vendors.css"/>
179      <asset:stylesheet href="static/css/pages/execution-show.css"/>
180      <asset:javascript src="static/pages/execution-show.js" defer="defer"/>
181  </head>
182  <g:set var="isAdhoc" value="${!scheduledExecution && execution.workflow.commands.size() == 1}"/>
183  <body id="executionShowPage">
184
185
186    <div class="content">
187    <div id="layoutBody">
188        <div class="container-fluid">
189
190              <nav id="subtitlebar" class=" subtitlebar has-content execution-page">
191                <div class="subtitle-head flex-container reverse flex-align-items-stretch" data-ko-bind="nodeflow">
192                    <div class="subtitle-head-item execution-head-info flex-item-1">
193                        <section class="flex-container reverse">
194                            <section class="flex-item-1 text-right">
195                            <div style="display:inline-block;vertical-align:bottom;margin-right:.3em;">
196                              <g:render template="/scheduledExecution/showExecutionLink"
197                                          model="[scheduledExecution: scheduledExecution,
198                                                  linkCss           : 'text-h4',
199                                                  noimgs            : true,
200                                                  execution         : execution,
201                                                  hideExecStatus    : true,
202                                                  followparams      : [mode: followmode, lastlines: params.lastlines]
203                                          ]"/>
204                            </div>
205
206                                <g:if test="${deleteExecAuth || authChecks[AuthConstants.ACTION_READ]}">
207                                    <div class="btn-group" data-bind="visible: completed()">
208                                        <button type="button"
209                                                class="btn btn-default btn-sm dropdown-toggle"
210                                                data-toggle="dropdown"
211                                                aria-expanded="false">
212                                            <i class="glyphicon glyphicon-list"></i>
213                                            <span class="caret"></span>
214                                        </button>
215                                        <ul class="dropdown-menu dropdown-menu-right" role="menu">
216                                            <g:if test="${eprev}">
217                                              <li>
218                                                <g:link action="show" controller="execution" id="${eprev.id}"
219                                                      params="[project: eprev.project]"
220                                                      title="Previous Execution #${eprev.id}">
221                                                  <i class="glyphicon glyphicon-arrow-left"></i>
222                                                  <g:message code="previous.execution"/>
223                                                </g:link>
224                                              </li>
225                                            </g:if>
226
227                                            <g:if test="${enext}">
228                                              <li>
229                                                <g:link action="show" controller="execution"
230                                                    title="Next Execution #${enext.id}"
231                                                    params="[project: enext.project]"
232                                                    id="${enext.id}">
233                                                    <i class="glyphicon glyphicon-arrow-right"></i>
234                                                    <g:message code="next.execution"/>
235                                                </g:link>
236                                              </li>
237                                            </g:if>
238                                            <g:if test="${deleteExecAuth}">
239                                                <li>
240                                                    <a href="#execdelete"
241                                                      data-toggle="modal">
242                                                        <i class="fas fa-trash"></i>
243                                                        <g:message code="button.action.delete.this.execution"/>
244                                                    </a>
245                                                </li>
246                                            </g:if>
247
248                                            <g:if test="${authChecks[AuthConstants.ACTION_READ]}">
249                                                <li class="divider  ">
250
251                                                </li>
252                                                <li>
253                                                    <a type="button" href="#details_modal" data-toggle="modal">
254                                                        <g:icon name="info-sign"/>
255                                                        <g:message code="definition"/>
256                                                    </a>
257                                                </li>
258
259                                            </g:if>
260                                        </ul>
261                                    </div>
262                                </g:if>
263
264
265
266
267                            </section>
268                            <section class="flex-item-2">
269
270                                <section class="section-space">
271
272                                    %{-- end of ifScheduledExecutions --}%
273                                    <tmpl:wfstateSummaryLine/>
274
275                                </section>
276
277
278                                <g:if test="${execution.retryAttempt}">
279                                    <section class="text-secondary section-space">
280                                        <i class="glyphicon glyphicon-repeat"></i>
281                                        <g:message code="execution.retry.info.label"
282                                                  args="${[execution.retryAttempt, execution.retry]}"/>
283                                    </section>
284                                </g:if>
285                            </section>
286                        </section>
287
288                        <section class="section-space execution-action-links " style="padding-top:.6em;">
289
290                                <g:if test="${null == execution.dateCompleted}">
291                                    <span data-bind="if: canKillExec()">
292                                        <span data-bind="visible: !completed() ">
293                                            <!-- ko if: !killRequested() || killStatusFailed() -->
294                                            <span class="btn btn-sm btn-danger pull-right"
295                                                  data-bind="click: killExecAction">
296                                                <g:message code="button.action.kill.job"/>
297                                                <i class="glyphicon glyphicon-remove"></i>
298                                            </span>
299                                            <!-- /ko -->
300                                            <!-- ko if: killRequested() -->
301                                            <!-- ko if: killStatusPending() -->
302                                            <g:img class="loading-spinner" file="spinner-gray.gif" width="16px"
303                                                  height="16px"/>
304                                            <!-- /ko -->
305                                            <span class="loading" data-bind="text: killStatusText"></span>
306                                            <!-- /ko -->
307                                            <!-- ko if: killedbutNotSaved() -->
308                                            <span class="btn btn-danger btn-xs pull-right"
309                                                  data-bind="click: markExecAction">
310                                                <g:message code="button.action.incomplete.job" default="Mark as Incomplete"/>
311                                                <i class="glyphicon glyphicon-remove"></i>
312                                            </span>
313                                            <!-- /ko -->
314                                        </span>
315                                    </span>
316                                </g:if>
317                            <g:if test="${scheduledExecution}">
318                                    <g:if test="${authChecks[AuthConstants.ACTION_RUN] && g.executionMode(
319                                            active: true,
320                                            project: execution.project
321                                    )}">
322                                    %{--Run again link--}%
323                                        <g:link controller="scheduledExecution"
324                                                action="execute"
325                                                id="${scheduledExecution.extid}"
326                                                class=" pull-right"
327                                                params="${[retryExecId: execution.id, project: execution.project]}"
328                                                title="${g.message(code: 'execution.job.action.runAgain')}"
329                                                style="${wdgt.styleVisible(
330                                                        if: null != execution.dateCompleted &&
331                                                            null ==
332                                                            execution.failedNodeList
333                                                )};"
334                                                data-bind="visible: completed() && !failed()">
335                                            <g:message code="execution.action.runAgain"/>
336                                            <i class="fas fa-redo-alt"></i>
337                                        </g:link>
338                                    %{--Run again and retry failed links in a dropdown --}%
339                                        <div class="btn-group pull-right"
340                                            style="${wdgt.styleVisible(
341                                                    if: null != execution.dateCompleted &&
342                                                        null !=
343                                                        execution.failedNodeList
344                                            )};"
345                                            data-bind="visible: failed()">
346                                            <button class="btn btn-default btn-sm dropdown-toggle"
347                                                    data-target="#"
348                                                    data-toggle="dropdown">
349                                                <g:message code="execution.action.runAgain.ellipsis"/>
350                                                <i class="caret"></i>
351                                            </button>
352                                            <ul class="dropdown-menu pull-left" role="menu">
353                                                <li class="retrybuttons">
354                                                    <g:link controller="scheduledExecution"
355                                                            action="execute"
356                                                            id="${scheduledExecution.extid}"
357                                                            params="${[retryExecId: execution.id, project: execution.project]}"
358                                                            title="${g.message(code: 'execution.job.action.runAgain')}"
359                                                            data-bind="visible: completed()">
360                                                        <b class="glyphicon glyphicon-play"></b>
361
362
363                                                        <g:message code="execution.action.runAgain"/>
364                                                    </g:link>
365                                                </li>
366                                                <li class="divider">
367
368                                                </li>
369                                                <li class="retrybuttons">
370                                                    <g:link controller="scheduledExecution" action="execute"
371                                                            id="${scheduledExecution.extid}"
372                                                            params="${[retryFailedExecId: execution.id, project: execution.project]}"
373                                                            title="${g.message(code: 'retry.job.failed.nodes')}">
374                                                        <b class="glyphicon glyphicon-play"></b>
375                                                        <g:message code="retry.failed.nodes"/>
376                                                    </g:link>
377                                                </li>
378
379                                            %{--                              todo extra actions--}%
380
381                                                <g:ifMenuItems type="EXECUTION_RETRY"  project="${params.project}" execution="${execution.id.toString()}">
382                                                    <li role="separator" class="divider"></li>
383                                                    <g:forMenuItems type="EXECUTION_RETRY" var="item"  project="${params.project}" execution="${execution.id.toString()}">
384                                                        <li>
385                                                            <a href="${enc(attr:item.getExecutionHref(params.project, execution.id.toString()))}"
386                                                              title="${enc(attr:g.message(code:item.titleCode,default:item.title))}">
387                                                                <span class="sidebar-mini"><i class="${enc(attr: item.iconCSS ?: 'fas fa-plug')}"></i></span>
388                                                                <span class="sidebar-normal">
389                                                                    <g:message code="${item.titleCode}" default="${item.title}"/>
390                                                                </span>
391                                                            </a>
392                                                        </li>
393                                                    </g:forMenuItems>
394                                                </g:ifMenuItems>
395                                            </ul>
396                                        </div>
397
398                                    </g:if>
399
400                                </g:if>
401                                <g:if test="${isAdhoc}">
402                                %{--run again links--}%
403                                    <g:if test="${adhocRunAllowed && g.executionMode(
404                                            active: true,
405                                            project: execution.project
406                                    )}">
407                                    %{--run again only--}%
408                                        <g:link
409                                                controller="framework"
410                                                action="adhoc"
411                                                params="${[fromExecId: execution.id, project: execution.project]}"
412                                                title="${g.message(code: 'execution.action.runAgain')}"
413                                                class="  pull-right"
414                                                style="${wdgt.styleVisible(
415                                                        if: null != execution.dateCompleted &&
416                                                            null ==
417                                                            execution.failedNodeList
418                                                )}"
419                                                data-bind="visible: completed() && !failed()">
420
421                                            <g:message code="execution.action.runAgain"/>
422                                            <i class="fas fa-redo-alt"></i>
423                                        </g:link>
424                                    %{--run again and retry failed --}%
425                                        <div class="btn-group pull-right"
426                                            style="${wdgt.styleVisible(
427                                                    if: null != execution.dateCompleted &&
428                                                        null !=
429                                                        execution.failedNodeList
430                                            )}"
431                                            data-bind="visible: failed()">
432                                            <button class="btn btn-default btn-sm dropdown-toggle "
433                                                    data-target="#"
434                                                    data-toggle="dropdown">
435                                                <g:message code="execution.action.runAgain.ellipsis"/>
436                                                <i class="caret"></i>
437                                            </button>
438                                            <ul class="dropdown-menu pull-right" role="menu">
439
440                                                <li>
441                                                    <g:link
442                                                            controller="framework"
443                                                            action="adhoc"
444                                                            params="${[fromExecId: execution.id, project: execution.project]}"
445                                                            title="${g.message(code: 'execution.action.runAgain')}">
446
447                                                        <b class="glyphicon glyphicon-play"></b>
448                                                        <g:message code="execution.action.runAgain"/>&hellip;
449                                                    </g:link>
450                                                </li>
451                                                <li class="divider  ">
452
453                                                </li>
454                                                <li>
455                                                    <g:link
456                                                            controller="framework"
457                                                            action="adhoc"
458                                                            params="${[retryFailedExecId: execution.id, project: execution.project]}"
459                                                            title="${g.message(code: 'retry.failed.nodes.description')}">
460
461                                                        <b class="glyphicon glyphicon-play"></b>
462                                                        <g:message code="retry.failed.nodes"/>&hellip;
463                                                    </g:link>
464                                                </li>
465                                            </ul>
466                                        </div>
467                                    </g:if>
468                                </g:if>
469
470                        </section>
471
472                    </div>
473
474                    <div class="subtitle-head-item execution-aux-info flex-item-1">
475                        <section>
476                            <g:if test="${isAdhoc}">
477                                <div class="text-h5">
478                                    <b class="exec-status icon "
479                                      data-bind="attr: { 'data-execstate': executionState, 'data-statusstring':executionStatusString }">
480                                    </b>
481                                    <g:render template="wfItemView" model="[
482                                            item: execution.workflow.commands[0],
483                                            icon: 'icon-small'
484                                    ]"/>
485                                </div>
486                            </g:if>
487                            <g:if test="${scheduledExecution}">
488                                <g:render template="/scheduledExecution/showHead"
489                                          model="[scheduledExecution: scheduledExecution,
490                                                  includeExecStatus : true,
491                                                  jobDescriptionMode: 'expanded',
492                                                  jobActionButtons  : true,
493                                                  linkCss           : 'text-h4',
494                                                  scmExportEnabled  : scmExportEnabled,
495                                                  scmExportStatus   : scmExportStatus,
496                                                  scmImportEnabled  : scmImportEnabled,
497                                                  scmImportStatus   : scmImportStatus
498                                          ]"/>
499
500                                <g:if test="${execution.argString}">
501                                    <section class=" section-space exec-args-section argstring-scrollable">
502                                        <span class="text-secondary"><g:message code="options.prompt"/></span class="text-secondary">
503                                        <g:render template="/execution/execArgString"
504                                                  model="[argString: execution.argString, inputFilesMap: inputFilesMap]"/>
505                                    </section>
506                                </g:if>
507
508
509
510                            </g:if>
511
512                        </section>
513
514                    </div>
515                </div>
516
517                <div class="" data-bind="if: !completed() " data-ko-bind="nodeflow">
518                    <g:if test="${scheduledExecution}">
519                    %{--progress bar--}%
520                        <div>
521                            <section
522                                    data-bind="if: !completed() && !queued() && jobAverageDuration()>0">
523                                <g:set var="progressBind"
524                                      value="${', css: { \'progress-bar-info\': jobPercentageFixed() < 105 ,  \'progress-bar-warning\': jobPercentageFixed() > 104  }'}"/>
525                                <g:render template="/common/progressBar"
526                                          model="[completePercent : execution.dateCompleted ? 100 : 0,
527                                                  progressClass   : 'rd-progress-exec progress-embed progress-square',
528                                                  progressBarClass: '',
529                                                  containerId     : 'progressContainer2',
530                                                  innerContent    : '',
531                                                  showpercent     : true,
532                                                  height          : 28,
533                                                  progressId      : 'progressBar',
534                                                  bind            : 'jobPercentageFixed()',
535                                                  bindText        : '(jobPercentageFixed()  < 105 ? jobPercentageFixed() + \'%\' : \'+\' + jobOverrunDuration()) + \' of average \' + formatDurationHumanize(jobAverageDuration())',
536                                                  progressBind    : progressBind,
537                                          ]"/>
538                            </section>
539                            <section data-bind="if: queued()">
540                                <div  class="progress progress-striped" style="height: 28px">
541                                    <div class="progress-bar" role="progressbar" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100"
542                                        style="width: 100%;line-height: 28px">
543                                        Queued
544                                    </div>
545                                </div>
546                            </section>
547                        </div>
548                    </g:if>
549                    <g:if test="${isAdhoc}">
550
551                    %{--progress bar--}%
552                        <div>
553                            <section>
554                                <g:render template="/common/progressBar"
555                                          model="[completePercent : 100,
556                                                  indefinite      : true,
557                                                  progressClass   : 'rd-progress-exec progress-embed progress-square',
558                                                  progressBarClass: '',
559                                                  containerId     : 'progressContainer2',
560                                                  innerContent    : '',
561                                                  showpercent     : false,
562                                                  height          : 28,
563                                                  progressId      : 'progressBar',
564
565                                          ]"/>
566                            </section>
567
568                        </div>
569                    </g:if>
570                </div>
571              </nav>
572
573
574              <div class="row">
575                  <div class="col-sm-12">
576                      <div class="card card-plain " data-ko-bind="nodeflow">
577                          <div class="btn-group " data-bind="if: views().length>2">
578                              <button class="btn btn-default btn-sm dropdown-toggle "
579                                      id="views_dropdown_button"
580                                      data-target="#"
581                                      data-toggle="dropdown">
582                                  <span class="colon-after"><g:message code="view"/></span>
583                                  <span data-bind="text: activeTabData() && activeTabData().title">
584
585                                  </span>
586                                  <i class="caret"></i>
587                              </button>
588                              <ul class="dropdown-menu pull-left" role="menu" data-bind="foreach: views">
589
590                                  <li data-bind="attr: {id: 'tab_link_'+id }">
591                                      <a href="#"
592                                         data-bind="click: function(){$root.activeTab(id)}, attr: {href: '#'+id }">
593                                          <span data-bind="text: title"></span>
594                                          <!-- ko if: $root.activeTab()===id -->
595                                          <i class="fas fa-check" style="margin-left:1em"></i>
596                                          <!-- /ko -->
597                                      </a>
598                                  </li>
599
600                              </ul>
601
602                          </div>
603                          <!-- ko foreach: viewButtons -->
604                          <a href="#"
605                             data-bind="click:function(){$root.activeTab(id)}, attr: {href: '#'+id, id: 'btn_view_'+id }, visible: $root.activeTab()!==id"
606                             class="btn btn-sm">
607                              <span data-bind="text: title"></span> &raquo;
608                          </a>
609                          <!-- /ko -->
610
611
612                          <span data-bind="visible: activeTab().startsWith('output')">
613
614
615                              <span data-bind="visible: completed()" class="execution-action-links pull-right">
616
617                                  <span class="btn-group">
618                                      <button type="button" class="btn btn-default btn-xs dropdown-toggle"
619                                              data-toggle="dropdown">
620                                          <g:message code="execution.log" />
621                                          <span class="caret"></span>
622                                      </button>
623                                      <ul class="dropdown-menu pull-right" role="menu">
624                                          <li>
625                                              <g:link class=""
626                                                      title="${message(
627                                                              code: 'execution.show.log.text.button.description',
628                                                              default: 'View text output'
629                                                      )}"
630                                                      controller="execution"
631                                                      action="downloadOutput"
632                                                      id="${execution.id}"
633                                                      params="[
634                                                              view     : 'inline',
635                                                              formatted: false,
636                                                              project  : execution.project,
637                                                              stripansi: true
638                                                      ]"
639                                                      target="_blank">
640
641                                                  <g:message code="execution.show.log.text.button.title"/>
642                                              </g:link>
643                                          </li>
644                                          <li>
645
646                                              <g:link class=""
647                                                      title="${message(
648                                                              code: 'execution.show.log.html.button.description',
649                                                              default: 'View rendered output'
650                                                      )}"
651                                                      controller="execution"
652                                                      action="renderOutput"
653                                                      id="${execution.id}"
654                                                      params="[
655                                                              project: execution.project,
656                                                              ansicolor: 'on',
657                                                              loglevels: 'on',
658                                                              convertContent: 'on'
659                                                      ]"
660                                                      target="_blank">
661
662                                                  <g:message code="execution.show.log.html.button.title"/>
663                                              </g:link>
664                                          </li>
665                                          <li role="separator" class="divider"></li>
666                                          <li class="dropdown-header">
667                                              <g:message code="execution.show.log.download.button.title"/>
668                                          </li>
669                                          <li>
670                                              <g:link class="_guess_tz_param"
671                                                      data-tz-url-param="timeZone"
672                                                      title="${message(
673                                                              code: 'execution.show.log.download.button.description',
674                                                              default: 'Download {0} bytes',
675                                                              args: [filesize > 0 ? filesize : '?']
676                                                      )}"
677                                                      controller="execution"
678                                                      action="downloadOutput"
679                                                      id="${execution.id}"
680                                                      params="[project: execution.project]"
681                                                      target="_blank">
682
683                                                  <b class="glyphicon glyphicon-download"></b>
684                                                  <g:message code="formatted.text" />
685                                              </g:link>
686                                          </li>
687                                      </ul>
688                                  </span>
689
690                              </span>
691
692                              <g:render template="/common/modal"
693                                        model="[modalid   : 'view-options-modal',
694                                                titleCode : 'execution.page.view.options.title',
695                                                cancelCode: 'close']">
696                                  <div class="container form-horizontal">
697
698                                      <div class="form-group">
699                                          <label class="col-sm-2 control-label" for="view-option-style-mode">
700                                              Style
701                                          </label>
702
703                                          <div class="col-sm-10">
704
705                                              <select data-bind="options: logoutput().options.styleModesAvailable, value:logoutput().options.styleMode"
706                                                      class="form-control"
707                                                      id="view-option-style-mode">
708
709                                              </select>
710
711                                          </div>
712                                      </div>
713
714
715                                      <div class="form-group">
716
717                                          <label class="col-sm-2 control-label">
718                                              Text
719                                          </label>
720                                          <div class="col-sm-10">
721                                              <div class="checkbox">
722                                                  <input type="checkbox"
723                                                         data-bind="checked: logoutput().options.showAnsicolor"
724                                                         id="view-option-ansi-color"/>
725                                                  <label for="view-option-ansi-color">
726                                                      <g:message code="execution.show.mode.ansicolor.title"
727                                                                 default="Ansi Color"/>
728                                                  </label>
729                                              </div>
730                                          </div>
731
732                                          <div class="col-sm-offset-2 col-sm-10">
733                                              <div class="checkbox">
734                                                  <input type="checkbox"
735                                                         data-bind="checked: logoutput().options.wrapLines"
736                                                         id="view-option-wrap-lines"/>
737                                                  <label for="view-option-wrap-lines">
738                                                      <g:message code="execution.show.mode.wrapmode.title"
739                                                                 default="Wrap Long Lines"/>
740                                                  </label>
741                                              </div>
742                                          </div>
743
744                                          <div class="col-sm-offset-2 col-sm-10">
745                                              <div class="checkbox">
746                                                  <input type="checkbox"
747                                                         data-bind="checked: logoutput().options.followmodeNode"
748                                                         id="view-option-node-view"/>
749                                                  <label for="view-option-node-view">
750                                                      <g:message code="execution.show.mode.Compact.title"
751                                                                 default="Compact"/>
752                                                  </label>
753                                              </div>
754                                          </div>
755
756                                      </div>
757
758                                      <div class="form-group">
759
760                                          <label class="col-sm-2 control-label">Columns</label>
761
762                                          <div class="col-sm-10">
763
764                                              <div>
765
766                                                  <div class="checkbox-inline">
767                                                      <input type="checkbox"
768                                                             value="true"
769                                                             data-bind="checked: logoutput().options.showTime"
770                                                             id="view-option-show-time"/>
771                                                      <label for="view-option-show-time">
772                                                          <g:message code="execution.show.mode.column.time" />
773                                                      </label>
774                                                  </div>
775
776                                                  <div class="checkbox-inline">
777                                                      <input type="checkbox"
778                                                             value="true"
779                                                             data-bind="checked: logoutput().options.showNodeCol"
780                                                             id="view-option-show-node"/>
781                                                      <label for="view-option-show-node">
782                                                          <g:message code="execution.show.mode.column.node" />
783                                                      </label>
784                                                  </div>
785
786                                                  <div class="checkbox-inline">
787                                                      <input type="checkbox"
788                                                             value="true"
789                                                             data-bind="checked: logoutput().options.showStep"
790                                                             id="view-option-show-step"/>
791                                                      <label for="view-option-show-step">
792                                                          <g:message code="execution.show.mode.column.step" />
793                                                      </label>
794                                                  </div>
795                                              </div>
796
797                                          </div>
798                                      </div>
799
800
801                                      <div class="form-group">
802                                          <label class="col-sm-2 control-label">
803                                              <g:message code="execution.show.mode.inset.label" />
804                                          </label>
805                                          <div class="col-sm-10">
806                                              <div class="checkbox">
807                                                  <input type="checkbox"
808                                                         data-bind="checked: logoutput().options.showNodeInset"
809                                                         id="view-option-node-inset"/>
810                                                  <label for="view-option-node-inset">
811                                                      <g:message code="execution.show.mode.inset.node" />
812                                                  </label>
813                                              </div>
814                                          </div>
815
816                                      </div>
817
818                                  </div>
819
820                              </g:render>
821
822                          </span>
823
824                      </div>
825
826                      <div class="card exec-output "
827                           data-ko-bind="nodeflow"
828                           data-mode="normal"
829                           data-bind="attr: {'data-mode': logoutput().options.styleMode }, css: {'exec-output-bg': activeTab()==='output' }">
830
831                          <div class="card-content " data-bind="css: {tight: activeTab().startsWith('output') }">
832                              <g:render template="/common/messages"/>
833
834
835                              <div class="tab-content" id="exec-main-view">
836
837                                  <!-- ko foreach: contentViews -->
838                                  <div class="tab-pane" data-bind="css: {active: $root.activeTab()===id}, attr: {id:id}">
839                                      <span data-bind="attr: {id:id+'_content'}, html:content"></span>
840                                  </div>
841                                  <!-- /ko -->
842                                  <div class="tab-pane " id="nodes" data-bind="css: {active: activeTab()==='nodes'}">
843                                      <div class="flowstate ansicolor ansicolor-on" id="nodeflowstate">
844                                          <g:render template="wfstateNodeModelDisplay" bean="${workflowState}"
845                                                    var="workflowState"/>
846                                      </div>
847                                  </div>
848
849                                  <div style="height: calc(100vh - 250px); display: none; contain: layout;"
850                                       id="output"
851                                       class="card-content-full-width"
852                                       data-bind="visible: activeTab() === 'output' || activeTab().startsWith('outputL')"
853                                  >
854                                      <div class="execution-log-viewer" data-execution-id="${execution.id}" data-theme="light" data-follow="true"></div>
855                                  </div>
856
857                              </div>
858                          </div>
859
860                          <g:if test="${authChecks[AuthConstants.ACTION_READ]}">
861                              <g:render template="/common/modal"
862                                        model="[
863                                                modalid   : 'details_modal',
864                                                modalsize : 'modal-lg',
865                                                title     : message(code: 'definition'),
866                                                cancelCode: 'close',
867                                                links     : isAdhoc ? [
868                                                        [
869                                                                messageCode: 'execution.action.saveAsJob.ellipsis',
870                                                                href       : createLink(
871                                                                        controller: 'scheduledExecution',
872                                                                        action: 'createFromExecution',
873                                                                        params: [executionId: execution.id, project: execution.project]
874                                                                ),
875                                                                bind       : 'visible: completed()',
876                                                                css        : 'btn-success'
877                                                        ]
878                                                ] : []
879                                        ]">
880
881                                  <div>
882                                      <g:render template="execDetails"
883                                                model="[execdata: execution, showArgString: false, hideAdhoc: false, isScheduled:isScheduled]"/>
884                                  </div>
885
886                              </g:render>
887
888                          </g:if>
889                    </div>
890                  </div>
891          <g:if test="${scheduledExecution}">
892
893              <g:set var="hasEventReadAuth" value="${auth.resourceAllowedTest(
894                      project: scheduledExecution.project,
895                      action: AuthConstants.ACTION_READ,
896                      kind: AuthConstants.TYPE_EVENT
897              )}"/>
898              <div class="col-sm-12">
899
900                  <div class="card" id="activity_section">
901                      <div class="card-content">
902
903                          <div class="vue-tabs">
904                              <div class="nav-tabs-navigation">
905                                  <div class="nav-tabs-wrapper">
906                                      <ul class="nav nav-tabs activity_links">
907                                          <li class="active">
908                                              <a href="#stats" data-toggle="tab"><g:message code="job.view.stats.label" /></a>
909                                          </li>
910                                          <g:if test="${hasEventReadAuth}">
911                                              <li>
912                                                  <a href="#history" data-toggle="tab"><g:message code="page.section.Activity" /></a>
913                                              </li>
914                                          </g:if>
915                                      </ul>
916                                  </div>
917                              </div>
918                              <div class="tab-content">
919                                  <div class="tab-pane active" id="stats">
920
921
922                                      <section class="_jobstats_content section-space-bottom-lg container-fluid" id="_job_stats_main">
923                                          <g:render template="/scheduledExecution/renderJobStats"
924                                                    model="${[scheduledExecution: scheduledExecution]}"/>
925                                      </section>
926
927
928                                      <div id="_job_stats_extra_placeholder"></div>
929                                  </div>
930                                  <g:if test="${hasEventReadAuth}">
931                                      <div class="tab-pane" id="history">
932
933                                          <div data-ko-bind="history" class="_history_content vue-project-activity">
934
935                                              <activity-list :event-bus="EventBus"></activity-list>
936                                          </div>
937                                      </div>
938                                  </g:if>
939                              </div>
940                          </div>
941
942
943                      </div>
944                  </div>
945              </div>
946
947          </g:if>
948    </div>
949
950
951  </div>
952    </div>
953    </div>
954  <g:render template="/menu/copyModal"
955          model="[projectNames: projectNames]"/>
956
957  %{--delete execution modal--}%
958  <g:if test="${deleteExecAuth}">
959    <div class="modal" id="execdelete" tabindex="-1" role="dialog" aria-labelledby="deleteexectitle" aria-hidden="true">
960      <div class="modal-dialog">
961        <div class="modal-content">
962          <div class="modal-header">
963            <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
964            <h4 class="modal-title" id="deleteexectitle"><g:message code="delete.execution.title" /></h4>
965          </div>
966
967          <div class="modal-body">
968            <p class=" "><g:message code="really.delete.this.execution" /></p>
969          </div>
970          <div class="modal-footer">
971            <g:form controller="execution" action="delete" method="post" useToken="true">
972              <g:hiddenField name="id" value="${execution.id}"/>
973              <button type="submit" class="btn btn-default btn-xs " data-dismiss="modal">
974                <g:message code="cancel" />
975              </button>
976              <input type="submit" value="${g.message(code:'button.action.Delete')}" class="btn btn-danger btn-xs"/>
977            </g:form>
978          </div>
979        </div>
980      </div>
981    </div>
982  </g:if>
983  %{--/delete execution modal--}%
984
985
986  <script type="text/html" id="step-info-simple">
987    %{--Display the lowest level step info: [icon] identity --}%
988        <i class="rdicon icon-small" data-bind="css: stepinfo.type"></i>
989        <span data-bind="text: stepinfo.stepident"></span>
990  </script>
991  <script type="text/html" id="step-info">
992    %{--wrap step-info-simple in tooltip --}%
993    <span data-bind="attr: {title: stepinfo.stepctxPathFull}, bootstrapTooltip: stepinfo.stepctxPathFull" data-placement="top" data-container='body'>
994        <span data-bind="template: { name: 'step-info-simple', data:stepinfo, as: 'stepinfo' }"></span>
995    </span>
996  </script>
997  <script type="text/html" id="step-info-simple-link">
998    %{--wrap step-info-simple in tooltip --}%
999    <span data-bind="if: stepinfo.hasLink()">
1000        <a data-bind="urlPathParam: stepinfo.linkJobId(), attr: {title: 'Click to view Job: '+stepinfo.linkTitle() }"
1001           href="${createLink(
1002                controller: 'scheduledExecution',
1003                action: 'show',
1004                params: [project: execution.project, id: '<$>']
1005        )}">
1006            <span data-bind="template: { name: 'step-info-simple', data:stepinfo, as: 'stepinfo' }"></span>
1007        </a>
1008    </span>
1009    <span data-bind="if: !stepinfo.hasLink()">
1010        <span data-bind="template: { name: 'step-info-simple', data:stepinfo, as: 'stepinfo' }"></span>
1011    </span>
1012  </script>
1013  <script type="text/html" id="step-info-path">
1014    %{-- Display the full step path with icon and identity --}%
1015    <span data-bind="if: stepinfo.hasParent()">
1016        <span data-bind="with: stepinfo.parentStepInfo()">
1017            <span data-bind="template: { name: 'step-info-path', data:$data, as: 'stepinfo' }"></span>
1018        </span>
1019        <g:icon name="menu-right" css="text-strong"/>
1020
1021    </span>
1022    <span data-bind="template: { name: 'step-info-simple', data:stepinfo, as: 'stepinfo' }"></span>
1023  </script>
1024  <script type="text/html" id="step-info-path-links">
1025    %{-- Display the full step path with icon and identity --}%
1026    <span data-bind="if: stepinfo.hasParent()">
1027        <span data-bind="with: stepinfo.parentStepInfo()">
1028            <span data-bind="template: { name: 'step-info-path-links', data:$data, as: 'stepinfo' }"></span>
1029        </span>
1030        <g:icon name="menu-right" css="text-strong"/>
1031
1032    </span>
1033    <span data-bind="template: { name: 'step-info-simple-link', data:stepinfo, as: 'stepinfo' }"></span>
1034  </script>
1035  <script type="text/html" id="step-info-parent-path">
1036    %{-- Display the full step path with icon and identity --}%
1037
1038    <span data-bind="if: stepinfo.hasParent()">
1039        <span data-bind="with: stepinfo.parentStepInfo()">
1040            <span data-bind="template: { name: 'step-info-path', data:$data, as: 'stepinfo' }"></span>
1041        </span>
1042        <g:icon name="menu-right" css="text-strong"/>
1043    </span>
1044  </script>
1045  <script type="text/html" id="step-info-parent-path-links">
1046    %{-- Display the full step path with icon and identity --}%
1047
1048    <span data-bind="if: stepinfo.hasParent()">
1049        <span data-bind="with: stepinfo.parentStepInfo()">
1050            <span data-bind="template: { name: 'step-info-path-links', data:$data, as: 'stepinfo' }"></span>
1051        </span>
1052        <g:icon name="menu-right" css="text-strong"/>
1053    </span>
1054  </script>
1055
1056  <script type="text/html" id="step-info-path-base">
1057    %{-- Display the full step path with icon and identity --}%
1058    <span data-bind="template: { name: 'step-info-parent-path', data:stepinfo, as: 'stepinfo' }"></span>
1059
1060    <span data-bind="template: { name: 'step-info', data:stepinfo, as: 'stepinfo' }"></span>
1061  </script>
1062
1063  <script type="text/html" id="step-info-extended">
1064  %{--Display the lowest level extended info:  [icon] number. identity --}%
1065    <span data-bind="attr: {title: stepinfo.stepctxPathFull}, bootstrapTooltip: stepinfo.stepctxPathFull" data-placement="top" data-container='body'>
1066    <i class="rdicon icon-small" data-bind="css: stepinfo.type"></i>
1067    <span data-bind="text: stepinfo.stepdesc"></span>
1068    </span>
1069  </script>
1070  <g:jsonToken id="exec_cancel_token" url="${request.forwardURI}"/>
1071  <!--[if (gt IE 8)|!(IE)]><!--> <asset:javascript src="ace-bundle.js"/><!--<![endif]-->
1072        <script type="application/javascript">
1073    var workflow=null;
1074    var followControl=null;
1075    var flowState=null;
1076    var nodeflowvm=null;
1077    var logoutput=null;
1078    function followOutput(){
1079        nodeflowvm.logoutput().beginFollowingOutput('${enc(js: execution?.id)}');
1080    }
1081    function followState(){
1082        try{
1083            flowState.beginFollowing();
1084        }catch(e){
1085            nodeflowvm.errorMessage('Could not load flow state: '+e);
1086            nodeflowvm.stateLoaded(false);
1087        }
1088    }
1089
1090    var activity;
1091    function init() {
1092        var execInfo=loadJsonData('execInfoJSON');
1093        var workflowData=loadJsonData('workflowDataJSON');
1094        RDWorkflow.nodeSteppluginDescriptions=loadJsonData('nodeStepPluginsJSON');
1095        RDWorkflow.wfSteppluginDescriptions=loadJsonData('wfStepPluginsJSON');
1096        workflow = new RDWorkflow(workflowData);
1097
1098      var multiworkflow=new MultiWorkflow(workflow,{
1099            dynamicStepDescriptionDisabled:${enc(js:feature.isDisabled(name:'workflowDynamicStepSummaryGUI'))},
1100            url:appLinks.scheduledExecutionWorkflowJson,
1101            id:execInfo.jobId||execInfo.execId,//id of job or execution
1102            workflow:workflowData
1103        });
1104      followControl = new FollowControl('${execution?.id}','outputappendform',{
1105        parentElement:'commandPerform',
1106        fileloadId:'fileload',
1107        fileloadPctId:'fileloadpercent',
1108        fileloadProgressId:'fileloadprogress',
1109        cmdOutputErrorId:'cmdoutputerror',
1110        outfileSizeId:'outfilesize',
1111        workflow:workflow,
1112        multiworkflow:multiworkflow,
1113        appLinks:appLinks,
1114
1115        extraParams:"<%="true" == params.disableMarkdown ? '&disableMarkdown=true' : ''%>&markdown=${enc(js:enc(url: params.markdown))}&ansicolor=${enc(js:enc(url: params.ansicolor))}&renderContent=${enc(js:enc(url: params.renderContent))}",
1116        lastlines: '${enc(js:params.int('lastlines') ?: defaultLastLines)}',
1117        maxLastLines:'${enc(js:params.int('maxlines') ?: maxLastLines)}',
1118        collapseCtx: {value:${enc(js:null == execution?.dateCompleted)},changed:false},
1119        showFinalLine: {value:false,changed:false},
1120        tailmode: ${enc(js:followmode == 'tail')},
1121        browsemode: ${enc(js:followmode == 'browse')},
1122        nodemode: ${enc(js:followmode == 'node')},
1123        execData: {},
1124        groupOutput:{value:${enc(js:followmode == 'browse')}},
1125        updatepagetitle:${enc(js:null == execution?.dateCompleted)},
1126        killjobauth:${enc(js: authChecks[AuthConstants.ACTION_KILL] ? true : false)},
1127      <g:if test="${authChecks[AuthConstants.ACTION_KILL]}">
1128          killjobhtml: '<span class="btn btn-danger btn-xs textbtn" onclick="followControl.docancel();">Kill <g:message code="domain.ScheduledExecution.title"/> <i class="glyphicon glyphicon-remove"></i></span>',
1129      </g:if>
1130      <g:if test="${!authChecks[AuthConstants.ACTION_KILL]}">
1131          killjobhtml: "",
1132      </g:if>
1133        totalDuration : '${enc(js:scheduledExecution?.getTotalTimeStats()?: -1)}',
1134        totalCount: '${enc(js: scheduledExecution?.getExecCountStats() ?: -1)}',
1135        colStep:{value:${enc(js: !isAdhoc)} },
1136        colNode:{value:false}
1137      });
1138      nodeflowvm=new NodeFlowViewModel(
1139        workflow,
1140        "${enc(js:g.createLink(controller: 'execution', action: 'tailExecutionOutput', id: execution.id,params:[format:'json']))}",
1141        "${enc(js:g.createLink(controller: 'execution', action: 'ajaxExecNodeState', id: execution.id))}",
1142        multiworkflow,
1143        {
1144            followControl:followControl,
1145            executionId:'${enc(js: execution.id)}',
1146            logoutput: new LogOutput({
1147                followControl:followControl,
1148                bindFollowControl:true,
1149                options:{
1150                    followmode:"${enc(js: followmode)}",
1151                    showStep:${enc(js: !isAdhoc)},
1152                    showNodeCol:false,
1153                }
1154            } ),
1155            views: [
1156                {id: 'nodes', title: message('execution.page.show.tab.Nodes.title'), showButton: true},
1157                {id: 'output', title: message('execution.show.mode.Log.title'), showButton: true}
1158            ]
1159        }
1160        );
1161        flowState = new FlowState('${enc(js: execution?.id)}','flowstate',{
1162        workflow:workflow,
1163        loadUrl: "${enc(js:g.createLink(controller: 'execution', action: 'ajaxExecState', id: execution.id))}",
1164        outputUrl:"${g.enc(js:createLink(controller: 'execution', action: 'tailExecutionOutput', id: execution.id,params:[format:'json']))}",
1165        selectedOutputStatusId:'selectedoutputview',
1166        reloadInterval:1500,
1167     });
1168
1169      nodeflowvm.followFlowState(flowState,true);
1170
1171        ko.mapping.fromJS({
1172            completed:'${execution.dateCompleted != null}',
1173            startTime:'${enc(js:execution.dateStarted)}',
1174            endTime:'${enc(js:execution.dateCompleted)}',
1175            executionState:'${enc(js:execution.executionState)}',
1176            executionStatusString:'${enc(js:execution.status)}'
1177        },{},nodeflowvm);
1178
1179        nodeflowvm.selectedNodes.subscribe(function (newValue) {
1180            if (newValue) {
1181                flowState.loadUrlParams=jQuery.extend(flowState.loadUrlParamsBase,{nodes:newValue.join(",")});
1182            }else{
1183                flowState.loadUrlParams=flowState.loadUrlParamsBase;
1184            }
1185        });
1186
1187        //knockout activeTab change listener to begin output or state listener
1188        nodeflowvm.activeTab.subscribe(function(val){
1189            window.location.hash = "#" + val
1190            if (val === 'nodes') {
1191                followState();
1192           }
1193        });
1194
1195        let doupdate = true//!nodeflowvm.completed()
1196        let prefixed=''
1197        const updateTitle = function (prefix) {
1198            let title=document.title
1199            if(prefixed && title.startsWith(prefixed)){
1200                title=title.substring(prefixed.length)
1201            }
1202            document.title = prefix + title;
1203            prefixed=prefix
1204        }
1205
1206        nodeflowvm.executionState.subscribe(function (val) {
1207            if (val === 'RUNNING' && !doupdate) {
1208                doupdate = true
1209            } else if (val === 'RUNNING' && doupdate) {
1210                doupdate = true
1211
1212                updateTitle('[RUNNING] ')
1213            } else if (null != val && val !== 'RUNNING' && doupdate) {
1214                var prefix = (
1215                    val === 'SUCCEEDED' ?
1216                    '✅ [OK] ' :
1217                    val === 'ABORTED' ?
1218                    '✖︎ [KILLED] ' :
1219                    val === 'TIMEDOUT' ?
1220                    '⏱︎ [TIMEOUT] ' :
1221                    val === 'FAILED' ?
1222                    '⛔︎ [FAILED] ' :
1223                    val === 'QUEUED' ?
1224                    '�� [QUEUED] ' :
1225                    ('✴️ [' + (val) + '] ')//��
1226                );
1227                updateTitle(prefix)
1228            }
1229        })
1230
1231
1232
1233        jQuery('.apply_ace').each(function () {
1234            _applyAce(this);
1235        });
1236
1237        PageActionHandlers.registerHandler('copy_other_project',function(el){
1238            jQuery('#jobid').val(el.data('jobId'));
1239            jQuery('#selectProject').modal();
1240            jQuery.ajax({
1241                dataType:'json',
1242                method: 'GET',
1243                url:_genUrl(appLinks.authProjectsToCreateAjax),
1244                success:function(data){
1245                    jQuery('#jobProject').empty();
1246                    for (let i in data.projectNames ) {
1247                        jQuery('#jobProject').append(
1248                            '<option value="' + data.projectNames[i] + '">' + data.projectNames[i] + '</option>'
1249                        );
1250                    }
1251                }
1252            });
1253        });
1254        followState();
1255        var outDetails = window.location.hash;
1256        if(outDetails.startsWith('#output')) {
1257            nodeflowvm.activeTab(outDetails.slice(1))
1258        } else if (outDetails === '#nodes') {
1259            nodeflowvm.activeTab("nodes");
1260        }else{
1261            //default to nodes tab
1262            nodeflowvm.activeTab("nodes");
1263        }
1264        initKoBind(null, {nodeflow: nodeflowvm})
1265    }
1266    jQuery(init);
1267    </script>
1268  </body>
1269</html>
1270