/** * Javascript Library for Layout Builder Admin * * - Page Layouts Model * * @author AlpusTheme * @since 1.0 * @package Alpha Framework * @subpackage Theme */ "use strict"; window.themeAdmin = window.themeAdmin || {}; (function($) { /** * Layout Builder Model Class * * @since 1.0 */ var LayoutBuilderModel = { /** * Setup layout builder model. * * @since 1.0 */ init: function() { this.conditions = JSON.parse(JSON.stringify(alpha_layout_vars.conditions)) || {}; this.schemes = alpha_layout_vars.schemes || {}; this.clipboard = false; this.controls = []; for (var part in alpha_layout_vars.controls) { if (!part.startsWith("content")) { for (var key in alpha_layout_vars.controls[part]) { this.controls.push(key); } } } }, /** * Get conditions by category or get all conditions. * * @since 1.0 * @param {string} category * @param {number} conditionNo */ getConditions: function(category = "", conditionNo = -1) { if (!category) { return this.conditions; } if (!this.conditions[category]) { this.conditions[category] = []; } if (conditionNo >= 0 && this.conditions[category][conditionNo]) { return this.conditions[category][conditionNo]; } return this.conditions[category]; }, /** * Get layout option values by category. * * @since 1.0 * @param {string} category * @param {number} conditionNo */ getOptionValues: function(category, conditionNo) { return this.conditions[category] && this.conditions[category][conditionNo] ? this.conditions[category][conditionNo].options : false; }, /** * Get condition title of given category. * * @since 1.0 * @param {string} category * @param {boolean} getLayoutTitle */ getConditionTitle: function(category, getLayoutTitle) { if (category && this.schemes[category]) { return getLayoutTitle ? this.schemes[category].layout_title : this.schemes[category].title; } return ""; }, /** * Set condition title * @param {string} category * @param {number} conditionNo * @param {string} title */ setConditionTitle: function(category, conditionNo, title) { if (this.conditions[category][conditionNo]) { this.conditions[category][conditionNo].title = title; } this.requireSave(); }, /** * Get scheme by category. * * @since 1.0 * @param {string} category * @param {string} type */ getScheme: function(category, type = "") { return type ? this.schemes[category].scheme[type] : this.schemes[category].scheme; }, /** * Get layout option controls for given layout part. * * @since 1.0 * @param {string} part */ getOptionControls: function(part) { return alpha_layout_vars.controls[part] ? alpha_layout_vars.controls[part] : false; }, /** * Get templates by block type. * * @since 1.0 * @param {string} block_type */ getTemplates: function(block_type) { return alpha_layout_vars.templates[block_type]; }, /** * Check if new conditions could be added for given category. * * @since 1.0 * @param {string} category Conditions category to check * @param {string} type Condition type to check. */ canExtendCondition: function(category, type = "") { if ( !category || !this.schemes[category] || !this.schemes[category].scheme ) { return false; } return ( !type || (this.schemes[category].scheme[type] && (this.schemes[category].scheme[type].list || this.schemes[category].scheme[type].ajaxselect)) ); }, /** * Update condition UI. * @since 1.0 * @param {string} category */ updateCategoryUI: function(category = "") { var _updateCategoryUI = function(category) { // update UI var $count = $( ".alpha-condition-cat-" + category + "> .alpha-condition-count" ); var count = this.conditions[category].filter(function(v) { return v; }).length; $count.text("(" + count + ")"); count ? $count.slideDown() : $count.slideUp(); }.bind(this); // update special category category && _updateCategoryUI(category); // count total var count = 0; for (var cat in this.conditions) { count += this.conditions[cat].filter(function(v) { return v; }).length; // update all categories category || _updateCategoryUI(cat); } $(".alpha-condition-cat-site > .alpha-condition-count") .text("(" + count + ")") .slideDown(); }, /** * Add a new empty condition. * * @since 1.0 * @param {string} category * @param {string} type * @return {number} added index */ addCondition: function(category) { if (!this.conditions[category]) { this.conditions[category] = []; } var data = {}; data.title = this.getConditionTitle(category, true) + " " + (this.conditions[category].length + 1); data.scheme = {}; if (this.schemes[category].scheme && this.schemes[category].scheme.all) { data.scheme.all = true; } this.conditions[category].push(data); this.updateCategoryUI(category); this.requireSave(); // return added index return this.conditions[category].length - 1; }, /** * Delete a condition. * * @since 1.0 * @param {string} category * @param {number} conditionNo */ deleteCondition: function(category, conditionNo) { if ("undefined" != typeof this.conditions[category][conditionNo]) { this.conditions[category].splice(conditionNo, 1); $(".alpha-layout-item[data-category=" + category + "]").each( function() { var no = this.getAttribute("data-condition-no"); if (no > conditionNo) { this.setAttribute("data-condition-no", no - 1); $(this).data("condition-no", no - 1); } } ); $("#alpha_layout_content") .isotope("updateSortData") .isotope(); } this.updateCategoryUI(category); this.requireSave(); }, /** * Reset a condition. If no parameter is given, all options will be reset. * * @since 1.0 * @param {string} category * @param {number} conditionNo */ // resetCondition: function (category, conditionNo) { // if (category) { // this.conditions[category][conditionNo] = // alpha_layout_vars.conditions[category] && alpha_layout_vars.conditions[category][conditionNo] ? // JSON.parse(JSON.stringify(alpha_layout_vars.conditions[category][conditionNo])) : // {}; // } else { // this.conditions = // alpha_layout_vars.conditions ? // JSON.parse(JSON.stringify(alpha_layout_vars.conditions)) : // {}; // } // this.requireSave(); // }, /** * Duplicate a condition. * * @since 1.0 * @param {string} category * @param {number} conditionNo */ duplicateCondition: function(category, conditionNo) { if ( category && "number" == typeof conditionNo && this.conditions[category][conditionNo] ) { var duplicated = JSON.parse( JSON.stringify(this.conditions[category][conditionNo]) ); $(".alpha-layout-item[data-category=" + category + "]").each( function() { var no = this.getAttribute("data-condition-no"); if (no > conditionNo) { this.setAttribute("data-condition-no", no * 1 + 1); $(this).data("condition-no", no * 1 + 1); } } ); this.conditions[category].splice(conditionNo, 0, duplicated); this.updateCategoryUI(category); this.requireSave(); return conditionNo + 1; } }, /** * Copy options * * @since 1.0 * @param {string} category * @param {number} conditionNo */ copyOptions: function(category, conditionNo) { this.clipboard = { category: category, options: this.getOptionValues(category, conditionNo) }; }, /** * Paste options * * @since 1.0 * @param {string} category * @param {number} conditionNo * @param {jQuery} $item */ pasteOptions: function(category, conditionNo, $item) { if (this.clipboard) { if (category == this.clipboard.category) { // paste all options if (this.conditions[category][conditionNo]) { this.conditions[category][ conditionNo ].options = this.clipboard.options; } else { this.conditions[category][conditionNo] = { options: this.clipboard.options }; } } else { if (this.conditions[category][conditionNo].options) { // remove current options except content for (var optionName in this.conditions[category][conditionNo] .options) { if (this.controls.indexOf(optionName)) { delete this.conditions[category][conditionNo].options[ optionName ]; } } } else { this.conditions[category][conditionNo].options = {}; } // paste copied options except content for (var optionName in this.clipboard.options) { if (this.controls.indexOf(optionName)) { this.conditions[category][conditionNo].options[ optionName ] = this.clipboard.options[optionName]; } } } LayoutBuilderView.refreshLayoutStatus($item); this.requireSave(); } }, /** * Notify that save is required. * * @since 1.0 */ requireSave: function() { $(".alpha-layouts-save").addClass("require-save"); $(window).trigger("show_btn_header"); }, /** * Add a new condition with type or update existing condition's type. * * @since 1.0 * @param {string} category * @param {number} conditionNo * @param {string} scheme * @param {mixed} value {boolean} isChecked or {array} list */ setConditionScheme: function(category, conditionNo, scheme, value) { if ("undefined" == typeof this.conditions[category][conditionNo]) { // add var v = {}; v[scheme] = value; v.all = true; this.conditions[category][conditionNo] = { scheme: v }; } else if (this.conditions[category][conditionNo]) { // update if (!this.conditions[category][conditionNo].scheme) { var v = {}; v[scheme] = value; this.conditions[category][conditionNo].scheme = v; } if (value) { this.conditions[category][conditionNo].scheme[scheme] = value; } else { delete this.conditions[category][conditionNo].scheme[scheme]; } } this.requireSave(); }, /** * Set type and list for given condition. * * @since 1.0 * @param {string} category * @param {number} conditionNo * @param {string} type * @param {array} list */ setConditionList: function(category, conditionNo, type, list) { this.conditions[category][conditionNo] = list ? { type: type, list: list } : { type: type }; this.requireSave(); }, /** * Set layout options for given condition. * * @since 1.0 * @param {string} category * @param {number} conditionNo * @param {string} option * @param {mixed} value */ setConditionOption: function(category, conditionNo, option, value) { if (!this.conditions[category][conditionNo].options) { this.conditions[category][conditionNo].options = {}; } if (value) { this.conditions[category][conditionNo].options[option] = value; } else { delete this.conditions[category][conditionNo].options[option]; } this.requireSave(); }, /** * Save all modifications of conditions. * * @since 1.0 */ save: function() { $(".alpha-layouts-save").removeClass("require-save"); $(window).trigger("show_btn_header"); $.post( alpha_layout_vars.ajax_url, { action: "alpha_layout_builder_save", nonce: alpha_layout_vars.nonce, conditions: this.conditions }, function() {} ).fail(function() { $(".alpha-layouts-save").addClass("require-save"); $(".alpha-modal-message").remove(); // issue : show message $(".alpha-layouts-save").before( '' ); $(window).trigger("show_btn_header"); }); } }; /** * Layout Builder View Class * * @since 1.0 */ var LayoutBuilderView = { /** * Setup layout builder view. * * @since 1.0 */ init: function() { // Delete button this.buttonDelete = ''; // Duplicate button this.buttonDuplicate = ''; // Set conditions button this.buttonSet = ''; // get layout box template. this.layoutBoxTemplate = $("#alpha_layout_template").text(); $("#alpha_layout_template").remove(); // register events. $(document.body) // events for context menu .on("click", ".alpha-layouts-save", this.onSave) .on("contextmenu", ".alpha-layout-item", this.onContextMenu.bind(this)) .on("click", "#alpha_layout_content", this.closeContextMenu) .on("click", ".alpha-condition-menu > a", this.clickContextMenuItem) .on("click", ".alpha-condition-copy", this.copyOptions) .on("click", ".alpha-condition-paste", this.pasteOptions) .on("click", ".alpha-condition-edit-back", this.goBackFromEdit) // events for condition .on("click", ".alpha-condition-cat", this.clickCategory.bind(this)) .on("click", ".alpha-layout-more", this.addCondition.bind(this)) .on("click", ".alpha-condition-delete", this.deleteCondition.bind(this)) // .on('click', '.alpha-condition-reset', this.resetCondition.bind(this)) // .on('click', '.alpha-layouts-reset', this.resetAll.bind(this)) .on( "click", ".alpha-condition-duplicate", this.duplicateCondition.bind(this) ) .on( "change", ".alpha-scheme-options > div > label input[type=checkbox]", this.changeConditionScheme.bind(this) ) .on("change", ".alpha-scheme-list", this.changeConditionItem) // events for layout box .on( "input", ".alpha-condition-title", this.changeConditionTitle.bind(this) ) .on("click", ".alpha-layout .layout-part", this.editPart) .on("click", ".alpha-condition-set", this.editCondition) .on("click", this.clickOther.bind(this)) // events for layout control .on( "change", ".alpha-block-select input", this.changeBlockMode.bind(this) ) .on( "change", ".alpha-layout-options input", this.changeOptionInput.bind(this) ) .on( "change", ".alpha-layout-options select", this.changeOptionInput.bind(this) ); this.setupLayouts(); }, /** * Initialize plugins for layout controls. * * @since 1.0 */ refreshUI: function(mode) { if (!mode || "layout" == mode || "add" == mode) { $("#alpha_layout_content").isotope(); } if (!mode || "add" == mode) { this.refreshLayoutStatus(); } }, /** * Display status of layout parts. * * @since 1.0 * @param {jQuery} $container This can be omitted. */ refreshLayoutStatus: function($container) { $container || ($container = $("#alpha_layout_content")); $container.is(".alpha-layout-item") || ($container = $container.find(".alpha-layout-item")); $container.each(function() { var $item = $(this); var category = $item.data("category"); var conditionNo = $item.data("conditionNo"); var optionValues = LayoutBuilderModel.getOptionValues( category, conditionNo ); if (optionValues) { for (var part in alpha_layout_vars.controls) { if (LayoutBuilderModel.controls.indexOf(part)) { var optionControls = LayoutBuilderModel.getOptionControls(part); var $part = $item.find('.layout-part[data-part="' + part + '"]'); var set = false; // Reset $part.removeClass("set hide"); $part.children(".block-value").text(""); // Check set for (var control in optionControls) { if (optionValues[control]) { set = true; break; } } if (optionControls[part] && "hide" == optionValues[part]) { // Hide $part.addClass("hide"); } else if (set) { // Set $part.addClass("set"); if (optionControls[part]) { var blocks = LayoutBuilderModel.getTemplates( optionControls[part].type.replace("block_", "") ); if (blocks && blocks[optionValues[part]]) { $part .children(".block-value") .text(blocks[optionValues[part]]); } } } } } } }); }, /** * Setup layouts * * @since 1.0 */ setupLayouts: function() { var layoutItems = ""; var schemes = LayoutBuilderModel.schemes; if (schemes) { for (var category in schemes) { // add layouts already set var layouts = LayoutBuilderModel.getConditions(category); for (var conditionNo in layouts) { layoutItems += this.getNewConditionUI(category, conditionNo); } // add more button if ( "site" != category && (LayoutBuilderModel.canExtendCondition(category) || !layouts.length) ) { layoutItems += this.getAddMoreUI(category); } } // show conditions $("#alpha_layout_content") .html(layoutItems) .isotope({ layoutMode: "fitRows", filter: ".alpha-layout-item", sortBy: ["category", "no"], getSortData: { category: function(el) { var category = el.getAttribute("data-category"); var categories = Object.keys(LayoutBuilderModel.schemes); return categories.indexOf(category); }, no: function(el) { return parseInt(el.getAttribute("data-condition-no")); } } }); // init plugins LayoutBuilderModel.updateCategoryUI(); this.refreshUI(); } }, /** * Refresh conditions * @param {string} category * @param {number} conditionNo */ refreshCondition: function(category, conditionNo) { var selector = ".alpha-layout-item"; var view = this; category && (selector += '[data-category="' + category + '"]'); conditionNo && (selector += '[data-condition-no="' + conditionNo + '"]'); $(selector).each(function() { view.editPart({ currentTarget: $(this) .find(".layout-part.active") .get(0) }); }); }, /** * Event handler to save layout controls. * * @since 1.0 */ onSave: function() { LayoutBuilderModel.save(); }, /** * Event handler to show context menu. * * @since 1.0 * @param {Event} e */ onContextMenu: function(e) { this.closeContextMenu(); var $item = $(e.currentTarget); var $container = $(".alpha-admin-panel-content"); var containerOffset = $container.get(0).getBoundingClientRect(); var category = $item.data("category"); var html = '
"; $container.append(html); $(".alpha-condition-menu").data("item", $item); e.preventDefault(); }, /** * Close context menu of condition. * * @since 1.0 */ closeContextMenu: function() { $(".alpha-condition-menu").remove(); }, /** * Event handler to show context menu for condition. * * @since 1.0 * @param {Event} e */ clickContextMenuItem: function(e) { e.preventDefault(); }, /** * Event handler to copy options. * * @since 1.0 * @param {Event} e */ copyOptions: function(e) { var $menuItem = $(e.currentTarget); var $item = $menuItem.parent().data("item"); LayoutBuilderModel.copyOptions( $item.data("category"), $item.data("condition-no") ); }, /** * Event handler to paste options. * * @since 1.0 * @param {Event} e */ pasteOptions: function(e) { var $item = $(e.currentTarget) .parent() .data("item"); // from menu item. LayoutBuilderModel.pasteOptions( $item.data("category"), $item.data("condition-no"), $item ); }, /** * Event handler to show conditions by category. * * @since 1.0 */ clickCategory: function(e) { var $category = $(e.currentTarget).addClass("active"); var category = $category.data("category"); // toggle category $category.siblings(".active").removeClass("active"); // filter layouts $("#alpha_layout_content").isotope({ filter: "site" == category ? ".alpha-layout-item" : '[data-category="' + category + '"]' }); }, /** * Event handler to reset condition. * * @since 1.0 */ // resetCondition: function (e) { // var $reset = $(e.currentTarget); // var $item = $reset.is('.alpha-condition-menu > a') ? // $reset.parent().data('item') : // $reset.closest('.alpha-layout-item'); // LayoutBuilderModel.resetCondition($item.data('category'), $item.data('condition-no')); // var $activePart = $item.find('.layout-part.active'); // $activePart.length && // this.editPart({ // currentTarget: $activePart.get(0) // }); // }, /** * Event handler to reset all conditions. * * @since 1.0 */ // resetAll: function () { // LayoutBuilderModel.resetCondition(); // $('.alpha-condition-cat-all').click(); // }, /** * Get add more item html. * @param {string} category */ getAddMoreUI: function(category) { return ( '" + control.description + "