jfu-1.0.x-dev/assets/js/index.js

assets/js/index.js
// Define component display quote
Vue.component('display-quote', {
  props: ['o_data', 'index', 'field_name', 'is_load', 'cardinality', 'options'],
  data: function () {
    return {
      isActive: true
    };
  },
  methods: {
    activeToggle: function() {
      this.isActive = !this.isActive;
    }
  },
  template: `
    <div class="component-item--wrapper" draggable="true" v-on:dragstart="dragStart($event, index)" v-on:dragend="dragEnd($event, field_name)">
      <a v-show="cardinality != 1" class="tabledrag-handle" title="Drag to re-order">
        <div class="handle">&nbsp;</div>
      </a>
      <div class="jfu-toggle" v-on:click="activeToggle">
        <span>{{ o_data.label }}</span>
        <span class="jfu-toggle-trigger-icon" v-bind:class="{'is-active' :isActive}"></span>
      </div>
      <div class="component-item quote-item accordion-item--content" v-bind:class="{'is-active' :isActive}">
        <display-content-quote v-bind:o_data="o_data" v-bind:options="options"></display-content-quote>
        <display-operations v-bind:index="index" v-bind:field_name="field_name" v-bind:is_load="is_load" v-bind:type="o_data.type" v-bind:options="options"></display-operations>
      </div>
    </div>
  `
});

// Define component display content quote
Vue.component('display-content-quote', {
  props: ['o_data', 'options'],
  template: `
    <div class="component-item--inner" style="display: flex; align-items: center;">
      <div v-if="o_data.image.value" class="component-item quote-item--image" style="max-width: 200px; width: 100%;">
        <display-image v-bind:o_image="o_data.image" v-bind:options="options"></display-image>
      </div>
      <div class="component-item quote-item--content">
        <div class="quote-item--body">
          <blockquote>
            <display-text v-bind:content="o_data.body.value"></display-text>
          </blockquote>
        </div>
        <div class="quote-item--author">
          <span>{{ o_data.author }}</span>
          <span class="quote-item--author-postion">{{ o_data.position }}</span>
        </div>
        <div class="quote-item--link" v-if="o_data.link.value">
          <display-link v-bind:o_link="o_data.link" v-bind:options="options"></display-link>
        </div>
      </div>
    </div>
  `
});

// Define component form quote
Vue.component('form-quote', {
  props: ['o_json', 'edit_index', 'field_name', 'options'],
  data: function() {
    return {
      current_json: this.o_json
    };
  },
  template: `
    <form v-on:submit.prevent="edit_index === '' ? addToJfu($event) : updateToJfu($event, o_json.type, edit_index)">
      <form-elements-quote v-bind:o_json="o_json" v-bind:field_name="field_name" v-bind:index="0" v-bind:options="options"></form-elements-quote>
      <input class="link-to link-to-add" type="submit" v-bind:value="options.defaultLabels.save" />
    </form>
  `
});

// Define component form elements quote
Vue.component('form-elements-quote', {
  props: ['o_json', 'index', 'field_name', 'options'],
  template: `
    <div class="form-component">
      <form-classes v-bind:o_json="o_json" v-bind:index="index" v-bind:field_name="field_name" v-bind:options="options"></form-classes>
      <p>
        <label>{{ options.defaultLabels.author }}
          <input type="text" name="author" maxlength="255" v-model="o_json.author" required />
        </label>
      </p>
      <p>
        <label>{{ options.defaultLabels.position }}
          <input type="text" name="position" v-model="o_json.position" />
        </label>
      </p>
      <form-text v-bind:o_json="o_json.body" v-bind:field_name="field_name" v-bind:options="options" v-bind:index="index"></form-text>
      <form-image v-bind:o_image="o_json.image" v-bind:field_name="field_name" v-bind:index="index" v-bind:component="'quote'" v-bind:options="options"></form-image>
      <form-link v-bind:o_link="o_json.link" v-bind:field_name="field_name" v-bind:options="options"></form-link>
      <p>
        <label>{{ options.defaultLabels.full_width }}
          <label><input type="checkbox" id="full_width" v-model="o_json.options.full_width">{{ options.defaultLabels.activate_full_width }}</label>
        </label>
      </p>
    </div>
  `
});

// Define component display accordion
Vue.component('display-accordion', {
  props: ['o_data', 'index', 'field_name', 'is_load', 'cardinality', 'options'],
  data: function () {
    return {
      isActive: true
    };
  },
  methods: {
    activeToggle: function() {
      this.isActive = !this.isActive;
    }
  },
  template: `
    <div class="component-item--wrapper" draggable="true" v-on:dragstart="dragStart($event, index)" v-on:dragend="dragEnd($event, field_name)">
      <a v-show="cardinality != 1" class="tabledrag-handle" title="Drag to re-order">
        <div class="handle">&nbsp;</div>
      </a>
      <div class="jfu-toggle" v-on:click="activeToggle">
        <span>{{ o_data.label }}</span>
        <span class="jfu-toggle-trigger-icon" v-bind:class="{'is-active' :isActive}"></span>
      </div>
      <div class="accordion-item--content" v-bind:class="{'is-active' :isActive}">
        <display-content-accordion v-bind:o_data="o_data"></display-content-accordion>
        <display-operations v-bind:index="index" v-bind:field_name="field_name" v-bind:is_load="is_load" v-bind:type="o_data.type" v-bind:options="options"></display-operations>
      </div>
    </div>
  `
});

// Define component display content accordion
Vue.component('display-content-accordion', {
  props: ['o_data'],
  data: function () {
    return {
      isActive: false
    };
  },
  methods: {
    activeToggle: function() {
      this.isActive = !this.isActive;
    }
  },
  template: `
    <div class="component-item accordion-item">
      <div class="accordion-item--title-container" v-on:click="activeToggle">
        <h3 class="accordion-item--title">{{ o_data.title }}</h3>
        <span class="jfu-toggle-trigger-icon" v-bind:class="{'is-active' :isActive}"></span>
      </div>
      <div class="accordion-item--content" v-bind:class="{'is-active' :isActive}">
        <display-text v-bind:content="o_data.body.value"></display-text>
      </div>
    </div>
  `
});

// Define component form accordion
Vue.component('form-accordion', {
  props: ['o_json', 'edit_index', 'field_name', 'options'],
  data: function() {
    return {
      current_json: this.o_json
    };
  },
  template: `
    <form v-on:submit.prevent="edit_index === '' ? addToJfu($event) : updateToJfu($event, o_json.type, edit_index)">
      <form-elements-accordion v-bind:o_json="o_json" v-bind:field_name="field_name" v-bind:index="0" v-bind:options="options"></form-elements-accordion>
      <input class="link-to link-to-add" type="submit" v-bind:value="options.defaultLabels.save" />
    </form>
  `
});

// Define component form content accordion
Vue.component('form-elements-accordion', {
  props: ['o_json', 'field_name', 'options', 'index'],
  template: `
    <div class="form-component">
      <form-classes v-bind:o_json="o_json" v-bind:index="index" v-bind:field_name="field_name" v-bind:options="options"></form-classes>
      <p>
        <label>{{ options.defaultLabels.title }}
          <input type="text" name="accordion_title" maxlength="255" v-model="o_json.title" required />
        </label>
      </p>
      <form-text v-bind:o_json="o_json.body" v-bind:field_name="field_name" v-bind:options="options" v-bind:index="index"></form-text>
      <p>
        <label>{{ options.defaultLabels.full_width }}
          <label><input type="checkbox" id="full_width" v-model="o_json.options.full_width">{{ options.defaultLabels.activate_full_width }}</label>
        </label>
      </p>
    </div>
  `
});

// Define component display block
Vue.component('display-block', {
  props: ['o_data', 'index', 'field_name', 'is_load', 'cardinality', 'options'],
  data: function () {
    return {
      isActive: true
    };
  },
  methods: {
    activeToggle: function() {
      this.isActive = !this.isActive;
    }
  },
  template: `
    <div class="component-item--wrapper" draggable="true" v-on:dragstart="dragStart($event, index)" v-on:dragend="dragEnd($event, field_name)">
      <a v-show="cardinality != 1" class="tabledrag-handle" title="Drag to re-order">
        <div class="handle">&nbsp;</div>
      </a>
      <div class="jfu-toggle" v-on:click="activeToggle">
        <span>{{ o_data.label }}</span>
        <span class="jfu-toggle-trigger-icon" v-bind:class="{'is-active' :isActive}"></span>
      </div>
      <div class="accordion-item--content" v-bind:class="{'is-active' :isActive}">
        <display-content-block v-bind:o_data="o_data" v-bind:options="options"></display-content-block>
        <display-operations v-bind:index="index" v-bind:field_name="field_name" v-bind:is_load="is_load" v-bind:type="o_data.type" v-bind:options="options"></display-operations>
      </div>
    </div>
  `
});

// Define component display content block
Vue.component('display-content-block', {
  props: ['o_data', 'options'],
  template: `
    <div>
      <div><span>{{ options.defaultLabels.block_style }}: <em>{{ o_data.options.type }}</em></span></div>
      <div class="component-item block-item">
        <div v-if="o_data.title" class="block-item--title">
          <h3>{{ o_data.title }}</h3>
        </div>
        <display-text v-bind:content="o_data.subtitle"></display-text>
        <div v-if="o_data.body.value" class="block-item--body">
          <display-text v-bind:content="o_data.body.value"></display-text>
        </div>
        <div class="block-item--blockid">
          <span v-if="o_data.blockid">[{{ o_data.blockname }}:{{ o_data.blockid }}]</span>
          <span v-else>{{ options.defaultLabels.valid_block }}</span>
          <a v-if="o_data.bc_link" v-bind:href="options.urlBase + o_data.bc_link" class="jfu-edit-block" target=”_blank”>Editar</a>
        </div>
      </div>
    </div>
  `
});

// Define component form block
Vue.component('form-block', {
  props: ['o_json', 'edit_index', 'field_name', 'options'],
  data: function() {
    return {
      current_json: this.o_json,
      search_data:[],
      block_selected: false
    };
  },
  template: `
    <form v-on:submit.prevent="edit_index === '' ? addToJfu($event) : updateToJfu($event, o_json.type, edit_index)">
      <form-elements-block v-bind:o_json="o_json" v-bind:field_name="field_name" v-bind:index="0" v-bind:options="options"></form-elements-block>
      <input class="link-to link-to-add" type="submit" v-bind:value="options.defaultLabels.save" />
    </form>
  `
});

// Define component form elements block
Vue.component('form-elements-block', {
  props: ['o_json', 'field_name', 'index', 'options'],
  data: function() {
    return {
      current_json: this.o_json,
      search_data:[],
      block_selected: false,
      search_store: false
    };
  },
  template: `
    <div class="form-component">
      <p>
        <label for="jfu-block-type">{{ options.defaultLabels.type }}</label>
        <select v-model="o_json.options.type" id="jfu-block-type">
          <option value="default">{{ options.defaultLabels.default }}</option>
          <option value="row">{{ options.defaultLabels.row }}</option>
        </select>
      </p>
      <form-classes v-bind:o_json="o_json" v-bind:index="index" v-bind:field_name="field_name" v-bind:options="options"></form-classes>
      <p>
        <label>{{ options.defaultLabels.title }}
          <input type="text" name="block_title" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.title" v-model="o_json.title" />
        </label>
      </p>
      <p>
        <label>{{ options.defaultLabels.subtitle }}
          <input type="text" name="block_subtitle" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.subtitle" v-model="o_json.subtitle" />
        </label>
      </p>
      <form-text v-bind:o_json="o_json.body" v-bind:field_name="field_name" v-bind:options="options" v-bind:index="index"></form-text>
      <p>
        <div class="block-item--selected-block">
          <div class="block-item--selected-block--item">{{ options.defaultLabels.selected_block }}: <span >{{ o_json.blockname }}</span></div>
        </div>
        <div class="jfu-block-item--field">
          <label>{{ options.defaultLabels.search_block_for_the_blockname }}
            <input
              type="text"
              name="blocklabel"
              size="60"
              maxlength="255"
              v-bind:placeholder="options.defaultLabels.block_name"
              v-model="o_json.blocklabel"
              @keyup="getData()"
              autocomplete="off"
              required />
          </label>
          <span v-bind:title="options.defaultLabels.update_lb" class="jbi--icon" @click="updateSearchStore()">Update</span>
        </div>
        <span class="jfu-input--description">{{ options.defaultLabels.help_text_block }}</span>
        <div class="jfu-block-options">
          <ul class="jfu-block-options--list">
            <li v-for="(data, index) in search_data" style="display: block;">
              <a href="#" class="list-group-item" @click="getName(data.id, data.admin_label)">{{ data.admin_label  }}</a>
            </li>
          </ul>
        </div>
        <div class="form-component--inline">
          <label>{{ options.defaultLabels.machine_name }}
            <input
              type="text"
              name="blockid"
              size="60"
              maxlength="255"
              placeholder="Block ID"
              v-model="o_json.blockid"
              @keyup="getData()"
              autocomplete="off"
              disabled
              required />
          </label>
        </div>
      </p>
      <p>
        <label>{{ options.defaultLabels.full_width }}
          <label for="full_width"><input type="checkbox" id="full_width" v-model="o_json.options.full_width">{{ options.defaultLabels.activate_full_width }}</label>
        </label>
      </p>
    </div>
  `,
  methods: {
    getData:function() {
      if (!this.search_store) {
        this.search_data = {};
        axios.get(this.options.urlBase + '/jfu_rest_api/block_list_resource?_format=json').then(response => {
          this.search_store = response.data;
          if (this.o_json.blocklabel.length >= 1) {
            this.getBlock(response.data);
          }
        });
      }
      else {
        if (this.o_json.blocklabel.length >= 1) {
          this.getBlock(this.search_store);
        }
      }
    },
    getName:function(id, label) {
      var encoded_id = btoa(id);
      if (encoded_id.length != 0) {
        axios.get(this.options.urlBase + '/jfu_rest_api/block_content_id/' + encoded_id +'?_format=json').then(response => {
          if (response.status == 200) {
            this.o_json.bc_link = response.data.bc_link;
          }
        });
      }

      this.o_json.blockid = id;
      this.o_json.blocklabel = label;
      this.o_json.blockname = label;
      this.block_selected = true;
      this.search_data = [];
    },
    getBlock:function (data) {
      let searchData = {};

      for (const [i, item] of Object.entries(data)) {
        if (item['admin_label'].substr(0, this.o_json.blocklabel.length).toUpperCase() == this.o_json.blocklabel.toUpperCase()) {
          searchData[i] = {
            //'block_name' : item['admin_label'],
            'admin_label' : item['admin_label'],
            'id' : i
          }
        }
      }

      this.search_data = searchData;
    },
    updateSearchStore:function () {
      this.search_store = false;
    }
  }
});

// Define component display gallery
Vue.component('display-gallery', {
  props: ['o_data', 'index', 'field_name', 'is_load', 'cardinality', 'options'],
  data: function () {
    return {
      isActive: true
    };
  },
  methods: {
    activeToggle: function() {
      this.isActive = !this.isActive;
    }
  },
  template: `
    <div class="component-item--wrapper" draggable="true" v-on:dragstart="dragStart($event, index)" v-on:dragend="dragEnd($event, field_name)">
      <a v-show="cardinality != 1" class="tabledrag-handle" title="Drag to re-order">
        <div class="handle">&nbsp;</div>
      </a>
      <div class="jfu-toggle" v-on:click="activeToggle">
        <span>{{ o_data.label }}</span>
        <span class="jfu-toggle-trigger-icon" v-bind:class="{'is-active' :isActive}"></span>
      </div>
      <div class="accordion-item--content" v-bind:class="{'is-active' :isActive}">
        <display-content-gallery v-bind:o_data="o_data" v-bind:options="options"></display-content-gallery>
        <display-operations v-bind:index="index" v-bind:field_name="field_name" v-bind:is_load="is_load" v-bind:type="o_data.type" v-bind:options="options"></display-operations>
      </div>
    </div>
  `
});

// Define component display content gallery
Vue.component('display-content-gallery', {
  props: ['o_data', 'options'],
  template: `
    <div class="component-item gallery-container">
      <h3 class="gallery-item--title">{{ o_data.title }}</h3>
      <display-text v-bind:content="o_data.subtitle"></display-text>
      <div class="gallery-item--content">
        <display-text v-bind:content="o_data.body.value"></display-text>
      </div>
      <div class="gallery-wrapper">
        <div v-for="(item, index) in o_data.items.items" class="gallery-item">
          <display-multiple-image v-bind:o_m_image="item" v-bind:options="options"></display-multiple-image>
        </div>
      </div>
    </div>
  `
});

// Define component form gallery
Vue.component('form-gallery', {
  props: ['o_json', 'edit_index', 'index', 'field_name', 'options'],
  data: function() {
    return {
      current_json: this.o_json
    };
  },
  template: `
    <form v-on:submit.prevent="edit_index === '' ? addToJfu($event) : updateToJfu($event, o_json.type, edit_index)">
      <form-elements-gallery v-bind:o_json="o_json" v-bind:field_name="field_name" v-bind:index="0" v-bind:options="options"></form-elements-gallery>
      <div class="error-messages" style="display: none;">
        <span>{{ options.defaultLabels.image_required }}</span>
      </div>
      <input class="link-to link-to-add" type="submit" v-bind:value="options.defaultLabels.save" />
    </form>
  `
});

// Define component form elements gallery
Vue.component('form-elements-gallery', {
  props: ['o_json', 'index', 'field_name', 'options'],
  data: function() {
    return {
      current_json: this.o_json
    };
  },
  template: `
    <div class="form-component">
      <form-classes v-bind:o_json="o_json" v-bind:index="index" v-bind:field_name="field_name" v-bind:options="options"></form-classes>
      <p>
        <label>{{ options.defaultLabels.title }}
          <input type="text" name="gallery-title" v-model="o_json.title" />
        </label>
      </p>
      <p>
        <label>{{ options.defaultLabels.subtitle }}
          <input type="text" name="gallery-subtitle" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.subtitle" v-model="o_json.subtitle" />
        </label>
      </p>
      <form-text v-bind:o_json="o_json.body" v-bind:field_name="field_name" v-bind:options="options" v-bind:index="index"></form-text>
      <form-multiple-image v-bind:o_multiple_images="o_json.items.items" v-bind:field_name="field_name" v-bind:index="index" v-bind:component="'gallery'" v-bind:options="options"></form-multiple-image>
      <p>
        <label>{{ options.defaultLabels.full_width }}
          <label><input type="checkbox" id="full_width" v-model="o_json.options.full_width">{{ options.defaultLabels.activate_full_width }}</label>
        </label>
      </p>
    </div>
  `
});

//Define component to multiple images
Vue.component('form-multiple-image', {
  props: ['o_multiple_images', 'index', 'field_name', 'component', 'options'],
  updated: function () {
    this.$nextTick(function () {
      // Reaload ajax link
      app.reloadAjaxLinks();
    });
  },
  methods: {
    addElement: function (event) {
      var container = jQuery(event.target).parent();
      var i = this.o_multiple_images.length - 1;
      if (this.o_multiple_images[i].image.fid === '') {
        container.find('.error-messages').show();
        setTimeout(function() {
          container.find('.error-messages').hide();
        }, 2000);
        return;
      }

      var anotherImage = {
        "type": "gallery_image",
        "title": "",
        "image": {
          "type": "image",
          "fid": "",
          "alt": "",
          "value": "",
          "target": ""
        }
      };
      this.o_multiple_images.push(anotherImage);
    },
    deleteItem: function(event, items, key) {
      items.splice(parseInt(key), 1);
    }
  },
  template: `
    <div class="jfu-form-multiple-image">
      <div class="jfu-link-image">
        <div v-for="(m_image, i) in o_multiple_images" class="jfu-link-image--item" @drop="onDrop($event, i, o_multiple_images)" @dragover.prevent @dragenter.prevent>
          <div class="jfu-tabledrag" draggable  @dragstart="startDrag($event, i)">
            <div class="jfu-toggle jfu-multi-row-accordion-edit" v-on:click="activeToggle('.gallery-item-' + i, '.gallery-toggle-item-' + i)">
              <a class="tabledrag-handle" title="Drag to re-order"><div class="handle">&nbsp;</div></a>
              <span>{{ options.defaultLabels.gallery + ' - ' + m_image.title }}
                <span class="jfu-drop-changed" :class="'jfu-drop-changed-' + i"> * </span>
              </span>
              <span class="jfu-toggle-trigger-icon is-active" v-bind:class="'gallery-toggle-item-' + i"></span>
            </div>
            <div class="accordion-item--content is-active" :class="'gallery-item-' + i">
              <p>
                <label>{{ options.defaultLabels.title }}
                  <input type="text" v-bind:name="field_name + 'm_image_title'" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.title" v-model="m_image.title" />
                </label>
              </p>
              <form-image v-bind:o_image="m_image.image" v-bind:field_name="field_name" v-bind:subindex="i" v-bind:index="index" v-bind:component="component" v-bind:options="options"></form-image>
              <div class="operations">
                <span class="jfu-button jfu-button--delete" v-on:click="deleteItem($event, o_multiple_images, i)">{{ options.defaultLabels.delete_item }}</span>
              </div>
            </div>
          </div>
        </div>
        <div class="error-messages" style="display: none;">
          <span>{{ options.defaultLabels.image_required }}</span>
        </div>
        <span v-on:click="addElement($event)" class="jfu-button jfu-button--edit ade--link">{{ options.defaultLabels.add_new_item }}</span>
      </div>
    </div>
  `
});

//Define component display multiple images
Vue.component('display-multiple-image', {
  props: ['o_m_image', 'options'],
  template: `
    <div>
      <display-image v-bind:o_image="o_m_image.image" v-bind:options="options"></display-image>
      <display-text v-bind:content="o_m_image.title"></display-text>
    </div>
  `
});

// Define component display embed
Vue.component('display-embed', {
  props: ['o_data', 'index', 'field_name', 'is_load', 'cardinality', 'options'],
  data: function () {
    return {
      isActive: true
    };
  },
  methods: {
    activeToggle: function() {
      this.isActive = !this.isActive;
    }
  },
  template: `
    <div class="component-item--wrapper" draggable="true" v-on:dragstart="dragStart($event, index)" v-on:dragend="dragEnd($event, field_name)">
      <a v-show="cardinality != 1" class="tabledrag-handle" title="Drag to re-order">
        <div class="handle">&nbsp;</div>
      </a>
      <div class="jfu-toggle" v-on:click="activeToggle">
        <span>{{ o_data.label }}</span>
        <span class="jfu-toggle-trigger-icon" v-bind:class="{'is-active' :isActive}"></span>
      </div>
      <div class="accordion-item--content" v-bind:class="{'is-active' :isActive}">
        <display-content-embed v-bind:o_data="o_data"></display-content-embed>
        <display-operations v-bind:index="index" v-bind:field_name="field_name" v-bind:is_load="is_load" v-bind:type="o_data.type" v-bind:options="options"></display-operations>
      </div>
    </div>
  `
});

// Define component display content embed
Vue.component('display-content-embed', {
  props: ['o_data'],
  template: `
    <div class="component-item embed-item">
      <div  v-if="o_data.title != '' || o_data.body.value != ''" class="embed-item--iframe">{{ o_data.iframe }}</div>
      <div v-else class="embed-item--iframe embed-item--iframe-full">{{ o_data.iframe }}</div>
      <div class="embed-item--info" v-if="o_data.title != '' || o_data.body.value != '' || o_data.subtitle != ''">
        <div class="embed-item--title-container">
          <h3 class="embed-item--title">{{ o_data.title }}</h3>
        </div>
        <display-text v-bind:content="o_data.subtitle"></display-text>
        <div class="embed-item--content">
          <display-text v-bind:content="o_data.body.value"></display-text>
        </div>
      </div>
    </div>
  `
});

// Define component form embed
Vue.component('form-embed', {
  props: ['o_json', 'edit_index', 'field_name', 'options'],
  data: function() {
    return {
      current_json: this.o_json
    };
  },
  template: `
    <form v-on:submit.prevent="edit_index === '' ? addToJfu($event) : updateToJfu($event, o_json.type, edit_index)">
      <form-elements-embed v-bind:o_json="o_json" v-bind:field_name="field_name" v-bind:options="options" v-bind:index="0"></form-elements-embed>
      <div class="error-messages" style="display: none;">
        <span>Invalid iframe.</span>
      </div>
      <input class="link-to link-to-add" type="submit" v-bind:value="options.defaultLabels.save" />
    </form>
  `
});

// Define component form elements embed
Vue.component('form-elements-embed', {
  props: ['o_json', 'field_name', 'options', 'index'],
  data: function() {
    return {
      current_json: this.o_json,
      current_selector: '#edit-'  + this.field_name.replaceAll('_', '-') + '-wrapper'
    };
  },
  methods: {
    youtubeValidateUrl: function(event) {
      var url = jQuery(this.current_selector + ' input[name="embed_video_youtube_' + this.index + '"]').val();
      this.valueYoutubeIframe(url);
    },
    vimeoValidateUrl: function(event) {
      var url = jQuery(this.current_selector + ' input[name="embed_video_vimeo_' + this.index + '"]').val();
      this.valueVimeoIframe(url);
    },
    changeEmbedType: function(event) {
      var embed_type = jQuery(this.current_selector + ' select[name="embed_type_' + this.index + '"]').val();
      jQuery(this.current_selector + ' input[name="embed_video_youtube_' + this.index + '"]').parent().hide();
      jQuery(this.current_selector + ' input[name="embed_video_vimeo_' + this.index + '"]').parent().hide();
      jQuery(this.current_selector + ' textarea[name="embed_iframe_' + this.index + '"]').parent().hide();
      switch (embed_type) {
        case 'youtube':
          jQuery(this.current_selector + ' input[name="embed_video_youtube_' + this.index + '"]').parent().show();
          var url = jQuery(this.current_selector + ' input[name="embed_video_youtube_' + this.index + '"]').val();
          if (url !== '') {
            this.valueYoutubeIframe(url);
          }
          break;
        case 'vimeo':
          jQuery(this.current_selector + ' input[name="embed_video_vimeo_' + this.index + '"]').parent().show();
          var url = jQuery(this.current_selector + ' input[name="embed_video_vimeo_' + this.index + '"]').val();
          if (url !== '') {
            this.valueVimeoIframe(url);
          }
          break;
        case 'embed':
          var url_youtube = jQuery(this.current_selector + ' input[name="embed_video_youtube_' + this.index + '"]').val();
          var url_vimeo = jQuery(this.current_selector + ' input[name="embed_video_vimeo_' + this.index + '"]').val();
          if (url_youtube != '' || url_vimeo != '') {
            jQuery(this.current_selector + ' textarea[name="embed_iframe_' + this.index + '"]').val('');
          }
          jQuery(this.current_selector + ' textarea[name="embed_iframe_' + this.index + '"]').parent().show();
          break;
        default:
          break;
      }
    },
    valueYoutubeIframe: function(url) {
      var match = url.match(/^https?:\/\/(www\.)?((?!.*list=)youtube\.com\/watch\?.*v=|youtu\.be\/)(?<id>[0-9A-Za-z_-]*)/);
      if (match !== null) {
        var id = match['groups']['id'];
        this.o_json.iframe = '<iframe width="640" height="360" src="https://www.youtube.com/embed/' + id + '" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" target_type="youtube" target_id="' + id + '" allowfullscreen></iframe>';
        jQuery(this.current_selector + ' textarea[name="embed_iframe_' + this.index + '"]').val(this.o_json.iframe);
      }
    },
    valueVimeoIframe: function(url) {
      var match = url.match(/^https?:\/\/(www\.)?vimeo.com\/(channels\/[a-zA-Z0-9]*\/)?(?<id>[0-9]*)(\/[a-zA-Z0-9]+)?(\#t=(\d+)s)?$/);
      if (match !== null) {
        var id = match['groups']['id'];
        this.o_json.iframe = '<iframe src="https://player.vimeo.com/video/' + id + '" width="640" height="360" frameborder="0" allow="autoplay; fullscreen; picture-in-picture" target_type="vimeo" target_id="' + id + '" allowfullscreen></iframe>';
        jQuery(this.current_selector + ' textarea[name="embed_iframe_' + this.index + '"]').val(this.o_json.iframe);
      }
    }
  },
  updated: function () {
    this.$nextTick(function () {
      if (this.o_json.iframe.indexOf('<iframe') != -1) {
        app.selectDefaultEmbed(this.index);
      }

      if (app.editIndex === '' && this.o_json.iframe === '') {
        jQuery(this.current_selector + ' input[name="embed_video_youtube_' + this.index + '"]').val('');
        jQuery(this.current_selector + ' input[name="embed_video_vimeo_' + this.index + '"]').val('');
      }
    });
  },
  template: `
    <div class="form-component">
      <form-classes v-bind:o_json="o_json" v-bind:index="index" v-bind:field_name="field_name" v-bind:options="options"></form-classes>
      <p>
        <label for="jfu-embed-type">{{ options.defaultLabels.embed_type }}</label>
        <select @change="changeEmbedType($event)" v-bind:name="'embed_type_' + index" id="jfu-embed-type">
          <option value="embed">{{ options.defaultLabels.embed }}</option>
          <option value="youtube">{{ options.defaultLabels.youtube_l }}</option>
          <option value="vimeo">{{ options.defaultLabels.vimeo_l }}</option>
        </select>
      </p>
      <p style="display: none;">
        <label for="jfu-embed-youtube">{{ options.defaultLabels.text_youtube }}</label>
        <input id="jfu-embed-youtube" style="margin-bottom: 10px;" @blur="youtubeValidateUrl($event)" type="text" v-bind:name="'embed_video_youtube_' + index" />
          <span class="jfu-input--description">{{ options.defaultLabels.help_text_embed }} <strong>https://www.youtube.com/watch?v=XxXXxXXXxxXX</strong>.</span>
      </p>
      <p style="display: none;">
        <label for="jfu-embed-vimeo">{{ options.defaultLabels.text_vimeo }}</label>
        <input id="jfu-embed-vimeo" style="margin-bottom: 10px;" @blur="vimeoValidateUrl($event)" type="text" v-bind:name="'embed_video_vimeo_' + index" />
          <span class="jfu-input--description">{{ options.defaultLabels.help_text_embed }} <strong>https://vimeo.com/XXXXXXXXXX</strong>.</span>
      </p>
      <p>
        <label for="jfu-embed-iframe">{{ options.defaultLabels.iframe }}</label>
        <textarea id="jfu-embed-iframe" v-bind:name="'embed_iframe_' + index" v-model="o_json.iframe" required></textarea>
        <em>"{{ options.defaultLabels.description_embed }}"</em>
      </p>
      <p>
        <label>{{ options.defaultLabels.title }}
          <input type="text" name="embed_title" v-model="o_json.title" />
        </label>
      </p>
      <p>
        <label>{{ options.defaultLabels.subtitle }}
          <input type="text" name="embed_subtitle" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.subtitle" v-model="o_json.subtitle" />
        </label>
      </p>
      <form-text v-bind:o_json="o_json.body" v-bind:field_name="field_name" v-bind:options="options" v-bind:index="index"></form-text>
      <p>
        <label>{{ options.defaultLabels.full_width }}
          <label><input type="checkbox" id="full_width" v-model="o_json.options.full_width">{{ options.defaultLabels.activate_full_width }}</label>
        </label>
      </p>
    </div>
  `
});

// Define component display banners
Vue.component('display-banner', {
  props: ['o_data', 'index', 'field_name', 'is_load', 'cardinality', 'options'],
  data: function () {
    return {
      isActive: true
    };
  },
  methods: {
    activeToggle: function() {
      this.isActive = !this.isActive;
    }
  },
  template: `
    <div class="component-item--wrapper" draggable="true" v-on:dragstart="dragStart($event, index)" v-on:dragend="dragEnd($event, field_name)">
      <a v-show="cardinality != 1" class="tabledrag-handle" title="Drag to re-order">
        <div class="handle">&nbsp;</div>
      </a>
      <div class="jfu-toggle" v-on:click="activeToggle">
        <span>{{ o_data.label }}</span>
        <span class="jfu-toggle-trigger-icon" v-bind:class="{'is-active' :isActive}"></span>
      </div>
      <div class="accordion-item--content" v-bind:class="{'is-active' :isActive}">
        <display-content-banner v-bind:o_data="o_data" v-bind:options="options"></display-content-banner>
        <display-operations v-bind:index="index" v-bind:field_name="field_name" v-bind:is_load="is_load" v-bind:type="o_data.type" v-bind:options="options"></display-operations>
      </div>
    </div>
  `
});

// Define component display content banners
Vue.component('display-content-banner', {
  props: ['o_data', 'options'],
  template: `
    <div :class="'banner-' + o_data.options.type">
      <div><span>Estilo: <em>{{ o_data.options.type }}</em></span></div>
      <display-text v-bind:content="o_data.title"></display-text>
      <div class="banner-wrapper banner-wrapper-slideshow">
        <div v-for="(item, index) in o_data.items.items" class="banner-item" style="width: 100%;">
          <display-multiple-banner v-bind:o_m_image="item" v-bind:options="options" v-bind:type="o_data.options.type"></display-multiple-banner>
          <span class="bws--before" v-if="index > 0"></span>
          <span class="bws--after" v-if="index > 0"></span>
        </div>
      </div>
      <div v-for="(item, index) in o_data.items.items">
        <div v-if="index > 0">
          <span v-if="index == Object.keys(o_data.items.items).length - 1">
            <strong>SLIDESHOW:</strong> ({{ index + 1 }}) items
          </span>
        </div>
      </div>
    </div>
  `
});

//Define component display multiple images
Vue.component('display-multiple-banner', {
  props: ['o_m_image', 'options', 'type'],
  template: `
    <div>
      <div v-if="type === 'fullcolor'">
        <div v-bind:style="{backgroundColor: o_m_image.options.color1, color: o_m_image.options.color2, width: '100%', height: '200px'}"></div>
      </div>
      <div v-else>
        <display-image v-bind:o_image="o_m_image.image" v-bind:options="options"></display-image>
      </div>
      <div class="banner-item--content" v-bind:style="{color: o_m_image.options.color2, position: 'absolute'}">
        <display-text v-bind:content="o_m_image.title"></display-text>
        <display-text v-bind:content="o_m_image.subtitle"></display-text>
        <display-text v-bind:content="o_m_image.body.value"></display-text>
        <display-link v-bind:o_link="o_m_image.link" v-bind:options="options"></display-link>
      </div>
    </div>
  `
});

// Define component form banner
Vue.component('form-banner', {
  props: ['o_json', 'edit_index', 'index', 'field_name', 'component', 'options'],
  data: function() {
    return {
      current_json: this.o_json
    };
  },
  template: `
    <form v-on:submit.prevent="edit_index === '' ? addToJfu($event) : updateToJfu($event, o_json.type, edit_index)">
      <form-elements-banner v-bind:o_json="o_json" v-bind:field_name="field_name" v-bind:index="0" v-bind:options="options"></form-elements-banner>
      <div class="error-messages" style="display: none;">
        <span>{{ options.defaultLabels.image_required }}</span>
      </div>
      <input class="link-to link-to-add" type="submit" v-bind:value="options.defaultLabels.save" />
    </form>
  `
});

// Define component form elements banner
Vue.component('form-elements-banner', {
  props: ['o_json', 'index', 'field_name', 'options'],
  data: function() {
    return {
      current_json: this.o_json
    };
  },
  template: `
    <div class="form-component">
      <p>
        <label for="banner-type">{{ options.defaultLabels.type }}</label>
        <select v-model="o_json.options.type" id="banner-type">
          <option value="default">{{ options.defaultLabels.default }}</option>
          <option value="center">{{ options.defaultLabels.center_elements }}</option>
          <option value="fullcolor">{{ options.defaultLabels.solid_background }}</option>
        </select>
      </p>
      <form-classes v-bind:o_json="o_json" v-bind:index="index" v-bind:field_name="field_name" v-bind:options="options"></form-classes>
      <p>
        <label>{{ options.defaultLabels.banner_title }}
          <input type="text" name="banner_title" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.banner_title" v-model="o_json.title" />
        </label>
      </p>
      <div>
        <form-multiple-banner v-bind:o_multiple_images="o_json.items.items" v-bind:field_name="field_name" v-bind:index="index" v-bind:component="'banner'" v-bind:options="options" v-bind:type="o_json.options.type"></form-multiple-banner>
      </div>
      <p>
        <label>{{ options.defaultLabels.full_width }}
          <label><input type="checkbox" id="full_width" v-model="o_json.options.full_width">{{ options.defaultLabels.activate_full_width }}</label>
        </label>
      </p>
    </div>
  `
});

//Define component to multiple images
Vue.component('form-multiple-banner', {
  props: ['o_multiple_images', 'index', 'field_name', 'component', 'options', 'type'],
  updated: function () {
    this.$nextTick(function () {
      // Reaload ajax link
      app.reloadAjaxLinks();
    });
  },
  methods: {
    addElement: function (event) {
      var container = jQuery(event.target).parent();
      var i = this.o_multiple_images.length - 1;
      if (this.type !== 'fullcolor' && this.o_multiple_images[i].image.fid === '') {
        container.find('.error-messages').show();
        setTimeout(function() {
          container.find('.error-messages').hide();
        }, 2000);
        return;
      }

      var anotherImage = {
        "type": "banner-item",
        "title": "",
        "subtitle": "",
        "image": {
          "type": "image",
          "fid": "",
          "value": "",
          "title": "",
          "alt": ""
        },
        "responsive_image": {
          "type": "image",
          "fid": "",
          "value": "",
          "title": "",
          "alt": ""
        },
        "link": {
          "type": "link",
          "value": "",
          "text": "",
          "target": "",
          "external": true,
          "relative": ""
        },
        "body": {
          "type": "text",
          "value": "",
          "format": "advance_html"
        },
        "options": {
          "color1": "",
          "color2": ""
        }
      };

      this.o_multiple_images.push(anotherImage);
    },
    deleteItem: function(event, items, key) {
      items.splice(parseInt(key), 1);
    }
  },
  template: `
    <div class="jfu-form-multiple-image">
      <div class="jfu-link-image">
        <div v-for="(m_image, i) in o_multiple_images" class="jfu-link-image--item" @drop="onDrop($event, i, o_multiple_images)" @dragover.prevent @dragenter.prevent>
          <div class="jfu-tabledrag" draggable  @dragstart="startDrag($event, i)">
            <div class="jfu-toggle jfu-multi-row-accordion-edit" v-on:click="activeToggle('.banner-item-' + i, '.banner-toggle-item-' + i)">
              <a class="tabledrag-handle" title="Drag to re-order"><div class="handle">&nbsp;</div></a>
              <span>{{ 'Banner - ' + (m_image.title ? m_image.title : i + 1) }}
                <span class="jfu-drop-changed" :class="'jfu-drop-changed-' + i"> * </span>
              </span>
              <span class="jfu-toggle-trigger-icon is-active" v-bind:class="'banner-toggle-item-' + i"></span>
            </div>
            <div class="accordion-item--content is-active" :class="'banner-item-' + i">
              <p>
                <label>{{ options.defaultLabels.title }}
                  <input type="text" v-bind:name="field_name + 'm_image_title'" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.title" v-model="m_image.title" />
                </label>
              </p>
              <p>
                <label>{{ options.defaultLabels.subtitle }}
                  <input type="text" v-bind:name="field_name + 'm_image_subtitle'" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.subtitle" v-model="m_image.subtitle" />
                </label>
              </p>
              <div v-if="type === 'fullcolor'" class="form-component--inline">
                <p>
                  <label>{{ options.defaultLabels.background_color }}
                    <input type="color" v-model="m_image.options.color1" required>
                  </label>
                </p>
                <p>
                  <label>{{ options.defaultLabels.element_color }}
                    <input type="color" v-model="m_image.options.color2" required>
                  </label>
                </p>
              </div>
              <div v-else>
                <form-image v-bind:o_image="m_image.image" v-bind:field_name="field_name" v-bind:subindex="i" v-bind:index="index" v-bind:component="component" v-bind:options="options" v-bind:subindexitem="'image'"></form-image>
                <form-image v-bind:o_image="m_image.responsive_image" v-bind:field_name="field_name" v-bind:subindex="i" v-bind:index="index" v-bind:component="component" v-bind:options="options" v-bind:subindexitem="'responsive_image'"></form-image>
              </div>
              <form-link v-bind:o_link="m_image.link" v-bind:field_name="field_name" v-bind:options="options"></form-link>
              <form-text v-bind:o_json="m_image.body" v-bind:field_name="field_name" v-bind:options="options" v-bind:index="index" v-bind:subindex="i"></form-text>
              <div v-if="o_multiple_images.length > 1" class="operations">
                <span class="jfu-button jfu-button--delete" v-on:click="deleteItem($event, o_multiple_images, i)">{{ options.defaultLabels.delete_item }}</span>
              </div>
            </div>
          </div>
        </div>
        <div class="error-messages" style="display: none;">
          <span>{{ options.defaultLabels.image_required }}</span>
        </div>
        <span v-on:click="addElement($event)" class="jfu-button jfu-button--edit ade--link">{{ options.defaultLabels.add_new_item }}</span>
      </div>
    </div>
  `
});

//Define component display multiple images
Vue.component('display-multiple-image', {
  props: ['o_m_image', 'options'],
  template: `
    <div>
      <display-image v-bind:o_image="o_m_image.image" v-bind:options="options"></display-image>
      <display-text v-bind:content="o_m_image.title"></display-text>
    </div>
  `
});

// Define component display simple card
Vue.component('display-simple_card', {
  props: ['o_data', 'index', 'field_name', 'is_load', 'cardinality', 'options'],
  data: function () {
    return {
      isActive: true
    };
  },
  methods: {
    activeToggle: function() {
      this.isActive = !this.isActive;
    }
  },
  template: `
    <div class="component-item--wrapper" draggable="true" v-on:dragstart="dragStart($event, index)" v-on:dragend="dragEnd($event, field_name)">
      <a v-show="cardinality != 1" class="tabledrag-handle" title="Drag to re-order">
        <div class="handle">&nbsp;</div>
      </a>
      <div class="jfu-toggle" v-on:click="activeToggle">
        <span>{{ o_data.label }}</span>
        <span class="jfu-toggle-trigger-icon" v-bind:class="{'is-active' :isActive}"></span>
      </div>
      <div class="accordion-item--content" v-bind:class="{'is-active' :isActive}">
        <display-content-simple_card v-bind:o_data="o_data" v-bind:options="options"></display-content-simple_card>
        <display-operations v-bind:index="index" v-bind:field_name="field_name" v-bind:is_load="is_load" v-bind:type="o_data.type" v-bind:options="options"></display-operations>
      </div>
    </div>
  `
});

// Define component display content simple card
Vue.component('display-content-simple_card', {
  props: ['o_data', 'options'],
  template: `
    <div class="component-item simple-card-item"  v-bind:class="'jfu--text-image--' + o_data.options.aligment">
      <div class="simple-card-item--item simple-card-info">
        <div class="simple-card-info--title">
          <h3 v-if="o_data.title" class="simple-card--title">{{ o_data.title }}</h3>
        </div>
        <div class="simple-card-info--content">
          <div class="simple-card-info--subtitle" v-if="o_data.subtitle">
            <display-text v-bind:content="o_data.subtitle"></display-text>
          </div>
          <p class="simple-card-info--description" v-if="o_data.body">
            <display-text v-bind:content="o_data.body.value"></display-text>
          </p>
          <div v-if="o_data.link">
            <display-link v-bind:o_link="o_data.link" v-bind:options="options"></display-link>
          </div>
        </div>
      </div>
      <div class="simple-card-item--item simple-card-info--image">
        <display-image v-bind:o_image="o_data.image" v-bind:options="options"></display-image>
      </div>
    </div>
  `
});

// Define component form simple card
Vue.component('form-simple_card', {
  props: ['o_json', 'edit_index', 'field_name', 'options'],
  data: function() {
    return {
      current_json: this.o_json
    };
  },
  template: `
    <form v-on:submit.prevent="edit_index === '' ? addToJfu($event) : updateToJfu($event, o_json.type, edit_index)">
      <form-elements-simple_card v-bind:o_json="o_json" v-bind:field_name="field_name" v-bind:index="0" v-bind:options="options"></form-elements-simple_card>
      <div class="error-messages" style="display: none;">
        <span>{{ options.defaultLabels.image_required }}</span>
      </div>
      <input class="link-to link-to-add" type="submit" v-bind:value="options.defaultLabels.save" />
    </form>
  `
});

// Define component form elements simple card
Vue.component('form-elements-simple_card', {
  props: ['o_json', 'index', 'field_name', 'options'],
  data: function() {
    return {
      current_json: this.o_json
    };
  },
  template: `
    <div class="form-component">
      <form-classes v-bind:o_json="o_json" v-bind:index="index" v-bind:field_name="field_name" v-bind:options="options"></form-classes>
      <p>
        <label>{{ options.defaultLabels.title }}
          <input type="text" name="simple_card_title" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.title" v-model="o_json.title" />
        </label>
      </p>
      <p>
        <label>{{ options.defaultLabels.subtitle }}
          <input type="text" name="simple_card_title" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.subtitle" v-model="o_json.subtitle" />
        </label>
      </p>
      <form-text v-bind:o_json="o_json.body" v-bind:field_name="field_name" v-bind:options="options" v-bind:index="index"></form-text>
      <form-image v-bind:o_image="o_json.image" v-bind:field_name="field_name" v-bind:index="index" v-bind:component="'simple_card'" v-bind:options="options"></form-image>
      <div v-if="o_json.image.fid != ''">
        <label for="jfu-simple-card-aligment">{{ options.defaultLabels.aligment }}</label>
        <select v-model="o_json.options.aligment" id="jfu-simple-card-aligment">
          <option value="left">{{ options.defaultLabels.left }}</option>
          <option value="right">{{ options.defaultLabels.right }}</option>
          <option value="top">{{ options.defaultLabels.top }}</option>
          <option value="bottom">{{ options.defaultLabels.bottom }}</option>
        </select>
      </div>
      <form-link v-bind:o_link="o_json.link" v-bind:field_name="field_name" v-bind:options="options"></form-link>
      <p>
        <label>{{ options.defaultLabels.full_width }}
          <label><input type="checkbox" id="full_width" v-model="o_json.options.full_width">{{ options.defaultLabels.activate_full_width }}</label>
        </label>
      </p>
    </div>
  `
});

// Define component display content columns
Vue.component('display-content_columns', {
  props: ['o_data', 'index', 'field_name', 'is_load', 'cardinality', 'options'],
  data: function () {
    return {
      isActive: true
    };
  },
  methods: {
    activeToggle: function() {
      this.isActive = !this.isActive;
    }
  },
  template: `
    <div class="component-item--wrapper" draggable="true" v-on:dragstart="dragStart($event, index)" v-on:dragend="dragEnd($event, field_name)">
      <a v-show="cardinality != 1" class="tabledrag-handle" title="Drag to re-order">
        <div class="handle">&nbsp;</div>
      </a>
      <div class="jfu-toggle" v-on:click="activeToggle">
        <span>{{ o_data.label }}</span>
        <span class="jfu-toggle-trigger-icon" v-bind:class="{'is-active' :isActive}"></span>
      </div>
      <div class="accordion-item--content" v-bind:class="{'is-active' :isActive}">
        <display-content-content_columns v-bind:o_data="o_data" v-bind:options="options"></display-content-content_columns>
        <display-operations v-bind:index="index" v-bind:field_name="field_name" v-bind:is_load="is_load" v-bind:type="o_data.type" v-bind:options="options"></display-operations>
      </div>
    </div>
  `
});

// Define component display content content_columns
Vue.component('display-content-content_columns', {
  props: ['o_data', 'options'],
  template: `
    <div class="component-item content-columns" >
      <div class="content-columns-header">
        <h3 v-if="o_data.title" class="content-columns--title">{{ o_data.title }}</h3>
        <p v-if="o_data.subtitle">
          <display-text v-bind:content="o_data.subtitle"></display-text>
        </p>
        <div class="content-columns-header--content">
          <p v-if="o_data.body">
            <display-text v-bind:content="o_data.body.value"></display-text>
          </p>
        </div>
      </div>
      <div :class="'content-columns--content content-columns--' + o_data.options.type" style="display: flex;">
        <div v-for="(item, index) in o_data.items.items" style="padding: 5px 10px; width: 100%;" class="content-columns--item">
          <div v-if="o_data.options.type =='text'">
            <display-text-image v-bind:o_link_image="item" v-bind:options="options"></display-text-image>
          </div>
          <div v-else>
            <display-link-image v-bind:o_link_image="item" v-bind:options="options"></display-link-image>
          </div>
        </div>
      </div>
      <div v-if="o_data.link">
        <display-link v-bind:o_link="o_data.link" v-bind:options="options"></display-link>
      </div>
    </div>
  `
});

// Define component form content_columns
Vue.component('form-content_columns', {
  props: ['o_json', 'edit_index', 'field_name', 'options'],
  data: function() {
    return {
      current_json: this.o_json
    };
  },
  template: `
    <form v-on:submit.prevent="edit_index === '' ? addToJfu($event) : updateToJfu($event, o_json.type, edit_index)">
      <form-elements-content_columns v-bind:o_json="o_json" v-bind:field_name="field_name" v-bind:index="0" v-bind:options="options"></form-elements-content_columns>
      <div class="error-messages" style="display: none;">
        <span>{{ options.defaultLabels.field_required }}</span>
      </div>
      <input class="link-to link-to-add" type="submit" v-bind:value="options.defaultLabels.save" />
    </form>
  `
});

// Define component form elements content columns
Vue.component('form-elements-content_columns', {
  props: ['o_json', 'index', 'field_name', 'options'],
  updated: function () {
    this.$nextTick(function () {
      app.reloadAjaxLinks();
    });
  },
  data: function() {
    return {
      current_json: this.o_json
    };
  },
  template: `
    <div class="form-component">
      <p>
        <label for="jfu-content-columns-type">{{ options.defaultLabels.type }}</label>
        <select v-model="o_json.options.type" id="jfu-content-columns-type">
          <option value="default">{{ options.defaultLabels.default }}</option>
          <option value="icons">{{ options.defaultLabels.icons }}</option>
          <option value="text">{{ options.defaultLabels.texts }}</option>
        </select>
      </p>
      <form-classes v-bind:o_json="o_json" v-bind:index="0" v-bind:field_name="field_name" v-bind:options="options"></form-classes>
      <p>
        <label>{{ options.defaultLabels.title }}
          <input type="text" name="content_columns_title" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.title" v-model="o_json.title" />
        </label>
      </p>
      <p>
        <label>{{ options.defaultLabels.subtitle }}
          <input type="text" name="content_columns_subtitle" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.subtitle" v-model="o_json.subtitle" />
        </label>
      </p>
      <form-text v-bind:o_json="o_json.body" v-bind:field_name="field_name" v-bind:options="options" v-bind:index="index"></form-text>
      <form-link v-bind:o_link="o_json.link" v-bind:field_name="field_name" v-bind:index="0" v-bind:options="options"></form-link>
      <div v-if="o_json.options.type == 'text'">
        <form-text-image v-bind:o_text_images="o_json.items.items" v-bind:field_name="field_name" v-bind:index="index" v-bind:component="'content_columns'" v-bind:options="options"></form-text-image>
      </div>
      <div v-else>
        <form-link-image v-bind:o_link_images="o_json.items.items" v-bind:field_name="field_name" v-bind:index="index" v-bind:component="'content_columns'" v-bind:options="options"></form-link-image>
      </div>
      <form-allow-group-items v-bind:o_json="o_json" v-bind:index="0" v-bind:field_name="field_name" v-bind:options="options" v-bind:component="'content_columns'"></form-allow-group-items>
      <p>
        <label>{{ options.defaultLabels.full_width }}
          <label><input type="checkbox" id="full_width" v-model="o_json.options.full_width">{{ options.defaultLabels.activate_full_width }}</label>
        </label>
      </p>
    </div>
  `
});

// Define component display data number card
Vue.component('display-data_number_card', {
  props: ['o_data', 'index', 'field_name', 'is_load', 'cardinality', 'options'],
  data: function () {
    return {
      isActive: true
    };
  },
  methods: {
    activeToggle: function() {
      this.isActive = !this.isActive;
    }
  },
  template: `
    <div class="component-item--wrapper" draggable="true" v-on:dragstart="dragStart($event, index)" v-on:dragend="dragEnd($event, field_name)">
      <a v-show="cardinality != 1" class="tabledrag-handle" title="Drag to re-order">
        <div class="handle">&nbsp;</div>
      </a>
      <div class="jfu-toggle" v-on:click="activeToggle">
        <span>{{ o_data.label }}</span>
        <span class="jfu-toggle-trigger-icon" v-bind:class="{'is-active' :isActive}"></span>
      </div>
      <div class="accordion-item--content" v-bind:class="{'is-active' :isActive}">
        <display-content-data_number_card v-bind:o_data="o_data" v-bind:options="options"></display-content-data_number_card>
        <display-operations v-bind:index="index" v-bind:field_name="field_name" v-bind:is_load="is_load" v-bind:type="o_data.type" v-bind:options="options"></display-operations>
      </div>
    </div>
  `
});

// Define component display content data number card
Vue.component('display-content-data_number_card', {
  props: ['o_data', 'options'],
  template: `
    <div class="component-item data-number-card-item"  v-bind:class="'jfu--text-image--' + o_data.options.aligment">
      <div class="data-number-card-item--item data-number-card-info">
        <div class="data-number-card-info--title">
          <h3 v-if="o_data.title" class="data-number-card--title">{{ o_data.title }}</h3>
        </div>
        <div class="data-number-card-info--content">
          <div class="data-number-card-info--subtitle" v-if="o_data.subtitle">
            <display-text v-bind:content="o_data.subtitle"></display-text>
          </div>
          <p class="data-number-card-info--description" v-if="o_data.body">
            <display-text v-bind:content="o_data.body.value"></display-text>
          </p>
          <div class="data-number-card-items data-number-card-wrapper">
            <div v-for="item in o_data.items.items" class="data-number-item">
              <span class="number--prefix">{{item.prefix}}</span>
              <span class="number--number">{{item.value}}</span>
              <span class="number--suffix">{{item.suffix}}</span>
              <p class="number--text">{{item.text}}</p>
            </div>
          </div>
          <div v-if="o_data.link">
            <display-link v-bind:o_link="o_data.link" v-bind:options="options"></display-link>
          </div>
        </div>
      </div>
      <div class="data-number-card-item--item data-number-card-info--image" v-if="o_data.image.value">
        <display-image v-bind:o_image="o_data.image" v-bind:options="options"></display-image>
      </div>
    </div>
  `
});

// Define component form data number card
Vue.component('form-data_number_card', {
  props: ['o_json', 'edit_index', 'field_name', 'options'],
  data: function() {
    return {
      current_json: this.o_json
    };
  },
  template: `
    <form v-on:submit.prevent="edit_index === '' ? addToJfu($event) : updateToJfu($event, o_json.type, edit_index)">
      <form-elements-data_number_card v-bind:o_json="o_json" v-bind:field_name="field_name" v-bind:index="0" v-bind:options="options"></form-elements-data_number_card>
      <input class="link-to link-to-add" type="submit" v-bind:value="options.defaultLabels.save" />
    </form>
  `
});

// Define component form elements data number card
Vue.component('form-elements-data_number_card', {
  props: ['o_json', 'index', 'field_name', 'options'],
  data: function() {
    return {
      current_json: this.o_json
    };
  },
  template: `
    <div class="form-component">
      <form-classes v-bind:o_json="o_json" v-bind:index="index" v-bind:field_name="field_name" v-bind:options="options"></form-classes>
      <p>
        <label>{{ options.defaultLabels.title }}
          <input type="text" name="data_number_card_title" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.title" v-model="o_json.title" />
        </label>
      </p>
      <p>
        <label>{{ options.defaultLabels.subtitle }}
          <input type="text" name="data_number_card_title" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.subtitle" v-model="o_json.subtitle" />
        </label>
      </p>
      <form-text v-bind:o_json="o_json.body" v-bind:field_name="field_name" v-bind:options="options" v-bind:index="index"></form-text>
      <form-image v-bind:o_image="o_json.image" v-bind:field_name="field_name" v-bind:index="index" v-bind:component="'data_number_card'" v-bind:options="options"></form-image>
      <div v-if="o_json.image.fid != ''">
        <label for="jfu-data-number-card-aligment">{{ options.defaultLabels.aligment }}</label>
        <select v-model="o_json.options.aligment" id="jfu-data-number-card-aligment">
          <option value="left">{{ options.defaultLabels.left }}</option>
          <option value="right">{{ options.defaultLabels.right }}</option>
          <option value="top">{{ options.defaultLabels.top }}</option>
          <option value="bottom">{{ options.defaultLabels.bottom }}</option>
        </select>
      </div>
      <form-link v-bind:o_link="o_json.link" v-bind:field_name="field_name" v-bind:options="options"></form-link>
      <form-data-number v-bind:o_data_number="o_json.items.items" v-bind:field_name="field_name" v-bind:options="options"></form-data-number>
      <form-allow-group-items v-bind:o_json="o_json" v-bind:index="index" v-bind:field_name="field_name" v-bind:options="options" v-bind:component="'data_number_card'"></form-allow-group-items>
      <p>
        <label>{{ options.defaultLabels.full_width }}
          <label><input type="checkbox" id="full_width" v-model="o_json.options.full_width">{{ options.defaultLabels.activate_full_width }}</label>
        </label>
      </p>
    </div>
  `
});

// Define component form data number
Vue.component('form-data-number', {
  props: ['o_data_number', 'index', 'field_name', 'component', 'options'],
  methods: {
    addElement: function (event) {
      var container = jQuery(event.target).parent().parent();
      var i = this.o_data_number.length - 1;
      if (this.o_data_number[i].prefix === '' && this.o_data_number[i].suffix === ''
        && this.o_data_number[i].value === '' && this.o_data_number[i].text === '') {
        container.find('.error-messages').show();
        setTimeout(function() {
          container.find('.error-messages').hide();
        }, 2000);
        return;
      }
      var number = {
        "type": "data_number",
        "suffix": "",
        "value": "",
        "prefix": "",
        "text": ""
      };
      this.o_data_number.push(number);
    },
    deleteItem: function(event, items, key) {
      items.splice(parseInt(key), 1);
    }
  },
  template: `
    <div class="jfu-data-number">
      <div v-for="(value, i) in o_data_number" class="jfu-form-data-number jfu-field-form--container" @drop="onDrop($event, i, o_data_number)" @dragover.prevent @dragenter.prevent>
        <div class="jfu-tabledrag" draggable  @dragstart="startDrag($event, i)">
          <div class="jfu-toggle jfu-multi-row-accordion-edit" v-on:click="activeToggle('.data-number-item-' + i, '.data-number-toggle-item-' + i)">
            <a class="tabledrag-handle" title="Drag to re-order"><div class="handle">&nbsp;</div></a>
            <span>{{ 'Data - ' + (value.prefix ? value.prefix : i + 1) }}
              <span class="jfu-drop-changed" :class="'jfu-drop-changed-' + i"> * </span>
            </span>
            <span class="jfu-toggle-trigger-icon is-active" v-bind:class="'data-number-toggle-item-' + i"></span>
          </div>
          <div class="accordion-item--content is-active" :class="'data-number-item-' + i">
            <div style="display: flex; align-items: center;">
              <p>
                <label>{{ options.defaultLabels.prefix }}
                  <input type="text" v-bind:name="field_name + '_prefix'" size="60" maxlength="10" v-bind:placeholder="options.defaultLabels.prefi" v-model="value.prefix" />
                </label>
              </p>
              <p style="padding: 0 10px;">
                <label>{{ options.defaultLabels.value }}
                  <input type="number" v-bind:name="field_name + '_value'" size="60" maxlength="10" v-bind:placeholder="options.defaultLabels.value" v-model="value.value" required />
                </label>
              </p>
              <p>
                <label>{{ options.defaultLabels.suffix }}
                  <input type="text"  v-bind:name="field_name + '_suffix'" size="60" maxlength="10" v-bind:placeholder="options.defaultLabels.suffix" v-model="value.suffix" />
                </label>
              </p>
            </div>
            <p>
              <label>{{ options.defaultLabels.text }}
                <input type="text" v-bind:name="field_name + '_text'" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.text" v-model="value.text" />
              </label>
            </p>
            <div v-if="o_data_number.length > 1" class="operations" style="padding-top: 10px;">
              <span class="jfu-button jfu-button--delete" v-on:click="deleteItem($event, o_data_number, i)">{{ options.defaultLabels.delete_item }}</span>
            </div>
          </div>
        </div>
      </div>
      <div class="error-messages" style="display: none;">
        <span>{{ options.defaultLabels.field_required }}</span>
      </div>
      <span v-on:click="addElement($event)" class="jfu-button jfu-button--edit ade--data-number">{{ options.defaultLabels.add_new_item }}</span>
    </div>
  `
});

// Define component display rich text
Vue.component('display-rich_text', {
  props: ['o_data', 'index', 'field_name', 'is_load', 'cardinality', 'options'],
  data: function () {
    return {
      isActive: true
    };
  },
  methods: {
    activeToggle: function() {
      this.isActive = !this.isActive;
    }
  },
  template: `
    <div class="component-item--wrapper" draggable="true" v-on:dragstart="dragStart($event, index)" v-on:dragend="dragEnd($event, field_name)">
      <a v-show="cardinality != 1" class="tabledrag-handle" title="Drag to re-order">
        <div class="handle">&nbsp;</div>
      </a>
      <div class="jfu-toggle" v-on:click="activeToggle">
        <span>{{ o_data.label }}</span>
        <span class="jfu-toggle-trigger-icon" v-bind:class="{'is-active' :isActive}"></span>
      </div>
      <div class="accordion-item--content" v-bind:class="{'is-active' :isActive}">
        <display-content-rich_text v-bind:o_data="o_data"></display-content-rich_text>
        <display-operations v-bind:index="index" v-bind:field_name="field_name" v-bind:is_load="is_load" v-bind:type="o_data.type" v-bind:options="options"></display-operations>
      </div>
    </div>
  `
});

// Define component display content rich text
Vue.component('display-content-rich_text', {
  props: ['o_data'],
  template: `
    <div class="component-item rich-text-item">
      <div class="rich_text-item--title-container">
        <h3 v-if="o_data.title" class="rich-text--title">{{ o_data.title }}</h3>
      </div>
      <div class="rich_text-item--content">
        <div class="rich-text-info--subtitle" v-if="o_data.subtitle">
          <display-text v-bind:content="o_data.subtitle"></display-text>
        </div>
        <p class="rich-text-info--description" v-if="o_data.body">
          <display-text v-bind:content="o_data.body.value"></display-text>
        </p>
      </div>
    </div>
  `
});

// Define component form rich text
Vue.component('form-rich_text', {
  props: ['o_json', 'edit_index', 'field_name', 'options'],
  data: function() {
    return {
      current_json: this.o_json
    };
  },
  template: `
    <form v-on:submit.prevent="edit_index === '' ? addToJfu($event) : updateToJfu($event, o_json.type, edit_index)">
      <form-elements-rich_text v-bind:o_json="o_json" v-bind:field_name="field_name" v-bind:index="0" v-bind:options="options"></form-elements-rich_text>
      <input class="link-to link-to-add" type="submit" v-bind:value="options.defaultLabels.save" />
    </form>
  `
});

// Define component form content rich text
Vue.component('form-elements-rich_text', {
  props: ['o_json', 'field_name', 'index', 'options'],
  template: `
    <div class="form-component">
      <form-classes v-bind:o_json="o_json" v-bind:index="index" v-bind:field_name="field_name" v-bind:options="options"></form-classes>
      <p>
        <label>{{ options.defaultLabels.title }}
          <input type="text" name="rich_text_title" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.title" v-model="o_json.title" />
        </label>
      </p>
      <p>
        <label>{{ options.defaultLabels.subtitle }}
          <input type="text" name="rich_text_title" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.subtitle" v-model="o_json.subtitle" />
        </label>
      </p>
      <form-text v-bind:o_json="o_json.body" v-bind:field_name="field_name" v-bind:options="options" v-bind:index="index"></form-text>
      <p>
        <label>{{ options.defaultLabels.full_width }}
          <label><input type="checkbox" id="full_width" v-model="o_json.options.full_width">{{ options.defaultLabels.activate_full_width }}</label>
        </label>
      </p>
    </div>
  `
});

// Define component display link_list
Vue.component('display-link_list', {
  props: ['o_data', 'index', 'field_name', 'is_load', 'cardinality', 'options'],
  data: function () {
    return {
      isActive: true
    };
  },
  methods: {
    activeToggle: function() {
      this.isActive = !this.isActive;
    }
  },
  template: `
    <div class="component-item--wrapper" draggable="true" v-on:dragstart="dragStart($event, index)" v-on:dragend="dragEnd($event, field_name)">
      <a v-show="cardinality != 1" class="tabledrag-handle" title="Drag to re-order">
        <div class="handle">&nbsp;</div>
      </a>
      <div class="jfu-toggle" v-on:click="activeToggle">
        <span>{{ o_data.label }}</span>
        <span class="jfu-toggle-trigger-icon" v-bind:class="{'is-active' :isActive}"></span>
      </div>
      <div class="accordion-item--content" v-bind:class="{'is-active' :isActive}">
        <display-content-link_list v-bind:o_data="o_data"  v-bind:options="options"></display-content-link_list>
        <display-operations v-bind:index="index" v-bind:field_name="field_name" v-bind:is_load="is_load" v-bind:type="o_data.type" v-bind:options="options"></display-operations>
      </div>
    </div>
  `
});

// Define component display content link_list
Vue.component('display-content-link_list', {
  props: ['o_data', 'options'],
  template: `
    <div class="component-item rich-text-item">
      <div class="link_list-item--title-container">
        <h3 v-if="o_data.title" class="rich-text--title">{{ o_data.title }}</h3>
      </div>
      <div class="link_list-item--content">
        <div class="rich-text-info--subtitle" v-if="o_data.subtitle">
          <display-text v-bind:content="o_data.subtitle"></display-text>
        </div>
        <div v-for="item in o_data.items.items" v-if="item">
          <display-link v-bind:o_link="item" v-bind:options="options"></display-link>
        </div>
      </div>
    </div>
  `
});

// Define component form link_list
Vue.component('form-link_list', {
  props: ['o_json', 'edit_index', 'field_name', 'options'],
  data: function() {
    return {
      current_json: this.o_json
    };
  },
  template: `
    <form v-on:submit.prevent="edit_index === '' ? addToJfu($event) : updateToJfu($event, o_json.type, edit_index)">
      <form-elements-link_list v-bind:o_json="o_json" v-bind:field_name="field_name" v-bind:index="0" v-bind:options="options"></form-elements-link_list>
      <div class="error-messages" style="display: none;">
        <span>{{ options.defaultLabels.link_required }}</span>
      </div>
      <input class="link-to link-to-add" type="submit" v-bind:value="options.defaultLabels.save" />
    </form>
  `
});

// Define component form content link_list
Vue.component('form-elements-link_list', {
  props: ['o_json', 'field_name', 'index', 'options'],
  template: `
    <div class="form-component">
      <form-classes v-bind:o_json="o_json" v-bind:index="index" v-bind:field_name="field_name" v-bind:options="options"></form-classes>
      <p>
        <label>{{ options.defaultLabels.title }}
          <input type="text" name="link_list_title" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.title" v-model="o_json.title" />
        </label>
      </p>
      <p>
        <label>{{ options.defaultLabels.subtitle }}
          <input type="text" name="link_list_title" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.subtitle" v-model="o_json.subtitle" />
        </label>
      </p>
      <form-link_list_items v-bind:o_link_list_items="o_json.items.items" v-bind:field_name="field_name" v-bind:options="options"></form-link_list_items>
      <form-allow-group-items v-bind:o_json="o_json" v-bind:index="index" v-bind:field_name="field_name" v-bind:options="options" v-bind:component="'link_list'"></form-allow-group-items>
      <p>
        <label>{{ options.defaultLabels.full_width }}
          <label><input type="checkbox" id="full_width" v-model="o_json.options.full_width">{{ options.defaultLabels.activate_full_width }}</label>
        </label>
      </p>
    </div>
  `
});

// Define component form link_list_items
Vue.component('form-link_list_items', {
  props: ['o_link_list_items', 'index', 'field_name', 'component', 'options'],
  methods: {
    addElement: function (event) {
      var container = jQuery(event.target).parent();
      var i = this.o_link_list_items.length - 1;
      if (this.o_link_list_items[i].value === '') {
        container.find('.error-messages').show();
        setTimeout(function() {
          container.find('.error-messages').hide();
        }, 2000);
        return;
      }
      var link = {
        "type": "link",
        "value": "",
        "text": "",
        "target": "",
        "external": true,
        "relative": ""
      };
      this.o_link_list_items.push(link);
    },
    deleteItem: function(event, items, key) {
      items.splice(parseInt(key), 1);
    }
  },
  template: `
    <div class="jfu-link-list-items">
      <div class="jfu-link-list--container">
        <span>Add links</span>
        <div v-for="(value, i) in o_link_list_items" class="jfu-field-form--container" @drop="onDrop($event, i, o_link_list_items)" @dragover.prevent @dragenter.prevent>
          <div class="jfu-tabledrag" draggable  @dragstart="startDrag($event, i)">
            <div class="jfu-toggle jfu-multi-row-accordion-edit" v-on:click="activeToggle('.link-lis-item-' + i, '.link-lis-toggle-item-' + i)">
              <a class="tabledrag-handle" title="Drag to re-order">
                <div class="handle">&nbsp;</div>
              </a>
              <span>{{ options.defaultLabels.link + ' - ' + (value.text ? value.text : value.value) }}
                <span class="jfu-drop-changed" :class="'jfu-drop-changed-' + i"> * </span>
              </span>
              <span class="jfu-toggle-trigger-icon is-active" v-bind:class="'link-lis-toggle-item-' + i"></span>
            </div>
            <div class="accordion-item--content is-active" :class="'link-lis-item-' + i">
              <p>
                <form-link v-bind:o_link="value" v-bind:field_name="field_name" v-bind:index="i" v-bind:options="options"></form-link>
              </p>
              <div v-if="o_link_list_items.length > 1" class="operations" style="padding: 10px;">
                <span class="jfu-button jfu-button--delete" v-on:click="deleteItem($event, o_link_list_items, i)">{{ options.defaultLabels.delete_item }}</span>
              </div>
            </div>
          </div>
        </div>
        <div class="error-messages" style="display: none;">
          <span>{{ options.defaultLabels.link_required }}</span>
        </div>
        <span v-on:click="addElement($event)" class="jfu-button jfu-button--edit ade--link-list-item">{{ options.defaultLabels.add_new_item }}</span>
      </div>
    </div>
  `
});

// Define component display table.
Vue.component('display-table', {
  props: ['o_data', 'index', 'field_name', 'is_load', 'cardinality', 'options'],
  data: function () {
    return {
      isActive: true
    };
  },
  methods: {
    activeToggle: function() {
      this.isActive = !this.isActive;
    }
  },
  template: `
    <div class="component-item--wrapper" draggable="true" v-on:dragstart="dragStart($event, index)" v-on:dragend="dragEnd($event, field_name)">
      <a v-show="cardinality != 1" class="tabledrag-handle" title="Drag to re-order">
        <div class="handle">&nbsp;</div>
      </a>
      <div class="jfu-toggle" v-on:click="activeToggle">
        <span>{{ o_data.label }}</span>
        <span class="jfu-toggle-trigger-icon" v-bind:class="{'is-active' :isActive}"></span>
      </div>
      <div class="accordion-item--content" v-bind:class="{'is-active' :isActive}">
        <display-content-table v-bind:o_data="o_data"></display-content-table>
        <display-operations v-bind:index="index" v-bind:field_name="field_name" v-bind:is_load="is_load" v-bind:type="o_data.type" v-bind:options="options"></display-operations>
      </div>
    </div>
  `
});

// Define component display content table.
Vue.component('display-content-table', {
  props: ['o_data'],
  template: `
    <div class="component-item table-item">
      <div class="table-item--title-container">
        <h3 v-if="o_data.title" class="table--title">{{ o_data.title }}</h3>
      </div>
      <div class="table-item--content">
        <div class="table-info--subtitle" v-if="o_data.subtitle">
          <display-text v-bind:content="o_data.subtitle"></display-text>
        </div>
        <p class="table-info--description" v-if="o_data.body">
          <display-text v-bind:content="o_data.body.value"></display-text>
        </p>
        <form-elements-table_show v-bind:o_json="o_data"></form-elements-table_show>
      </div>
    </div>
  `
});

// Define component form table.
Vue.component('form-table', {
  props: ['o_json', 'edit_index', 'field_name', 'options'],
  data: function() {
    return {
      current_json: this.o_json
    };
  },
  template: `
    <form v-on:submit.prevent="edit_index === '' ? addToJfu($event) : updateToJfu($event, o_json.type, edit_index)">
      <form-elements-table v-bind:o_json="o_json" v-bind:field_name="field_name" v-bind:options="options" v-bind:index="0"></form-elements-table>
      <input class="link-to link-to-add" type="submit" v-bind:value="options.defaultLabels.save" />
    </form>
  `
});

// Define component form content table.
Vue.component('form-elements-table', {
  props: ['o_json', 'field_name', 'index', 'options'],
  template: `
    <div class="form-component">
      <form-classes v-bind:o_json="o_json" v-bind:index="index" v-bind:field_name="field_name" v-bind:options="options"></form-classes>
      <p>
        <label>{{ options.defaultLabels.title }}
          <input type="text" name="rich_text_title" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.title" v-model="o_json.title" />
        </label>
      </p>
      <p>
        <label>{{ options.defaultLabels.subtitle }}
          <input type="text" name="rich_text_title" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.subtitle" v-model="o_json.subtitle" />
        </label>
      </p>
      <form-text v-bind:o_json="o_json.body" v-bind:field_name="field_name" v-bind:options="options" v-bind:index="index"></form-text>
      <form-elements-table_thead v-bind:o_json="o_json"  v-bind:field_name="field_name" v-bind:options="options"></form-elements-table_thead>
      <form-elements-table_tbody v-bind:o_json="o_json" v-bind:field_name="field_name" v-bind:options="options"></form-elements-table_tbody>
      <form-elements-table_show v-bind:o_json="o_json"></form-elements-table_show>
      <p>
        <label>{{ options.defaultLabels.full_width }}
          <label><input type="checkbox" id="full_width" v-model="o_json.options.full_width">{{ options.defaultLabels.activate_full_width }}</label>
        </label>
      </p>
    </div>
  `
});

// Define component form content table thead.
Vue.component('form-elements-table_thead', {
  props: ['o_json', 'field_name', 'options'],
  methods: {
    addElement: function (event) {
      var thead = {
        "type": "text",
        "value": "",
        "class": ""
      };

      this.o_json.thead.items.push(thead);
      for (let item in this.o_json.tbody.items) {
        var tbody = {
          "type": "text",
          "value": "",
          "class": ""
        };

        this.o_json.tbody.items[item].cells.push(tbody)
      }
    },
    deleteItem: function(event, items, key) {
      for (let item in this.o_json.tbody.items) {
        this.o_json.tbody.items[item].cells.splice(parseInt(key), 1)
      }

      items.splice(parseInt(key), 1);
    },
    activeToggle: function(key) {
      if (this.$el.querySelector('.table-thead-item-' + key).classList.contains('is-active')) {
        this.$el.querySelector('.table-thead-item-' + key).classList.remove('is-active');
        this.$el.querySelector('.table-toggle-item-' + key).classList.remove('is-active');
      }
      else {
        this.$el.querySelector('.table-thead-item-' + key).classList.add('is-active');
        this.$el.querySelector('.table-toggle-item-' + key).classList.add('is-active');
      }
    },
    startDrag (evt, item) {
      evt.dataTransfer.dropEffect = 'move'
      evt.dataTransfer.effectAllowed = 'move'
      evt.dataTransfer.setData('item_key', item)
    },
    onDrop (evt, list) {
      const item_key = evt.dataTransfer.getData('item_key');
      const element_head = this.o_json.thead.items.splice(item_key, 1)[0];
      this.o_json.thead.items.splice(list, 0, element_head);
      for (let item in this.o_json.tbody.items) {
        const element_body = this.o_json.tbody.items[item].cells.splice(item_key, 1)[0];
        this.o_json.tbody.items[item].cells.splice(list, 0, element_body)
      }

      this.$el.querySelector('.table-head-changed-' + list).classList.remove('table-head-changed');
    }
  },
  template: `
    <div class="jfu-table-thead">
      <span>{{ options.defaultLabels.add_head }}</span>
      <div class="jfu-field-form--container">
        <div v-for="(value, i) in o_json.thead.items" class="jfu-form-thead jfu-field-form--container"  @drop="onDrop($event, i)" @dragover.prevent @dragenter.prevent>
          <div class="jfu-table-tabledrag" draggable  @dragstart="startDrag($event, i)">
            <a class="tabledrag-handle" title="Drag to re-order">
              <div class="handle">&nbsp;</div>
            </a>
            <div class="jfu-toggle jfu-table-edit" v-on:click="activeToggle(i)">
              <span>{{ options.defaultLabels.head + ' - ' + value.value }}
                <span class="table-head-changed" :class="'table-head-changed-' + i"> * </span>
              </span>
              <span class="jfu-toggle-trigger-icon is-active is-active" :class="'table-toggle-item-' + i"></span>
            </div>
            <div class="accordion-item--content is-active" :class="'table-thead-item-' + i">
              <div style="display: flex; align-items: center;">
                <p>
                  <label>{{ options.defaultLabels.value }}
                    <input type="text" v-bind:name="field_name + '_value'" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.value" v-model="value.value"/>
                  </label>
                </p>
                <p>
                  <label>{{ options.defaultLabels.custom_classes }}
                    <input type="text" v-bind:name="field_name + '_custom_classes'" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.custom_classes" v-model="value.class"/>
                  </label>
                </p>
              </div>
              <div class="operations" style="padding-top: 10px;" v-if="o_json.thead.items.length > 1">
                <span class="jfu-button jfu-button--delete" v-on:click="deleteItem($event, o_json.thead.items, i)">{{ options.defaultLabels.delete_item }}</span>
              </div>
            </div>
          </div>
        </div>
        <span v-on:click="addElement($event)" class="jfu-button jfu-button--edit ade--thead">{{ options.defaultLabels.add_head }}</span>
      </div>
    </div>
  `
});

// Define component form content table tbody.
Vue.component('form-elements-table_tbody', {
  props: ['o_json', 'field_name', 'options'],
  methods: {
    addElement: function (event) {
      var cells =[];
      for (let key in this.o_json.thead.items) {
        var cell_items =  {
          "type": "text",
          "value": "",
          "class": this.o_json.thead.items[key].class
        }
        cells.push(cell_items);
      }

      var items = {
        "class": "",
        "cells": cells
      }

      this.o_json.tbody.items.push(items);

    },
    deleteItem: function(event, items, key) {
      items.splice(parseInt(key), 1);
    },
    activeToggle: function(key) {
      if (this.$el.querySelector('.table-tbody-item-' + key).classList.contains('is-active')) {
        this.$el.querySelector('.table-tbody-item-' + key).classList.remove('is-active');
        this.$el.querySelector('.table-toggle-item-' + key).classList.remove('is-active');
      }
      else {
        this.$el.querySelector('.table-tbody-item-' + key).classList.add('is-active');
        this.$el.querySelector('.table-toggle-item-' + key).classList.add('is-active');
      }
    },
    startDrag (evt, item) {
      evt.dataTransfer.dropEffect = 'move'
      evt.dataTransfer.effectAllowed = 'move'
      evt.dataTransfer.setData('item_key', item)
    },
    onDrop (evt, list) {
      const item_key = evt.dataTransfer.getData('item_key');
      const element_body = this.o_json.tbody.items.splice(item_key, 1)[0];
      this.o_json.tbody.items.splice(list, 0, element_body);
      this.$el.querySelector('.table-row-changed-' + list).classList.remove('table-row-changed');
    }
  },
  template: `
    <div class="jfu-table-tbody">
      <span>{{ options.defaultLabels.add_body }}</span>
      <div class=" jfu-field-form--container">
        <div class=" jfu-field-form--container" v-for="(tbody, tb) in o_json.tbody.items" @drop="onDrop($event, tb)" @dragover.prevent @dragenter.prevent>
          <div class="jfu-table-tabledrag" draggable  @dragstart="startDrag($event, tb)">
            <a class="tabledrag-handle" title="Drag to re-order">
              <div class="handle">&nbsp;</div>
            </a>
            <div class="jfu-toggle jfu-table-edit" v-on:click="activeToggle(tb)">
              <span>{{ options.defaultLabels.row + ' - ' + tbody.cells[0].value }}
                <span class="table-row-changed" :class="'table-row-changed-' + tb"> * </span>
              </span>
              <span class="jfu-toggle-trigger-icon is-active is-active" :class="'table-toggle-item-' + tb"></span>
            </div>
            <div class="accordion-item--content is-active" :class="'table-tbody-item-' + tb">
              <div v-for="(cell, c) in tbody.cells">
                <p>
                  <label>{{ options.defaultLabels.cell }} {{ c + 1 }}
                    <input type="text" v-bind:name="'table_value_' + c" size="60" maxlength="255" placeholder="Value" v-model="tbody.cells[c].value"/>
                  </label>
                </p>
              </div>
              <p>
                <label>{{ options.defaultLabels.custom_classes }}
                  <input type="text" v-bind:name="'table_custom_classes'" size="60" maxlength="50" v-bind:placeholder="options.defaultLabels.custom_classes" v-model="tbody.class"/>
                </label>
              </p>
              <div class="operations" style="padding-top: 10px;">
                <span class="jfu-button jfu-button--delete" v-on:click="deleteItem($event, o_json.tbody.items, tb)">{{ options.defaultLabels.delete_item }}</span>
              </div>
            </div>
          </div>
        </div>
        <span v-on:click="addElement($event)" class="jfu-button jfu-button--edit ade--tbody">{{ options.defaultLabels.add_body }}</span>
      </div>
    </div>
  `
});

// Define component show table.
Vue.component('form-elements-table_show', {
  props: ['o_json'],
  template: `
    <table>
      <tr>
        <th v-for="(val, i) in o_json.thead.items">{{val.value}}</th>
      </tr>
      <tr v-for="(valitem, ii) in o_json.tbody.items">
        <td v-for="(valcell, ic) in valitem.cells">{{valcell.value}}</td>
      </tr>
    </table>
  `
});

// Define component display tabs
Vue.component('display-tabs', {
  props: ['o_data', 'index', 'field_name', 'is_load', 'cardinality', 'options'],
  data: function() {
    return {
      tabActive: 0,
      isActive: true
    };
  },
  methods: {
    changeTab: function(index) {
      this.tabActive = index;
    },
    activeToggle: function() {
      this.isActive = !this.isActive;
    }
  },
  template: `
    <div class="component-item--wrapper" draggable="true" v-on:dragstart="dragStart($event, index)" v-on:dragend="dragEnd($event, field_name)">
      <a v-show="cardinality != 1" class="tabledrag-handle" title="Drag to re-order">
        <div class="handle">&nbsp;</div>
      </a>
      <div class="jfu-toggle" v-on:click="activeToggle">
        <span>{{ o_data.label }}</span>
        <span class="jfu-toggle-trigger-icon" v-bind:class="{'is-active' :isActive}"></span>
      </div>
      <div class="accordion-item--content" v-bind:class="{'is-active' :isActive}">
        <div class="component-item tabs-item">
          <h3 class="tab-item--title">{{ o_data.title }}</h3>
          <div class="tab-header">
            <div class="tab-header--item" v-for="(header, kh) in o_data.items" v-on:click="changeTab(kh)" v-bind:class="kh === tabActive ? 'active' : ''"><h3>{{ header.title }}</h3></div>
          </div>
          <div class="tab-container">
            <div v-for="(data_item, k) in o_data.items" v-bind:style="tabActive === k ? 'display: block;' : 'display: none;'">
              <component v-bind:is="'display-content-' + data_item.body.type" v-bind:o_data="data_item.body" v-bind:field_name="field_name" v-bind:index="index" v-bind:component="data_item.body.type" v-bind:cardinality="cardinality" v-bind:options="options"></component>
            </div>
          </div>
        </div>
        <display-operations v-bind:index="index" v-bind:field_name="field_name" v-bind:is_load="is_load" v-bind:type="o_data.type" v-bind:options="options"></display-operations>
      </div>
    </div>
  `
});

// Define component form tabs
Vue.component('form-tabs', {
  props: ['o_json', 'edit_index', 'field_name', 'components', 'options'],
  data: function() {
    var tabs_ignore_components = ['tabs']
    for (const i in this.components) {
      if(this.components[i].module_name !== 'jfu') {
        tabs_ignore_components.push(i);
      }
    }

    return {
      current_json: this.o_json,
      tabComponentSelected: '',
      tabTitle: '',
      tabActive: 0,
      tabs_ignore_components: tabs_ignore_components
    };
  },
  updated: function () {
    this.$nextTick(function () {
      if (this.o_json.items.length > 0) {
        app.reloadAjaxLinks();

        for (const i in this.o_json.items) {
          var type = this.o_json.items[i].body.type;
          if (type === 'embed') {
            app.selectDefaultEmbed(i);
            if (app.editIndex === '' && this.o_json.items[i].body.iframe === '') {
              var current_selector = '#edit-'  + this.field_name.replaceAll('_', '-') + '-wrapper';
              jQuery(current_selector + ' input[name="embed_video_youtube_' + i + '"]').val('');
              jQuery(current_selector + ' input[name="embed_video_vimeo_' + i + '"]').val('');
            }
          }
        }
      }
    });
  },
  methods: {
    addTab: function(event) {
      if (this.tabTitle !== '' && this.tabComponentSelected) {
        var component = {
          type: 'tab',
          title: this.tabTitle
        };

        component.body = this.components[this.tabComponentSelected];
        this.o_json.items.push(JSON.parse(JSON.stringify(component)));

        this.tabTitle = '';
        this.tabComponentSelected = '';
        this.tabActive = this.o_json.items.length - 1;
      }
      else {
        var container = jQuery(event.target).parent().parent();
        container.find('.error-messages').show();
        setTimeout(function() {
          container.find('.error-messages').hide();
        }, 2000);
        return;
      }
    },
    changeTab: function(index) {
      this.tabActive = index;
    },
    deleteTab: function(event, items, key) {
      items.splice(parseInt(key), 1);
    }
  },
  template: `
    <form v-on:submit.prevent="edit_index === '' ? addToJfu($event) : updateToJfu($event, o_json.type, edit_index)">
      <div class="form-component">
        <form-classes v-bind:o_json="o_json" v-bind:index="0" v-bind:field_name="field_name" v-bind:options="options"></form-classes>
        <p>
          <label>{{ options.defaultLabels.title }}
            <input type="text" name="tabs-title" v-model="o_json.title" />
          </label>
        </p>
        <div class="tab-add-new">
          <div class="tan--item tan--title">
            <label>{{ options.defaultLabels.tab_title }}
              <input type="text" v-bind:name="field_name + 'tab_title'" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.tab_title" v-model="tabTitle" />
            </label>
          </div>
          <div class="tan--item tan--select">
            <label for="jfu-tabs-select">{{ options.defaultLabels.select_components }}</label>
            <select v-model="tabComponentSelected" id="jfu-tabs-select">
              <option v-for="(component, i) in components" v-if="!tabs_ignore_components.includes(i)" v-bind:value="i">{{ component.label }}</option>
            </select>
          </div>
          <div class="tan--item tan--added">
            <span v-on:click="addTab($event)" class="jfu-button jfu-button--edit">{{ options.defaultLabels.add_tab }}</span>
          </div>
          <div class="error-messages" style="display: none;">
            <span>{{ options.defaultLabels.component_required }}.</span>
          </div>
        </div>
        <div class="tab-header">
          <div class="tab-header--item" v-for="(tab, it) in o_json.items" v-on:click="changeTab(it)" @drop="onDrop($event, it, o_json.items)" @dragover.prevent @dragenter.prevent v-bind:class="it === tabActive ? 'active' : ''">
            <h3 draggable  @dragstart="startDrag($event, it)">{{ tab.title }} <span class="jfu-drop-changed" :class="'jfu-drop-changed-' + it"> * </span></h3>
          </div>
        </div>
        <div class="tab-container">
          <div v-for="(tab_json, key) in o_json.items" v-bind:style="tabActive === key ? 'display: block;' : 'display: none;'">
            <p>
              <label>{{ options.defaultLabels.tab_title }}
                <input type="text" v-bind:name="field_name + 'tab_title'" size="60" maxlength="255" placeholder="Title" v-model="tab_json.title" />
              </label>
            </p>
            <component v-bind:is="'form-elements-' + tab_json.body.type" v-bind:o_json="tab_json.body" v-bind:field_name="field_name" v-bind:edit_index="edit_index" v-bind:index="key" v-bind:component="tab_json.body.type" v-bind:options="options"></component>
            <div class="operations">
              <span class="jfu-button jfu-button--delete" v-on:click="deleteTab($event, o_json.items, key)">{{ options.defaultLabels.delete_tab }}</span>
            </div>
          </div>
        </div>
      </div>
      <div class="error-messages" style="display: none;">
        <span>{{ options.defaultLabels.component_required }}</span>
      </div>
      <input class="link-to link-to-add" type="submit" v-bind:value="options.defaultLabels.save" />
    </form>
  `
});

// Define component display image
Vue.component('display-image', {
  props: ['o_image', 'options'],
  computed: {
    generateUrlImage: function () {
      return this.o_image.value.replace('public://', this.options.urlPublicImage);
    }
  },
  template: `
    <img v-bind:src="generateUrlImage" v-bind:alt="o_image.alt" v-bind:title="o_image.title">
  `
});

// Define component display image
Vue.component('form-image', {
  props: ['o_image', 'field_name', 'component', 'index', 'subindex', 'options', 'subindexitem'],
  methods: {
    urlUploadImage: function(index, subindex) {
      var url = this.options.urlUploadImage + '?field_name=' + this.field_name + '&component=' + this.component + '&index=' + index + '&subindexitem=' + this.subindexitem;
      if (typeof subindex !== 'undefined') {
        url += '&subindex=' + subindex;
      }

      return url;
    }
  },
  template: `
    <div class="jfu-image-form jfu-field-form--container">
      <span v-html="subindexitem === 'responsive_image' ? options.defaultLabels.responsive_image : options.defaultLabels.image"></span>
      <div class="jfu-image-upload">
        <div class="jfu-image--actions">
          <a v-html="o_image.fid !== '' ? options.defaultLabels.change_image : options.defaultLabels.load_image" class="use-ajax" data-dialog-options="{&quot;width&quot;:400,&quot;classes&quot;:{&quot;ui-dialog&quot;:&quot;jfu-modal-image&quot;}}" data-dialog-type="modal" v-bind:href="urlUploadImage(index, subindex)"></a>
          <display-delete-image v-if="o_image.fid !== ''" v-bind:o_json="o_image" v-bind:options="options"></display-delete-image>
        </div>
        <div class="jfu-image--meta-information">
          <div class="jfu-image-thumb--wrapper">
            <display-image v-bind:o_image="o_image" v-bind:options="options"></display-image>
          </div>
          <div v-if="o_image.fid !== ''" class="jfu-image--meta-data">
            <div class="jfu-image--meta-data--item">
              <label>Alt
                <input type="text" v-bind:name="field_name + '_image_alt_' + index" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.alternative_text" v-model="o_image.alt" />
              </label>
            </div>
            <div class="jfu-image--meta-data--item">
              <label>{{ options.defaultLabels.title }}
                <input type="text" v-bind:name="field_name + '_image_title_' + index" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.title_text" v-model="o_image.title" />
              </label>
            </div>
          </div>
        </div>
        <div class="jfu-input--description">
          {{ options.defaultLabels[field_name]['limit_' + component] }}<br>{{ options.defaultLabels[field_name]['allowed_types_' + component] }}<br>
          <span v-html="subindexitem === 'responsive_image' ? options.defaultLabels[field_name]['responsive_image_larger_' + component] : options.defaultLabels[field_name]['images_larger_' + component]"></span>
        </div>
      </div>
    </div>
  `
});

// Define component display text
Vue.component('display-text', {
  props: ['content'],
  template: '<div v-html="content"></div>'
});

// Define component form text
Vue.component('form-text', {
  props: ['o_json', 'field_name', 'options', 'index', 'subindex'],
  methods: {
    setTextFormat: function(event) {
      var data = jQuery(event.target).parent().find('input[name=jfu_text_format_value]').val();
      var format = jQuery(event.target).attr('data_format');

      jQuery(document).on('ajaxComplete', function (event, jqxhr, settings) {
        if (settings.url.indexOf('jfu/dialog/text-format') !== -1) {
          if (format !== 'plain_text' && format !== 'restricted_html') {
            const interval_id = setInterval(jfuTextFormatSetData, 250, data);

            function jfuTextFormatSetData(data) {
              if (jQuery('.jfu-text-format-dialog .ck-editor__editable').length !== 0) {
                const dom_editable_element = document.querySelector('.jfu-text-format-dialog .ck-editor__editable');
                const editor_instance = dom_editable_element.ckeditorInstance;
                if (data !== '') {
                  editor_instance.setData(data);
                }
                else {
                  editor_instance.setData('');
                  editor_instance.editing.view.focus();
                }

                clearInterval(interval_id);
              }
            }
          }
          else {
            jQuery('.jfu-text-format-dialog textarea[name="jfu_body[value]"]').val(data);
          }
        }
      });
    }
  },
  template: `
  <div class="jfu-text-format-container jfu-field-form--container">
    <span>{{ options.defaultLabels.description }}</span>
    <div v-if="o_json.value" class="jfu-text-format-display">
      <display-text v-bind:content="o_json.value"></display-text>
    </div>
    <div v-else>
      <p class="jfu-description-message">{{ options.defaultLabels.description_help }}</p>
    </div>
    <input type="hidden" id="jfu-text-format-value" name="jfu_text_format_value" v-bind:value="o_json.value">
    <a v-bind:data_format="o_json.format" v-on:click="setTextFormat($event)" v-html="o_json.value !== '' ? options.defaultLabels.description_edit : options.defaultLabels.description_add" class="use-ajax jfu-tex-formt-delta" data-dialog-options="{&quot;width&quot;:800,&quot;data&quot;:&quot;david&quot;,&quot;classes&quot;:{&quot;ui-dialog&quot;:&quot;jfu-modal-text-format&quot;}}" data-dialog-type="modal" v-bind:href="options.urlTextFormat + '?field_name=' + field_name + '&index=' + index + '&subindex=' + subindex"></a>
  </div>
  `
});

// Define component display link
Vue.component('display-link', {
  props: ['o_link', 'options'],
  computed: {
    generateUrl: function () {
      var url = this.o_link.value;
      if (!this.o_link.external) {
        url = this.options.urlBase + this.o_link.value;
        var nid = url.substring(url.lastIndexOf('(') + 1, url.lastIndexOf(')'));
        if (nid) {
          url = this.options.urlBase + '/node/' + nid;
        }
      }

      return url;
    }
  },
  template: `
    <a class="item--link" v-bind:href="generateUrl" v-bind:target="o_link.target">{{ o_link.text }}</a>
  `
});

// Define component form link
Vue.component('form-link', {
  props: ['o_link', 'type', 'field_name', 'options'],
  data: function() {
    return {
      search_items: {}
    };
  },
  computed: {
    isExternal: function () {
      var isExternal = true;
      var link_value = this.o_link.value;
      if (link_value.indexOf('http') === -1) {
        isExternal = false;
        this.o_link.relative = link_value;
        var nid = link_value.substring(link_value.lastIndexOf('(') + 1, link_value.lastIndexOf(')'));
        if (nid) {
          this.o_link.relative = '/node/' + nid;
        }
      }

      this.o_link.external = isExternal;
    }
  },
  methods: {
    listLinkContent: function(string) {
      this.search_items = {};
      var endpoint = this.options.urlReferenceAutocomplete + '?q=' + string;
      axios.get(endpoint).then(response => {
        this.search_items = response.data;
      });
    },
    setValue: function(string) {
      this.o_link.value = string;
      this.search_items = {};
    }
  },
  template: `
    <div class="jfu-form-link jfu-field-form--container">
      <p>
        <label>{{ options.defaultLabels.link_url }}
          <input type="text" v-bind:name="field_name + '_link_url'" size="60" v-model="o_link.value" v-bind:data-type="isExternal" @keyup="listLinkContent(o_link.value)"/>
        </label>
        <div class="jfu-autocomplete-options">
          <ul class="jfu-autocomplete-options--list">
            <li v-for="item in search_items" style="display: block;">
              <span class="list-group-item" v-on:click="setValue(item.value)">{{ item.label }}</span>
            </li>
          </ul>
        </div>
        <span class="jfu-input--description" v-html="options.defaultLabels.description_link_url"></span>
      </p>
      <p>
        <label>{{ options.defaultLabels.link_text }}
          <input type="text" v-bind:name="field_name + '_link_text'" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.link_text" v-model="o_link.text" />
        </label>
      </p>
      <p>
        <label><input type="checkbox" v-bind:name="field_name + '_link_open_another_tab'" size="60" v-model="o_link.target" />{{ options.defaultLabels.open_in_another_tab }}</label>
      </p>
    </div>
  `
});

// Define component display link image
Vue.component('display-link-image', {
  props: ['o_link_image', 'options'],
  computed: {
    generateUrl: function () {
      var url = this.o_link_image.link.value;
      if (!this.o_link_image.link.external) {
        url = this.options.urlBase + this.o_link_image.link.value;
        var nid = url.substring(url.lastIndexOf('(') + 1, url.lastIndexOf(')'));
        if (nid) {
          url = this.options.urlBase + '/node/' + nid;
        }
      }

      return url;
    }
  },
  template: `
    <a class="item--link" v-bind:href="generateUrl" v-bind:target="o_link_image.link.target">
      <display-image v-bind:o_image="o_link_image.image" v-bind:options="options"></display-image>
      <display-text v-bind:content="o_link_image.title"></display-text>
    </a>
  `
});

// Define component form link image
Vue.component('form-link-image', {
  props: ['o_link_images', 'index', 'field_name', 'component', 'options'],
  methods: {
    addElement: function (event) {
      var container = jQuery(event.target).parent();
      var i = this.o_link_images.length - 1;
      if (this.o_link_images[i].image.fid === '' && this.o_link_images[i].link.value === ''
        && this.o_link_images[i].title === '') {
        container.find('.error-messages').show();
        setTimeout(function() {
          container.find('.error-messages').hide();
        }, 2000);
        return;
      }

      var link_image = {
        "type": "link_image",
        "title": "",
        "image": {
          "type": "image",
          "fid": "",
          "alt": "",
          "value": "",
          "target": ""
        },
        "body": {
          "type": "text",
          "value": "",
          "format": "advance_html"
        },
        "link": {
          "type": "link",
          "value": "",
          "text": "",
          "target": "",
          "external": true,
          "relative": ""
        }
      };
      this.o_link_images.push(link_image);
    },
    deleteItem: function(event, items, key) {
      items.splice(parseInt(key), 1);
    }
  },
  template: `
    <div class="jfu-form-link-image">
      <div class="jfu-link-image">
        <div v-for="(link_image, i) in o_link_images" class="jfu-link-image--item" @drop="onDrop($event, i, o_link_images)" @dragover.prevent @dragenter.prevent>
          <div class="jfu-tabledrag" draggable  @dragstart="startDrag($event, i)">
            <div class="jfu-toggle jfu-multi-row-accordion-edit" v-on:click="activeToggle('.link-image-item-' + i, '.link-image-toggle-item-' + i)">
              <a class="tabledrag-handle" title="Drag to re-order"><div class="handle">&nbsp;</div></a>
              <span>{{ options.defaultLabels.column + ' - ' + (link_image.title ? link_image.title : i + 1) }}
                <span class="jfu-drop-changed" :class="'jfu-drop-changed-' + i"> * </span>
              </span>
              <span class="jfu-toggle-trigger-icon is-active" v-bind:class="'link-image-toggle-item-' + i"></span>
            </div>
            <div class="accordion-item--content is-active" :class="'link-image-item-' + i">
              <p>
                <label>{{ options.defaultLabels.title }}
                  <input type="text" v-bind:name="field_name + 'link_image_title'" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.title" v-model="link_image.title" />
                </label>
              </p>
              <form-image v-bind:o_image="link_image.image" v-bind:field_name="field_name" v-bind:subindex="i" v-bind:index="index" v-bind:component="component" v-bind:options="options"></form-image>
              <form-link v-bind:o_link="link_image.link" v-bind:field_name="field_name" v-bind:type="component" v-bind:options="options"></form-link>
              <div v-if="o_link_images.length > 1" class="operations">
                <span class="jfu-button jfu-button--delete" v-on:click="deleteItem($event, o_link_images, i)">{{ options.defaultLabels.delete_item }}</span>
              </div>
            </div>
          </div>
        </div>
        <div class="error-messages" style="display: none;">
          <span>{{ options.defaultLabels.field_required }}</span>
        </div>
        <span v-on:click="addElement($event)" class="jfu-button jfu-button--edit ade--link">{{ options.defaultLabels.add_new_item }}</span>
      </div>
    </div>
  `
});

// Define component display text image
Vue.component('display-text-image', {
  props: ['o_link_image', 'options'],
  template: `
    <div>
      <display-image v-bind:o_image="o_link_image.image" v-bind:options="options"></display-image>
      <h3><display-text v-bind:content="o_link_image.title"></display-text></h3>
      <display-text v-bind:content="o_link_image.body.value"></display-text>
    </div>
  `
});

// Define component form text image
Vue.component('form-text-image', {
  props: ['o_text_images', 'field_name', 'index', 'component', 'options'],
  methods: {
    addElement: function (event) {
      var container = jQuery(event.target).parent();
      var i = this.o_text_images.length - 1;
      if (this.o_text_images[i].title === '' && this.o_text_images[i].image.fid === '') {
        container.find('.error-messages').show();
        setTimeout(function() {
          container.find('.error-messages').hide();
        }, 2000);
        return;
      }

      var link_text_image = {
        "type": "link_image",
        "title": "",
        "image": {
          "type": "image",
          "fid": "",
          "alt": "",
          "value": "",
          "target": ""
        },
        "body": {
          "type": "text",
          "value": "",
          "format": "advance_html"
        },
        "link": {
          "type": "link",
          "value": "",
          "target": "",
          "external": true,
          "relative": ""
        }
      };
      this.o_text_images.push(link_text_image);
    },
    deleteItem: function(event, items, key) {
      items.splice(parseInt(key), 1);
    }
  },
  template: `
    <div class="jfu-form-text-image">
      <div class="jfu-link-image">
        <div v-for="(link_image, i) in o_text_images" class="jfu-link-image--item" @drop="onDrop($event, i, o_text_images)" @dragover.prevent @dragenter.prevent>
          <div class="jfu-tabledrag" draggable  @dragstart="startDrag($event, i)">
            <div class="jfu-toggle jfu-multi-row-accordion-edit" v-on:click="activeToggle('.text-image-item-' + i, '.text-image-toggle-item-' + i)">
              <a class="tabledrag-handle" title="Drag to re-order"><div class="handle">&nbsp;</div></a>
              <span>{{ options.defaultLabels.content + ' - ' + (link_image.title ? link_image.title : i + 1) }}
                <span class="jfu-drop-changed" :class="'jfu-drop-changed-' + i"> * </span>
              </span>
              <span class="jfu-toggle-trigger-icon is-active" v-bind:class="'text-image-toggle-item-' + i"></span>
            </div>
            <div class="accordion-item--content is-active" :class="'text-image-item-' + i">
              <p>
                <label>{{ options.defaultLabels.title }}
                  <input type="text" v-bind:name="field_name + 'link_image_title'" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.title" v-model="link_image.title" />
                </label>
              </p>
              <form-image v-bind:o_image="link_image.image" v-bind:field_name="field_name" v-bind:subindex="i" v-bind:index="index" v-bind:component="component" v-bind:options="options"></form-image>
              <hr>
              <p>
                <label>{{ options.defaultLabels.item_text }}
                  <form-text v-bind:o_json="link_image.body" v-bind:field_name="field_name" v-bind:options="options" v-bind:index="index" v-bind:subindex="i"></form-text>
                </label>
              </p>
              <div v-if="o_text_images.length > 1" class="operations">
                <span class="jfu-button jfu-button--delete" v-on:click="deleteItem($event, o_text_images, i)">{{ options.defaultLabels.delete_item }}</span>
              </div>
            </div>
          </div>
        </div>
        <div class="error-messages" style="display: none;">
          <span>{{ options.defaultLabels.field_required }}</span>
        </div>
        <span v-on:click="addElement($event)" class="jfu-button jfu-button--edit ade--link">{{ options.defaultLabels.add_new_item }}</span>
      </div>
    </div>
  `
});

// Define component display operations
Vue.component('display-delete-image', {
  props: ['o_json', 'options'],
  template: `
    <div class="jfu-delete-image">
      <span class="jfu-button jfu-button--delete" v-on:click="deleteImage($event, o_json)">{{ options.defaultLabels.delete_image }}</span>
    </div>
  `,
  methods: {
    deleteImage: function(event, o_json) {
      o_json.fid = "";
      o_json.alt = "";
      o_json.value = "";
      o_json.title = "";
    }
  }
});

// Define component display multi_row_accordion
Vue.component('display-multi_row_accordion', {
  props: ['o_data', 'index', 'field_name', 'is_load', 'cardinality', 'options'],
  data: function () {
    return {
      isActive: true
    };
  },
  methods: {
    activeToggle: function() {
      this.isActive = !this.isActive;
    }
  },
  template: `
    <div class="component-item--wrapper" draggable="true" v-on:dragstart="dragStart($event, index)" v-on:dragend="dragEnd($event, field_name)">
      <a v-show="cardinality != 1" class="tabledrag-handle" title="Drag to re-order">
        <div class="handle">&nbsp;</div>
      </a>
      <div class="jfu-toggle" v-on:click="activeToggle">
        <span>{{ o_data.label }}</span>
        <span class="jfu-toggle-trigger-icon" v-bind:class="{'is-active' :isActive}"></span>
      </div>
      <div class="accordion-item--content" v-bind:class="{'is-active' :isActive}">
        <display-content-multi_row_accordion v-bind:o_data="o_data"></display-content-multi_row_accordion>
        <display-operations v-bind:index="index" v-bind:field_name="field_name" v-bind:is_load="is_load" v-bind:type="o_data.type" v-bind:options="options"></display-operations>
      </div>
    </div>
  `
});

// Define component display content accordion
Vue.component('display-content-multi_row_accordion', {
  props: ['o_data'],
  data: function () {
    return {
      isActive: 0
    };
  },
  methods: {
    activeToggle: function(i) {
      this.isActive = i;
    }
  },
  template: `
    <div class="component-item accordion-item">
      <div class="accordion-info--title">
        <h3 v-if="o_data.title" class="accordion--title">{{ o_data.title }}</h3>
      </div>
      <div class="accordion-info--content">
        <div class="accordion-info--subtitle" v-if="o_data.subtitle">
          <display-text v-bind:content="o_data.subtitle"></display-text>
        </div>
        <p class="accordion-info--description" v-if="o_data.body">
          <display-text v-bind:content="o_data.body.value"></display-text>
        </p>
        <div class="accordion-item" v-for="(item, i) in o_data.items.items">
          <div v-if="item.title" class="accordion-item--title-container" v-on:click="activeToggle(i)">
            <h5 class="accordion-item--title">{{ item.title }}</h5>
            <span class="jfu-toggle-trigger-icon" v-bind:class="{'is-active' :i === isActive}"></span>
          </div>
          <div v-if="item.body.value" class="accordion-item--content" v-bind:class="{'is-active' : i === isActive}">
            <display-text v-bind:content="item.body.value"></display-text>
          </div>
          </div>
        </div>
      </div>
    </div>
  `
});

// Define component form multi_row_accordion
Vue.component('form-multi_row_accordion', {
  props: ['o_json', 'edit_index', 'field_name', 'options'],
  template: `
    <form v-on:submit.prevent="edit_index === '' ? addToJfu($event) : updateToJfu($event, o_json.type, edit_index)">
      <form-elements-multi_row_accordion v-bind:o_json="o_json" v-bind:field_name="field_name" v-bind:index="0" v-bind:options="options"></form-elements-multi_row_accordion>
      <input class="link-to link-to-add" type="submit" v-bind:value="options.defaultLabels.save" />
    </form>
  `
});

// Define component form content multi_row_accordion
Vue.component('form-elements-multi_row_accordion', {
  props: ['o_json', 'field_name', 'options', 'index'],
  template: `
    <div class="form-component">
      <form-classes v-bind:o_json="o_json" v-bind:index="index" v-bind:field_name="field_name" v-bind:options="options"></form-classes>
      <p>
        <label>{{ options.defaultLabels.title }}
          <input type="text" name="multi_row_accordion_title" maxlength="255" v-model="o_json.title" placeholder="Title"/>
        </label>
      </p>
      <p>
        <label>{{ options.defaultLabels.subtitle }}
          <input type="text" name="multi_row_accordion_subtitle" maxlength="255" v-model="o_json.subtitle" placeholder="Subtitle"/>
        </label>
      </p>
      <form-text v-bind:o_json="o_json.body" v-bind:field_name="field_name" v-bind:options="options" v-bind:index="index"></form-text>
      <form-multi_row_accordion_item v-bind:o_items="o_json.items.items" v-bind:field_name="field_name" v-bind:index="index" v-bind:options="options"></form-multi_row_accordion_item>
      <p>
        <label>{{ options.defaultLabels.full_width }}
          <label><input type="checkbox" id="full_width" v-model="o_json.options.full_width">{{ options.defaultLabels.activate_full_width }}</label>
        </label>
      </p>
    </div>
  `
});

//Define component to multiple text
Vue.component('form-multi_row_accordion_item', {
  props: ['o_items', 'index', 'field_name', 'options'],
  data: function () {
    return {
      classExisting: true
    };
  },
  updated: function () {
    this.$nextTick(function () {
      // Reaload ajax link
      app.reloadAjaxLinks();
    });
  },
  methods: {
    addElement: function (event) {
      var container = jQuery(event.target).parent();
      var i = this.o_items.length - 1;
      if (this.o_items[i].title === '' && this.o_items[i].body.value === '') {
        container.find('.error-messages').show();
        setTimeout(function() {
          container.find('.error-messages').hide();
        }, 2000);
        return;
      }

      var item = {
        "type": "multi_row_accordion_item",
        "title": "",
        "body": {
          "type": "text",
          "value": "",
          "format": "basic_html"
        }
      };
      this.o_items.push(item);
    },
    deleteItem: function(event, items, key) {
      items.splice(parseInt(key), 1);
    }
  },
  template: `
    <div class="jfu-course-mvp-text">
      <div v-for="(item, i) in o_items" class="jfu-link-image--item" @drop="onDrop($event, i, o_items)" @dragover.prevent @dragenter.prevent>
        <div class="jfu-tabledrag" draggable  @dragstart="startDrag($event, i)">
          <div class="jfu-toggle jfu-multi-row-accordion-edit" v-on:click="activeToggle('.mra-item-' + i, '.mra-toggle-item-' + i)">
            <a class="tabledrag-handle" title="Drag to re-order"><div class="handle">&nbsp;</div></a>
            <span>{{ options.defaultLabels.accordion + ' - ' + item.title }}
              <span class="jfu-drop-changed" :class="'jfu-drop-changed-' + i"> * </span>
            </span>
            <span class="jfu-toggle-trigger-icon is-active" v-bind:class="'mra-toggle-item-' + i"></span>
          </div>
          <div class="accordion-item--content is-active" :class="'mra-item-' + i">
            <p>
              <label>{{ options.defaultLabels.title }}
                <input type="text" name="multi_row_accordion_item_title" maxlength="255" v-model="item.title" placeholder="Title" required />
              </label>
            </p>
            <form-text v-bind:o_json="item.body" v-bind:field_name="field_name" v-bind:options="options" v-bind:index="index" v-bind:subindex="i"></form-text>
            <div v-if="o_items.length > 1" class="operations">
              <span class="jfu-button jfu-button--delete" v-on:click="deleteItem($event, o_items, i)">{{ options.defaultLabels.delete_item }}</span>
            </div>
          </div>
        </div>
      </div>
      <div class="error-messages" style="display: none;">
        <span>{{ options.defaultLabels.field_required }}</span>
      </div>
      <span v-on:click="addElement($event)" class="jfu-button jfu-button--edit ade--link">{{ options.defaultLabels.add_new_item }}</span>
    </div>
  `
});

// Define component display operations
Vue.component('display-operations', {
  props: ['index', 'type', 'is_load', 'field_name', 'options'],
  template: `
    <div class="operations">
      <span v-if="is_load" class="jfu-button jfu-button--edit" v-on:click="editComponent($event, index, type, field_name)">{{ options.defaultLabels.edit }}</span>
      <span class="jfu-button jfu-button--delete" v-on:click="deleteComponent($event, index, field_name)">{{ options.defaultLabels.delete }}</span>
      <!--span style="background-color: #d07c26;" class="jfu-button jfu-button--duplicate" v-on:click="duplicateComponent($event, index, field_name)">Duplicate</span-->
    </div>
  `,
  methods: {
    duplicateComponent: function(event, index, field_name) {
      var temp_add = app.jfu[field_name];
      var temp = Object.values(temp_add);
      var component_duplicated = JSON.parse(JSON.stringify(temp_add[index]));
      app.fieldName = field_name;

      var indexDuplicate =  parseInt(index) + 1;
      temp.splice(indexDuplicate, 0, component_duplicated);

      for (var i = 0; i < temp.length; i++) {
        temp_add[i] = temp[i];
      }

      var reload_jfu_json = JSON.parse(JSON.stringify(temp_add));
      var selector_used_field = '.field--name-' + field_name.replace(/_/g, '-');

      var cardinality = app.cardinality[field_name];
      if (cardinality === -1) {
        // jQuery trigger event mousedown when adding some component.
        var selector_add_another_item = '.add-another-item-' + field_name.replace(/_/g, '-');

        jQuery(selector_add_another_item).trigger('mousedown');
        jQuery(document).ajaxComplete(function(event, xhr, settings) {
          app.reloadField(reload_jfu_json, selector_used_field, cardinality);
        });
      }
      else {
        app.reloadField(reload_jfu_json, selector_used_field, cardinality);
        if (app.cardinality[field_name] === Object.keys(app.jfu[field_name]).length) {
          app.showButtonAddComponent[field_name] = false;
        }
      }

      app.cleanValues();
      app.fieldName = '';
    },
    deleteComponent: function(event, index, field_name) {
      var jfu_field = app.jfu[field_name];
      var temp_delete = Object.values(jfu_field);
      temp_delete.splice(parseInt(index), 1);
      var selector_used_field = '.field--name-' + field_name.replace(/_/g, '-');
      var jfu_temp_field = {};
      for (var i = 0; i < temp_delete.length; i++) {
        jfu_temp_field[i] = temp_delete[i];
      }

      app.jfu[field_name] = jfu_temp_field;

      var cardinality = app.cardinality[field_name];
      app.reloadField(temp_delete, selector_used_field, cardinality);

      if (app.cardinality[field_name] > Object.keys(app.jfu[field_name]).length) {
        app.showButtonAddComponent[field_name] = true;
      }
    },
    editComponent: function(event, index, type, field_name) {
      app.cachedData = JSON.parse(JSON.stringify(app.jfu[field_name][index]));

      var temp_edit = Object.values(app.jfu[field_name]);
      var cc = temp_edit[index];
      app.editIndex = index;

      var jfu_components_field = app.jfuComponents[field_name];
      jfu_components_field[type] = cc;
      app.fieldName = field_name;
      app.currentModal = 'current--modal-' + field_name;
      app.selectedComponent = type;
    }
  }
});

// Define component form add custom classes
Vue.component('form-classes', {
  props: ['o_json', 'index', 'field_name', 'options'],
  computed: {
    allow_classes: function() {
      return this.options['configClasses'][this.field_name][this.o_json.type].allow_classes;
    },
    allow_class_list: function() {
      return this.options['configClasses'][this.field_name][this.o_json.type].allow_select_class;
    },
    class_list: function() {
      var class_list = this.options['configClasses'][this.field_name][this.o_json.type].class_list.replace(/\s/g, '');
      return class_list.split(',');
    }
  },
  template: `
    <div class="jfu-classes">
      <p v-if="allow_classes === 1">
        <label>{{ options.defaultLabels.custom_classes }}
          <input type="text" v-bind:name="field_name + '-' + o_json.type + '-' + index" size="60" maxlength="255" v-bind:placeholder="options.defaultLabels.custom_classes" v-model="o_json.options.custom_classes" />
        </label>
        <span>{{ options.defaultLabels.allow_class_text_help }}</span>
      </p>
      <p v-if="allow_class_list === 1 && class_list[0] !== ''">
        <label for="jfu-classes-select">{{ options.defaultLabels.class_list }}</label>
        <select v-model="o_json.options.selected_class" id="jfu-classes-select">
          <option value="">{{ options.defaultLabels.none }}</option>
          <option v-for="(c, i) in class_list" :value="c">{{ c }}</option>
        </select>
      </p>
    </div>
  `
});

// Define component form allow group items
Vue.component('form-allow-group-items', {
  props: ['o_json', 'index', 'field_name', 'options', 'component'],
  computed: {
    allow_group_items: function() {
      return this.options['configClasses'][this.field_name][this.o_json.type].allow_group_items;
    }
  },
  template: `
    <div v-if="allow_group_items === 1" class="group-items">
      <p>
        <div class="jfu-field-form--container">
          <p>
            <label>{{ options.defaultLabels.group_items_quantity }}
              <input type="number" v-bind:name="field_name + '_group_items_quantity'" size="60" maxlength="10" placeholder="Quantity" v-model="o_json.options.group_items_quantity"/>
            </label>
            <span class="jfu-input--description">{{ options.defaultLabels[field_name]['group_items_quantity_help_' + component] }}</span>
          </p>
          <p>
            <label>{{ options.defaultLabels.custom_classes }}
              <input type="text" v-bind:name="field_name + '-group-items-' + o_json.type + '-' + index" size="60" maxlength="255" placeholder="Custom classes" v-model="o_json.options.group_classes" />
            </label>
            <span class="jfu-input--description">{{ options.defaultLabels.allow_class_text_help }}</span>
          </p>
        </div>
      </p>
    </div>
  `
});

Vue.mixin({
  methods: {
    dragStart: function(event, index) {
      app.draggableIndex = parseInt(index);
      event.dataTransfer.dropEffect = 'move';
    },
    dragEnd: function(event, field_name) {
      app.reorderedComponents(event, field_name);
      app.draggableIndex = -1;
    },
    addToJfu: function(event) {
      var field_name = app.fieldName;
      var component_type = app.selectedComponent;

      // Custom validate
      var valid = this.customValidate(app.jfuComponents[field_name][component_type], component_type);
      if (valid) {
        var container = jQuery(event.target).children('.error-messages');
        container.show();
        setTimeout(function() {
          container.hide();
        }, 2000);
        return;
      }

      //Just for banners
      if (component_type === 'banner') {
        if (app.jfuComponents[field_name][component_type].options.type !== 'fullcolor') {
          app.jfuComponents[field_name][component_type].options.color1 = '';
          app.jfuComponents[field_name][component_type].options.color2 = '';
        }
      }

      //Just for table
      if (component_type === 'table') {
        let allItems = app.jfuComponents[field_name][component_type];
        for (let keyHead  in allItems.thead.items) {
          app.jfuComponents[field_name][component_type].tbody.items[0].cells[keyHead].class = allItems.thead.items[keyHead].class;
        }
      }

      var add = app.jfuComponents[field_name];
      var add_current_json = JSON.parse(JSON.stringify(add[component_type]));

      var temp_add = app.jfu[field_name];
      var temp = Object.values(temp_add);

      temp.push(add_current_json);
      for (var i = 0; i < temp.length; i++) {
        temp_add[i] = temp[i];
      }

      var reload_jfu_json = JSON.parse(JSON.stringify(temp_add));
      var selector_used_field = '.field--name-' + app.fieldNameClass;
      var cardinality = app.cardinality[field_name];
      if (cardinality === -1) {
        // jQuery trigger event mousedown when adding some component.
        var selector_add_another_item = '.add-another-item-' + app.fieldNameClass;

        jQuery(selector_add_another_item).trigger('mousedown');
        jQuery(document).ajaxComplete(function(event, xhr, settings) {
          app.reloadField(reload_jfu_json, selector_used_field, cardinality);
        });
      }
      else {
        app.reloadField(reload_jfu_json, selector_used_field, cardinality);
        if (app.cardinality[field_name] === Object.keys(app.jfu[field_name]).length) {
          app.showButtonAddComponent[field_name] = false;
        }
      }

      app.cleanValues();
      app.selectedComponent = '';
      app.currentModal = '';
    },
    updateToJfu: function(event, type, index) {
      var field_name = app.fieldName;
      var jfu_components_field = app.jfuComponents[field_name];

      var valid = this.customValidate(app.jfuComponents[field_name][type], type);
      if (valid) {
        var container = jQuery(event.target).children('.error-messages');
        container.show();
        setTimeout(function() {
          container.hide();
        }, 2000);
        return;
      }

      //Just for banners
      if (type === 'banner') {
        if (jfu_components_field.banner.options.type !== 'fullcolor') {
          jfu_components_field[type].options.color1 = '';
          jfu_components_field[type].options.color2 = '';
        };
      };

      //Just for table
      if (type === 'table') {
        let tableItems = jfu_components_field[type];
        for (let tbodyItem  in tableItems.tbody.items) {
          for (let cell  in tableItems.tbody.items[tbodyItem].cells) {
            jfu_components_field[type].tbody.items[tbodyItem].cells[cell].class = jfu_components_field[type].thead.items[cell].class;
          }
        }
      }

      var update_string_json = JSON.stringify(jfu_components_field[type]);
      var update_current_json = JSON.parse(update_string_json);
      var update_jfu_field = Object.values(app.jfu[field_name]);
      var selector_used_field = '.field--name-' + field_name.replace(/_/g, '-');
      update_jfu_field[app.editIndex] = update_current_json;
      var jfu_field = {};
      for (var i = 0; i < update_jfu_field.length; i++) {
        jfu_field[i] = update_jfu_field[i];
      }

      app.jfu[field_name] = jfu_field;

      jQuery(selector_used_field + ' textarea[target-weight="' + index + '"]').val(update_string_json);

      app.cleanValues();
      app.editIndex = '';
      app.selectedComponent = '';
      app.currentModal = '';
      app.fieldName = '';
    },
    reloadAjaxLinks: function() {
      Drupal.ajax.bindAjaxLinks(document.body);
    },
    customValidate: function(component, type) {
      var errors = false;
      if (typeof component.items !== 'undefined') {
        if (typeof component.items.items !== 'undefined') {
          var items = component.items.items;
          switch (type) {
            case 'content_columns':
            case 'banner':
              var optional_type =  component.options.type;
              break;
            default:
              var optional_type = '';
              break;
          }
          errors = this.validateComponent(items, type, optional_type);
        }
        else {
          if (component.title === '' || component.items.length === 0) {
            errors = true;
          }
          else {
            for (var i = 0; i < component.items.length; i++) {
              errors = this.customValidate(component.items[i].body, component.items[i].body.type);
              if (errors) {
                break;
              }
            }
          }
        }
      }

      switch (type) {
        case 'embed':
          var embed = component.iframe;
          if (embed.indexOf('<iframe') !== 0 || embed.substring(embed.lastIndexOf('</iframe>')) !== '</iframe>') {
            errors = true;
          }
          break;
        case 'simple_card':
          if (component.image.fid === '') {
            errors = true;
          }
          break;
        default:
          break;
      }

      return errors;
    },
    validateComponent: function(items, component_type, optional_type) {
      var errors = false;
      var last_index = items.length - 1;
      switch (component_type) {
        case 'banner':
        case 'gallery':
          for (let i in items) {
            if (optional_type !== 'fullcolor' && items[i].image.fid === '') {
              errors = true;
            }
          }
          break;
        case 'data_number_card':
          if (items[last_index].prefix === '' && items[last_index].suffix === ''
            && items[last_index].value === '' && items[last_index].text === '') {
            errors = true;
          }
          break;
        case 'content_columns':
          switch (optional_type) {
            case 'default':
            case 'icons':
              if (items[last_index].image.fid === '' && items[last_index].link.value === ''
                && items[last_index].title === '') {
                errors = true;
              }
              break;
            case 'text':
              if (items[last_index].body.value === '' && items[last_index].link.value === ''
                && items[last_index].title === '') {
                errors = true;
              }
              break;
            default:
              break;
          }
          case 'link_list':
            for (let i in items) {
              if (items[i].value === '' || items[i].text === '') {
                errors = true;
              }
            }
            break;
          break;
        default:
          break;
      }

      return errors;
    },
    activeToggle: function(item, toggle) {
      if (this.$el.querySelector(item).classList.contains('is-active')) {
        this.$el.querySelector(item).classList.remove('is-active');
        this.$el.querySelector(toggle).classList.remove('is-active');
      }
      else {
        this.$el.querySelector(item).classList.add('is-active');
        this.$el.querySelector(toggle).classList.add('is-active');
      }
    },
    startDrag (evt, item) {
      evt.dataTransfer.dropEffect = 'move'
      evt.dataTransfer.effectAllowed = 'move'
      evt.dataTransfer.setData('item_key', item)
    },
    onDrop (evt, list, o_items) {
      const item_key = evt.dataTransfer.getData('item_key');
      const element = o_items.splice(item_key, 1)[0];
      o_items.splice(list, 0, element);

      this.$el.querySelector('.jfu-drop-changed-' + list).classList.remove('jfu-drop-changed');
    }
  }
});

var app = new Vue({
  el: '#app',
  data: function() {
    var jfu = {};
    var show_button_add_component = {};
    var cardinality = drupalSettings.cardinality;
    for (let field in drupalSettings.field_names) {
      jfu[field] = {};
      var selector = '[id*="' + field.replace(/_/g, '-') + '-values"] textarea';
      if (cardinality[field] === 1) {
        selector = '[id*="edit-' + field.replace(/_/g, '-') + '-0-value"]';
      }
      var values = [];
      jQuery(selector).each(function(index, value) {
        let el = jQuery(value);
        let el_value = el.val();
        let weight_attr = el.attr('target-weight');
        if (el_value !== '') {
          values.push('"' + weight_attr + '":' + el_value);
        }
      });
      if (values !== '') {
        var json_field_string = '{' + values.join() + '}';
        jfu[field] = JSON.parse(json_field_string);
      }

      show_button_add_component[field] = true;
      if (cardinality[field] === Object.keys(jfu[field]).length) {
        show_button_add_component[field] = false;
      }
    }

    // TODO: ask what it is used for.
    let indexElement = 0;
    for (let field in drupalSettings.field_names) {
      indexElement++;
    }

    return {
      jfu: jfu,
      jfuComponents: drupalSettings.jfu_components,
      jfuToReset: JSON.parse(JSON.stringify(drupalSettings.jfu_components)),
      cardinality: cardinality,
      selectedComponent: '',
      jfuOrder: indexElement,
      showButtonAddComponent: show_button_add_component,
      currentModal: '',
      editIndex: '',
      fieldName: '',
      options: {
        defaultLabels: drupalSettings.defaultLabels,
        urlBase: drupalSettings.url_base,
        urlUploadImage: drupalSettings.url_upload_image,
        urlTextFormat: drupalSettings.url_text_format,
        urlPublicImage: drupalSettings.url_public,
        urlReferenceAutocomplete: drupalSettings.url_autocomplete_link,
        configClasses: drupalSettings.config_components,
        jfuModulePath: drupalSettings.jfu_module_path,
      },
      cachedData: []
    };
  },
  methods: {
    clearSelectedComponent: function(event) {
      this.selectedComponent = '';
    },
    loadModal: function(event) {
      this.fieldName = event.target.attributes.target_field_name.value;
      this.fieldNameClass = this.fieldName.replace(/_/g, '-');
      this.currentModal = 'current--modal-' + this.fieldName;
    },
    loadFormComponent: function(event, type) {

    },
    selectDefaultEmbed: function(index) {
      var current_selector = '#edit-'  + this.fieldName.replaceAll('_', '-') + '-wrapper';
      jQuery(current_selector + ' select[name="embed_type_' + index + '"]').val('embed');
      jQuery(current_selector + ' input[name="embed_video_vimeo_' + index + '"]').parent().hide();
      jQuery(current_selector + ' input[name="embed_video_youtube_' + index + '"]').parent().hide();
      jQuery(current_selector + ' textarea[name="embed_iframe_' + index + '"]').parent().hide();
      var iframe_val = jQuery(current_selector + ' textarea[name="embed_iframe_' + index + '"]').val();
      if (iframe_val !== '') {
        var iframe_el = jQuery(iframe_val);
        var embed_type = iframe_el.attr('target_type');
        var target_id = iframe_el.attr('target_id');
        switch (embed_type) {
          case 'youtube':
            var youtube_url = 'https://www.youtube.com/watch?v=' + target_id;
            jQuery(current_selector + ' input[name="embed_video_youtube_' + index + '"]').val(youtube_url);
            jQuery(current_selector + ' input[name="embed_video_youtube_' + index + '"]').parent().show();
            jQuery(current_selector + ' select[name="embed_type_' + index + '"]').val(embed_type);
            break;
          case 'vimeo':
            var vimeo_url = 'https://vimeo.com/' + target_id;
            jQuery(current_selector + ' input[name="embed_video_vimeo_' + index + '"]').val(vimeo_url);
            jQuery(current_selector + ' input[name="embed_video_vimeo_' + index + '"]').parent().show();
            jQuery(current_selector + ' select[name="embed_type_' + index + '"]').val(embed_type);
            break;
          default:
            jQuery(current_selector + ' textarea[name="embed_iframe_' + index + '"]').parent().show();
            jQuery(current_selector + ' select[name="embed_type_' + index + '"]').val('embed');
            break;
        }
      }
      else {
        jQuery(current_selector + ' textarea[name="embed_iframe_' + index + '"]').parent().show();
      }
    },
    closeModal: function(event) {
      if (this.editIndex !== '') {
        this.jfu[this.fieldName][this.editIndex] = this.cachedData;
      }

      this.cleanValues();
      this.currentModal = '';
      this.fieldName = '';
      this.selectedComponent = '';
      this.editIndex = '';
      jQuery('[data-drupal-selector="edit-jfu-fids-delete"]').val('');
    },
    cleanValues: function() {
      var reset = this.jfuToReset[this.fieldName];
      this.jfuComponents[this.fieldName] = JSON.parse(JSON.stringify(reset));
    },
    reloadField: function(object, selector_field, cardinality) {
      if (cardinality === 1) {
        var json_component = JSON.stringify(object[0]);
        jQuery(selector_field + ' textarea[target-weight="0"]').val(json_component);
      }
      else {
        jQuery(selector_field + ' tbody tr').each(function(index, value) {
          if (object[index] !== 'undefined') {
            var json_component = JSON.stringify(object[index]);
            jQuery(selector_field + ' textarea[target-weight="' + index + '"]').val(json_component);
          }
        });
      }
    },
    reorderedComponents: function(event, field_name) {
      if(this.draggableIndex !== -1 && typeof this.draggableIndex !== 'undefined') {
        let reorderedList=[];
        let lastI = -1;
        // get all rendered list items
        // and their coordinates
        var reordered_field = this.jfu[field_name];

        let selector_used_field = '.field--name-' + field_name.replace(/_/g, '-');
        jQuery(selector_used_field + ' .display-component .component-item--wrapper').each(function(i, elem) {
          // ignore dragging element
          if (i !== app.draggableIndex) {
            if (elem.offsetTop < event.pageY && i > app.draggableIndex) {
              // if we are here, that means
              // that element moved up
              reorderedList[i - 1] = reordered_field[i];
              if (lastI === -1 || lastI < i) {
                lastI = i;
              }
            }
            else if (elem.offsetTop > event.pageY && i < app.draggableIndex) {
              // if we are here, that means
              // that element moved down
              reorderedList[i + 1] = reordered_field[i];
              if (lastI === -1 || lastI > i) {
                lastI = i;
              }
            }
            else {
              // otherwise position doesn't change
              reorderedList[i] = reordered_field[i];
            }
          }
        });

        // if positions changed - we should reassign items
        if (lastI !== -1) {
          var cardinality = this.cardinality[field_name];
          reorderedList[lastI] = reordered_field[this.draggableIndex];
          this.jfu[field_name] = Object.assign([], reorderedList, reorderedList);
          this.reloadField(this.jfu[field_name], selector_used_field, cardinality);
        }
      }
    }
  }
});

Главная | Обратная связь

drupal hosting | друпал хостинг | it patrol .inc