From b6c3bdaf64983fbd8c58d798cef697d531ec061a Mon Sep 17 00:00:00 2001 From: Jaden Diefenbaugh <blakcap@users.noreply.github.com> Date: Mon, 13 Mar 2017 16:41:37 +0100 Subject: [PATCH] add report view for readonly --- .../app/directives/view/itemContainer.js | 68 ++++++ .../reports/app/directives/view/layout.js | 49 ++++ .../app/directives/view/panelContent.js | 66 ++++++ .../app/directives/view/panelExperiments.js | 99 ++++++++ .../reports/app/directives/view/panelItems.js | 48 ++++ .../reports/app/directives/view/plotItem.js | 64 ++++++ .../reports/app/directives/view/tableItem.js | 213 ++++++++++++++++++ .../reports/app/directives/view/textItem.js | 102 +++++++++ 8 files changed, 709 insertions(+) create mode 100644 beat/web/reports/static/reports/app/directives/view/itemContainer.js create mode 100644 beat/web/reports/static/reports/app/directives/view/layout.js create mode 100644 beat/web/reports/static/reports/app/directives/view/panelContent.js create mode 100644 beat/web/reports/static/reports/app/directives/view/panelExperiments.js create mode 100644 beat/web/reports/static/reports/app/directives/view/panelItems.js create mode 100644 beat/web/reports/static/reports/app/directives/view/plotItem.js create mode 100644 beat/web/reports/static/reports/app/directives/view/tableItem.js create mode 100644 beat/web/reports/static/reports/app/directives/view/textItem.js diff --git a/beat/web/reports/static/reports/app/directives/view/itemContainer.js b/beat/web/reports/static/reports/app/directives/view/itemContainer.js new file mode 100644 index 000000000..3aed999c4 --- /dev/null +++ b/beat/web/reports/static/reports/app/directives/view/itemContainer.js @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ + * Contact: beat.support@idiap.ch + * + * This file is part of the beat.web module of the BEAT platform. + * + * Commercial License Usage + * Licensees holding valid commercial BEAT licenses may use this file in + * accordance with the terms contained in a written agreement between you + * and Idiap. For further information contact tto@idiap.ch + * + * Alternatively, this file may be used under the terms of the GNU Affero + * Public License version 3 as published by the Free Software and appearing + * in the file LICENSE.AGPL included in the packaging of this file. + * The BEAT platform is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero Public License along + * with the BEAT platform. If not, see http://www.gnu.org/licenses/. + */ + +/* + * groupItemContainer + * Desc: + * container for an item in the group + */ +angular.module('reportApp') +.directive("groupItemContainer", [function(){ + return { + scope: { + group: '=', + reportItem: '=' + }, + link: function(scope){ + scope.item = scope.reportItem; + scope.domId = `${scope.group.name}_${scope.id}`; + }, + template: ` +<div + group-table-item + ng-if="item.id.includes('table')" + class='panel panel-default' + group='group' + id='item.id' + content='item.content' + > +</div> +<div + group-plot-item + ng-if="item.id.includes('plot')" + class='panel panel-default' + group='group' + id='item.id' + content='item.content' + > +</div> +<div + group-text-item + ng-if="item.id.includes('text')" + class='panel panel-default' + group='group' + report-item='item' + > +</div> +` + }; +}]); diff --git a/beat/web/reports/static/reports/app/directives/view/layout.js b/beat/web/reports/static/reports/app/directives/view/layout.js new file mode 100644 index 000000000..fe1d3750f --- /dev/null +++ b/beat/web/reports/static/reports/app/directives/view/layout.js @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ + * Contact: beat.support@idiap.ch + * + * This file is part of the beat.web module of the BEAT platform. + * + * Commercial License Usage + * Licensees holding valid commercial BEAT licenses may use this file in + * accordance with the terms contained in a written agreement between you + * and Idiap. For further information contact tto@idiap.ch + * + * Alternatively, this file may be used under the terms of the GNU Affero + * Public License version 3 as published by the Free Software and appearing + * in the file LICENSE.AGPL included in the packaging of this file. + * The BEAT platform is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero Public License along + * with the BEAT platform. If not, see http://www.gnu.org/licenses/. + */ + +/* + * groupsLayout + * Desc: + * controls the layout of the reports content, generating group panels + * using the GroupsService data. + */ +angular.module('reportApp').directive("groupsLayout", ['GroupsService', function(GroupsService){ + return { + // isolate scope for modularity & less coupling + scope: { + }, + link: function(scope, el, attr){ + scope.groups = GroupsService.groups; + }, + template: ` +<div id='groupsLayout' class='panel-group'> + <div + group-panel-content + class='panel panel-default' + ng-repeat='group in groups' + group='group' + > + </div> +</div> +` + }; +}]); diff --git a/beat/web/reports/static/reports/app/directives/view/panelContent.js b/beat/web/reports/static/reports/app/directives/view/panelContent.js new file mode 100644 index 000000000..89158ec44 --- /dev/null +++ b/beat/web/reports/static/reports/app/directives/view/panelContent.js @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ + * Contact: beat.support@idiap.ch + * + * This file is part of the beat.web module of the BEAT platform. + * + * Commercial License Usage + * Licensees holding valid commercial BEAT licenses may use this file in + * accordance with the terms contained in a written agreement between you + * and Idiap. For further information contact tto@idiap.ch + * + * Alternatively, this file may be used under the terms of the GNU Affero + * Public License version 3 as published by the Free Software and appearing + * in the file LICENSE.AGPL included in the packaging of this file. + * The BEAT platform is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero Public License along + * with the BEAT platform. If not, see http://www.gnu.org/licenses/. + */ + +/* + * groupPanelContent + * Desc: + * lays out the content of a group in a panel + */ +angular.module('reportApp').directive("groupPanelContent", ['GroupsService', function(GroupsService){ + return { + scope: { + group: '=' + }, + link: function(scope){ + }, + template: ` +<div id="{{group.name}}-heading" class="panel-heading" role="tab"> + <h4 class="panel-title"> + <a + role="button" + data-toggle="collapse" + data-parent="#{{group.name}}-heading" + href="#collapse-{{group.name}}" + aria-expanded="true" + aria-controls="collapse-{{group.name}}"> + {{ group.name }} + </a> + </h4> +</div> +<div id="collapse-{{group.name}}" + class="panel-collapse collapse in" + role="tabpanel" + aria-labelledby="{{group.name}}-heading"> + <div class="panel-body panel-group"> + <!--<div group-panel-experiments group='group' class='panel panel-default'></div>!--> + <div + ng-if='group.experiments.length > 0' + group-panel-items + group='group' + > + </div> + </div> +</div> +` + }; +}]); + diff --git a/beat/web/reports/static/reports/app/directives/view/panelExperiments.js b/beat/web/reports/static/reports/app/directives/view/panelExperiments.js new file mode 100644 index 000000000..df8bd1632 --- /dev/null +++ b/beat/web/reports/static/reports/app/directives/view/panelExperiments.js @@ -0,0 +1,99 @@ +/* + * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ + * Contact: beat.support@idiap.ch + * + * This file is part of the beat.web module of the BEAT platform. + * + * Commercial License Usage + * Licensees holding valid commercial BEAT licenses may use this file in + * accordance with the terms contained in a written agreement between you + * and Idiap. For further information contact tto@idiap.ch + * + * Alternatively, this file may be used under the terms of the GNU Affero + * Public License version 3 as published by the Free Software and appearing + * in the file LICENSE.AGPL included in the packaging of this file. + * The BEAT platform is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero Public License along + * with the BEAT platform. If not, see http://www.gnu.org/licenses/. + */ + +/* + * groupPanelExperiments + * Desc: + * displays the experiments panel of the group + */ +angular.module('reportApp').directive("groupPanelExperiments", ['GroupsService', 'ExperimentsService', 'UrlService', function(GroupsService, ExperimentsService, UrlService){ + return { + scope: { + group: '=' + }, + link: function(scope){ + scope.experiments = ExperimentsService.experiments; + scope.expsNotInGroup = () => { + return Object.keys(ExperimentsService.experiments) + // exps not in group + .filter(e => !scope.group.experiments.includes(e)) + // exp has compatible analyzer + .filter(e => scope.group.analyzer === '' || ExperimentsService.getAnalyzerFromExpName(e) === scope.group.analyzer) + ; + }; + scope.getExpDatabases = (expName) => { + let dbs = scope.experiments[expName].declaration.datasets; + return Array.from(new Set(Object.values(dbs).map(db => db.database))); + }; + scope.getAnalyzerFromExpName = ExperimentsService.getAnalyzerFromExpName; + scope.getExpUrl = UrlService.getExperimentUrl; + scope.getBlockUrl = UrlService.getBlockUrl; + scope.getDatabaseUrl = UrlService.getDatabaseUrl; + }, + template: ` +<div id="{{group.name}}-explist-heading" class="panel-heading" role="tab"> + <h4 class="panel-title"> + <a + role="button" + data-toggle="collapse" + data-parent="#{{group.name}}-explist-heading" + href="#collapse-{{group.name}}-explist" + aria-expanded="true" + aria-controls="collapse-{{group.name}}-explist"> + Experiments + </a> + <i style='margin-left: 5px;' ng-if='group.analyzer.length > 0'> + Analyzer: <a href='{{ getBlockUrl(group.analyzer) }}'>{{ group.analyzer }}</a> + </i> + </h4> +</div> +<div id="collapse-{{group.name}}-explist" + class="panel-collapse collapse in" + role="tabpanel" + aria-labelledby="{{group.name}}-explist-heading"> + <div class="panel-body"> + <table ng-if='group.experiments.length > 0' class="table table-striped table-hover"> + <thead> + <tr> + <th>Experiment</th> + <th>Databases</th> + <th>Alias</th> + </tr> + </thead> + <tbody> + <tr ng-repeat='expName in group.experiments' ng-if='experiments[expName]'> + <td><a href='{{ getExpUrl(expName) }}'>{{ expName }}</a></td> + <td> + <span ng-repeat='db in getExpDatabases(expName)'> + <a href='{{ getDatabaseUrl(db) }}'>{{ db }}</a> + + </span> + </td> + <td>{{ group.aliases[expName] }}</td> + </tr> + </tbody> + </table> + </div> +</div> +` + }; +}]); diff --git a/beat/web/reports/static/reports/app/directives/view/panelItems.js b/beat/web/reports/static/reports/app/directives/view/panelItems.js new file mode 100644 index 000000000..6ff325d43 --- /dev/null +++ b/beat/web/reports/static/reports/app/directives/view/panelItems.js @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ + * Contact: beat.support@idiap.ch + * + * This file is part of the beat.web module of the BEAT platform. + * + * Commercial License Usage + * Licensees holding valid commercial BEAT licenses may use this file in + * accordance with the terms contained in a written agreement between you + * and Idiap. For further information contact tto@idiap.ch + * + * Alternatively, this file may be used under the terms of the GNU Affero + * Public License version 3 as published by the Free Software and appearing + * in the file LICENSE.AGPL included in the packaging of this file. + * The BEAT platform is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero Public License along + * with the BEAT platform. If not, see http://www.gnu.org/licenses/. + */ + +/* + * groupPanelItems + * Desc: + * displays the panel of report items of the group + */ +angular.module('reportApp').directive("groupPanelItems", [function(){ + return { + scope: { + group: '=' + }, + link: function(scope){ + }, + template: ` +<div> + <div + group-item-container + ng-repeat='item in group.reportItems' + group='group' + report-item='item' + style='margin-bottom: 5px;' + > + </div> +</div> +` + }; +}]); diff --git a/beat/web/reports/static/reports/app/directives/view/plotItem.js b/beat/web/reports/static/reports/app/directives/view/plotItem.js new file mode 100644 index 000000000..78570e388 --- /dev/null +++ b/beat/web/reports/static/reports/app/directives/view/plotItem.js @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ + * Contact: beat.support@idiap.ch + * + * This file is part of the beat.web module of the BEAT platform. + * + * Commercial License Usage + * Licensees holding valid commercial BEAT licenses may use this file in + * accordance with the terms contained in a written agreement between you + * and Idiap. For further information contact tto@idiap.ch + * + * Alternatively, this file may be used under the terms of the GNU Affero + * Public License version 3 as published by the Free Software and appearing + * in the file LICENSE.AGPL included in the packaging of this file. + * The BEAT platform is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero Public License along + * with the BEAT platform. If not, see http://www.gnu.org/licenses/. + */ + +/* + * groupPlotItem + * Desc: + * displays a plot report item + */ +angular.module('reportApp') +.directive("groupPlotItem", ['ExperimentsService', function(ExperimentsService){ + return { + scope: { + group: '=', + id: '=', + content: '=' + }, + link: function(scope){ + scope.domId = `${scope.group.name}_${scope.id}`; + }, + template: ` +<div id="{{domId}}-heading" class="panel-heading" role="tab"> + <h4 class="panel-title"> + <a + class='' + role="button" + data-toggle="collapse" + data-parent="#{{domId}}-heading" + href="#collapse-{{domId}}" + aria-expanded="true" + aria-controls="collapse-{{domId}}"> + </a> + </h4> +</div> +<div id="collapse-{{domId}}" + class="panel-collapse collapse in" + role="tabpanel" + aria-labelledby="{{domId}}-heading"> + <div class='panel-body'> + <p>{{ id }} content</p> + <strong class='text-danger'>Plot items in reports are not implemented yet.</strong> + </div> +</div> +` + }; +}]); diff --git a/beat/web/reports/static/reports/app/directives/view/tableItem.js b/beat/web/reports/static/reports/app/directives/view/tableItem.js new file mode 100644 index 000000000..bed05cf76 --- /dev/null +++ b/beat/web/reports/static/reports/app/directives/view/tableItem.js @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ + * Contact: beat.support@idiap.ch + * + * This file is part of the beat.web module of the BEAT platform. + * + * Commercial License Usage + * Licensees holding valid commercial BEAT licenses may use this file in + * accordance with the terms contained in a written agreement between you + * and Idiap. For further information contact tto@idiap.ch + * + * Alternatively, this file may be used under the terms of the GNU Affero + * Public License version 3 as published by the Free Software and appearing + * in the file LICENSE.AGPL included in the packaging of this file. + * The BEAT platform is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero Public License along + * with the BEAT platform. If not, see http://www.gnu.org/licenses/. + */ + +/* + * groupTableItem + * Desc: + * displays a table report item + */ +angular.module('reportApp') +.directive("groupTableItem", ['GroupsService', 'ExperimentsService', function(GroupsService, ExperimentsService){ + return { + scope: { + group: '=', + id: '=', + content: '=' + }, + link: function(scope){ + // aliases + scope.fields = scope.content.fields; + // ids + scope.domId = `${scope.group.name}_${scope.id}`; + scope.colSelectorId = `${scope.domId}_columnSelector`; + + // add 'expName' to the beginning of the fields to show in the table + // if it isnt already there + if(scope.fields.length === 0 || scope.fields[0] !== 'Experiment'){ + scope.fields.unshift('Experiment'); + } + + // 1 - 10 + scope.floatingPointRange = [...(new Array(10)).keys()].map(i => i + 1); + + // get possible table entries + scope.tableables = ExperimentsService.tableables || {}; + + // gets the field type (int, float, string, nothing) + scope.getFieldType = (field) => { + if(field === scope.fields[0]){ + return 'string'; + } + + let hasFieldObj = Object.values(scope.tableables) + .find(o => o[field]); + let fVal = hasFieldObj ? hasFieldObj[field] : {}; + let type; + if(fVal.type){ + type = fVal.type; + } else if(Number.isSafeInteger(fVal)){ + type = 'integer'; + } else if(Number.isFinite(fVal)){ + type = 'float'; + } else if(typeof fVal === 'string'){ + type = 'string'; + } else { + type = undefined; + } + + return type; + }; + // gets the field val for the given exp + scope.getFieldVal = (expName, field) => { + let fVal = scope.tableables[expName] ? + scope.tableables[expName][field] : undefined; + let val; + if(field === scope.fields[0]){ + val = scope.group.aliases[expName].length > 0 ? scope.group.aliases[expName] : expName; + } else if(!fVal){ + val = '-'; + } else { + let tmp; + if(fVal.value){ + tmp = fVal.value; + } else { + tmp = fVal; + } + + let type = scope.getFieldType(field); + if(type && type.startsWith('float')){ + val = tmp.toFixed(parseInt(scope.content.precision)); + } else { + val = tmp; + } + } + + return val; + }; + + // need to nest actual value in an obj to get angular + // to watch it correctly + scope.sortField = { val: 'Experiment', isReversed: false }; + // sort rows (one row per exp) + scope.sortFunc = (expName) => { + return scope.getFieldType(scope.sortField.val) ? + scope.getFieldVal(expName, scope.sortField.val) : expName; + }; + // sets the new sort field and direction + scope.setSortField = (field) => { + if(scope.sortField.val === field){ + scope.sortField.isReversed = !scope.sortField.isReversed; + } else { + scope.sortField.val = field; + scope.sortField.isReversed = false; + } + }; + + // a different view of the table + scope.getCSV = () => { + let fields = scope.fields; + let exps = scope.group.experiments + // clone arr + .map(e => `${e}`) + .sort((ea, eb) => (scope.sortField.isReversed ? -1 : 1) * (scope.sortFunc(ea) > scope.sortFunc(eb) ? -1 : 1)) + ; + + let str = ''; + + let fieldsStr = fields + .map(f => `${f}(${scope.getFieldType(f)})`) + .join(','); + + let expsStrs = exps + .map(e => fields.map(f => `${scope.getFieldVal(e, f)}`).join(',')) + .join('\n'); + + str = `${fieldsStr}\n${expsStrs}`; + + return str; + }; + + // toggle val for viewing CSV + scope.isViewingCSV = { val: false }; + scope.toggleViewingCSV = () => { + scope.isViewingCSV.val = !scope.isViewingCSV.val; + }; + }, + template: ` +<div id="{{domId}}-heading" class="panel-heading" role="tab"> + <h4 class="panel-title"> + <a + class='' + role="button" + data-toggle="collapse" + data-parent="#{{domId}}-heading" + href="#collapse-{{domId}}" + aria-expanded="true" + aria-controls="collapse-{{domId}}"> + </a> + <div class="btn-group" role="group" role='tab'> + <button class='btn btn-default' ng-click='toggleViewingCSV()'> + Toggle CSV View + </button> + </div> + </h4> +</div> +<div id="collapse-{{domId}}" + class="panel-collapse collapse in" + role="tabpanel" + aria-labelledby="{{domId}}-heading"> + <div ng-if='isViewingCSV.val' class='panel-body'> + <pre>{{ getCSV() }}</pre> + </div> + <div ng-if='!isViewingCSV.val' class='panel-body' style='height: 100%; overflow-x: auto;'> + <table class="table table-striped table-hover"> + <thead> + <tr> + <th ng-repeat='field in fields'> + <span + ng-if="sortField.val == field" + class='glyphicon' + ng-class="{ + 'glyphicon-chevron-up': sortField.isReversed, + 'glyphicon-chevron-down': !sortField.isReversed + }" + > + </span> + <a role='button' ng-click='setSortField(field)'> + {{ field }} <i>({{ getFieldType(field) }})</i> + </a> + </th> + </tr> + </thead> + <tbody> + <tr ng-repeat="exp in group.experiments | orderBy:sortFunc:sortField.isReversed"> + <td ng-repeat='field in fields'> + {{ getFieldVal(exp, field) }} + </td> + </tr> + </tbody> + </table> + </div> +</div> +` + }; +}]); diff --git a/beat/web/reports/static/reports/app/directives/view/textItem.js b/beat/web/reports/static/reports/app/directives/view/textItem.js new file mode 100644 index 000000000..113cc4b2f --- /dev/null +++ b/beat/web/reports/static/reports/app/directives/view/textItem.js @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2016 Idiap Research Institute, http://www.idiap.ch/ + * Contact: beat.support@idiap.ch + * + * This file is part of the beat.web module of the BEAT platform. + * + * Commercial License Usage + * Licensees holding valid commercial BEAT licenses may use this file in + * accordance with the terms contained in a written agreement between you + * and Idiap. For further information contact tto@idiap.ch + * + * Alternatively, this file may be used under the terms of the GNU Affero + * Public License version 3 as published by the Free Software and appearing + * in the file LICENSE.AGPL included in the packaging of this file. + * The BEAT platform is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. + * + * You should have received a copy of the GNU Affero Public License along + * with the BEAT platform. If not, see http://www.gnu.org/licenses/. + */ + +/* + * groupTextItem + * Desc: + * displays a text report item + */ +angular.module('reportApp') +.directive("groupTextItem", ['GroupsService', '$sce', 'UrlService', 'reportFactory', function(GroupsService, $sce, UrlService, reportFactory){ + return { + scope: { + group: '=', + reportItem: '=', + }, + link: function(scope){ + // aliases + scope.trustAsHtml = $sce.trustAsHtml; + scope.item = scope.reportItem; + scope.domId = `${scope.group.name}_${scope.item.id}`; + + // readonly codemirror options + scope.srccmOptions = { + mode: 'rst', + readOnly: true + }; + + // handle compiling content + scope.compiledContent = { val: '' }; + scope.compileContent = () => { + let url = UrlService.getCompileRstUrl(); + let mapToTextBlock = `${scope.group.name}|${scope.group.reportItems.indexOf(scope.reportItem)}`; + console.log(mapToTextBlock); + + return reportFactory.compileRST(url, mapToTextBlock) + .then(data => { + scope.compiledContent.val = data.data.html_str; + }); + }; + + // handle toggling between html & rst view + scope.isViewingSrc = { val: false }; + + // compile the content when loaded + scope.compileContent(); + }, + template: ` +<div id="{{domId}}-heading" class="panel-heading" role="tab"> + <h4 class="panel-title"> + <a + class='' + role="button" + data-toggle="collapse" + data-parent="#{{domId}}-heading" + href="#collapse-{{domId}}" + aria-expanded="true" + aria-controls="collapse-{{domId}}"> + </a> + <div class="btn-group" role="group" role='tab'> + <!-- dont show split view, just toggle between html/rst view !--> + <button class='btn btn-default' ng-click='isViewingSrc.val = !isViewingSrc.val'> + Toggle View + </button> + </div> + </h4> +</div> +<div id="collapse-{{domId}}" + class="panel-collapse collapse in" + role="tabpanel" + aria-labelledby="{{domId}}-heading"> + <div class='panel-body'> + <div ng-if='!isViewingSrc.val' class='row'> + <div ng-bind-html='trustAsHtml(compiledContent.val)'></div> + </div> + <div ng-if='isViewingSrc.val'> + <i>Readonly view</i> + <ui-codemirror ng-model='item.content' ui-codemirror-opts='srccmOptions'></ui-codemirror> + </div> + </div> +</div> +` + }; +}]); -- GitLab