var CustomizeBuilder_V1;

(function($) {
	var $document = $(document);
	var wpcustomize = wp.customize || null;
	var is_rtl = Customify_Layout_Builder.is_rtl;

	CustomizeBuilder_V1 = function(options, id) {
		var Builder = {
			id: id,
			controlId: "",
			cols: 12,
			cellHeight: 45,
			items: [],
			container: null,
			ready: false,
			devices: { desktop: "Desktop", mobile: "Mobile/Tablet" },
			activePanel: "desktop",
			panels: {},
			activeRow: "main",
			draggingItem: null,
			getTemplate: _.memoize(function() {
				var control = this;
				var compiled,
					/*
					 * Underscore's default ERB-style templates are incompatible with PHP
					 * when asp_tags is enabled, so WordPress uses Mustache-inspired templating syntax.
					 *
					 * @see trac ticket #22344.
					 */
					options = {
						evaluate: /<#([\s\S]+?)#>/g,
						interpolate: /\{\{\{([\s\S]+?)\}\}\}/g,
						escape: /\{\{([^\}]+?)\}\}(?!\})/g,
						variable: "data"
					};

				return function(data, id, data_variable_name) {
					if (_.isUndefined(id)) {
						id = "tmpl-customize-control-" + control.type;
					}
					if (
						!_.isUndefined(data_variable_name) &&
						_.isString(data_variable_name)
					) {
						options.variable = data_variable_name;
					} else {
						options.variable = "data";
					}
					compiled = _.template($("#" + id).html(), null, options);
					return compiled(data);
				};
			}),
			drag_drop: function() {
				var that = this;

				$(".customify--device-panel", that.container).each(function() {
					var panel = $(this);
					var device = panel.data("device");
					var sortable_ids = [];
					that.panels[device] = {};
					$(".customify--cb-items", panel).each(function(index) {
						var data_name = $(this).attr("data-id") || "";
						var id;
						if (!data_name) {
							id = "_sid_" + device + index;
						} else {
							id = "_sid_" + device + "-" + data_name;
						}
						$(this).attr("id", id);
						sortable_ids[index] = "#" + id;
					});
					$(".grid-stack", panel).each(function() {
						var _id = $(this).attr("data-id") || "";
						that.panels[device][_id] = $(this);
						$(this).droppable({
							out: function(event, ui) {},
							over: function(event, ui) {
								//var $wrapper = $( this );
								/**
								 * @see http://api.jqueryui.com/droppable/#event-over
								 */
							},
							drop: function(event, ui) {
								var $wrapper = $(this);
								that.gridster($wrapper, ui, event);
								that.save();
							}
						});
					});

					var sidebar = $("#_sid_mobile-sidebar", panel);
					var sidebar_id = sidebar.attr("id") || false;

					$(
						".customify-available-items .grid-stack-item",
						panel
					).draggable({
						revert: "invalid",
						connectToSortable: sidebar_id
							? "#" + sidebar_id
							: false,
						start: function(event, ui) {
							$("body").addClass("builder-item-moving");
							$(".customify--cb-items", panel).css("z-index", "");
							ui.helper.parent().css("z-index", 9999);
						},
						stop: function(event, ui) {
							$("body").removeClass("builder-item-moving");
							$(".customify--cb-items", panel).css("z-index", "");
							ui.helper.parent().css("z-index", "");
						}
					});

					if (sidebar.length > 0) {
						sidebar.sortable({
							revert: true,
							change: function(event, ui) {
								that.save();
							},
							receive: function(event, ui) {
								$(this)
									.find(".grid-stack-item")
									.removeAttr("style")
									.attr("data-gs-width", 1);
								that.save();
							}
						});

						that.panels[device]["sidebar"] = sidebar;
					}

					$(
						".customify-available-items .grid-stack-item",
						panel
					).resizable({
						handles: "w, e",
						stop: function(event, ui) {
							that.setGridWidth(ui.element.parent(), ui);
							that.save();
						}
					});
				});
			},
			sortGrid: function($wrapper) {
				$(".grid-stack-item", $wrapper).each(function() {
					var el = $(this);
					var x = el.attr("data-gs-x") || 0;
					x = parseInt(x);
					var next = el.next();
					if (next.length > 0) {
						var nx = next.attr("data-gs-x") || 0;
						nx = parseInt(nx);
						if (x > nx) {
							el.insertAfter(next);
						}
					}
				});
			},
			getX: function($item) {
				var x = $item.attr("data-gs-x") || 0;
				return parseInt(x);
			},
			getW: function($item, df) {
				if (_.isUndefined(df)) {
					df = false;
				}
				var w;
				if (df) {
					w = $item.attr("data-df-width") || 1;
				} else {
					w = $item.attr("data-gs-width") || 1;
				}
				return parseInt(w);
			},
			gridGetItemInfo: function($item, flag, $wrapper) {
				var that = this;
				var x = that.getX($item);
				var w = that.getW($item);
				var slot_before = 0;
				var slot_after = 0;
				var i;

				var br = false;
				// Get empty slots before
				i = x - 1;
				while (i >= 0 && !br) {
					if (flag[i] === 0) {
						slot_before++;
					} else {
						br = true;
					}
					i--;
				}

				// Get empty slots after
				br = false;
				i = x + w;
				while (i < that.cols && !br) {
					if (flag[i] === 0) {
						slot_after++;
					} else {
						br = true;
					}
					i++;
				}

				return {
					flag: flag,
					x: x,
					w: w,
					item: $item,
					before: slot_before, // empty before
					after: slot_after, // empty after
					id: $item.attr("data-id") || "",
					wrapper: $wrapper
				};
			},
			updateItemsPositions: function(flag) {
				var maxCol = this.cols;
				for (var i = 0; i <= maxCol; i++) {
					if (
						typeof flag[i] === "object" ||
						typeof flag[i] === "function"
					) {
						flag[i].attr("data-gs-x", i);
					}
				}
			},
			gridster: function($wrapper, ui, event) {
				var flag = [],
					backupFlag = [],
					that = this;
				var maxCol = this.cols;

				var addItemToFlag = function(node) {
					var x = node.x,
						w = node.w;
					var el = node.el;

					for (var i = x; i < x + w; i++) {
						if (i === x) {
							flag[i] = el; // mean start item item
						} else {
							flag[i] = 1;
						}
					}
				};

				var removeNode = function(node) {
					var x = node.x,
						w = node.w;
					var el = node.el;
					for (var i = x; i < x + w; i++) {
						flag[i] = 0;
					}
				};

				var getEmptySlots = function() {
					var emptySlots = 0;
					for (var i = 0; i < maxCol; i++) {
						if (flag[i] === 0) {
							emptySlots++;
						}
					}

					return emptySlots;
				};

				var getRightEmptySlotFromX = function(x, stopWhenNotEmpty) {
					var emptySlots = 0;
					for (var i = x; i < maxCol; i++) {
						if (flag[i] === 0) {
							emptySlots++;
						} else {
							if (stopWhenNotEmpty) {
								return emptySlots;
							}
						}
					}
					return emptySlots;
				};

				var getLeftEmptySlotFromX = function(x, stopWhenNotEmpty) {
					var emptySlots = 0;
					if (typeof stopWhenNotEmpty === "undefined") {
						stopWhenNotEmpty = false;
					}
					for (var i = x; i >= 0; i--) {
						if (flag[i] === 0) {
							emptySlots++;
						} else {
							if (stopWhenNotEmpty) {
								return emptySlots;
							}
						}
					}
					return emptySlots;
				};

				var isEmptyX = function(x) {
					if (flag[x] === 0) {
						return true;
					}
					return false;
				};

				var checkEnoughSpaceFromX = function(x, w) {
					var check = true;
					var i = x;
					var j;
					while (i < x + w && check) {
						if (flag[i] !== 0) {
							return false;
						}
						i++;
					}
					return check;
				};

				var getPrevBlock = function(x) {
					if (x < 0) {
						return {
							x: -1,
							w: 1
						};
					}

					var i,
						_x = -1,
						_xw,
						found;

					if (flag[x] <= 1) {
						i = x;
						found = false;
						while (i >= 0 && !found) {
							if (flag[i] !== 1 && flag[i] !== 0) {
								_x = i;
								found = true;
							}
							i--;
						}
					} else {
						_x = x;
					}
					// tìm kiếm độ rộng của chuỗi này
					i = _x + 1;
					_xw = _x; // chiều rộng nhỏ nhất là môt

					while (flag[i] === 1) {
						_xw++;
						i++;
					}
					return {
						x: _x,
						w: _xw + 1 - _x
					};
				};

				var getNextBlock = function(x) {
					var i,
						_x = -1,
						_xw,
						found;

					if (flag[x] < maxCol) {
						i = x;
						found = false;
						while (i < maxCol && !found) {
							if (flag[i] !== 1 && flag[i] !== 0) {
								_x = i;
								found = true;
							}
							i++;
						}
					} else {
						_x = x;
					}
					// Calculate the width of this item
					i = _x + 1;
					_xw = _x; // the min width is 1

					while (flag[i] === 1) {
						_xw++;
						i++;
					}
					return {
						x: _x,
						w: _xw + 1 - _x
					};
				};

				/**
				 *  Move all Items form x to left
				 * @param x
				 * @param number position left need to move
				 * @returns {*}
				 */
				var moveAllItemsFromXToLeft = function(x, number) {
					var backupFlag = flag.slice();
					var maxNumber = getLeftEmptySlotFromX(x);

					if (maxNumber === 0) {
						return number;
					}
					var prev = getPrevBlock(x);
					var newX = prev.x >= 0 ? prev.x + prev.w - 1 : x;
					var nMove = number;
					if (number > maxNumber) {
						nMove = maxNumber;
					} else {
						nMove = number;
					}

					// Find empty positions from x to left;
					// xE is new empty position from x
					var xE = 0,
						c = 0,
						i = newX;
					while (c <= nMove && i >= 0) {
						if (flag[i] === 0) {
							c++;
							xE = i;
						}
						i--;
					}

					// Move item from x to xE and we need empty flag from x to xE
					var flagNoEmpty = [],
						j = 0;
					for (i = xE; i <= newX; i++) {
						flag[i] = 0;
						if (backupFlag[i] !== 0) {
							flagNoEmpty[j] = backupFlag[i];
							j++;
						}
					}

					j = 0;
					for (i = xE; i <= newX; i++) {
						if (typeof flagNoEmpty[j] !== "undefined") {
							flag[i] = flagNoEmpty[j];
						} else {
							flag[i] = 0;
						}
						j++;
					}

					// Return the number positions need to move
					var left = number - nMove;
					return left;
				};

				var moveAllItemsFromXToRight = function(x, number) {
					var backupFlag = flag.slice();
					var maxNumber = getRightEmptySlotFromX(x);
					if (maxNumber === 0) {
						return number;
					}

					var prev = getPrevBlock(x);
					var newX = prev.x >= 0 ? prev.x : x;
					var nMove = number;
					if (number <= maxNumber) {
						nMove = number;
					} else {
						nMove = maxNumber;
					}

					// Find empty positions from x to right, stop when see any item while finding.
					var xE = x,
						c = 0,
						i = newX;
					while (c < nMove && i < maxCol) {
						if (flag[i] === 0) {
							c++;
							xE = i;
						}
						i++;
					}

					// The new position is x, and need empty flag from x to xE
					var flagNoEmpty = [],
						j = 0;

					for (i = newX; i <= xE; i++) {
						flag[i] = 0;
						if (backupFlag[i] !== 0) {
							flagNoEmpty[j] = backupFlag[i];
							j++;
						}
					}

					j = flagNoEmpty.length - 1;
					for (i = xE; i >= newX; i--) {
						if (typeof flagNoEmpty[j] !== "undefined") {
							flag[i] = flagNoEmpty[j];
						} else {
							flag[i] = 0;
						}
						j--;
					}

					// Return the number positions need to move
					var left = number - nMove;
					return left;
				};

				var updateItemsPositions = function() {
					that.updateItemsPositions(flag);
				};

				/**
				 * Insert to Flag an item with the width is x and position is x
				 *
				 * @param node object Item node
				 * @param swap boolean swap items or not
				 * @returns {boolean}
				 */
				var insertToFlag = function(node, swap) {
					var x = node.x,
						w = node.w;

					// get Empty slots
					var emptySlots = getEmptySlots();

					// Not enough empty slots, fallback.
					if (emptySlots <= 0) {
						return false;
					}

					if (_.isUndefined(swap)) {
						swap = false;
					}

					var _x;
					var _re;
					var _le;
					var _w;

					// Check nếu từ vị trí hiện tại đủ chỗ trống rồi thì ko cần dịch chuyển nữa.
					if (!swap) {
						if (isEmptyX(x)) {
							// Nếu đã đủ chỗ trống ko cần resize
							_w = w;

							if (checkEnoughSpaceFromX(x, _w)) {
								addItemToFlag(node);
								node.el.attr("data-gs-x", x);
								node.el.attr("data-gs-width", _w);
								return true;
							}

							_re = getRightEmptySlotFromX(x, true);
							_le = getLeftEmptySlotFromX(x - 1, true);

							// Kiểm tra xem tổng số chỗ trống có lớn hơn chiều rộng hiện tại không
							// Và số trống còn thiếu về bên trái phải nhỏ hơn số chỗ trống bên trái
							if (_re + _le >= w && w - _re <= _le) {
								_x = x - (w - _re);
							} else {
								_x = x - _le;
							}

							if (_x < 0) {
								_x = 0;
							}
							console.log("_re", _re);
							console.log("_le", _le);
							console.log("__x", _x);

							while (_w >= 1) {
								if (checkEnoughSpaceFromX(_x, _w)) {
									console.log({ x: _x, w: _w });
									node.x = _x;
									node.w = _w;
									addItemToFlag(node);
									node.el.attr("data-gs-x", _x);
									node.el.attr("data-gs-width", _w);
									return true;
								}
								_w--;
							}
						}

						// Check nếu vị trí hiện tại x  có giá trị là 1 thì thử lùi về sau xem có chỗ nào đủ chỗ trống ko ?
						if (flag[x] === 1) {
							var prev = getPrevBlock(x);
							if (prev.x >= 0) {
								if (
									x > prev.x + Math.floor(prev.w / 2) &&
									x > prev.x
								) {
									_x = prev.x + prev.w;
									_re = getRightEmptySlotFromX(_x, true);
									console.log("__re", _re);
									console.log("__re_X", _x);
									if (_re >= w) {
										addItemToFlag({
											el: node.el,
											x: _x,
											w: w
										});
										node.el.attr("data-gs-x", _x);
										node.el.attr("data-gs-width", w);
										return true;
									}
								}
							}
						}
					}

					var remain = 0;

					// -------------------------
					console.log(
						"--------------------------------------------------------"
					);
					var _move_to_swap = function(node, _x) {
						var _block_prev;
						var _block_next;
						var _empty_slots = 0;
						var found = false;
						var i, el, er;

						console.log("insert at x", _x);
						console.log("insert node", node);

						if (isEmptyX(_x)) {
							// if insert at empty x
							console.log("empty_at_X", _x);
							_block_prev = getPrevBlock(_x);
							_block_next = getNextBlock(_x);
							if (_block_prev.x > -1) {
								// if drop item at position that have item before
								console.log("found_item_left", _block_prev);
								_empty_slots = getRightEmptySlotFromX(
									_block_prev.x
								); // find number empty slots from x top when have item
								if (_empty_slots >= node.w) {
									// If enough slot for this item
									if (checkEnoughSpaceFromX(_x, node.w)) {
										console.log("found", node);
										x = _x;
										found = true;
									} else if (node.ox > _x) {
										// move item from left to right
										i = _block_prev.x + _block_prev.w;
										el = getLeftEmptySlotFromX(i);
										if (el <= node.w) {
											el = node.w - el;
										} else {
											el = node.w;
										}
										moveAllItemsFromXToRight(i + 1, el);
										_empty_slots = getRightEmptySlotFromX(
											i
										);
										console.log("loop_start_i", i);
										found = false;
										while (
											i > _block_prev.x + _block_prev.w &&
											!found
										) {
											if (
												checkEnoughSpaceFromX(i, node.w)
											) {
												console.log(
													"found_in_loop__i",
													i
												);
												x = i;
												found = true;
											}
											i--;
										}
									}
								}

								if (!found && node.ox < _x) {
									// move item from left to right
									// try move item to the left
									console.log(
										"try_move_items_to_left",
										_block_prev
									);
									i = _block_prev.x + _block_prev.w - 1;
									el = getLeftEmptySlotFromX(_block_prev.x);
									console.log("el", el);
									if (el > node.w) {
										el = node.w;
									}
									el -= 2;
									moveAllItemsFromXToLeft(_block_prev.x, el);
									console.log(
										"try_move_items_to_left_flag",
										flag
									);
									console.log("el2", el);
									_empty_slots = getRightEmptySlotFromX(i);
									i -= _empty_slots;
									_block_next = getNextBlock(_x);
									var max = _block_prev.x + _block_prev.w;
									if (_block_next.x > -1) {
										max = _block_next.x;
									}
									console.log("loop_start 2_i", i);
									while (i < max && !found) {
										if (checkEnoughSpaceFromX(i, node.w)) {
											console.log(
												"found_in_loop__@__i",
												i
											);
											x = i;
											found = true;
										}
										i++;
									}
								}

								if (!found) {
									// Try to resize node width;
									x = _block_prev.x + _block_prev.w;
									node.w = _empty_slots;
									node.x = x;
									console.log("resize_new_w", _empty_slots);
									console.log("resize_new_x", x);
								}
							} else if (_block_next.x > -1) {
								// try to get right item form x
								console.log("found_item_right", _block_next);
								_block_next = getNextBlock(_x);
								_empty_slots = getRightEmptySlotFromX(
									_x,
									false
								);
								console.log("move_all_item_to Right");
								var n_move =
									_empty_slots >= node.w
										? node.w
										: _empty_slots;
								moveAllItemsFromXToRight(_x, n_move);
								i = _block_next.x;
								console.log("loop_start Right", i);
								while (i >= 0 && !found) {
									if (checkEnoughSpaceFromX(i, node.w)) {
										x = i;
										node.x = x;
										found = true;
										console.log("found_in_while_r", i);
									}
									i--;
								}

								if (!found) {
									// Try to resize node width;
									x = _x;
									node.w = _empty_slots;
									node.x = x;
									console.log("resize_r_new_w", _empty_slots);
									console.log("resize_r_new_x", x);
								}
							} else {
								// the row is empty
								// Do somthing
							}
						} else {
							// if x is not empty
							// insert before item, that drop in
							console.log("x is not empty");
							_block_prev = getPrevBlock(_x);

							if (node.ox < _block_prev.x) {
								// drop from left to right
								moveAllItemsFromXToLeft(_x, node.w);
								console.log("Move All items to left");
								if (isEmptyX(_x)) {
									x = _x;
								} else {
									while (
										!isEmptyX(_x) &&
										_x <= that.cols - 1
									) {
										_x++;
									}
									x = _x;
								}
							} else {
								moveAllItemsFromXToRight(_x, node.w);
								console.log("Move All items to right");
								if (isEmptyX(_x)) {
									x = _x;
								} else {
									while (!isEmptyX(_x) && _x >= 0) {
										_x--;
									}
									x = _x;
								}
							}
						}

						if (x > that.cols) {
							x = that.cols - 1;
						}
						node.x = x;
						console.log("new node x", x);
					};

					_move_to_swap(node, _.clone(x));

					// console.log( '--------------------------------------------------------' );
					// -------------------------
					var newX = x;
					var i;
					var found = false;
					var le = 0;

					//console.log( 'in_the_end_x', x );
					//console.log( 'in_the_end_w', w );
					if (x + w > that.cols - 1) {
						le = getLeftEmptySlotFromX(x, true);
						console.log("le", le);
						if (le > 0) {
							console.log("move_Left", x + w - that.cols - 1);
						}
					}
					updateItemsPositions();
					//console.log( 'Flag update', flag );
					le = 0;
					while (w >= 1) {
						// Nếu số chỗ trống lớn hơn hoặc  = chiều rộng của item
						if (emptySlots >= w) {
							// Nếu tại vị trí hiện tại mà đủ chỗ trống
							if (checkEnoughSpaceFromX(x, w)) {
								console.log("", { x: x, w: w });
								node.w = w;
								addItemToFlag(node);
								node.el.attr("data-gs-x", x);
								node.el.attr("data-gs-width", w);
								return true;
							}

							found = false;
							le = getLeftEmptySlotFromX(x, true);
							// Nếu trỗ trông bên trái nhiều hơn bên phải
							newX = x - le;
							// tìm kiếm từ vị trí trống từ new sang bên phải xem có chỗ nào chèn dc ko ?
							console.log("newX", newX);
							i = newX;
							while (i < maxCol && !found) {
								if (checkEnoughSpaceFromX(i, w)) {
									console.log("Insert in While", {
										x: i,
										w: w
									});
									node.w = w;
									addItemToFlag({ el: node.el, x: i, w: w });
									node.el.attr("data-gs-x", i);
									node.el.attr("data-gs-width", w);
									found = true;
									return true;
								}
								i++;
							}
						}
						w--;
					}

					// Chèn vào bất kỳ đâu đủ chỗ
					w = node.w;
					found = false;
					while (w >= 1) {
						i = 0;
						while (i < maxCol && !found) {
							if (checkEnoughSpaceFromX(i, w)) {
								console.log("Insert in While 2", {
									x: i,
									w: w
								});
								addItemToFlag({ el: node.el, x: i, w: w });
								node.el.attr("data-gs-x", i);
								node.el.attr("data-gs-width", w);
								found = true;
								return true;
							}
							i++;
						}
						w--;
					}

					console.log("Insert END While", { x: i, w: w });
					return false;
				};

				/**
				 * Dổi chỗ 2 item trong 1 hàng
				 * @param x Vị trị bắt đầu của item dc thay đổi
				 * @param newX Vị trí của item chuyển đến
				 */
				var swap = function(node, newX) {
					var x = node.x;
					var w = node.w;

					removeNode(node);
					console.log("Swap newX", newX);
					console.log("Before Swap FLAG", flag);

					var block2 = getPrevBlock(newX);

					var block2_right = 0;
					if (block2.x > -1) {
						block2_right = block2.x + block2.w;
					}
					if (checkEnoughSpaceFromX(newX, w)) {
						addItemToFlag({ el: node.el, x: newX, w: w });
						return true;
					} else if (
						block2_right > 0 &&
						checkEnoughSpaceFromX(block2_right, w) &&
						newX >= block2_right
					) {
						var block3 = getNextBlock(newX);
						if (block3.x > -1) {
							// Nếu phía sau nó có item
							// nếu item chèn vào mà đủ chỗ
							// nhưng từ trí từ vị trí thả chuột xuống ko đủ chỗ để chèn vào
							// lùi vị trí chèn vào cho đến khi nào đủ
							if (node.w + newX >= block3.x) {
								var _newX = _.clone(newX);
								while (_newX > block2_right) {
									if (checkEnoughSpaceFromX(_newX, w)) {
										addItemToFlag({
											el: node.el,
											x: _newX,
											w: w
										});
										return true;
									}
									_newX--;
								}
							}
						}

						if (newX + w > that.cols) {
							// Nếu newX + w vượt quá số cols. thì thử lùi lại xem  có đủ chỗ ko ?
							var _x = that.cols - w;
							if (checkEnoughSpaceFromX(_x, w)) {
								addItemToFlag({ el: node.el, x: _x, w: w });
								return true;
							}
						}
						addItemToFlag({ el: node.el, x: block2_right, w: w });
						return true;
					}

					node.x = newX;

					insertToFlag(node, true);
				};

				//-----------------------------------------------------------------------------------------------------------------------------
				var that = this;
				flag = that.getFlag($wrapper);
				backupFlag = flag.slice();
				var wOffset = $wrapper.offset();
				that.draggingItem = ui.draggable;
				var width = $wrapper.width();
				var colWidth = width / that.cols;
				var x = 0;
				var iOffset = ui.offset;
				var w, cw, itemWidth, in_this_row;
				cw = that.getW(ui.draggable, false);
				w = that.getW(ui.draggable, true);
				itemWidth = ui.draggable.width();

				console.log("DROP ITEM WIDTH", w);
				console.log("DROP ITEM cw WIDTH", cw);
				var ox = that.getX(ui.draggable);
				if (is_rtl) {
					removeNode({
						el: ui.draggable,
						x: ox,
						w: w
					});
				}

				var xc = 0,
					xi = 0,
					found = false;

				if (!ui.draggable.parent().is($wrapper)) {
					in_this_row = false;
					console.log("Not in this row");
					if (w < cw) {
						w = cw;
					}
				} else {
					in_this_row = true;
					console.log("Item in this row");
					w = cw;
				}

				// Kiểm tra RTL
				if (!is_rtl) {
					// nếu ko phải RTL
					// Lấy vị trí thả xuống từ con trỏ chuột tính theo bên trái của trình duyệt và mép trái của row
					xc = Math.round((event.clientX - wOffset.left) / colWidth);

					// Lấy vị trí thả xuống từ mép trái của item
					xi = Math.round(
						(iOffset.left - wOffset.left - 10) / colWidth
					);
					if (xi < 0) {
						xi = 0;
					}
				} else {
					// Nếu là RTL

					// Lấy vị trí thả xuống từ con trỏ chuột tính theo bên trái của trình duyệt và mép phải của row
					xc = Math.round(
						(wOffset.left + width + 10 - event.clientX) / colWidth
					);

					// Lấy vị trí thả xuống từ mép phải của item theo bên phải của row
					xi = Math.round(
						(wOffset.left +
							width -
							(iOffset.left + itemWidth + 10)) /
							colWidth
					);
					if (xi < 0) {
						xi = 0;
					}
				}
				if (xc > that.cols) {
					xc = that.cols;
				}

				x = xi;
				var _i;
				_i = xi;

				if (is_rtl) {
					if (!isEmptyX(_i)) {
						while (_i < that.cols && !found) {
							if (isEmptyX(_i)) {
								found = true;
							} else {
								_i++;
							}
						}
						if (x > xc) {
							// x = xc;
						}
					} else {
						x = xi;
						found = true;
					}
				} else {
					if (!isEmptyX(x)) {
						while (x <= xc && !found) {
							if (isEmptyX(x)) {
								found = true;
							} else {
								x++;
							}
						}
						if (x > xc) {
							x = xc;
						}
					} else {
						x = xi;
						found = true;
					}
				}

				if (!found) {
					if (in_this_row) {
						x = xi;
					} else {
						x = xc;
					}
				}

				if (x < 0) {
					x = 0;
				}

				if (x + w >= that.cols) {
					found = true;
					_i = x;
					while (_i + w > that.cols && found) {
						if (!isEmptyX(_i)) {
							_i++;
							found = false;
						} else {
							_i--;
						}
						console.log("loop_i", _i);
					}

					console.log("Find new _i, w: " + w, _i);

					x = _i;
				}

				delete found;

				console.log("DROP Cursor", xc);
				console.log("DROP row x cacl", x);
				console.log("DROP item w", w);

				var node = {
					el: ui.draggable,
					x: x,
					w: w,
					ox: ox,
					ow: cw
				};

				if (node.x <= 0) {
					node.x = 0;
				}

				var did = false;
				if (in_this_row) {
					node.x = parseInt(ui.draggable.attr("data-gs-x") || 0);
					node.w = parseInt(ui.draggable.attr("data-gs-width") || 1);
					console.log("swap node", node);
					swap(node, x);
					did = true;
				} else {
					did = insertToFlag(node);
					console.log("Insert node");
				}

				// console.log( 'Drop on X: ' + x + ', width: '+ w );
				// console.log( 'Drop Flag: ', flag );

				if (!did) {
					ui.draggable.removeAttr("style");
					console.log("Can not insert");
					flag = backupFlag; // rollback;
				} else {
					// Add drop item from somewhere to current row
					ui.draggable.removeClass("item-from-list");

					$wrapper.append(ui.draggable);
					ui.draggable.removeAttr("style");
					console.log("DID Flag: ", flag);
					//ui.draggable.attr( 'data-gs-x', x );
					//ui.draggable.attr( 'data-gs-y', y );
					that.draggingItem = null;
				}

				updateItemsPositions();
				that.updateAllGrids();

				//-----------------------------------------------------------------------------------------------------------------------------
			},
			updateAllGrids: function() {
				var that = this;
				_.each(that.panels[that.activePanel], function(row, row_id) {
					that.updateGridFlag(row);
				});
			},
			setGridWidth: function($wrapper, ui) {
				var that = this;
				var $item = ui.element;
				var width = $wrapper.width();
				var itemWidth = ui.size.width;
				var originalElementWidth = ui.originalSize.width;
				var colWidth = Math.ceil(width / that.cols) - 1;
				var isShiftLeft, isShiftRight;

				if (!is_rtl) {
					isShiftLeft = ui.originalPosition.left > ui.position.left;
					isShiftRight = ui.originalPosition.left < ui.position.left;
				} else {
					isShiftLeft = ui.originalPosition.left > ui.position.left;
					isShiftRight = originalElementWidth !== itemWidth;
				}

				var ow = ui.originalElement.attr("data-gs-width") || 1;
				var ox = ui.originalElement.attr("data-gs-x") || 0;
				ow = parseInt(ow);
				ox = parseInt(ox);

				var addW;
				var newX;
				var newW;
				var flag = that.getFlag($wrapper);
				var itemInfo = that.gridGetItemInfo(
					ui.originalElement,
					flag,
					$wrapper
				);
				var diffLeft, diffRight;

				if (isShiftLeft) {
					if (!is_rtl) {
						// Ok
						newX = Math.floor((ui.position.left - 1) / colWidth);
						addW = ox - newX;
						if (addW > itemInfo.before) {
							addW = itemInfo.before;
						}

						newX = ox - addW;
						newW = ow + addW;
						$item.attr("data-gs-x", newX).removeAttr("style");
						$item.attr("data-gs-width", newW).removeAttr("style");
					} else {
						// RTL

						// Ok
						newX = Math.floor((ui.position.left - 1) / colWidth);
						newX = that.cols - newX;
						addW = newX - ox - ow;
						if (addW > itemInfo.after) {
							addW = itemInfo.after;
						}
						newW = ow + addW;
						$item.attr("data-gs-x", ox).removeAttr("style");
						$item.attr("data-gs-width", newW).removeAttr("style");
					}

					that.updateGridFlag($wrapper);
					return;
				} else if (isShiftRight) {
					if (!is_rtl) {
						// Ok
						newX = Math.round((ui.position.left - 1) / colWidth);
						addW = newX - ox;
						newW = ow - addW;
						if (newW <= 0) {
							newW = 1;
							addW = 0;
						}
						newX = ox + addW;
						$item.attr("data-gs-x", newX).removeAttr("style");
						$item.attr("data-gs-width", newW).removeAttr("style");
					} else {
						// RTL

						if (ui.originalPosition.left !== ui.position.left) {
							// Nếu resize ở mép trái của Item
							// Ok
							newX = Math.floor(
								(ui.position.left - 1) / colWidth
							);
							newX = that.cols - newX;
							addW = ow + ox - newX;
							if (addW > ow) {
								addW = 0;
							}
							newX = ox;
							newW = ow - addW;
							if (newX <= 0) {
								newX = 0;
							}

							console.log("diffRight_RTL_COL_New __left");
						} else {
							// Nếu resize ở mép phải của Item
							// Ok
							newX = Math.ceil(
								(ui.position.left + ui.size.width - 11) /
									colWidth
							);
							newX = that.cols - newX;
							addW = ox - newX;
							if (addW > itemInfo.before) {
								addW = itemInfo.before;
							}
							newX = ox - addW;
							newW = ow + addW;
						}
						$item.attr("data-gs-x", newX).removeAttr("style");
						$item.attr("data-gs-width", newW).removeAttr("style");
					}

					that.updateGridFlag($wrapper);
					return;
				}

				var w;
				var x = itemInfo.x;
				var x_c;

				if (itemWidth < ui.originalSize.width) {
					// Resize from right to left
					// Ok
					x_c = Math.round(
						(ui.position.left + ui.size.width - 11) / colWidth
					);
					if (x_c <= x) {
						x_c = x + 1;
					}
					w = itemInfo.w - (x + itemInfo.w - x_c);
				} else {
					// Resize from left to right
					//Ok
					x_c = Math.ceil(
						(ui.position.left + ui.size.width - 11) / colWidth
					);
					w = itemInfo.w + (x_c - (x + itemInfo.w));
					if (
						itemInfo.x + w >
						itemInfo.x + itemInfo.w + itemInfo.after
					) {
						w = itemInfo.w + itemInfo.after;
					}
				}

				if (w <= 0) {
					w = 1;
				}

				$item.attr("data-gs-width", w).removeAttr("style");
				that.updateGridFlag($wrapper);
			},
			getFlag: function($row) {
				var that = this;
				var flag = $row.data("gridRowFlag") || [];
				var i;
				if (_.isEmpty(flag)) {
					for (i = 0; i < that.cols; i++) {
						flag[i] = 0;
					}
					$row.data("gridRowFlag", flag);
				}

				return flag;
			},
			updateGridFlag: function($row) {
				var that = this;
				var rowFlag = [];
				var i;
				for (i = 0; i < that.cols; i++) {
					rowFlag[i] = 0;
				}
				var items;
				items = $(".grid-stack-item", $row);
				items.each(function(index) {
					$(this).removeAttr("style");
					var x = that.getX($(this));
					var w = that.getW($(this));

					for (i = x; i < x + w; i++) {
						if (i === x) {
							rowFlag[i] = $(this);
						} else {
							rowFlag[i] = 1;
						}
					}
				});

				$row.data("gridRowFlag", rowFlag);
				that.updateItemsPositions(rowFlag);
				that.sortGrid($row);
				return rowFlag;
			},
			addNewWidget: function($item, row) {
				var that = this;
				var panel = that.container.find(
					".customify--device-panel.customify--panel-" +
						that.activePanel
				);
				var el = row;
				if (!_.isObject(el)) {
					el = panel.find(".customify--cb-items").first();
				}

				var elItem = $item;
				elItem
					.draggable({
						revert: "invalid",
						appendTo: panel,
						scroll: false,
						zIndex: 99999,
						handle: ".grid-stack-item-content",
						start: function(event, ui) {
							$("body").addClass("builder-item-moving");
							$(".customify--cb-items", panel).css("z-index", "");
							ui.helper.parent().css("z-index", 9999);
						},
						stop: function(event, ui) {
							$("body").removeClass("builder-item-moving");
							$(".customify--cb-items", panel).css("z-index", "");
							that.save();
						},
						drag: function(event, ui) {}
					})
					.resizable({
						handles: "w, e",
						start: function(event, ui) {
							// RTL
							ui.originalElement.css({
								right: "auto",
								left: ui.position.left
							});
						},
						stop: function(event, ui) {
							that.setGridWidth(ui.element.parent(), ui);
							that.save();
						}
					});

				el.append(elItem);
				that.updateGridFlag(el);
			},
			addPanel: function(device) {
				var that = this;
				var template = that.getTemplate();
				var template_id = "tmpl-customify--cb-panel";
				if ($("#" + template_id).length == 0) {
					return;
				}
				if (!_.isObject(options.rows)) {
					options.rows = {};
				}
				var html = template(
					{
						device: device,
						id: options.id,
						rows: options.rows
					},
					template_id
				);
				return (
					'<div class="customify--device-panel customify-vertical-panel customify--panel-' +
					device +
					'" data-device="' +
					device +
					'">' +
					html +
					"</div>"
				);
			},
			addDevicePanels: function() {
				var that = this;
				_.each(that.devices, function(device_name, device) {
					var panelHTML = that.addPanel(device);
					$(".customify--cb-devices-switcher", that.container).append(
						'<a href="#" class="switch-to switch-to-' +
							device +
							'" data-device="' +
							device +
							'">' +
							device_name +
							"</a>"
					);
					$(".customify--cb-body", that.container).append(panelHTML);
				});

				if ($("#customify-upsell-tmpl").length) {
					$($("#customify-upsell-tmpl").html()).insertAfter(
						$(".customify--cb-devices-switcher", that.container)
					);
				}
			},
			addItem: function(node) {
				var that = this;
				var template = that.getTemplate();
				var template_id = "tmpl-customify--cb-item";
				if ($("#" + template_id).length == 0) {
					return;
				}
				var html = template(node, template_id);
				return $(html);
			},
			addAvailableItems: function() {
				var that = this;

				_.each(that.devices, function(device_name, device) {
					var $itemWrapper = $(
						'<div class="customify-available-items" data-device="' +
							device +
							'"></div>'
					);
					$(".customify--panel-" + device, that.container).append(
						$itemWrapper
					);

					_.each(that.items, function(node) {
						var _d = true;
						if (
							!_.isUndefined(node.devices) &&
							!_.isEmpty(node.devices)
						) {
							if (_.isString(node.devices)) {
								if (node.devices != device) {
									_d = false;
								}
							} else {
								var _has_d = false;
								_.each(node.devices, function(_v) {
									if (device == _v) {
										_has_d = true;
									}
								});
								if (!_has_d) {
									_d = false;
								}
							}
						}

						if (_d) {
							var item = that.addItem(node);
							$itemWrapper.append(item);
						}
					});
				});
			},
			switchToDevice: function(device, toggle_button) {
				var that = this;
				var numberDevices = _.size(that.devices);
				if (numberDevices > 1) {
					$(
						".customify--cb-devices-switcher a",
						that.container
					).removeClass("customify--tab-active");
					$(
						".customify--cb-devices-switcher .switch-to-" + device,
						that.container
					).addClass("customify--tab-active");
					$(".customify--device-panel", that.container).addClass(
						"customify--panel-hide"
					);
					$(
						".customify--device-panel.customify--panel-" + device,
						that.container
					).removeClass("customify--panel-hide");
					that.activePanel = device;
				} else {
					$(
						".customify--cb-devices-switcher a",
						that.container
					).addClass("customify--tab-active");
				}

				if (_.isUndefined(toggle_button) || toggle_button) {
					if (device == "desktop") {
						$("#customize-footer-actions .preview-desktop").trigger(
							"click"
						);
					} else {
						$("#customize-footer-actions .preview-mobile").trigger(
							"click"
						);
					}
				}
			},
			addExistingRowsItems: function() {
				var that = this;

				var data = wpcustomize.control(that.controlId).params.value;
				if (!_.isObject(data)) {
					data = {};
				}
				_.each(that.panels, function($rows, device) {
					var device_data = {};
					if (_.isObject(data[device])) {
						device_data = data[device];
					}
					_.each(device_data, function(items, row_id) {
						if (!_.isUndefined(items)) {
							_.each(items, function(node, index) {
								var item = $(
									'.customify-available-items[data-device="' +
										device +
										'"] .grid-stack-item[data-id="' +
										node.id +
										'"]'
								).first();
								item.attr("data-gs-width", node.width);
								item.attr("data-gs-x", node.x);
								item.removeClass("item-from-list");
								that.addNewWidget(item, $rows[row_id]);
							});
						}
					});
				});

				that.ready = true;
			},
			focus: function() {
				this.container.on(
					"click",
					".customify--cb-item-setting, .customify--cb-item-name, .item-tooltip",
					function(e) {
						e.preventDefault();
						var section = $(this).data("section") || "";
						//console.log( 'Clicked section' , section );
						var control = $(this).attr("data-control") || "";
						var did = false;
						if (control) {
							if (!_.isUndefined(wpcustomize.control(control))) {
								wpcustomize.control(control).focus();
								did = true;
							}
						}
						if (!did) {
							if (
								section &&
								!_.isUndefined(wpcustomize.section(section))
							) {
								wpcustomize.section(section).focus();
								did = true;
							}
						}
					}
				);

				// Focus rows
				this.container.on(
					"click",
					".customify--cb-row-settings",
					function(e) {
						e.preventDefault();
						var id = $(this).attr("data-id") || "";

						var section = options.id + "_" + id;

						if (!_.isUndefined(wpcustomize.section(section))) {
							wpcustomize.section(section).focus();
						}
					}
				);
			},
			remove: function() {
				var that = this;
				$document.on(
					"click",
					".customify--device-panel .customify--cb-item-remove",
					function(e) {
						e.preventDefault();
						var item = $(this).closest(".grid-stack-item");
						var panel = item.closest(".customify--device-panel");
						item.attr("data-gs-width", 1);
						item.attr("data-gs-x", 0);
						item.removeAttr("style");
						$(".customify-available-items", panel).append(item);
						that.updateAllGrids();
						that.save();
					}
				);
			},
			encodeValue: function(value) {
				return encodeURI(JSON.stringify(value));
			},
			decodeValue: function(value) {
				return JSON.parse(decodeURI(value));
			},
			save: function() {
				var that = this;
				if (!that.ready) {
					return;
				}

				var data = {};
				_.each(that.panels, function($rows, device) {
					data[device] = {};
					_.each($rows, function(row, row_id) {
						var rowData = _.map(
							$(" > .grid-stack-item", row),
							function(el) {
								el = $(el);
								return {
									x: that.getX(el),
									y: 1,
									width: that.getW(el),
									height: 1,
									id: el.data("id") || ""
								};
							}
						);
						data[device][row_id] = rowData;
					});
				});


				wpcustomize
					.control(that.controlId)
					.setting.set(that.encodeValue(data));
				
			},
			showPanel: function() {
				var that = this;
				this.container
					.removeClass("customify--builder--hide")
					.addClass("customify--builder-show");
				setTimeout(function() {
					var h = that.container.height();
					$("#customize-preview")
						.addClass("cb--preview-panel-show")
						.css({ bottom: h - 1, "margin-top": "0px" });
				}, 100);
			},
			hidePanel: function() {
				this.container.removeClass("customify--builder-show");
				$("#customize-preview")
					.removeClass("cb--preview-panel-show")
					.removeAttr("style");
			},
			togglePanel: function() {
				var that = this;
				wpcustomize.state("expandedPanel").bind(function(paneVisible) {
					if (wpcustomize.panel(options.panel).expanded()) {
						$document.trigger("customify_panel_builder_open", [
							options.panel
						]);
						console.log("open-builder:", options.panel);
						top._current_builder_panel = id;
						that.showPanel();
					} else {
						that.hidePanel();
					}
				});

				that.container.on("click", ".customify--panel-close", function(
					e
				) {
					e.preventDefault();
					that.container.toggleClass("customify--builder--hide");
					if (that.container.hasClass("customify--builder--hide")) {
						$("#customize-preview").removeClass(
							"cb--preview-panel-show"
						);
					} else {
						$("#customize-preview").addClass(
							"cb--preview-panel-show"
						);
					}
				});
			},
			panelLayoutCSS: function() {
				//wpcustomize.state( 'paneVisible' ).get()
				var sidebarWidth = $("#customize-controls").width();
				if (!wpcustomize.state("paneVisible").get()) {
					sidebarWidth = 0;
				}
				if (is_rtl) {
					this.container
						.find(".customify--cb-inner")
						.css({ "margin-right": sidebarWidth });
				} else {
					this.container
						.find(".customify--cb-inner")
						.css({ "margin-left": sidebarWidth });
				}
			},
			init: function(controlId, items, devices) {
				var that = this;

				var template = that.getTemplate();
				var template_id = "tmpl-customify--builder-panel";
				var html = template(options, template_id);
				that.container = $(html);
				$("body .wp-full-overlay").append(that.container);
				that.controlId = controlId;
				that.items = items;
				that.devices = devices;

				if (options.section) {
					wpcustomize
						.section(options.section)
						.container.addClass("customify--hide");
				}

				that.addDevicePanels();
				that.switchToDevice(that.activePanel);
				that.addAvailableItems();
				that.switchToDevice(that.activePanel);
				that.drag_drop();
				that.focus();
				that.remove();
				that.addExistingRowsItems();

				if (wpcustomize.panel(options.panel).expanded()) {
					console.log("open-builder:", options.panel);
					that.showPanel();
				} else {
					that.hidePanel();
				}

				wpcustomize.previewedDevice.bind(function(newDevice) {
					if (newDevice === "desktop") {
						that.switchToDevice("desktop", false);
					} else {
						that.switchToDevice("mobile", false);
					}
				});

				that.togglePanel();
				if (wpcustomize.state("paneVisible").get()) {
					that.panelLayoutCSS();
				}
				wpcustomize.state("paneVisible").bind(function() {
					that.panelLayoutCSS();
				});

				$(window).resize(
					_.throttle(function() {
						that.panelLayoutCSS();
					}, 100)
				);

				// Switch panel.
				that.container.on(
					"click",
					".customify--cb-devices-switcher a.switch-to",
					function(e) {
						e.preventDefault();
						var device = $(this).data("device");
						that.switchToDevice(device);
					}
				);

				$document.trigger("customify_builder_panel_loaded", [id, that]);
			}
		};
	
		Builder.init(options.control_id, options.items, options.devices);
		return Builder;
	};

}( jQuery ));
