1685 lines
101 KiB
JavaScript
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=\">\">></option>\n <option v-if=\"showCompare('>=', row)\" value=\">=\">>=</option>\n <option v-if=\"showCompare('<', row)\" value=\"<\"><</option>\n <option v-if=\"showCompare('<=', row)\" value=\"<=\"><=</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> »\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\">☰</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\">☰</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);
|
|
|
|
})();
|