1030 lines
56 KiB
Twig
1030 lines
56 KiB
Twig
{#
|
|
/*
|
|
* Copyright (C) 2024 Xibo Signage Ltd
|
|
*
|
|
* Xibo - Digital Signage - https://xibosignage.com
|
|
*
|
|
* This file is part of Xibo.
|
|
*
|
|
* Xibo is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* any later version.
|
|
*
|
|
* Xibo 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. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
#}
|
|
|
|
{% extends "authed.twig" %}
|
|
{% import "inline.twig" as inline %}
|
|
{% import "forms.twig" as forms %}
|
|
|
|
{% block title %}{% set templateName = template.title %}{% trans %}{{ templateName }} - Module Template{% endtrans %} | {% endblock %}
|
|
|
|
{% set hideNavigation = "1" %}
|
|
|
|
{% block pageContent %}
|
|
<div id="developer-module-template-edit"
|
|
data-template-id="{{ template.templateId }}">
|
|
<div class="back-button">
|
|
<a id="backBtn" class="btn btn-primary" href="{{ url_for("developer.templates.view") }}">
|
|
<i class="fa fa-angle-left"></i>
|
|
<span>{{ "Back"|trans }}</span>
|
|
</a>
|
|
</div>
|
|
<div class="widget mt-3">
|
|
<div class="widget-body">
|
|
<div class="row">
|
|
<div class="col-md-12">
|
|
<form id="form-module-template"
|
|
class="XiboForm form-horizontal"
|
|
method="put"
|
|
action="{{ url_for("developer.templates.edit", {id: template.id}) }}">
|
|
|
|
<ul class="nav nav-tabs" role="tablist">
|
|
<li class="nav-item">
|
|
<a class="nav-link active" href="#tab-general" role="tab" data-toggle="tab">
|
|
<span>{% trans "General" %}</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="#tab-properties" role="tab" data-toggle="tab">
|
|
<span>{% trans "Properties" %}</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="#tab-twig" role="tab" data-toggle="tab">
|
|
<span>{% trans "Twig" %}</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="#tab-hbs" role="tab" data-toggle="tab">
|
|
<span>{% trans "HBS" %}</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="#tab-style" role="tab" data-toggle="tab">
|
|
<span>{% trans "Style" %}</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="#tab-head" role="tab" data-toggle="tab">
|
|
<span>{% trans "Head" %}</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="#tab-onTemplateRender" role="tab" data-toggle="tab">
|
|
<span>{% trans "onTemplateRender" %}</span>
|
|
</a>
|
|
</li>
|
|
<li class="nav-item">
|
|
<a class="nav-link" href="#tab-onTemplateVisible" role="tab" data-toggle="tab">
|
|
<span>{% trans "onTemplateVisible" %}</span>
|
|
</a>
|
|
</li>
|
|
</ul>
|
|
<div class="tab-content">
|
|
<div class="tab-pane active" id="tab-general">
|
|
{{ forms.alert("Changing the ID or DataType will break any existing Widgets which use this template.", "danger") }}
|
|
|
|
{% set title %}{% trans "ID" %}{% endset %}
|
|
{% set helpText %}{% trans "A unique ID for the module template" %}{% endset %}
|
|
{{ forms.input("templateId", title, template.templateId, helpText) }}
|
|
|
|
{% set title %}{% trans "Title" %}{% endset %}
|
|
{% set helpText %}{% trans "A title for the module template" %}{% endset %}
|
|
{{ forms.input("title", title, template.title, helpText) }}
|
|
|
|
{% set attributes = [
|
|
{ name: "data-search-url", value: url_for("developer.templates.datatypes.search") },
|
|
{ name: "data-search-term", value: "name" },
|
|
{ name: "data-id-property", value: "id" },
|
|
{ name: "data-text-property", value: "name" },
|
|
{ name: "data-initial-key", value: "dataType" },
|
|
{ name: "data-initial-value", value: template.dataType },
|
|
{ name: "data-hide-search", value: 1}
|
|
] %}
|
|
{% set title %}{% trans "Data Type" %}{% endset %}
|
|
{% set helpText %}{% trans "Which data type does this template need?" %}{% endset %}
|
|
{{ forms.dropdown("dataType", "single", title, null, null, "id", "id", helpText, "pagedSelect", "", "", "", attributes) }}
|
|
|
|
{% set title %}{% trans "Show In" %}{% endset %}
|
|
{% set helpText %}{% trans "Which Editor should this template be available in?" %}{% endset %}
|
|
{% set options = [
|
|
{ id: "none", name: "None"|trans },
|
|
{ id: "layout", name: "Layout Editor"|trans },
|
|
{ id: "playlist", name: "Playlist Editor"|trans },
|
|
{ id: "both", name: "Both"|trans },
|
|
] %}
|
|
{{ forms.dropdown("showIn", "single", title, template.showIn, options, "id", "name", helpText) }}
|
|
|
|
{% set title %}{% trans "Enabled?" %}{% endset %}
|
|
{% set helpText %}{% trans "Is this template enabled?" %}{% endset %}
|
|
{{ forms.checkbox("enabled", title, template.isEnabled, helpText) }}
|
|
</div>
|
|
<div class="tab-pane" id="tab-properties">
|
|
<div class="form-group row">
|
|
<div class="col-sm-12">
|
|
<small class="form-text text-muted">{{ "Properties"|trans }}</small>
|
|
<input type="hidden" name="developer-template-properties" value="{{ propertiesJSON }}">
|
|
|
|
<div class="developer-template-controls-tools text-right">
|
|
<button type="button" class="scroll-to-start-btn btn btn-outline-primary mb-3 d-none" title="{{ "Go to start"|trans }}">
|
|
<i class="fa fa-arrow-left"></i>
|
|
</button>
|
|
<button type="button" class="add-property-btn btn btn-outline-primary mb-3 px-4" title="{{ "Add new property"|trans }}">
|
|
<i class="fa fa-plus"></i> {% trans "Add" %}
|
|
</button>
|
|
<button type="button" class="scroll-to-end-btn btn btn-outline-primary mb-3 d-none" title="{{ "Go to end"|trans }}">
|
|
<i class="fa fa-arrow-right"></i>
|
|
</button>
|
|
</div>
|
|
<div class="developer-template-controls-container">
|
|
<div class="developer-template-controls"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="form-group row code-input-group">
|
|
<div class="col-sm-12">
|
|
<textarea class="form-control" id="input-properties" name="properties" style="display:none;">{{ propertiesJSON }}</textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="tab-pane" id="tab-twig">
|
|
<div class="form-group row code-input-group xibo-code-input">
|
|
<div class="col-sm-12">
|
|
<small class="form-text text-muted">{{ "Twig"|trans }}</small>
|
|
<textarea class="form-control d-none code-input" id="input-twig" name="twig" rows="30" data-code-type="twig">{{ template.stencil.twig|raw }}</textarea>
|
|
|
|
<div class="code-input-editor-container" style="height: 70vh;">
|
|
<div class="code-input-editor"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="tab-pane" id="tab-hbs">
|
|
<div class="form-group row code-input-group xibo-code-input">
|
|
<div class="col-sm-12">
|
|
<small class="form-text text-muted">{{ "HBS"|trans }}</small>
|
|
<textarea class="form-control d-none code-input" id="input-hbs" name="hbs" rows="30" data-code-type="handlebars">{{ template.stencil.hbs|raw }}</textarea>
|
|
|
|
<div class="code-input-editor-container" style="height: 70vh;">
|
|
<div class="code-input-editor"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="tab-pane" id="tab-style">
|
|
<div class="form-group row code-input-group xibo-code-input">
|
|
<div class="col-sm-12">
|
|
<small class="form-text text-muted">{{ "Style"|trans }}</small>
|
|
<textarea class="form-control d-none code-input" id="input-style" name="style" rows="30" data-code-type="css">{{ template.stencil.style|raw }}</textarea>
|
|
|
|
<div class="code-input-editor-container" style="height: 70vh;">
|
|
<div class="code-input-editor"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="tab-pane" id="tab-head">
|
|
<div class="form-group row code-input-group xibo-code-input">
|
|
<div class="col-sm-12">
|
|
<small class="form-text text-muted">{{ "Head"|trans }}</small>
|
|
<textarea class="form-control d-none code-input" id="input-head" name="head" rows="30" data-code-type="html">{{ template.stencil.head|raw }}</textarea>
|
|
|
|
<div class="code-input-editor-container" style="height: 70vh;">
|
|
<div class="code-input-editor"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="tab-pane" id="tab-onTemplateRender">
|
|
<div class="form-group row code-input-group xibo-code-input">
|
|
<div class="col-sm-12">
|
|
<small class="form-text text-muted">{{ "onTemplateRender"|trans }}</small>
|
|
<textarea class="form-control d-none code-input" id="input-onTemplateRender" name="onTemplateRender" rows="30" data-code-type="javascript">{{ template.onTemplateRender|raw }}</textarea>
|
|
|
|
<div class="code-input-editor-container" style="height: 70vh;">
|
|
<div class="code-input-editor"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="tab-pane" id="tab-onTemplateVisible">
|
|
<div class="form-group row code-input-group xibo-code-input">
|
|
<div class="col-sm-12">
|
|
<small class="form-text text-muted">{{ "onTemplateVisible"|trans }}</small>
|
|
<textarea class="form-control d-none code-input" id="input-onTemplateVisible" name="onTemplateVisible" rows="30" data-code-type="javascript">{{ template.onTemplateVisible|raw }}</textarea>
|
|
|
|
<div class="code-input-editor-container" style="height: 70vh;">
|
|
<div class="code-input-editor"></div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{{ forms.checkbox("isInvalidateWidget", "Invalidate any widgets using this template"|trans, 1) }}
|
|
|
|
{{ forms.button("Save"|trans, "submit", null, null, null, "btn-success") }}
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block javaScript %}
|
|
{% verbatim %}
|
|
<script type="text/x-handlebars-template" id="developer-template-control">
|
|
<div class="developer-template-control-item">
|
|
<div class="developer-template-control-item-controls">
|
|
<div title="{% endverbatim %}{% trans "Move" %}{% verbatim %}"" class="item-move" >
|
|
<i class="fa fa-bars"></i>
|
|
</div>
|
|
<button type="button" class="btn delete-btn" title="{% endverbatim %}{{ "Delete"|trans }}{% verbatim %}">
|
|
<i class="fa fa-trash"></i>
|
|
</button>
|
|
</div>
|
|
<div class="developer-template-control-form"></div>
|
|
</div>
|
|
</script>
|
|
|
|
<script type="text/x-handlebars-template" id="developer-template-placeholder">
|
|
<div class="developer-template-placeholder">
|
|
<div>{% endverbatim %}{% trans "No properties, click Add to create one!" %}{% verbatim %}</div>
|
|
</div>
|
|
</script>
|
|
|
|
<script type="text/x-handlebars-template" id="developer-template-control-options">
|
|
<div class="developer-template-control-options control-container">
|
|
<div>
|
|
<strong class="float-left">options</strong>
|
|
{{#if helpText}}
|
|
<div class="input-info-container pt-0 float-left">
|
|
<i class="fa fa-question-circle input-info tooltip-always-on xibo-help-text"
|
|
data-toggle="tooltip"
|
|
data-placement="top"
|
|
title="{{helpText}}">
|
|
</i>
|
|
</div>
|
|
{{/if}}
|
|
</div>
|
|
|
|
<div class="xibo-form-input" data-control-title="options" data-control-type="options">
|
|
<input type="hidden" class="jsonField" name="options" value="{{originalValue}}">
|
|
</div>
|
|
|
|
<div class="options-items">
|
|
<button type="button" class="add-option-btn btn btn-block btn-outline-primary btn-sm">
|
|
<i class="fa fa-plus"></i> {% endverbatim %}{{ "Add option"|trans }}{% verbatim %}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</script>
|
|
|
|
<script type="text/x-handlebars-template" id="developer-template-control-option-item">
|
|
<div class="option-item d-inline-flex">
|
|
<label for="control_option_title" class="control-label text-muted">title</label>
|
|
<input type="text" class="form-control subcontrol-input" name="control_option_title" value="{{title}}">
|
|
<label for="control_option_name" class="control-label text-muted">name</label>
|
|
<input type="text" class="form-control subcontrol-input" name="control_option_name" value="{{name}}">
|
|
<button type="button" class="del-option-btn btn btn-outline-danger btn-sm">
|
|
<i class="fa fa-trash"
|
|
title="{% endverbatim %}{{ "Delete option"|trans }}{% verbatim %}">
|
|
</i>
|
|
</button>
|
|
</div>
|
|
</script>
|
|
|
|
<script type="text/x-handlebars-template" id="developer-template-control-playerCompatibility">
|
|
<div class="developer-template-control-playerCompatibility control-container">
|
|
<div>
|
|
<strong class="float-left">playerCompatibility</strong>
|
|
{{#if helpText}}
|
|
<div class="input-info-container pt-0 float-left">
|
|
<i class="fa fa-question-circle input-info tooltip-always-on xibo-help-text"
|
|
data-toggle="tooltip"
|
|
data-placement="top"
|
|
title="{{helpText}}">
|
|
</i>
|
|
</div>
|
|
{{/if}}
|
|
</div>
|
|
|
|
<div class="xibo-form-input" data-control-title="playerCompatibility" data-control-type="playerCompatibility">
|
|
<input type="hidden" class="jsonField" name="playerCompatibility" value="{{originalValue}}">
|
|
</div>
|
|
|
|
<div class="player-compat-items float-left">
|
|
<label for="player_compat_windows" class="control-label text-muted">windows</label>
|
|
<input type="text" class="form-control subcontrol-input" name="player_compat_windows" data-player="windows" value="{{value.windows}}">
|
|
|
|
<label for="player_compat_windows" class="control-label text-muted">android</label>
|
|
<input type="text" class="form-control subcontrol-input" name="player_compat_android" data-player="android" value="{{value.android}}">
|
|
|
|
<label for="player_compat_windows" class="control-label text-muted">linux</label>
|
|
<input type="text" class="form-control subcontrol-input" name="player_compat_linux" data-player="linux" value="{{value.linux}}">
|
|
|
|
<label for="player_compat_windows" class="control-label text-muted">webos</label>
|
|
<input type="text" class="form-control subcontrol-input" name="player_compat_webos" data-player="webos" value="{{value.webos}}">
|
|
|
|
<label for="player_compat_windows" class="control-label text-muted">tizen</label>
|
|
<input type="text" class="form-control subcontrol-input" name="player_compat_tizen" data-player="tizen" value="{{value.tizen}}">
|
|
</div>
|
|
</div>
|
|
</script>
|
|
|
|
<script type="text/x-handlebars-template" id="developer-template-control-visibility">
|
|
<div class="developer-template-control-visibility control-container">
|
|
<div>
|
|
<strong class="float-left">visibility</strong>
|
|
{{#if helpText}}
|
|
<div class="input-info-container pt-0 float-left">
|
|
<i class="fa fa-question-circle input-info tooltip-always-on xibo-help-text"
|
|
data-toggle="tooltip"
|
|
data-placement="top"
|
|
title="{{helpText}}">
|
|
</i>
|
|
</div>
|
|
{{/if}}
|
|
</div>
|
|
|
|
<div class="xibo-form-input" data-control-title="visibility" data-control-type="visibility">
|
|
<input type="hidden" class="jsonField" name="visibility" value="{{originalValue}}">
|
|
</div>
|
|
|
|
<div class="visibility-tests test-container float-left w-100"></div>
|
|
|
|
<button type="button" class="add-test-btn btn btn-block btn-outline-primary btn-sm">
|
|
<i class="fa fa-plus"></i> {% endverbatim %}{{ "Add test"|trans }}{% verbatim %}
|
|
</button>
|
|
</div>
|
|
</script>
|
|
|
|
<script type="text/x-handlebars-template" id="developer-template-control-validation">
|
|
<div class="developer-template-control-validation control-container">
|
|
<div>
|
|
<strong class="float-left">validation</strong>
|
|
{{#if helpText}}
|
|
<div class="input-info-container pt-0 float-left">
|
|
<i class="fa fa-question-circle input-info tooltip-always-on xibo-help-text"
|
|
data-toggle="tooltip"
|
|
data-placement="top"
|
|
title="{{helpText}}">
|
|
</i>
|
|
</div>
|
|
{{/if}}
|
|
</div>
|
|
|
|
<div class="xibo-form-input" data-control-title="validation" data-control-type="validation">
|
|
<input type="hidden" class="jsonField" name="validation" value="{{originalValue}}">
|
|
</div>
|
|
|
|
<div class="validation-options float-left w-100">
|
|
<div class="form-check float-right pb-1">
|
|
<input type="checkbox" class="form-check-input" id="onSave" name="onSave">
|
|
<label for="onSave" class="form-check-label"><strong>onSave</strong></label>
|
|
</div>
|
|
<div class="form-check float-right pb-1 pr-2">
|
|
<input type="checkbox" class="form-check-input" id="onStatus" name="onStatus">
|
|
<label for="onStatus" class="form-check-label"><strong>onStatus</strong></label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="validation-tests test-container float-left w-100"></div>
|
|
|
|
<button type="button" class="add-test-btn btn btn-block btn-outline-primary btn-sm">
|
|
<i class="fa fa-plus"></i> {% endverbatim %}{{ "Add test"|trans }}{% verbatim %}
|
|
</button>
|
|
</div>
|
|
</script>
|
|
|
|
<script type="text/x-handlebars-template" id="developer-template-control-test">
|
|
<div class="test-item">
|
|
<div class="item-header mb-2">
|
|
<div class="test-title">{% endverbatim %}{{ "Test"|trans }}{% verbatim %}</div>
|
|
<button type="button" class="del-test-btn btn btn-outline-danger btn-sm">
|
|
<i class="fa fa-trash"
|
|
title="{% endverbatim %}{{ "Delete test"|trans }}{% verbatim %}">
|
|
</i>
|
|
</button>
|
|
</div>
|
|
|
|
<div class="test-item-properties">
|
|
<label for="test_type" class="control-label text-muted">type</label>
|
|
<select name="test_type" class="test_type form-control">
|
|
<options>
|
|
<option value="and" {{#eq type "and"}}selected{{/eq}}>and</option>
|
|
<option value="or" {{#eq type "or"}}selected{{/eq}}>or</option>
|
|
</options>
|
|
</select>
|
|
|
|
<label for="test_message" class="control-label text-muted">message</label>
|
|
<input type="text" class="form-control subcontrol-input" name="test_message" value="{{message}}">
|
|
</div>
|
|
|
|
<div class="test-conditions-container">
|
|
<div class="test-conditions"></div>
|
|
|
|
<button type="button" class="add-condition-btn btn btn-block btn-outline-primary btn-sm">
|
|
<i class="fa fa-plus"></i> {% endverbatim %}{{ "Add condition"|trans }}{% verbatim %}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</script>
|
|
|
|
<script type="text/x-handlebars-template" id="developer-template-control-condition">
|
|
<div class="condition-item">
|
|
<div class="item-header">
|
|
<div class="condition-title">{% endverbatim %}{{ "Condition"|trans }}{% verbatim %}</div>
|
|
<button type="button" class="del-condition-btn btn btn-outline-danger btn-sm">
|
|
<i class="fa fa-trash"
|
|
title="{% endverbatim %}{{ "Delete condition"|trans }}{% verbatim %}">
|
|
</i>
|
|
</button>
|
|
</div>
|
|
<label for="condition_type" class="control-label text-muted">type</label>
|
|
<select name="condition_type" class="condition_type form-control">
|
|
<options>
|
|
<option value="ne" {{#eq type "ne"}}selected{{/eq}}>ne</option>
|
|
<option value="eq" {{#eq type "eq"}}selected{{/eq}}>eq</option>
|
|
<option value="neq" {{#eq type "neq"}}selected{{/eq}}>neq</option>
|
|
<option value="gt" {{#eq type "gt"}}selected{{/eq}}>gt</option>
|
|
<option value="lt" {{#eq type "lt"}}selected{{/eq}}>lt</option>
|
|
<option value="egt" {{#eq type "egt"}}selected{{/eq}}>egt</option>
|
|
<option value="elt" {{#eq type "elt"}}selected{{/eq}}>elt</option>
|
|
<option value="isTopLevel" {{#eq type "isTopLevel"}}selected{{/eq}}>isTopLevel</option>
|
|
</options>
|
|
</select>
|
|
|
|
<label for="condition_field" class="control-label text-muted">field</label>
|
|
<input type="text" class="form-control subcontrol-input" name="condition_field" value="{{field}}">
|
|
|
|
<label for="condition_value" class="control-label text-muted">value</label>
|
|
<input type="text" class="form-control subcontrol-input" name="condition_value" value="{{value}}">
|
|
</div>
|
|
</script>
|
|
{% endverbatim %}
|
|
|
|
<script type="text/javascript" nonce="{{ cspNonce }}">
|
|
var propertiesMap = {
|
|
type: {
|
|
type: 'dropdown',
|
|
helpText: '',
|
|
},
|
|
id: {
|
|
type: 'text',
|
|
helpText: '',
|
|
},
|
|
title: {
|
|
type: 'text',
|
|
helpText: '',
|
|
},
|
|
helpText: {
|
|
type: 'text',
|
|
helpText: '',
|
|
},
|
|
default: {
|
|
type: 'text',
|
|
helpText: "{{ "Default value"|trans }}",
|
|
},
|
|
variant: {
|
|
type: 'text',
|
|
helpText: '',
|
|
},
|
|
format: {
|
|
type: 'text',
|
|
helpText: '',
|
|
},
|
|
mode: {
|
|
type: 'text',
|
|
helpText: '',
|
|
},
|
|
target: {
|
|
type: 'text',
|
|
helpText: '',
|
|
},
|
|
propertyGroupId: {
|
|
type: 'text',
|
|
helpText: '',
|
|
},
|
|
dependsOn: {
|
|
type: 'text',
|
|
helpText: '',
|
|
},
|
|
customPopOver: {
|
|
type: 'text',
|
|
helpText: '',
|
|
},
|
|
allowLibraryRefs: {
|
|
type: 'checkbox',
|
|
helpText: '',
|
|
},
|
|
allowAssetRefs: {
|
|
type: 'checkbox',
|
|
helpText: '',
|
|
},
|
|
parseTranslations: {
|
|
type: 'checkbox',
|
|
helpText: '',
|
|
},
|
|
includeInXlf: {
|
|
type: 'checkbox',
|
|
helpText: '',
|
|
},
|
|
options: {
|
|
type: 'options',
|
|
helpText: "{{ "Options for the dropdown control"|trans }}",
|
|
visibility: function(ctrl) {
|
|
return ctrl.type === 'dropdown';
|
|
},
|
|
},
|
|
visibility: {
|
|
type: 'visibility',
|
|
helpText: '',
|
|
},
|
|
validation: {
|
|
type: 'validation',
|
|
helpText: '',
|
|
},
|
|
playerCompatibility: {
|
|
type: 'playerCompatibility',
|
|
helpText: '',
|
|
},
|
|
};
|
|
|
|
var controlTypes = [
|
|
'text',
|
|
'checkbox',
|
|
'number',
|
|
'textArea',
|
|
'dropdown',
|
|
'date',
|
|
'code',
|
|
'color',
|
|
'custom',
|
|
'divider',
|
|
'effectSelector',
|
|
'fontSelector',
|
|
'header',
|
|
'hidden',
|
|
'message',
|
|
'richText',
|
|
'snippet',
|
|
'canvasWidgetsSelector',
|
|
'commandBuilder',
|
|
'commandSelector',
|
|
'connectorProperties',
|
|
'datasetColStyle',
|
|
'datasetColStyleSelector',
|
|
'datasetColumnSelector',
|
|
'datasetField',
|
|
'datasetFilter',
|
|
'datasetOrder',
|
|
'datasetSelector',
|
|
'forecastUnitsSelector',
|
|
'languageSelector',
|
|
'mediaSelector',
|
|
'menuBoardCategorySelector',
|
|
'menuBoardSelector',
|
|
'playlistMixer',
|
|
'tickerTagSelector',
|
|
'tickerTagStyle',
|
|
'worldClock',
|
|
];
|
|
|
|
$(document).ready(function() {
|
|
// Create controls
|
|
var $controlsHiddenInput = $('#form-module-template').find('[name="properties"]');
|
|
var $controls = $('#form-module-template').find('.developer-template-controls');
|
|
var $controlsContainer = $('#form-module-template').find('.developer-template-controls-container');
|
|
var controls = ($controlsHiddenInput.val()) ? JSON.parse($controlsHiddenInput.val()) : [];
|
|
var controlTemplate = Handlebars.compile($("#developer-template-control").html())();
|
|
var placeholderTemplate = Handlebars.compile($("#developer-template-placeholder").html())();
|
|
createTemplateControlsFromJSON($controls, controls);
|
|
|
|
// Add new property
|
|
$('.developer-template-controls-tools .add-property-btn').on('click', (ev) => {
|
|
addControl();
|
|
});
|
|
|
|
// Scroll
|
|
$('.developer-template-controls-tools .scroll-to-end-btn').on('click', (ev) => {
|
|
scrollToEnd();
|
|
});
|
|
$('.developer-template-controls-tools .scroll-to-start-btn').on('click', (ev) => {
|
|
scrollToStart();
|
|
});
|
|
|
|
function scrollToStart() {
|
|
$controlsContainer.animate({scrollLeft: 0}, 1000);
|
|
}
|
|
|
|
function scrollToEnd() {
|
|
$controlsContainer.animate({scrollLeft: $controlsContainer.prop('scrollWidth')}, 1000);
|
|
}
|
|
|
|
function addControl(control) {
|
|
var $control = $(controlTemplate);
|
|
|
|
for (var ptm in propertiesMap) {
|
|
if (propertiesMap.hasOwnProperty(ptm)) {
|
|
var propertyFromMap = propertiesMap[ptm].type;
|
|
if (templates.forms[propertyFromMap]) {
|
|
var $property = $(templates.forms[propertyFromMap](
|
|
{
|
|
title: ptm,
|
|
helpText: propertiesMap[ptm].helpText,
|
|
value: (control) ? control[ptm] : null,
|
|
options: controlTypes.map((ct) => {
|
|
return {
|
|
name: ct,
|
|
title: ct,
|
|
};
|
|
}),
|
|
}
|
|
)).data('control-title', ptm)
|
|
.data('control-type', propertyFromMap);
|
|
|
|
$property.appendTo($control.find('.developer-template-control-form'));
|
|
} else {
|
|
var $templateDiv = $('#developer-template-control-' + propertyFromMap);
|
|
if($templateDiv.length > 0) {
|
|
var controlType = propertiesMap[ptm].type;
|
|
var subControlTemplate = Handlebars.compile(
|
|
$templateDiv.html()
|
|
);
|
|
var subControlValue = (control) ? control[ptm] : '';
|
|
var $subcontrol = $(subControlTemplate(
|
|
Object.assign(
|
|
propertiesMap[ptm],
|
|
{
|
|
value: subControlValue,
|
|
originalValue: (subControlValue) ? JSON.stringify(subControlValue) : '',
|
|
}
|
|
)
|
|
));
|
|
|
|
var saveSubControl = function($target) {
|
|
var $subControlAux = $target;
|
|
var controlType = $target.find('.xibo-form-input').data('controlType');
|
|
var valToSave = {};
|
|
|
|
// Save to hidden input
|
|
if (controlType === 'playerCompatibility') {
|
|
$subControlAux.find('.subcontrol-input').each((_key, sub) => {
|
|
var subValue = $(sub).val();
|
|
if(subValue) {
|
|
valToSave[$(sub).data('player')] = $(sub).val();
|
|
}
|
|
});
|
|
} else if (controlType === 'options') {
|
|
valToSave = [];
|
|
$subControlAux.find('.option-item').each((_key, option) => {
|
|
var optionTitle = $(option).find('[name="control_option_title"]').val();
|
|
var optionName = $(option).find('[name="control_option_name"]').val();
|
|
if(optionTitle && optionName) {
|
|
valToSave.push({
|
|
title: optionTitle,
|
|
name: optionName,
|
|
});
|
|
}
|
|
});
|
|
} else if(controlType === 'visibility') {
|
|
valToSave = [];
|
|
$subControlAux.find('.test-item').each((_key, test) => {
|
|
var testConditions = [];
|
|
var testType = $(test).find('[name="test_type"]').val();
|
|
var testMessage = $(test).find('[name="test_message"]').val();
|
|
|
|
$(test).find('.condition-item').each((_key, cond) => {
|
|
testConditions.push({
|
|
field: $(cond).find('[name="condition_field"]').val(),
|
|
type: $(cond).find('[name="condition_type"]').val(),
|
|
value: $(cond).find('[name="condition_value"]').val(),
|
|
});
|
|
});
|
|
|
|
valToSave.push({
|
|
type: testType,
|
|
message: testMessage,
|
|
conditions: testConditions,
|
|
});
|
|
});
|
|
} else if(controlType === 'validation') {
|
|
valToSave = {
|
|
tests: [],
|
|
};
|
|
$subControlAux.find('.validation-options input').each((_key, option) => {
|
|
var $optionInput = $(option);
|
|
valToSave[$optionInput.attr('name')] = $optionInput.is(':checked')
|
|
});
|
|
|
|
$subControlAux.find('.test-item').each((_key, test) => {
|
|
var testConditions = [];
|
|
var testType = $(test).find('[name="test_type"]').val();
|
|
var testMessage = $(test).find('[name="test_message"]').val();
|
|
|
|
$(test).find('.condition-item').each((_key, cond) => {
|
|
testConditions.push({
|
|
field: $(cond).find('[name="condition_field"]').val(),
|
|
type: $(cond).find('[name="condition_type"]').val(),
|
|
value: $(cond).find('[name="condition_value"]').val(),
|
|
});
|
|
});
|
|
|
|
valToSave.tests.push({
|
|
type: testType,
|
|
message: testMessage,
|
|
conditions: testConditions,
|
|
});
|
|
});
|
|
}
|
|
|
|
// Save values to hidden field
|
|
$subControlAux.find('[name="' + controlType + '"]').val(
|
|
$.isEmptyObject(valToSave) ? '' : JSON.stringify(valToSave)
|
|
);
|
|
|
|
// Save all controls
|
|
saveTemplateControlsToHiddenField();
|
|
};
|
|
|
|
// Options control
|
|
if(controlType === 'options') {
|
|
var controlSubOptionTemplate = Handlebars.compile($("#developer-template-control-option-item").html());
|
|
(subControlValue) && subControlValue.forEach((scv) => {
|
|
var $subControlOption = $(controlSubOptionTemplate(scv));
|
|
|
|
// Add to container
|
|
$subcontrol.find('.options-items .add-option-btn').before($subControlOption);
|
|
});
|
|
|
|
// Handle add button
|
|
$subcontrol.find('.add-option-btn').on('click', (ev) => {
|
|
// Add to container
|
|
$(ev.currentTarget).parents('.options-items').find('.add-option-btn').before($(controlSubOptionTemplate()));
|
|
});
|
|
|
|
// Handle delete
|
|
$subcontrol.on('click', '.del-option-btn', (ev) => {
|
|
var $parentContainer = $(ev.currentTarget).parents('.control-container');
|
|
// Remove option
|
|
$(ev.currentTarget).parents('.option-item').remove();
|
|
|
|
// Save control
|
|
saveSubControl($parentContainer);
|
|
|
|
// Save all controls
|
|
saveTemplateControlsToHiddenField();
|
|
});
|
|
} else if(controlType === 'visibility') {
|
|
var controlTestTemplate = Handlebars.compile($("#developer-template-control-test").html());
|
|
var controlConditionTemplate = Handlebars.compile($("#developer-template-control-condition").html());
|
|
|
|
(subControlValue) && subControlValue.forEach((test) => {
|
|
var $newTest = $(controlTestTemplate(test));
|
|
var $testContainer = $subcontrol.find('.' + controlType + '-tests');
|
|
|
|
test.conditions.forEach((condition) => {
|
|
var $newCondition = $(controlConditionTemplate(condition));
|
|
|
|
$newTest.find('.test-conditions').append($newCondition);
|
|
});
|
|
|
|
// Add to container
|
|
$testContainer.append($newTest);
|
|
});
|
|
|
|
// Handle add condition button
|
|
$subcontrol.on('click', '.add-condition-btn', (ev) => {
|
|
// Add to container
|
|
$(ev.currentTarget).siblings('.test-conditions').append($(controlConditionTemplate()));
|
|
});
|
|
|
|
// Handle delete condition
|
|
$subcontrol.on('click', '.del-condition-btn', (ev) => {
|
|
var $parentContainer = $(ev.currentTarget).parents('.control-container');
|
|
// Remove condition
|
|
$(ev.currentTarget).parents('.condition-item').remove();
|
|
|
|
// Save control
|
|
saveSubControl($parentContainer);
|
|
|
|
// Save all controls
|
|
saveTemplateControlsToHiddenField();
|
|
});
|
|
|
|
// Handle add test button
|
|
$subcontrol.on('click', '.add-test-btn', (ev) => {
|
|
// Add to container
|
|
$(ev.currentTarget).siblings('.test-container').append($(controlTestTemplate()));
|
|
});
|
|
|
|
// Handle delete test
|
|
$subcontrol.on('click', '.del-test-btn', (ev) => {
|
|
var $parentContainer = $(ev.currentTarget).parents('.control-container');
|
|
// Remove test
|
|
$(ev.currentTarget).parents('.test-item').remove();
|
|
|
|
// Save control
|
|
saveSubControl($parentContainer);
|
|
|
|
// Save all controls
|
|
saveTemplateControlsToHiddenField();
|
|
});
|
|
} else if (controlType === 'validation') {
|
|
var controlTestTemplate = Handlebars.compile($("#developer-template-control-test").html());
|
|
var controlConditionTemplate = Handlebars.compile($("#developer-template-control-condition").html());
|
|
var $subControlContainer = $subcontrol;
|
|
|
|
var updateCheckboxes = function() {
|
|
var hasTests = $subControlContainer.find('.validation-tests .test-item').length > 0;
|
|
|
|
// Show options if we have any test
|
|
$subControlContainer.find('.validation-options').toggle(hasTests)
|
|
.toggleClass('toSave', hasTests);
|
|
|
|
// Handle checkbox change
|
|
$subControlContainer.find('.validation-options input').off().on('change', function() {
|
|
saveSubControl($subControlContainer);
|
|
});
|
|
};
|
|
|
|
(subControlValue && subControlValue.tests) && subControlValue.tests.forEach((test) => {
|
|
var $newTest = $(controlTestTemplate(test));
|
|
var $testContainer = $subControlContainer.find('.' + controlType + '-tests');
|
|
|
|
test.conditions.forEach((condition) => {
|
|
var $newCondition = $(controlConditionTemplate(condition));
|
|
|
|
$newTest.find('.test-conditions').append($newCondition);
|
|
});
|
|
|
|
// Add to container
|
|
$testContainer.append($newTest);
|
|
});
|
|
|
|
// Handle add condition button
|
|
$subControlContainer.on('click', '.add-condition-btn', (ev) => {
|
|
// Add to container
|
|
$(ev.currentTarget).siblings('.test-conditions').append($(controlConditionTemplate()));
|
|
});
|
|
|
|
// Handle delete condition
|
|
$subControlContainer.on('click', '.del-condition-btn', (ev) => {
|
|
var $parentContainer = $(ev.currentTarget).parents('.control-container');
|
|
// Remove condition
|
|
$(ev.currentTarget).parents('.condition-item').remove();
|
|
|
|
// Save control
|
|
saveSubControl($parentContainer);
|
|
|
|
// Save all controls
|
|
saveTemplateControlsToHiddenField();
|
|
});
|
|
|
|
// Handle add test button
|
|
$subControlContainer.on('click', '.add-test-btn', (ev) => {
|
|
// Add to container
|
|
$(ev.currentTarget).siblings('.test-container').append($(controlTestTemplate()));
|
|
|
|
// Update checkboxes
|
|
updateCheckboxes();
|
|
});
|
|
|
|
// Handle delete test
|
|
$subControlContainer.on('click', '.del-test-btn', (ev) => {
|
|
var $parentContainer = $(ev.currentTarget).parents('.control-container');
|
|
// Remove test
|
|
$(ev.currentTarget).parents('.test-item').remove();
|
|
|
|
// Update checkboxes
|
|
updateCheckboxes();
|
|
|
|
// Save control
|
|
saveSubControl($parentContainer);
|
|
|
|
// Save all controls
|
|
saveTemplateControlsToHiddenField();
|
|
});
|
|
|
|
// Update checkboxes on first load
|
|
updateCheckboxes();
|
|
}
|
|
|
|
// Handle change to update values
|
|
$subcontrol.on('change', '.subcontrol-input', (ev) => {
|
|
saveSubControl($(ev.delegateTarget));
|
|
});
|
|
|
|
$subcontrol.appendTo($control.find('.developer-template-control-form'));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handle change to update values
|
|
$control.find('.xibo-form-input')
|
|
.on('change', saveTemplateControlsToHiddenField);
|
|
|
|
// Delete
|
|
$control.find('.delete-btn').on('click', (ev) => {
|
|
$(ev.currentTarget).parents('.developer-template-control-item').remove();
|
|
saveTemplateControlsToHiddenField();
|
|
});
|
|
|
|
// Remove placeholder container
|
|
hidePlaceholder();
|
|
|
|
// Append to container
|
|
$control.appendTo($controls);
|
|
}
|
|
|
|
function createTemplateControlsFromJSON($container, controls) {
|
|
if (controls.length > 0) {
|
|
controls.forEach((ct) => {
|
|
addControl(ct);
|
|
});
|
|
|
|
// Sortable
|
|
$controls.sortable({
|
|
axis: 'x',
|
|
handle: '.item-move',
|
|
items: '.developer-template-control-item',
|
|
containment: 'parent',
|
|
update: saveTemplateControlsToHiddenField,
|
|
});
|
|
} else {
|
|
// Show placeholder
|
|
showPlaceholder($container);
|
|
}
|
|
}
|
|
|
|
function saveTemplateControlsToHiddenField() {
|
|
var controlsToSave = [];
|
|
// Get properties from controls
|
|
$controls.find('.developer-template-control-item').each((_idx, control) => {
|
|
var newControl = {};
|
|
$(control).find('.xibo-form-input').each((_idx, property) => {
|
|
var controlType = $(property).data('control-type');
|
|
var controlTitle = $(property).data('control-title');
|
|
var controlValue = $(property).find('select, input').val();
|
|
|
|
if(controlType === 'checkbox') {
|
|
// If checkbox, get boolean from input
|
|
controlValue = $(property).find('input').is(':checked');
|
|
} else if(
|
|
$(property).find('select, input').hasClass('jsonField') &&
|
|
controlValue != ''
|
|
) {
|
|
// If property was saved as json, parse it here
|
|
controlValue = JSON.parse(controlValue);
|
|
}
|
|
|
|
newControl[controlTitle] = controlValue;
|
|
});
|
|
|
|
// If control type isn't a header, divider or message
|
|
// and id is empty, don't add it
|
|
if (
|
|
!['header', 'divider', 'message'].includes(newControl.type) &&
|
|
newControl.id === ''
|
|
) {
|
|
console.error('Properties other than header, divider or message need to have id!');
|
|
} else {
|
|
controlsToSave.push(newControl);
|
|
}
|
|
});
|
|
|
|
// If there are no properties, show placeholder
|
|
if ($controls.find('.developer-template-control-item').length === 0) {
|
|
showPlaceholder($controls);
|
|
}
|
|
|
|
// Save to hidden input
|
|
$controlsHiddenInput.val(JSON.stringify(controlsToSave));
|
|
}
|
|
|
|
function showPlaceholder($container) {
|
|
$container.append($(placeholderTemplate));
|
|
}
|
|
|
|
function hidePlaceholder() {
|
|
$('.developer-template-placeholder').remove();
|
|
}
|
|
});
|
|
</script>
|
|
|
|
{# Add code editor bundle #}
|
|
<script type="text/javascript" src="{{ theme.rootUri() }}dist/codeEditor.bundle.min.js?v={{ version }}&rev={{revision}}" nonce="{{ cspNonce }}" defer></script>
|
|
{% endblock %} |