Files
medicalalert-web-reloaded/wp/wp-content/plugins/facetwp/assets/js/dist/admin.min.js
Tony Volpe 779393381f Merged in feature/plugins-update (pull request #9)
wp plugin updates from pantheon

* wp plugin updates from pantheon
2023-12-15 18:08:21 +00:00

1685 lines
101 KiB
JavaScript

(function () {
'use strict';
(function ($) {
$(function () {
init_vue();
init_custom_js();
});
function init_vue() {
Vue.config.devtools = true;
Vue.component('v-select', VueSelect.VueSelect);
Vue.filter('i18n', function (str) { return FWP.__(str); });
// Defaults mixin
var builder_defaults = {
methods: {
defaultLayout: function defaultLayout() {
return {
items: [this.defaultRow()],
settings: this.getDefaultSettings('layout')
};
},
defaultRow: function defaultRow() {
return {
type: 'row',
items: [this.defaultCol()],
settings: this.getDefaultSettings('row')
};
},
defaultCol: function defaultCol() {
return {
type: 'col',
items: [],
settings: this.getDefaultSettings('col')
};
},
defaultItem: function defaultItem(source) {
return {
type: 'item',
source: source,
settings: this.getDefaultSettings('item', source)
};
},
mergeSettings: function mergeSettings(settings, type, source) {
var defaults = this.getDefaultSettings(type, source);
var default_keys = Object.keys(defaults);
var setting_keys = Object.keys(settings);
// Automatically inject new settings
var missing_keys = default_keys.filter(function (name) { return !setting_keys.includes(name); });
missing_keys.forEach(function (name, index) {
Vue.set(settings, name, defaults[name]);
});
return settings;
},
getSettingsMeta: function getSettingsMeta() {
var settings = {
num_columns: {
type: 'number',
title: FWP.__('Number of grid columns '),
defaultValue: 1
},
grid_gap: {
type: 'number',
title: FWP.__('Spacing between results'),
defaultValue: 10
},
no_results_text: {
type: 'textarea',
title: FWP.__('No results text')
},
text_style: {
type: 'text-style',
title: FWP.__('Text style'),
tab: 'style',
defaultValue: {
align: '',
bold: false,
italic: false
}
},
text_color: {
type: 'color',
title: FWP.__('Text color'),
tab: 'style'
},
font_size: {
type: 'slider',
title: FWP.__('Font size'),
tab: 'style',
defaultValue: {
unit: 'px',
size: 0
}
},
background_color: {
type: 'color',
title: FWP.__('Background color'),
tab: 'style'
},
border: {
type: 'border',
title: FWP.__('Border'),
tab: 'style',
defaultValue: {
style: 'none',
color: '',
width: {
unit: 'px',
top: 0,
right: 0,
bottom: 0,
left: 0
}
},
children: {
style: {
type: 'select',
title: FWP.__('Border style'),
choices: {
'none': FWP.__('None'),
'solid': FWP.__('Solid'),
'dashed': FWP.__('Dashed'),
'dotted': FWP.__('Dotted'),
'double': FWP.__('Double')
}
},
color: {
type: 'color',
title: FWP.__('Border color')
},
width: {
type: 'utrbl',
title: FWP.__('Border width')
}
}
},
button_text: {
type: 'text',
title: FWP.__('Button text')
},
button_text_color: {
type: 'color',
title: FWP.__('Button text color')
},
button_color: {
type: 'color',
title: FWP.__('Button color')
},
button_padding: {
type: 'utrbl',
title: FWP.__('Button padding'),
defaultValue: {
unit: 'px',
top: 0,
right: 0,
bottom: 0,
left: 0
}
},
separator: {
type: 'text',
title: FWP.__('Separator'),
defaultValue: ', '
},
custom_css: {
type: 'textarea',
title: FWP.__('Custom CSS'),
tab: 'style'
},
grid_template_columns: {
type: 'text',
title: FWP.__('Column widths'),
defaultValue: '1fr'
},
content: {
type: 'textarea',
title: FWP.__('Content')
},
image_size: {
type: 'select',
title: FWP.__('Image size'),
defaultValue: 'thumbnail',
choices: FWP.image_sizes,
v_show: [
{ type: 'source', value: 'featured_image' }
]
},
author_field: {
type: 'select',
title: FWP.__('Author field'),
defaultValue: 'display_name',
choices: {
'display_name': FWP.__('Display name'),
'user_login': FWP.__('User login'),
'ID': FWP.__('User ID')
}
},
field_type: {
type: 'select',
title: FWP.__('Field type'),
defaultValue: 'text',
choices: {
'text': 'Text',
'date': 'Date',
'number': 'Number'
}
},
date_format: {
type: 'text',
title: FWP.__('Date format'),
defaultValue: 'F j, Y',
v_show: [
{ type: 'field_type', value: 'date' },
{ type: 'source', value: 'post_date' },
{ type: 'source', value: 'post_modified' }
]
},
input_format: {
type: 'text',
title: FWP.__('Input format'),
defaultValue: 'Y-m-d',
v_show: [
{ type: 'field_type', value: 'date' },
{ type: 'source', value: 'post_date' },
{ type: 'source', value: 'post_modified' }
]
},
number_format: {
type: 'select',
title: FWP.__('Number format'),
choices: {
'': FWP.__('None'),
'n': '1234',
'n.n': '1234.5',
'n.nn': '1234.56',
'n,n': '1,234',
'n,n.n': '1,234.5',
'n,n.nn': '1,234.56'
},
v_show: [
{ type: 'field_type', value: 'number' }
]
},
link: {
type: 'link',
title: FWP.__('Link'),
defaultValue: {
type: 'none',
href: '',
target: ''
},
children: {
type: {
type: 'select',
title: FWP.__('Link type'),
choices: {
'none': FWP.__('None'),
'post': FWP.__('Post URL'),
'custom': FWP.__('Custom URL')
}
}
}
},
prefix: {
type: 'text',
title: FWP.__('Prefix')
},
suffix: {
type: 'text',
title: FWP.__('Suffix')
},
is_hidden: {
type: 'checkbox',
defaultValue: false,
suffix: FWP.__('Hide item?')
},
padding: {
type: 'utrbl',
title: FWP.__('Padding'),
defaultValue: {
unit: 'px',
top: 0,
right: 0,
bottom: 0,
left: 0
},
tab: 'style'
},
name: {
type: 'text',
title: FWP.__('Unique name'),
notes: '(Required) unique element name, without spaces'
},
css_class: {
type: 'text',
title: FWP.__('CSS class'),
tab: 'style'
}
};
settings.button_border = this.$root.cloneObj(settings.border);
settings.button_border.title = FWP.__('Button border');
settings.button_border.tab = 'basic';
settings.term_link = this.$root.cloneObj(settings.link);
settings.term_link.children.type.choices = {
'none': FWP.__('None'),
'term': FWP.__('Term URL'),
'custom': FWP.__('Custom URL')
};
return settings;
},
getDefaultFields: function getDefaultFields(type, source) {
var fields = [];
if ('layout' == type) {
fields.push('num_columns', 'grid_gap', 'no_results_text');
}
if ('row' == type) {
fields.push('grid_template_columns');
}
if ('item' == type) {
if ('html' == source) {
fields.push('content');
}
if ('featured_image' == source) {
fields.push('image_size', 'link');
}
if ('button' == source) {
fields.push('button_text', 'button_text_color', 'button_color', 'button_padding', 'button_border', 'link');
}
if ('post_date' == source || 'post_modified' == source) {
fields.push('date_format');
}
if ('post_title' == source) {
fields.push('link');
}
if ('post_author' == source) {
fields.push('author_field');
}
if (0 === source.indexOf('cf/')) {
fields.push('field_type', 'date_format', 'input_format', 'number_format', 'link');
}
if (0 === source.indexOf('woo/')) {
fields.push('field_type', 'date_format', 'input_format', 'number_format');
}
if (0 === source.indexOf('tax/')) {
fields.push('separator', 'term_link');
}
if (!['html', 'button', 'featured_image'].includes(source)) {
fields.push('prefix', 'suffix');
}
}
fields.push('border', 'background_color', 'padding', 'text_color', 'text_style', 'font_size', 'name', 'css_class');
if ('layout' == type) {
fields.push('custom_css');
}
if ('item' == type) {
fields.push('is_hidden');
}
return fields;
},
getDefaultSettings: function getDefaultSettings(type, source) {
var settings = {};
var settings_meta = this.getSettingsMeta();
var fields = this.getDefaultFields(type, source);
fields.forEach(function (name) {
var defaultValue = settings_meta[name].defaultValue || '';
if ('name' == name) {
defaultValue = 'el-' + Math.random().toString(36).substring(7);
}
settings[name] = defaultValue;
});
return settings;
}
}
};
/* ================ query builder ================ */
Vue.component('query-builder', {
props: {
query_obj: {
type: Object,
required: true
},
template: {
type: Object,
required: true
}
},
template: "\n <div class=\"qb-wrap\">\n <h3>Which results should be in the listing?</h3>\n\n <div class=\"qb-condition\">\n {{ 'Fetch' | i18n }}\n <v-select\n v-model=\"query_obj.post_type\"\n :options=\"FWP.query_data.post_types\"\n :multiple=\"true\"\n :searchable=\"false\"\n :close-on-select=\"false\"\n placeholder=\"All post types\">\n </v-select>\n\n {{ 'and show' | i18n }}\n <input type=\"number\" v-model.number=\"query_obj.posts_per_page\" class=\"qb-posts-per-page\" />\n {{ 'per page' | i18n }}\n </div>\n\n <div class=\"qb-condition\"\n v-show=\"query_obj.orderby.length\">\n {{ 'Sort by' | i18n }}\n </div>\n\n <div v-for=\"(row, index) in query_obj.orderby\" class=\"qb-condition\">\n <fselect :row=\"row\">\n <optgroup label=\"Posts\">\n <option value=\"ID\">ID</option>\n <option value=\"title\">{{ 'Post Title' | i18n }}</option>\n <option value=\"name\">{{ 'Post Name' | i18n }}</option>\n <option value=\"type\">{{ 'Post Type' | i18n }}</option>\n <option value=\"date\">{{ 'Post Date' | i18n }}</option>\n <option value=\"modified\">{{ 'Post Modified' | i18n }}</option>\n <option value=\"comment_count\">{{ 'Comment Count' | i18n }}</option>\n <option value=\"menu_order\">{{ 'Menu Order' | i18n }}</option>\n <option value=\"post__in\">post__in</option>\n </optgroup>\n <optgroup label=\"Custom Fields\">\n <option v-for=\"(label, name) in FWP.data_sources.custom_fields.choices\" :value=\"name\">{{ label }}</option>\n </optgroup>\n </fselect>\n <select v-model=\"row.type\" v-show=\"row.key.substr(0, 3) == 'cf/'\" class=\"qb-type\">\n <option value=\"CHAR\">TEXT</option>\n <option value=\"NUMERIC\">NUMERIC</option>\n </select>\n <select v-model=\"row.order\" class=\"qb-order\">\n <option value=\"ASC\">ASC</option>\n <option value=\"DESC\">DESC</option>\n </select>\n <span @click=\"deleteSortCriteria(index)\" class=\"qb-remove\" v-html=\"FWP.svg['minus-circle']\"></span>\n </div>\n\n <div class=\"qb-condition\"\n v-show=\"query_obj.filters.length\">\n {{ 'Narrow results by' | i18n }}\n </div>\n\n <div v-for=\"(row, index) in query_obj.filters\" class=\"qb-condition\">\n <fselect :row=\"row\">\n <optgroup v-for=\"data in FWP.query_data.filter_by\" :label=\"data.label\">\n <option v-for=\"(label, name) in data.choices\" :value=\"name\" v-html=\"label\"></option>\n </optgroup>\n </fselect>\n\n <select v-model=\"row.type\" v-show=\"row.key.substr(0, 3) == 'cf/'\" class=\"qb-type\">\n <option value=\"CHAR\">TEXT</option>\n <option value=\"NUMERIC\">NUMERIC</option>\n <option value=\"DATE\">DATE</option>\n </select>\n\n <select v-model=\"row.compare\" class=\"qb-compare\">\n <option v-if=\"showCompare('=', row)\" value=\"=\">=</option>\n <option v-if=\"showCompare('!=', row)\" value=\"!=\">!=</option>\n <option v-if=\"showCompare('>', row)\" value=\">\">&gt;</option>\n <option v-if=\"showCompare('>=', row)\" value=\">=\">&gt;=</option>\n <option v-if=\"showCompare('<', row)\" value=\"<\">&lt;</option>\n <option v-if=\"showCompare('<=', row)\" value=\"<=\">&lt;=</option>\n <option v-if=\"showCompare('IN', row)\" value=\"IN\">IN</option>\n <option v-if=\"showCompare('NOT IN', row)\" value=\"NOT IN\">NOT IN</option>\n <option v-if=\"showCompare('EXISTS', row)\" value=\"EXISTS\">EXISTS</option>\n <option v-if=\"showCompare('NOT EXISTS', row)\" value=\"NOT EXISTS\">NOT EXISTS</option>\n <option v-if=\"showCompare('EMPTY', row)\" value=\"EMPTY\">EMPTY</option>\n <option v-if=\"showCompare('NOT EMPTY', row)\" value=\"NOT EMPTY\">NOT EMPTY</option>\n </select>\n\n <v-select\n v-model=\"row.value\"\n v-show=\"maybeShowValue(row.compare)\"\n :options=\"[]\"\n :multiple=\"true\"\n :taggable=\"true\"\n :close-on-select=\"false\"\n :placeholder=\"getPlaceholder(row)\">\n <div slot=\"no-options\">\n Type a value, then press \"Enter\" to add it\n </div>\n </v-select>\n\n <span @click=\"deleteFilterCriteria(index)\" class=\"qb-remove\" v-html=\"FWP.svg['minus-circle']\"></span>\n </div>\n\n <div class=\"qb-actions\">\n <span class=\"facetwp-btn\" @click=\"addSortCriteria\">{{ 'Add query sort' | i18n }}</span>\n <span class=\"facetwp-btn\" @click=\"addFilterCriteria\">{{ 'Add query filter' | i18n }}</span>\n <span class=\"facetwp-btn\" @click=\"$root.getQueryArgs(template)\">{{ 'Convert to query args' | i18n }}</span>\n </div>\n </div>\n ",
methods: {
addTag: function addTag(newTag, value) {
value.push(newTag);
},
getPlaceholder: function getPlaceholder(ref) {
var key = ref.key;
return ('tax/' == key.substr(0, 4)) ? FWP.__('Enter term slugs') : FWP.__('Enter values');
},
maybeShowValue: function maybeShowValue(compare) {
return !['EXISTS', 'NOT EXISTS', 'EMPTY', 'NOT EMPTY'].includes(compare);
},
showCompare: function showCompare(option, ref) {
var key = ref.key;
var type = ref.type;
if ('tax/' == key.substr(0, 4)) {
if (!['IN', 'NOT IN', 'EXISTS', 'NOT EXISTS'].includes(option)) {
return false;
}
}
else if (['ID', 'post_author', 'post_status', 'post_name'].includes(key)) {
if (option != 'IN' && option != 'NOT IN') {
return false;
}
}
else if ('DATE' == type || 'post_date' == key || 'post_modified' == key) {
if (!['>', '>=', '<', '<='].includes(option)) {
return false;
}
}
else if ('CHAR' == type) {
if (['>', '>=', '<', '<='].includes(option)) {
return false;
}
}
return true;
},
addSortCriteria: function addSortCriteria() {
this.query_obj.orderby.push({
key: 'title',
order: 'ASC',
type: 'CHAR'
});
},
addFilterCriteria: function addFilterCriteria() {
this.query_obj.filters.push({
key: 'ID',
value: [],
compare: 'IN',
type: 'CHAR'
});
},
deleteSortCriteria: function deleteSortCriteria(index) {
Vue.delete(this.query_obj.orderby, index);
},
deleteFilterCriteria: function deleteFilterCriteria(index) {
Vue.delete(this.query_obj.filters, index);
}
}
});
Vue.component('fselect', {
data: function data() {
return {
prev_key: ''
};
},
props: ['row'],
template: "\n <select v-model=\"row.key\" class=\"qb-object\" :data-key=\"row.key\">\n <slot></slot>\n </select>\n ",
mounted: function mounted() {
fSelect(this.$el);
},
/**
* fSelects won't refresh when deleting, so we need to
* manually reload() the changed elements
*/
beforeUpdate: function beforeUpdate() {
this.prev_key = this.$el.getAttribute('data-key');
},
updated: function updated() {
if (this.row.key != this.prev_key) {
this.$el.fselect.reload();
}
}
});
/* ================ layout builder ================ */
Vue.component('builder', {
props: {
layout: Object
},
template: "\n <div class=\"builder-wrap\">\n <div class=\"builder-canvas-wrap\">\n <h3>How should an individual result appear?</h3>\n <div class=\"builder-canvas\">\n <draggable :list=\"layout.items\" handle=\".builder-row-actions.not-child\">\n <builder-row\n v-for=\"(row, index) in layout.items\"\n :row=\"row\"\n :rows=\"layout.items\"\n :index=\"index\"\n :key=\"index\">\n </builder-row>\n </draggable>\n </div>\n </div>\n <builder-settings :layout=\"layout\"></builder-settings>\n </div>\n "
});
Vue.component('setting-wrap', {
mixins: [builder_defaults],
props: ['settings', 'name', 'source', 'tab'],
template: "\n <div class=\"builder-setting\" v-show=\"isVisible\">\n <div v-if=\"meta.notes\" class=\"setting-title facetwp-tooltip\">\n {{ title }}\n <div class=\"facetwp-tooltip-content\" v-html=\"meta.notes\"></div>\n </div>\n <div v-else class=\"setting-title\" v-html=\"title\"></div>\n <div><component :is=\"getSettingComponent\" v-bind=\"$props\" :meta=\"meta\"></component></div>\n </div>\n ",
computed: {
getSettingComponent: function getSettingComponent() {
return 'setting-' + this.type;
},
isVisible: function isVisible() {
var ret = true;
var self = this;
if ('undefined' === typeof this.meta.tab) {
this.meta.tab = 'basic';
}
if (this.meta.tab !== this.tab) {
ret = false;
}
else if ('undefined' !== typeof this.meta.v_show) {
ret = false;
this.meta.v_show.forEach(function (cond, index) {
var type = cond.type;
var setting_val = ('source' == type) ? self[type] : self.settings[type];
var cond_value = cond.value || '';
var cond_compare = cond.compare || '==';
var is_match = ('==' == cond_compare)
? setting_val == cond_value
: setting_val != cond_value;
if (is_match) {
ret = true;
}
});
}
return ret;
}
},
created: function created() {
this.settings_meta = this.getSettingsMeta();
this.meta = this.settings_meta[this.name];
this.type = this.meta.type;
this.title = this.meta.title;
}
});
Vue.component('setting-text', {
props: ['settings', 'name', 'meta'],
template: '<input type="text" v-model="settings[name]" :placeholder="meta.placeholder" />'
});
Vue.component('setting-number', {
props: ['settings', 'name', 'meta'],
template: '<input type="number" v-model.number="settings[name]" :placeholder="meta.placeholder" />'
});
Vue.component('setting-textarea', {
props: ['settings', 'name', 'meta'],
template: '<textarea v-model="settings[name]"></textarea>'
});
Vue.component('setting-slider', {
props: ['settings', 'name', 'meta'],
template: "\n <div>\n <input type=\"range\" min=\"0\" max=\"80\" step=\"1\" v-model.number=\"settings[name].size\" />\n <span v-html=\"fontSizeLabel\" style=\"vertical-align:top\"></span>\n </div>\n ",
computed: {
fontSizeLabel: function fontSizeLabel() {
var val = this.settings[this.name];
return (0 === val.size) ? 'none' : val.size + val.unit;
}
}
});
Vue.component('setting-color', {
props: ['settings', 'name', 'meta'],
template: "\n <div class=\"color-wrap\">\n <div class=\"color-canvas\">\n <span class=\"color-preview\"></span>\n <input type=\"text\" class=\"color-input\" v-model=\"settings[name]\" />\n </div>\n <span class=\"color-clear\">X</span>\n </div>",
mounted: function mounted() {
var self = this;
var $canvas = self.$el.getElementsByClassName('color-canvas')[0];
var $preview = self.$el.getElementsByClassName('color-preview')[0];
var $input = self.$el.getElementsByClassName('color-input')[0];
var $clear = self.$el.getElementsByClassName('color-clear')[0];
$preview.style.backgroundColor = $input.value;
var picker = new Picker({
parent: $canvas,
popup: 'left',
alpha: false,
onDone: function onDone(color) {
var hex = color.hex().substr(0, 7);
self.settings[self.name] = hex;
$preview.style.backgroundColor = hex;
}
});
picker.onOpen = function(color) {
picker.setColor($input.value);
};
$clear.addEventListener('click', function() {
self.settings[self.name] = '';
$preview.style.backgroundColor = '';
});
}
});
Vue.component('setting-link', {
props: ['settings', 'name', 'meta'],
template: "\n <div>\n <setting-select\n :settings=\"settings[name]\"\n name=\"type\"\n :meta=\"meta.children.type\">\n </setting-select>\n\n <div v-show=\"settings[name].type == 'custom'\">\n <input\n type=\"text\"\n v-model=\"settings[name].href\"\n placeholder=\"https://\"\n />\n </div>\n <div v-show=\"settings[name].type != 'none'\">\n <input\n type=\"checkbox\"\n v-model=\"settings[name].target\"\n true-value=\"_blank\"\n false-value=\"\"\n />\n {{ 'Open in new tab?' | i18n }}\n </div>\n </div>\n "
});
Vue.component('setting-border', {
props: ['settings', 'name', 'meta'],
template: "\n <div>\n <setting-select\n :settings=\"settings[name]\"\n name=\"style\"\n :meta=\"meta.children.style\">\n </setting-select>\n\n <div v-show=\"settings[name].style != 'none'\">\n <div v-html=\"meta.children.color.title\" style=\"margin-top:10px\"></div>\n\n <setting-color\n :settings=\"settings[name]\"\n name=\"color\"\n :meta=\"meta.children.color\">\n </setting-color>\n\n <div v-html=\"meta.children.width.title\" style=\"margin-top:10px\"></div>\n\n <setting-utrbl\n :settings=\"settings[name]\"\n name=\"width\"\n :meta=\"meta.children.width\">\n </setting-utrbl>\n </div>\n </div>\n "
});
Vue.component('setting-checkbox', {
props: ['settings', 'name', 'meta'],
template: "\n <div>\n <input type=\"checkbox\" v-model=\"settings[name]\" /> {{ meta.suffix }}\n </div>\n "
});
Vue.component('setting-select', {
props: ['settings', 'name', 'meta'],
template: "\n <select v-model=\"settings[name]\">\n <option v-for=\"(label, value) in meta.choices\" :value=\"value\">{{ label }}</option>\n </select>\n "
});
Vue.component('setting-utrbl', {
props: ['settings', 'name', 'meta'],
template: "\n <div>\n <div class=\"utrbl utrbl-unit\"><input type=\"text\" v-model=\"settings[name].unit\" /><span>unit</span></div>\n <div class=\"utrbl\"><input type=\"text\" v-model.number=\"settings[name].top\" /><span>top</span></div>\n <div class=\"utrbl\"><input type=\"text\" v-model.number=\"settings[name].right\" /><span>right</span></div>\n <div class=\"utrbl\"><input type=\"text\" v-model.number=\"settings[name].bottom\" /><span>bottom</span></div>\n <div class=\"utrbl\"><input type=\"text\" v-model.number=\"settings[name].left\" /><span>left</span></div>\n </div>\n "
});
Vue.component('setting-text-style', {
props: ['settings', 'name', 'meta'],
template: "\n <div class=\"text-style-icons\">\n <span @click=\"toggleChoice('align', 'left')\" :class=\"{ active: isActive('align', 'left') }\" v-html=\"FWP.svg['align-left']\"></span>\n <span @click=\"toggleChoice('align', 'center')\" :class=\"{ active: isActive('align', 'center') }\" v-html=\"FWP.svg['align-center']\"></span>\n <span @click=\"toggleChoice('align', 'right')\" :class=\"{ active: isActive('align', 'right') }\" v-html=\"FWP.svg['align-right']\"></span>\n <span @click=\"toggleChoice('bold')\" :class=\"{ active: isActive('bold') }\" v-html=\"FWP.svg['bold']\"></span>\n <span @click=\"toggleChoice('italic')\" :class=\"{ active: isActive('italic') }\" v-html=\"FWP.svg['italic']\"></span>\n </div>\n ",
methods: {
toggleChoice: function toggleChoice(opt, val) {
var old_val = this.settings[this.name][opt];
if ('undefined' !== typeof val) {
this.settings[this.name][opt] = (val !== old_val) ? val : '';
}
else {
this.settings[this.name][opt] = ! old_val;
}
},
isActive: function isActive(opt, val) {
var new_val = ('undefined' !== typeof val) ? val : true;
return this.settings[this.name][opt] === new_val;
}
}
});
Vue.component('builder-settings', {
mixins: [builder_defaults],
props: {
layout: Object
},
data: function data() {
return {
title: '',
type: 'layout',
settings: this.layout.settings,
source: '',
active_tab: 'basic'
}
},
template: "\n <div class=\"builder-settings-wrap\">\n <h3>\n <div v-show=\"this.title\" class=\"builder-crumb\">\n <a href=\"javascript:;\" @click=\"$root.$emit('edit-layout')\">{{ 'Settings' | i18n }}</a> &raquo;\n </div>\n {{ settingTitle }}\n </h3>\n <div class=\"builder-settings\">\n <div class=\"template-tabs\">\n <span @click=\"setActiveTab('basic')\" :class=\"isActiveTab('basic')\">{{ 'Basic' | i18n }}</span>\n <span @click=\"setActiveTab('style')\" :class=\"isActiveTab('style')\">{{ 'Style' | i18n }}</span>\n </div>\n <setting-wrap\n v-for=\"name in settingsFields\"\n :settings=\"settings\"\n :name=\"name\"\n :source=\"source\"\n :tab=\"active_tab\"\n :key=\"uniqueKey()\">\n </setting-wrap>\n </div>\n </div>\n ",
computed: {
settingTitle: function settingTitle() {
return ('' === this.title) ? FWP.__('Settings') : this.title;
},
settingsFields: function settingsFields() {
return this.getDefaultFields(this.type, this.source);
}
},
methods: {
uniqueKey: function uniqueKey() {
// method to prevent caching
return Math.floor(Math.random() * 999999);
},
isActiveTab: function isActiveTab(which) {
return (this.active_tab === which) ? 'active' : '';
},
setActiveTab: function setActiveTab(which) {
this.active_tab = which;
}
},
created: function created() {
var self = this;
this.$root.$on('edit-layout', function () {
self.title = '';
self.type = 'layout';
self.settings = self.mergeSettings(self.layout.settings, self.type);
self.source = '';
});
this.$root.$on('edit-row', function (ref, num) {
var settings = ref.settings;
self.title = FWP.__('Row') + ' ' + num;
self.type = 'row';
self.settings = self.mergeSettings(settings, self.type);
self.source = '';
});
this.$root.$on('edit-col', function (ref, num) {
var settings = ref.settings;
self.title = FWP.__('Column') + ' ' + num;
self.type = 'col';
self.settings = self.mergeSettings(settings, self.type);
self.source = '';
});
this.$root.$on('edit-item', function (ref) {
var source = ref.source;
var settings = ref.settings;
self.title = FWP.layout_data[source];
self.type = 'item';
self.settings = self.mergeSettings(settings, self.type, source);
self.source = source;
});
}
});
Vue.component('builder-row', {
mixins: [builder_defaults],
props: {
row: Object,
rows: Array,
index: Number,
is_child: Boolean
},
template: "\n <div class=\"builder-row\">\n <div class=\"builder-row-actions\" :class=\"classIsChild\">\n <span @click=\"editRow\" title=\"Edit row\" v-html=\"FWP.svg['cog']\"></span>\n <span @click=\"addCol\" title=\"Add columm\" v-html=\"FWP.svg['columns']\"></span>\n <span @click=\"addRow\" title=\"Add row\" v-html=\"FWP.svg['plus']\"></span>\n <span @click=\"deleteRow\" title=\"Delete row\" v-html=\"FWP.svg['times']\"></span>\n </div>\n <div class=\"builder-row-inner\" :style=\"{ gridTemplateColumns: row.settings.grid_template_columns }\">\n <builder-col\n v-for=\"(col, index) in row.items\"\n :col=\"col\"\n :cols=\"row.items\"\n :index=\"index\"\n :key=\"index\">\n </builder-col>\n </div>\n </div>\n ",
computed: {
classIsChild: function classIsChild() {
return this.is_child ? 'is-child' : 'not-child';
}
},
methods: {
addRow: function addRow() {
this.rows.splice(this.index + 1, 0, this.defaultRow());
if (1 < this.rows.length) {
this.$root.$emit('edit-row', this.rows[this.index + 1], this.index + 2);
}
else {
this.$root.$emit('edit-layout');
}
},
addCol: function addCol() {
var len = this.row.items.push(this.defaultCol());
this.$root.$emit('edit-col', this.row.items[len - 1], len);
var grid_str = '1fr '.repeat(this.row.items.length).trim();
this.row.settings.grid_template_columns = grid_str;
},
editRow: function editRow() {
this.$root.$emit('edit-row', this.row, this.index + 1);
},
deleteRow: function deleteRow() {
Vue.delete(this.rows, this.index);
this.$root.$emit('edit-layout');
// Add default row
if (this.rows.length < 1) {
if (! this.is_child) {
this.addRow();
}
}
}
}
});
Vue.component('builder-col', {
mixins: [builder_defaults],
props: {
col: Object,
cols: Array,
index: Number
},
data: function data() {
return {
adding_item: false
}
},
template: "\n <div class=\"builder-col\">\n <col-resizer :cols=\"cols\" :index=\"index\" v-show=\"index < (cols.length - 1)\"></col-resizer>\n <popover :col=\"col\" v-if=\"adding_item\"></popover>\n <div class=\"builder-col-actions\">\n <span @click=\"editCol\" title=\"Edit columm\" v-html=\"FWP.svg['cog']\"></span>\n <span @click=\"deleteCol\" title=\"Delete column\" v-html=\"FWP.svg['times']\"></span>\n </div>\n <div class=\"builder-col-inner\" :class=\"[ !col.items.length ? 'empty-col' : '' ]\">\n <draggable v-model=\"col.items\" handle=\".item-drag\" group=\"drag-across-columns\" class=\"draggable\">\n <div v-for=\"(item, index) in col.items\" :key=\"index\">\n <builder-item\n v-if=\"item.type != 'row'\"\n :item=\"item\"\n :items=\"col.items\"\n :index=\"index\">\n </builder-item>\n <builder-row\n v-if=\"item.type == 'row'\"\n :row=\"item\"\n :rows=\"col.items\"\n :index=\"index\"\n :is_child=\"true\">\n </builder-row>\n </div>\n <div class=\"builder-empty-view\" @click=\"addItem\">\n <div class=\"builder-first-add\">+</div>\n </div>\n </draggable>\n </div>\n </div>\n ",
methods: {
addItem: function addItem() {
this.adding_item = ! this.adding_item;
},
editCol: function editCol() {
this.$root.$emit('edit-col', this.col, this.index + 1);
this.adding_item = false;
},
deleteCol: function deleteCol() {
// Remove the column
this.cols.splice(this.index, 1);
// Show the "Layout" settings
this.$root.$emit('edit-layout');
// Add default column
if (this.cols.length < 1) {
this.cols.push(this.defaultCol());
}
// Adjust the row's `grid_template_columns` string
var grid_str = '1fr '.repeat(this.cols.length).trim();
this.$parent.row.settings.grid_template_columns = grid_str;
},
away: function away() {
this.adding_item = false;
}
}
});
Vue.component('col-resizer', {
props: {
cols: Array,
index: Number
},
data: function data() {
return {
isResizing: false
}
},
template: '<div :class="classNames" @mousedown="onMouseDown"></div>',
computed: {
classNames: function classNames() {
return [
'resizer',
this.isResizing ? 'is-resizing' : ''
];
}
},
methods: {
onMouseDown: function onMouseDown(ref) {
var this$1$1 = this;
var resizer = ref.target;
var initialPageX = ref.pageX;
ref.pageY;
if (! resizer.classList.contains('resizer')) {
return;
}
var self = this;
var pane = resizer.parentElement;
var row_inner = pane.parentElement;
var initialPaneWidth = pane.offsetWidth;
var resize = function (initialSize, offset) {
if ( offset === void 0 ) offset = 0;
var containerWidth = row_inner.clientWidth;
var paneWidth = initialSize + offset;
var width = ((paneWidth / containerWidth) * 100).toFixed(1) + '%';
var gridColumns = this$1$1.$parent.$parent.row.settings.grid_template_columns.split(' ');
gridColumns[this$1$1.index] = width;
this$1$1.$parent.$parent.row.settings.grid_template_columns = gridColumns.join(' ');
};
// This adds is-resizing class to container
self.isResizing = true;
var onMouseMove = function (ref) {
var pageX = ref.pageX;
ref.pageY;
resize(initialPaneWidth, pageX - initialPageX);
};
var onMouseUp = function () {
// Run resize one more time to set computed width/height.
resize(pane.clientWidth);
// This removes is-resizing class to container
self.isResizing = false;
window.removeEventListener('mousemove', onMouseMove);
window.removeEventListener('mouseup', onMouseUp);
};
window.addEventListener('mousemove', onMouseMove);
window.addEventListener('mouseup', onMouseUp);
}
}
});
Vue.component('builder-item', {
props: {
item: Object,
items: Array,
index: Number
},
template: "\n <div class=\"builder-item\">\n <div class=\"builder-item-actions\">\n <span @click=\"deleteItem\" title=\"Delete item\" v-html=\"FWP.svg['times']\"></span>\n </div>\n <div class=\"builder-item-inner\" @click=\"editItem\" :class=\"[ item.settings.is_hidden ? 'is-hidden' : '' ]\">\n <span class=\"item-drag\" v-html=\"FWP.layout_data[item.source]\"></span>\n <span v-if=\"item.settings.is_hidden\" v-html=\"FWP.svg['eye-slash']\"></span>\n </div>\n </div>\n ",
methods: {
editItem: function editItem() {
this.$root.$emit('edit-item', this.item);
},
deleteItem: function deleteItem() {
this.items.splice(this.index, 1);
this.$root.$emit('edit-layout');
}
}
});
Vue.component('popover', {
mixins: [builder_defaults],
props: {
col: Object
},
data: function data() {
return {
keywords: ''
}
},
template: "\n <div class=\"popover\" tabindex=\"0\" @focusout=\"handleBlur\">\n <div class=\"popover-search\">\n <input\n type=\"text\"\n ref=\"keywords\"\n placeholder=\"Start typing\"\n v-model=\"keywords\"\n />\n </div>\n <div class=\"popover-choices\">\n <div\n @click=\"saveItem(source)\"\n v-for=\"(label, source) in FWP.layout_data\"\n v-show=\"isMatch(label)\"\n v-html=\"label\">\n </div>\n </div>\n </div>\n ",
methods: {
handleBlur: function handleBlur(e) {
if (!e.currentTarget.contains(e.relatedTarget)) {
this.$parent.adding_item = false;
}
},
isMatch: function isMatch(label) {
var bool = ('' == this.keywords) ? true : false;
if (false === bool) {
var needle = this.keywords.toLowerCase();
var haystack = label.toLowerCase();
if (haystack.includes(needle)) {
bool = true;
}
}
return bool;
},
saveItem: function saveItem(source) {
if ('row' == source) {
var len = this.col.items.push(this.defaultRow());
this.$root.$emit('edit-row', this.col.items[len - 1], len);
}
else {
var len$1 = this.col.items.push(this.defaultItem(source));
this.$root.$emit('edit-item', this.col.items[len$1 - 1]);
}
this.$parent.adding_item = false;
}
},
mounted: function mounted() {
this.$refs.keywords.focus();
}
});
/* ================ facets / templates ================ */
Vue.component('facets', {
props: ['facets'],
template: "\n <draggable class=\"facetwp-cards\" v-model=\"$root.app.facets\" handle=\".card-drag\">\n <div\n class=\"facetwp-card\"\n v-for=\"(facet, index) in facets\"\n @click=\"$root.editItem('facet', facet)\"\n >\n <div class=\"card-drag\">&#9776;</div>\n <div class=\"card-label\">\n <span class=\"label-text\">{{ facet.label }}</span>\n <span v-if=\"facet._code\" v-html=\"FWP.svg['lock']\"></span>\n </div>\n <div class=\"card-name\">{{ facet.name }}</div>\n <div class=\"card-type\">{{ facet.type }}</div>\n <div class=\"card-source\" v-html=\"getSource(facet.source)\"></div>\n <div class=\"card-rows\">{{ getRowCount(facet.name) }}</div>\n <div class=\"card-actions\">\n <div class=\"actions-wrap\">\n <div class=\"actions-btn\" v-html=\"FWP.svg['cog']\"></div>\n <div class=\"actions-modal\">\n <div @click.stop=\"$root.copyToClipboard(facet.name, 'facet', $event)\">Copy shortcode</div>\n <div @click.stop=\"$root.duplicateItem('facet', index)\">Duplicate</div>\n <div @click.stop=\"$root.deleteItem('facet', index)\">Delete</div>\n </div>\n </div>\n </div>\n </div>\n </draggable>\n ",
methods: {
getSource: function getSource(source) {
return FWP.layout_data[source] || '-';
},
getRowCount: function getRowCount(facet_name) {
if (this.$root.is_indexing) {
return '...';
}
return this.$root.row_counts[facet_name] || '-';
}
}
});
Vue.component('templates', {
props: ['templates'],
template: "\n <draggable class=\"facetwp-cards\" v-model=\"$root.app.templates\" handle=\".card-drag\">\n <div\n class=\"facetwp-card\"\n v-for=\"(template, index) in templates\"\n @click=\"$root.editItem('template', template)\"\n >\n <div class=\"card-drag\">&#9776;</div>\n <div class=\"card-label\">\n <span class=\"label-text\">{{ template.label }}</span>\n <span v-if=\"template._code\" v-html=\"FWP.svg['lock']\"></span>\n </div>\n <div class=\"card-name\">{{ template.name }}</div>\n <div class=\"card-display-mode\">{{ getDisplayMode(index) }}</div>\n <div class=\"card-post-types\">{{ getPostTypes(index) }}</div>\n <div class=\"card-actions\">\n <div class=\"actions-wrap\">\n <div class=\"actions-btn\" v-html=\"FWP.svg['cog']\"></div>\n <div class=\"actions-modal\">\n <div @click.stop=\"$root.copyToClipboard(template.name, 'template', $event)\">Copy shortcode</div>\n <div @click.stop=\"$root.duplicateItem('template', index)\">Duplicate</div>\n <div @click.stop=\"$root.deleteItem('template', index)\">Delete</div>\n </div>\n </div>\n </div>\n </div>\n </draggable>\n ",
methods: {
getDisplayMode: function getDisplayMode(index) {
var template = this.templates[index];
return ('undefined' !== typeof template.modes) ? template.modes.display : 'advanced';
},
getPostTypes: function getPostTypes(index) {
var template = this.templates[index];
if ('undefined' !== typeof template.modes) {
if ('visual' == template.modes.query) {
var post_types = template.query_obj.post_type;
if (0 === post_types.length) {
return '<any>';
}
else {
return post_types.map(function (type) { return type.label; }).join(', ');
}
}
}
return '<raw query>';
}
}
});
Vue.component('facet-edit', {
data: function data() {
return {
facet: {}
}
},
created: function created() {
this.facet = this.$root.editing;
},
methods: {
setName: function setName(e) {
this.facet.name = this.$root.sanitizeName(e.target.innerHTML);
},
unlock: function unlock() {
Vue.delete(this.facet, '_code');
}
},
template: "\n <div>\n <div class=\"item-locked\" v-if=\"facet._code\">\n This facet is registered in code. Click to allow edits:\n <span @click=\"unlock\" v-html=\"FWP.svg['lock-open']\"></span>\n </div>\n <div class=\"facetwp-content\" :class=\"[ 'type-' + facet.type, { locked: facet._code } ]\">\n <div class=\"facetwp-row\">\n <div>{{ 'Label' | i18n }}</div>\n <div>\n <input\n type=\"text\"\n v-model=\"facet.label\"\n @focus=\"$root.isNameEditable(facet)\"\n @keyup=\"$root.maybeEditName(facet)\"\n />\n <code class=\"item-name\" contenteditable v-text=\"facet.name\" @blur=\"setName\" @keydown.enter.prevent autocorrect=\"off\"></code>\n <span class=\"facetwp-btn\" @click=\"$root.copyToClipboard(facet.name, 'facet', $event)\">\n {{ 'Copy shortcode' | i18n }}\n </span>\n </div>\n </div>\n <div class=\"facetwp-row\">\n <div>{{ 'Facet type' | i18n }}</div>\n <div>\n <facet-types\n :facet=\"facet\"\n :selected=\"facet.type\"\n :types=\"FWP.facet_types\">\n </facet-types>\n </div>\n </div>\n <div class=\"facetwp-row field-data-source\">\n <div>{{ 'Data source' | i18n }}</div>\n <div>\n <data-sources :facet=\"facet\"></data-sources>\n </div>\n </div>\n <facet-settings :facet=\"facet\"></facet-settings>\n </div>\n </div>\n "
});
Vue.component('template-edit', {
mixins: [builder_defaults],
data: function data() {
return {
template: {},
tab: 'display'
}
},
created: function created() {
this.template = this.$root.editing;
// Set defaults for the layout builder
if (! this.template.layout) {
Vue.set(this.template, 'layout', this.defaultLayout());
}
// Set defaults for the query builder
if (! this.template.query_obj) {
Vue.set(this.template, 'query_obj', {
post_type: [],
posts_per_page: 10,
orderby: [],
filters: []
});
}
// Set the modes
if (! this.template.modes) {
Vue.set(this.template, 'modes', {
display: ('' !== this.template.template) ? 'advanced' : 'visual',
query: ('' !== this.template.query) ? 'advanced' : 'visual'
});
}
},
methods: {
setName: function setName(e) {
this.template.name = this.$root.sanitizeName(e.target.innerHTML);
},
isMode: function isMode(mode) {
return this.template.modes[this.tab] === mode;
},
switchMode: function switchMode() {
var now = this.template.modes[this.tab];
this.template.modes[this.tab] = ('visual' === now) ? 'advanced' : 'visual';
},
unlock: function unlock() {
Vue.delete(this.template, '_code');
}
},
template: "\n <div>\n <div class=\"item-locked\" v-if=\"template._code\">\n This template is registered in code. Click to allow edits:\n <span @click=\"unlock\" v-html=\"FWP.svg['lock-open']\"></span>\n </div>\n <div class=\"facetwp-content\" :class=\"{ locked: template._code }\">\n <div class=\"table-row\">\n <input\n type=\"text\"\n v-model=\"template.label\"\n @focus=\"$root.isNameEditable(template)\"\n @keyup=\"$root.maybeEditName(template)\"\n />\n <code class=\"item-name\" contenteditable v-text=\"template.name\" @blur=\"setName\" @keydown.enter.prevent autocorrect=\"off\"></code>\n <span class=\"facetwp-btn\" @click=\"$root.copyToClipboard(template.name, 'template', $event)\">\n {{ 'Copy shortcode' | i18n }}\n </span>\n </div>\n\n <label class=\"facetwp-dev-mode\">\n <input type=\"checkbox\" :checked=\"isMode('advanced')\" @change=\"switchMode()\"> Dev mode?\n </label>\n\n <div class=\"template-tabs top-level\">\n <span @click=\"tab = 'display'\" :class=\"{ active: tab == 'display' }\">{{ 'Display' | i18n }}</span>\n <span @click=\"tab = 'query'\" :class=\"{ active: tab == 'query' }\">{{ 'Query' | i18n }}</span>\n </div>\n\n <div v-show=\"tab == 'display'\">\n <div class=\"table-row\" v-show=\"template.modes.display == 'visual'\">\n <builder :layout=\"template.layout\"></builder>\n </div>\n <div class=\"table-row\" v-show=\"template.modes.display == 'advanced'\">\n <h3>{{ 'Display Code' | i18n }} <a class=\"facetwp-btn\" href=\"https://facetwp.com/help-center/listing-templates/listing-builder/using-the-listing-builder-in-dev-mode/#how-to-use-display-code-in-dev-mode\" target=\"_blank\">{{ 'Help' | i18n }}</a></h3>\n <textarea v-model=\"template.template\"></textarea>\n </div>\n </div>\n\n <div v-show=\"tab == 'query'\">\n <div class=\"table-row\" v-show=\"template.modes.query == 'visual'\">\n <query-builder :query_obj=\"template.query_obj\" :template=\"template\"></query-builder>\n </div>\n <div class=\"table-row\" v-show=\"template.modes.query == 'advanced'\">\n <h3>{{ 'Query Arguments' | i18n }} <a class=\"facetwp-btn\" href=\"https://facetwp.com/help-center/listing-templates/listing-builder/using-the-listing-builder-in-dev-mode/#how-to-use-query-arguments-in-dev-mode\" target=\"_blank\">{{ 'Help' | i18n }}</a></h3>\n <textarea v-model=\"template.query\"></textarea>\n </div>\n </div>\n </div>\n </div>\n "
});
Vue.component('facet-types', {
props: ['facet', 'selected', 'types'],
template: "\n <select v-model=\"facet.type\">\n <option v-for=\"(type, key) in types\" :value=\"key\" :selected=\"selected == key\">{{ type.label }}</option>\n </select>\n "
});
Vue.component('facet-settings', {
props: ['facet'],
template: '<component :is="dynComponent" :facet="facet"></component>',
methods: {
getFields: function getFields(aliases) {
var output = [];
$.each(aliases, function(name) {
output = output.concat(FWP.facet_fields[name].names);
});
return output;
}
},
computed: {
// dynamic component so the data bindings (e.g. v-model) get compiled
dynComponent: function dynComponent() {
return {
template: '<div class="facet-fields">' + this.settingsHtml + '</div>',
props: ['facet']
}
},
settingsHtml: function settingsHtml() {
var self = this;
var facet_obj = FWP.facet_types[self.facet.type];
var aliases = facet_obj.fields;
// Support for settings_html() in < 3.9
if ('undefined' === typeof aliases) {
if ('undefined' !== typeof FWP.clone[self.facet.type]) {
FWP.facet_fields[self.facet.type + '_fields'] = {
names: [],
html: FWP.clone[self.facet.type]
};
var $html = $(FWP.clone[self.facet.type]);
$.each($html.nodes[0].children, function(chunk) {
$(chunk).find('input, textarea, select, [setting-name]').each(function() {
var $el = $(this);
var setting_name = $el.attr('setting-name');
if (null === setting_name) {
setting_name = $el.attr('class').split(' ')[0].replace(/-/g, '_').substr(6);
}
FWP.facet_fields[self.facet.type + '_fields'].names.push(setting_name);
});
});
aliases = [self.facet.type + '_fields'];
}
}
// Get the actual fields by parsing the aliases (groups)
var fields = self.getFields(aliases);
var html = '';
// Add UI-dependant fields
if ('undefined' !== typeof facet_obj.ui_fields) {
if ('undefined' !== typeof self.facet.ui_type && '' != self.facet.ui_type) {
var ui_fields = facet_obj.ui_fields[self.facet.ui_type];
aliases = aliases.concat(ui_fields);
fields = fields.concat(this.getFields(ui_fields));
}
}
var combined = ['label', 'name', 'type', 'source', '_code'].concat(fields);
// Remove irrelevant settings
$.each(Object.keys(self.facet), function(setting_name) {
if (-1 == combined.indexOf(setting_name)) {
Vue.delete(self.facet, setting_name);
}
});
// Add new settings
$.each(aliases, function(alias_name) {
var $parsed = $(FWP.facet_fields[alias_name].html);
$.each(FWP.facet_fields[alias_name].names, function(setting_name) {
var name_dashed = setting_name.replace(/_/g, '-');
var $input = $parsed.find('.facet-' + name_dashed);
var val = $input.val();
if (0 < $input.len()) {
$input.attr('v-model', 'facet.' + setting_name);
if ('undefined' === typeof self.facet[setting_name]) {
if ($input.is('[type=checkbox]')) {
val = $input.nodes[0].checked ? 'yes' : 'no';
}
if ('[]' === val) {
val = [];
}
}
else {
val = self.facet[setting_name];
Vue.delete(self.facet, setting_name);
}
Vue.set(self.facet, setting_name, val);
}
});
// Update the documentFragment HTML to include the "v-model"
$.each($parsed.nodes[0].children, function(el) {
html += el.outerHTML;
});
});
return html;
}
},
watch: {
'facet.type': function(val) {
if ('search' == val || 'pager' == val || 'reset' == val || 'sort' == val) {
Vue.delete(this.facet, 'source');
}
}
}
});
Vue.component('data-sources', {
props: {
facet: Object,
settingName: {
type: String,
default: 'source'
}
},
template: "\n <select :class=\"className\" v-model=\"facet[settingName]\">\n <option v-if=\"settingName != 'source'\" value=\"\">{{ 'None' | i18n }}</option>\n <optgroup v-for=\"optgroup in FWP.data_sources\" :label=\"optgroup.label\">\n <option v-for=\"(label, key) in optgroup.choices\" :value=\"key\" :selected=\"facet[settingName] == key\">{{ label }}</option>\n </optgroup>\n </select>\n ",
computed: {
className: function className() {
return 'facet-' + this.settingName.replace(/_/g, '-');
}
},
mounted: function mounted() {
fSelect(this.$el);
}
});
Vue.component('facet-names', {
props: {
facet: Object,
setting: String
},
template: "\n <select :class=\"className\" v-model=\"facet[setting]\" multiple>\n <template v-for=\"(f) in FWP.data.facets\">\n <option v-if=\"!['reset'].includes(f.type)\" :value=\"f.name\" :class=\"bindSelectedClass(f.name)\">{{ f.label }}</option>\n </template>\n </select>\n ",
computed: {
className: function className() {
return 'facet-' + this.setting.replace(/_/g, '-');
}
},
methods: {
bindSelectedClass: function bindSelectedClass(name) {
return this.facet[this.setting].includes(name) ? 'selected' : '';
}
},
created: function created() {
if ('undefined' === typeof this.facet[this.setting]) {
this.facet[this.setting] = [];
}
},
mounted: function mounted() {
fSelect(this.$el, { 'placeholder': 'Choose facets' });
}
});
Vue.component('ui-type', {
props: {
facet: Object
},
created: function created() {
this.ui_fields = FWP.facet_types[this.facet.type].ui_fields || [];
this.sorted = Object.keys(this.ui_fields).reverse();
},
template: "\n <select class=\"facet-ui-type\" v-model=\"facet.ui_type\">\n <option value=\"\">{{ 'None' | i18n }}</option>\n <option v-for=\"name in sorted\" :value=\"name\" :selected=\"facet.ui_type == name\">{{ FWP.facet_types[name].label }}</option>\n </select>\n "
});
Vue.component('sort-options', {
props: {
facet: Object
},
template: "\n <div class=\"qb-wrap\">\n <div v-for=\"(rowOuter, indexOuter) in facet.sort_options\" class=\"qb-row\">\n <div>\n <input\n type=\"text\"\n v-model=\"rowOuter.label\"\n @focus=\"$root.isNameEditable(rowOuter)\"\n @keyup=\"$root.maybeEditName(rowOuter)\"\n />\n <code class=\"item-name\" contenteditable v-text=\"rowOuter.name\" @blur=\"setName(rowOuter, $event)\" @keydown.enter.prevent autocorrect=\"off\"></code>\n\n <div v-for=\"(row, index) in rowOuter.orderby\" class=\"qb-order-row\">\n <fselect :row=\"row\">\n <optgroup label=\"Posts\">\n <option value=\"ID\">ID</option>\n <option value=\"title\">{{ 'Post Title' | i18n }}</option>\n <option value=\"name\">{{ 'Post Name' | i18n }}</option>\n <option value=\"type\">{{ 'Post Type' | i18n }}</option>\n <option value=\"date\">{{ 'Post Date' | i18n }}</option>\n <option value=\"modified\">{{ 'Post Modified' | i18n }}</option>\n <option value=\"comment_count\">{{ 'Comment Count' | i18n }}</option>\n <option value=\"menu_order\">{{ 'Menu Order' | i18n }}</option>\n <option value=\"post__in\">post__in</option>\n </optgroup>\n <optgroup label=\"Custom Fields\">\n <option v-for=\"(label, name) in FWP.data_sources.custom_fields.choices\" :value=\"name\">{{ label }}</option>\n </optgroup>\n </fselect>\n <select v-model=\"row.type\" v-show=\"row.key.substr(0, 3) == 'cf/'\" class=\"qb-type\">\n <option value=\"CHAR\">TEXT</option>\n <option value=\"NUMERIC\">NUMERIC</option>\n </select>\n <select v-model=\"row.order\" class=\"qb-order\">\n <option value=\"ASC\">ASC</option>\n <option value=\"DESC\">DESC</option>\n </select>\n <span @click=\"addSortField(rowOuter.orderby, index)\" class=\"qb-add\" v-html=\"FWP.svg['plus-circle']\"></span>\n <span @click=\"removeItem(rowOuter.orderby, index)\" class=\"qb-remove\" v-html=\"FWP.svg['minus-circle']\" v-show=\"rowOuter.orderby.length > 1\"></span>\n </div>\n </div>\n <div class=\"align-right\">\n <span @click=\"moveUp(facet.sort_options, indexOuter)\" class=\"qb-move\" v-html=\"FWP.svg['arrow-circle-up']\" v-show=\"indexOuter > 0\"></span>\n <span @click=\"removeItem(facet.sort_options, indexOuter)\" class=\"qb-remove\" v-html=\"FWP.svg['minus-circle']\"></span>\n </div>\n </div>\n\n <div>\n <span class=\"facetwp-btn\" @click=\"addSort\">{{ 'Add sort' | i18n }}</span>\n </div>\n </div>\n ",
methods: {
addSort: function addSort() {
this.facet.sort_options.push({
label: 'New option',
name: 'new_option',
orderby: [{
key: 'title',
order: 'ASC',
type: 'CHAR'
}]
});
},
addSortField: function addSortField(opts, index) {
opts.splice(index + 1, 0, {
key: 'title',
order: 'ASC',
type: 'CHAR'
});
},
moveUp: function moveUp(opts, index) {
opts.splice(index -1, 0, opts.splice(index, 1)[0]);
},
removeItem: function removeItem(row, index) {
Vue.delete(row, index);
},
setName: function setName(row, e) {
row.name = this.$root.sanitizeName(e.target.innerHTML);
}
}
});
// Vue instance
FWP.vue = new Vue({
el: '#app',
data: {
app: FWP.data,
editing: {},
editing_facet: false,
editing_template: false,
row_counts: {},
active_tab: 'facets',
active_subnav: 'general',
is_support_loaded: false,
is_name_editable: false,
is_rebuild_open: false,
is_indexing: false,
timeout: null
},
methods: {
addItem: function addItem(type) {
if ('facet' == type) {
var len = this.app.facets.push({
'name': 'new_facet',
'label': 'New Facet',
'type': 'checkboxes',
'source': 'post_type'
});
this.editItem('facet', this.app.facets[len-1]);
}
else {
var len$1 = this.app.templates.push({
'name': 'new_template',
'label': 'New Template',
'query': '',
'template': ''
});
this.editItem('template', this.app.templates[len$1-1]);
}
},
duplicateItem: function duplicateItem(type, index) {
var facet = this.cloneObj(this.app[type + 's'][index]);
facet.label += ' (copy)';
facet.name += '_copy';
this.app[type + 's'].splice(index+1, 0, facet);
this.editItem(type, facet);
},
editItem: function editItem(type, data) {
this['editing_' + type] = true;
this.editing = data;
window.scrollTo(0, 0);
},
doneEditing: function doneEditing() {
this.editing_template = false;
this.editing_facet = false;
this.editing = {};
},
tabClick: function tabClick(which) {
this.doneEditing();
this.active_tab = which;
if ('support' === which) {
this.is_support_loaded = true;
}
},
getItemLabel: function getItemLabel() {
return this.editing.label;
},
deleteItem: function deleteItem(type, index) {
this.app[type + 's'].splice(index, 1);
},
saveChanges: function saveChanges() {
window.setStatus('load', FWP.__('Saving') + '...');
var data = JSON.parse(JSON.stringify(FWP.data));
// Remove code-based facets and templates
data.facets = data.facets.filter(function (obj) { return 'undefined' === typeof obj['_code']; });
data.templates = data.templates.filter(function (obj) { return 'undefined' === typeof obj['_code']; });
// Settings save hook
data = FWP.hooks.applyFilters('facetwp/save_settings', {
action: 'facetwp_save_settings',
nonce: FWP.nonce,
data: data
});
$.post(ajaxurl, data, {
done: function (ref) {
var code = ref.code;
var message = ref.message;
var code = ('success' == code) ? 'ok' : code;
window.setStatus(code, message);
},
fail: function (err) {
window.setStatus('error', err);
}
});
},
rebuildAction: function rebuildAction() {
this.is_indexing ? this.cancelReindex() : this.rebuildIndex();
},
rebuildIndex: function rebuildIndex() {
var self = this;
if (this.is_indexing) {
return;
}
this.is_indexing = true;
$.post(ajaxurl, { action: 'facetwp_rebuild_index', nonce: FWP.nonce });
window.setStatus('load', FWP.__('Indexing') + '... 0%');
this.timeout = setTimeout(function () {
self.getProgress();
}, 5000);
},
cancelReindex: function cancelReindex() {
var self = this;
$.post(ajaxurl, {
action: 'facetwp_get_info',
type: 'cancel_reindex',
nonce: FWP.nonce
}, {
done: function (ref) {
var message = ref.message;
self.is_indexing = false;
clearTimeout(self.timeout);
window.setStatus('error', message);
}
});
},
getProgress: function getProgress() {
var self = this;
var isNumeric = function (obj) { return !Array.isArray(obj) && (obj - parseFloat(obj) + 1) >= 0; };
$.post(ajaxurl, {
action: 'facetwp_heartbeat',
nonce: FWP.nonce
}, {
done: function (data) {
if ('-1' == data.pct) {
self.is_indexing = false;
if (data.rows.length < 1) {
window.setStatus('error', FWP.__('The index table is empty'));
}
else {
window.setStatus('ok', FWP.__('Indexing complete'));
// Update the row counts
$.each(data.rows, function(count, facet_name) {
Vue.set(self.row_counts, facet_name, count);
});
}
}
else if (isNumeric(data.pct)) {
window.setStatus('load', FWP.__('Indexing') + '... ' + data.pct + '%');
self.is_indexing = true;
self.timeout = setTimeout(function () {
self.getProgress();
}, 5000);
}
else {
window.setStatus('error', data);
self.is_indexing = false;
}
}
});
},
getInfo: function getInfo(type, label) {
window.setStatus('load', FWP.__(label) + '...');
$.post(ajaxurl, {
action: 'facetwp_get_info',
type: type,
nonce: FWP.nonce
}, {
done: function (ref) {
var message = ref.message;
window.setStatus('error', message);
}
});
},
getQueryArgs: function getQueryArgs(template) {
template.modes.query = 'advanced';
template.query = FWP.__('Loading') + '...';
$.post(ajaxurl, {
action: 'facetwp_get_query_args',
query_obj: template.query_obj,
nonce: FWP.nonce
}, {
done: function (message) {
var json = JSON.stringify(message, null, 2);
json = "<?php\nreturn " + json + ';';
json = json.replace(/[\{]/g, '[');
json = json.replace(/[\}]/g, ']');
json = json.replace(/":/g, '" =>');
template.query = json;
}
});
},
showIndexerStats: function showIndexerStats() {
this.getInfo('indexer_stats', 'Looking');
},
searchablePostTypes: function searchablePostTypes() {
this.getInfo('post_types', 'Looking');
},
purgeIndexTable: function purgeIndexTable() {
this.getInfo('purge_index_table', 'Purging');
},
copyToClipboard: function copyToClipboard(name, type, ref) {
var target = ref.target;
var $this = $(target);
var $el = $('.facetwp-clipboard');
var orig_text = $this.text();
try {
$el.removeClass('hidden');
$el.val('[facetwp ' + type + '="' + name + '"]');
$el.nodes[0].select();
document.execCommand('copy');
$el.addClass('hidden');
$this.text(FWP.__('Copied!'));
}
catch(err) {
$this.text(FWP.__('Press CTRL+C to copy'));
}
window.setTimeout(function () {
$this.text(orig_text);
}, 2000);
},
activate: function activate() {
$('.facetwp-activation-status').html(FWP.__('Activating') + '...');
$.post(ajaxurl, {
action: 'facetwp_license',
nonce: FWP.nonce,
license: $('.facetwp-license').val()
}, {
done: function (ref) {
var message = ref.message;
$('.facetwp-activation-status').html(message);
}
});
},
isNameEditable: function isNameEditable(ref) {
var name = ref.name;
this.is_name_editable = ('' == name || 'new_' == name.substr(0, 4));
},
maybeEditName: function maybeEditName(item) {
if (this.is_name_editable) {
item.name = this.sanitizeName(item.label);
}
},
sanitizeName: function sanitizeName(name) {
var val = name.trim().toLowerCase();
val = val.replace(/[^\w- ]/g, ''); // strip invalid characters
val = val.replace(/[- ]/g, '_'); // replace space and hyphen with underscore
val = val.replace(/[_]{2,}/g, '_'); // strip consecutive underscores
val = ('pager' == val || 'sort' == val || 'labels' == val) ? val + '_' : val; // reserved
return val;
},
documentClick: function documentClick(ref) {
var target = ref.target;
var el = target;
if (! el.classList.contains('btn-caret')) {
this.is_rebuild_open = false;
}
},
cloneObj: function cloneObj(obj) {
return JSON.parse(JSON.stringify(obj));
}
},
computed: {
isEditing: function isEditing() {
return this.editing_facet || this.editing_template;
},
indexButtonLabel: function indexButtonLabel() {
return this.is_indexing ? FWP.__('Stop indexer') : FWP.__('Re-index');
}
},
created: function created() {
document.addEventListener('click', this.documentClick);
},
mounted: function mounted() {
this.getProgress();
}
});
}
function init_custom_js() {
window.setStatus = function (code, message) {
$('.facetwp-response').html(message);
$('.facetwp-response-icon').nodes[0].setAttribute('data-status', code);
if ('error' == code) {
$('.facetwp-response').addClass('visible');
}
};
$().on('click', '.facetwp-settings-section .facetwp-switch', function () {
window.setStatus('error', 'Press "Save changes" to apply');
});
$().on('click', '.facetwp-response-wrap', function () {
$('.facetwp-response').toggleClass('visible');
});
// Export
$().on('click', '.export-submit', function () {
$('.import-code').val(FWP.__('Loading') + '...');
$.post(ajaxurl, {
action: 'facetwp_backup',
nonce: FWP.nonce,
action_type: 'export',
items: $('.export-items').val()
}, {
done: function (resp) {
$('.import-code').val(JSON.stringify(resp));
}
});
});
// Import
$().on('click', '.import-submit', function () {
window.setStatus('load', FWP.__('Importing') + '...');
try {
var code = JSON.parse($('.import-code').val());
$.post(ajaxurl, {
action: 'facetwp_backup',
nonce: FWP.nonce,
action_type: 'import',
import_code: code,
overwrite: $('.import-overwrite').nodes[0].checked ? 1 : 0
}, {
dataType: 'text',
done: function (resp) {
window.setStatus('ok', resp);
setTimeout(function () {
window.location.reload();
}, 1500);
}
});
}
catch(err) {
window.setStatus('error', 'Invalid JSON');
}
});
// Initialize tooltips
$().on('mouseover', '.facetwp-tooltip', function() {
if (!this.classList.contains('.ftip-enabled')) {
fTip(this, {
content: function (node) { return $(node).find('.facetwp-tooltip-content').html(); }
}).open();
}
});
// fSelect
fSelect('.export-items');
}
})(fUtil);
})();