Page Builder by SiteOrigin - Version 2.10.3

Version Description

  • 2 April 2019 =
  • Layout builder widget: Call styles sanitization in update.
  • Live editor: Only call process_raw_widgets once for preview data.
  • Add a setting for whether to display SiteOrigin Page Builder post state.
  • Sidebars emulator: Cache the result of url_to_postid().
  • Prevent affecting child layouts with parent layouts' CSS.
Download this release

Release Info

Developer gpriday
Plugin Icon 128x128 Page Builder by SiteOrigin
Version 2.10.3
Comparing to
See all releases

Code changes from version 2.10.2 to 2.10.3

compat/js/siteorigin-panels-layout-block.js CHANGED
@@ -1,352 +1,352 @@
1
- "use strict";
2
-
3
- function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
4
-
5
- function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
6
-
7
- function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
8
-
9
- function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
10
-
11
- function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
12
-
13
- function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
14
-
15
- function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
16
-
17
- function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
18
-
19
- function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
20
-
21
- var _lodash = lodash,
22
- isEqual = _lodash.isEqual,
23
- debounce = _lodash.debounce,
24
- isEmpty = _lodash.isEmpty,
25
- isFunction = _lodash.isFunction;
26
- var registerBlockType = wp.blocks.registerBlockType;
27
- var _wp$element = wp.element,
28
- Component = _wp$element.Component,
29
- Fragment = _wp$element.Fragment,
30
- RawHTML = _wp$element.RawHTML,
31
- createRef = _wp$element.createRef;
32
- var BlockControls = wp.editor.BlockControls;
33
- var _wp$components = wp.components,
34
- Toolbar = _wp$components.Toolbar,
35
- IconButton = _wp$components.IconButton,
36
- Spinner = _wp$components.Spinner;
37
- var __ = wp.i18n.__;
38
- var _window = window,
39
- soPanelsBlockEditorAdmin = _window.soPanelsBlockEditorAdmin;
40
-
41
- var SiteOriginPanelsLayoutBlock =
42
- /*#__PURE__*/
43
- function (_Component) {
44
- _inherits(SiteOriginPanelsLayoutBlock, _Component);
45
-
46
- function SiteOriginPanelsLayoutBlock(props) {
47
- var _this;
48
-
49
- _classCallCheck(this, SiteOriginPanelsLayoutBlock);
50
-
51
- _this = _possibleConstructorReturn(this, _getPrototypeOf(SiteOriginPanelsLayoutBlock).call(this, props));
52
- var editMode = soPanelsBlockEditorAdmin.defaultMode === 'edit' || isEmpty(props.panelsData);
53
- _this.state = {
54
- editing: editMode,
55
- loadingPreview: !editMode,
56
- previewHtml: ''
57
- };
58
- _this.panelsContainer = createRef();
59
- _this.previewContainer = createRef();
60
- _this.panelsInitialized = false;
61
- _this.previewInitialized = false;
62
- return _this;
63
- }
64
-
65
- _createClass(SiteOriginPanelsLayoutBlock, [{
66
- key: "componentDidMount",
67
- value: function componentDidMount() {
68
- this.isStillMounted = true;
69
-
70
- if (this.state.editing) {
71
- this.setupPanels();
72
- } else if (!this.state.editing && !this.previewInitialized) {
73
- this.fetchPreview(this.props);
74
- this.fetchPreview = debounce(this.fetchPreview, 500);
75
- }
76
- }
77
- }, {
78
- key: "componentWillUnmount",
79
- value: function componentWillUnmount() {
80
- this.isStillMounted = false;
81
-
82
- if (this.builderView) {
83
- this.builderView.off('content_change');
84
- }
85
- }
86
- }, {
87
- key: "componentDidUpdate",
88
- value: function componentDidUpdate(prevProps) {
89
- // let propsChanged = !isEqual( prevProps.panelsData, this.props.panelsData );
90
- if (this.state.editing && !this.panelsInitialized) {
91
- this.setupPanels();
92
- } else if (this.state.loadingPreview) {
93
- this.fetchPreview(this.props);
94
- } else if (!this.previewInitialized && this.previewContainer.current) {
95
- $(document).trigger('panels_setup_preview');
96
- this.previewInitialized = true;
97
- }
98
- }
99
- }, {
100
- key: "setupPanels",
101
- value: function setupPanels() {
102
- var _this2 = this;
103
-
104
- var $panelsContainer = jQuery(this.panelsContainer.current);
105
- var config = {
106
- editorType: 'standalone'
107
- };
108
- var builderModel = new panels.model.builder();
109
- this.builderView = new panels.view.builder({
110
- model: builderModel,
111
- config: config
112
- }); // Make sure panelsData is defined and clone so that we don't alter the underlying attribute.
113
-
114
- var panelsData = JSON.parse(JSON.stringify($.extend({}, this.props.panelsData))); // Disable block selection while dragging rows or widgets.
115
-
116
- var rowOrWidgetMouseDown = function rowOrWidgetMouseDown() {
117
- if (isFunction(_this2.props.onRowOrWidgetMouseDown)) {
118
- _this2.props.onRowOrWidgetMouseDown();
119
- }
120
-
121
- var rowOrWidgetMouseUp = function rowOrWidgetMouseUp() {
122
- $(document).off('mouseup', rowOrWidgetMouseUp);
123
-
124
- if (isFunction(_this2.props.onRowOrWidgetMouseUp)) {
125
- _this2.props.onRowOrWidgetMouseUp();
126
- }
127
- };
128
-
129
- $(document).on('mouseup', rowOrWidgetMouseUp);
130
- };
131
-
132
- this.builderView.on('row_added', function () {
133
- _this2.builderView.$('.so-row-move').off('mousedown', rowOrWidgetMouseDown);
134
-
135
- _this2.builderView.$('.so-row-move').on('mousedown', rowOrWidgetMouseDown);
136
-
137
- _this2.builderView.$('.so-widget').off('mousedown', rowOrWidgetMouseDown);
138
-
139
- _this2.builderView.$('.so-widget').on('mousedown', rowOrWidgetMouseDown);
140
- });
141
- this.builderView.on('widget_added', function () {
142
- _this2.builderView.$('.so-widget').off('mousedown', rowOrWidgetMouseDown);
143
-
144
- _this2.builderView.$('.so-widget').on('mousedown', rowOrWidgetMouseDown);
145
- });
146
- this.builderView.render().attach({
147
- container: $panelsContainer
148
- }).setData(panelsData);
149
- this.builderView.trigger('builder_resize');
150
- this.builderView.on('content_change', function () {
151
- var newPanelsData = _this2.builderView.getData();
152
-
153
- _this2.panelsDataChanged = !isEqual(panelsData, newPanelsData);
154
-
155
- if (_this2.panelsDataChanged) {
156
- if (_this2.props.onContentChange && isFunction(_this2.props.onContentChange)) {
157
- _this2.props.onContentChange(newPanelsData);
158
- }
159
-
160
- _this2.setState({
161
- loadingPreview: true,
162
- previewHtml: ''
163
- });
164
- }
165
- });
166
- $(document).trigger('panels_setup', this.builderView);
167
- this.panelsInitialized = true;
168
- }
169
- }, {
170
- key: "fetchPreview",
171
- value: function fetchPreview(props) {
172
- var _this3 = this;
173
-
174
- if (!this.isStillMounted) {
175
- return;
176
- }
177
-
178
- this.previewInitialized = false;
179
- var fetchRequest = this.currentFetchRequest = $.post({
180
- url: soPanelsBlockEditorAdmin.previewUrl,
181
- data: {
182
- action: 'so_panels_layout_block_preview',
183
- panelsData: JSON.stringify(props.panelsData)
184
- }
185
- }).then(function (preview) {
186
- if (_this3.isStillMounted && fetchRequest === _this3.currentFetchRequest && preview) {
187
- _this3.setState({
188
- previewHtml: preview,
189
- loadingPreview: false
190
- });
191
- }
192
- });
193
- return fetchRequest;
194
- }
195
- }, {
196
- key: "render",
197
- value: function render() {
198
- var _this4 = this;
199
-
200
- var panelsData = this.props.panelsData;
201
-
202
- var switchToEditing = function switchToEditing() {
203
- _this4.panelsInitialized = false;
204
-
205
- _this4.setState({
206
- editing: true
207
- });
208
- };
209
-
210
- var switchToPreview = function switchToPreview() {
211
- if (panelsData) {
212
- _this4.setState({
213
- editing: false
214
- });
215
- }
216
- };
217
-
218
- if (this.state.editing) {
219
- return React.createElement(Fragment, null, React.createElement(BlockControls, null, React.createElement(Toolbar, null, React.createElement(IconButton, {
220
- icon: "visibility",
221
- className: "components-icon-button components-toolbar__control",
222
- label: __('Preview layout.', 'siteorigin-panels'),
223
- onClick: switchToPreview
224
- }))), React.createElement("div", {
225
- key: "layout-block",
226
- className: "siteorigin-panels-layout-block-container",
227
- ref: this.panelsContainer
228
- }));
229
- } else {
230
- var loadingPreview = this.state.loadingPreview;
231
- return React.createElement(Fragment, null, React.createElement(BlockControls, null, React.createElement(Toolbar, null, React.createElement(IconButton, {
232
- icon: "edit",
233
- className: "components-icon-button components-toolbar__control",
234
- label: __('Edit layout.', 'siteorigin-panels'),
235
- onClick: switchToEditing
236
- }))), React.createElement("div", {
237
- key: "preview",
238
- className: "so-panels-block-layout-preview-container"
239
- }, loadingPreview ? React.createElement("div", {
240
- className: "so-panels-spinner-container"
241
- }, React.createElement("span", null, React.createElement(Spinner, null))) : React.createElement("div", {
242
- className: "so-panels-raw-html-container",
243
- ref: this.previewContainer
244
- }, React.createElement(RawHTML, null, this.state.previewHtml))));
245
- }
246
- }
247
- }]);
248
-
249
- return SiteOriginPanelsLayoutBlock;
250
- }(Component);
251
-
252
- registerBlockType('siteorigin-panels/layout-block', {
253
- title: __('SiteOrigin Layout', 'siteorigin-panels'),
254
- description: __("Build a layout using SiteOrigin's Page Builder.", 'siteorigin-panels'),
255
- icon: function icon() {
256
- return React.createElement("span", {
257
- className: "siteorigin-panels-block-icon"
258
- });
259
- },
260
- category: 'layout',
261
- keywords: ['page builder', 'column,grid', 'panel'],
262
- supports: {
263
- html: false
264
- },
265
- attributes: {
266
- panelsData: {
267
- type: 'object'
268
- }
269
- },
270
- edit: function edit(_ref) {
271
- var attributes = _ref.attributes,
272
- setAttributes = _ref.setAttributes,
273
- toggleSelection = _ref.toggleSelection;
274
-
275
- var onLayoutBlockContentChange = function onLayoutBlockContentChange(newPanelsData) {
276
- if (!_.isEmpty(newPanelsData.widgets)) {
277
- // Send panels data to server for sanitization.
278
- $.post(soPanelsBlockEditorAdmin.sanitizeUrl, {
279
- action: 'so_panels_layout_block_sanitize',
280
- panelsData: JSON.stringify(newPanelsData)
281
- }, function (sanitizedPanelsData) {
282
- if (sanitizedPanelsData !== '') {
283
- setAttributes({
284
- panelsData: sanitizedPanelsData
285
- });
286
- }
287
- });
288
- }
289
- };
290
-
291
- var disableSelection = function disableSelection() {
292
- toggleSelection(false);
293
- };
294
-
295
- var enableSelection = function enableSelection() {
296
- toggleSelection(true);
297
- };
298
-
299
- return React.createElement(SiteOriginPanelsLayoutBlock, {
300
- panelsData: attributes.panelsData,
301
- onContentChange: onLayoutBlockContentChange,
302
- onRowOrWidgetMouseDown: disableSelection,
303
- onRowOrWidgetMouseUp: enableSelection
304
- });
305
- },
306
- save: function save() {
307
- // Render in PHP
308
- return null;
309
- }
310
- });
311
-
312
- (function ($) {
313
- if (soPanelsBlockEditorAdmin.showAddButton) {
314
- $(function () {
315
- setTimeout(function () {
316
- var editorDispatch = wp.data.dispatch('core/editor');
317
- var editorSelect = wp.data.select('core/editor');
318
- var tmpl = $('#siteorigin-panels-add-layout-block-button').html();
319
- var $addButton = $(tmpl).insertAfter('.editor-writing-flow > div:first');
320
- $addButton.on('click', function () {
321
- var layoutBlock = wp.blocks.createBlock('siteorigin-panels/layout-block', {});
322
- var isEmpty = editorSelect.isEditedPostEmpty();
323
-
324
- if (isEmpty) {
325
- var blocks = editorSelect.getBlocks();
326
-
327
- if (blocks.length) {
328
- editorDispatch.replaceBlock(blocks[0].clientId, layoutBlock);
329
- } else {
330
- editorDispatch.insertBlock(layoutBlock);
331
- }
332
- } else {
333
- editorDispatch.insertBlock(layoutBlock);
334
- }
335
- });
336
-
337
- var hideButtonIfBlocks = function hideButtonIfBlocks() {
338
- var isEmpty = wp.data.select('core/editor').isEditedPostEmpty();
339
-
340
- if (isEmpty) {
341
- $addButton.show();
342
- } else {
343
- $addButton.hide();
344
- }
345
- };
346
-
347
- wp.data.subscribe(hideButtonIfBlocks);
348
- hideButtonIfBlocks();
349
- }, 100);
350
- });
351
- }
352
  })(jQuery);
1
+ "use strict";
2
+
3
+ function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
4
+
5
+ function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
6
+
7
+ function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
8
+
9
+ function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
10
+
11
+ function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
12
+
13
+ function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
14
+
15
+ function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
16
+
17
+ function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
18
+
19
+ function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
20
+
21
+ var _lodash = lodash,
22
+ isEqual = _lodash.isEqual,
23
+ debounce = _lodash.debounce,
24
+ isEmpty = _lodash.isEmpty,
25
+ isFunction = _lodash.isFunction;
26
+ var registerBlockType = wp.blocks.registerBlockType;
27
+ var _wp$element = wp.element,
28
+ Component = _wp$element.Component,
29
+ Fragment = _wp$element.Fragment,
30
+ RawHTML = _wp$element.RawHTML,
31
+ createRef = _wp$element.createRef;
32
+ var BlockControls = wp.editor.BlockControls;
33
+ var _wp$components = wp.components,
34
+ Toolbar = _wp$components.Toolbar,
35
+ IconButton = _wp$components.IconButton,
36
+ Spinner = _wp$components.Spinner;
37
+ var __ = wp.i18n.__;
38
+ var _window = window,
39
+ soPanelsBlockEditorAdmin = _window.soPanelsBlockEditorAdmin;
40
+
41
+ var SiteOriginPanelsLayoutBlock =
42
+ /*#__PURE__*/
43
+ function (_Component) {
44
+ _inherits(SiteOriginPanelsLayoutBlock, _Component);
45
+
46
+ function SiteOriginPanelsLayoutBlock(props) {
47
+ var _this;
48
+
49
+ _classCallCheck(this, SiteOriginPanelsLayoutBlock);
50
+
51
+ _this = _possibleConstructorReturn(this, _getPrototypeOf(SiteOriginPanelsLayoutBlock).call(this, props));
52
+ var editMode = soPanelsBlockEditorAdmin.defaultMode === 'edit' || isEmpty(props.panelsData);
53
+ _this.state = {
54
+ editing: editMode,
55
+ loadingPreview: !editMode,
56
+ previewHtml: ''
57
+ };
58
+ _this.panelsContainer = createRef();
59
+ _this.previewContainer = createRef();
60
+ _this.panelsInitialized = false;
61
+ _this.previewInitialized = false;
62
+ return _this;
63
+ }
64
+
65
+ _createClass(SiteOriginPanelsLayoutBlock, [{
66
+ key: "componentDidMount",
67
+ value: function componentDidMount() {
68
+ this.isStillMounted = true;
69
+
70
+ if (this.state.editing) {
71
+ this.setupPanels();
72
+ } else if (!this.state.editing && !this.previewInitialized) {
73
+ this.fetchPreview(this.props);
74
+ this.fetchPreview = debounce(this.fetchPreview, 500);
75
+ }
76
+ }
77
+ }, {
78
+ key: "componentWillUnmount",
79
+ value: function componentWillUnmount() {
80
+ this.isStillMounted = false;
81
+
82
+ if (this.builderView) {
83
+ this.builderView.off('content_change');
84
+ }
85
+ }
86
+ }, {
87
+ key: "componentDidUpdate",
88
+ value: function componentDidUpdate(prevProps) {
89
+ // let propsChanged = !isEqual( prevProps.panelsData, this.props.panelsData );
90
+ if (this.state.editing && !this.panelsInitialized) {
91
+ this.setupPanels();
92
+ } else if (this.state.loadingPreview) {
93
+ this.fetchPreview(this.props);
94
+ } else if (!this.previewInitialized && this.previewContainer.current) {
95
+ $(document).trigger('panels_setup_preview');
96
+ this.previewInitialized = true;
97
+ }
98
+ }
99
+ }, {
100
+ key: "setupPanels",
101
+ value: function setupPanels() {
102
+ var _this2 = this;
103
+
104
+ var $panelsContainer = jQuery(this.panelsContainer.current);
105
+ var config = {
106
+ editorType: 'standalone'
107
+ };
108
+ var builderModel = new panels.model.builder();
109
+ this.builderView = new panels.view.builder({
110
+ model: builderModel,
111
+ config: config
112
+ }); // Make sure panelsData is defined and clone so that we don't alter the underlying attribute.
113
+
114
+ var panelsData = JSON.parse(JSON.stringify($.extend({}, this.props.panelsData))); // Disable block selection while dragging rows or widgets.
115
+
116
+ var rowOrWidgetMouseDown = function rowOrWidgetMouseDown() {
117
+ if (isFunction(_this2.props.onRowOrWidgetMouseDown)) {
118
+ _this2.props.onRowOrWidgetMouseDown();
119
+ }
120
+
121
+ var rowOrWidgetMouseUp = function rowOrWidgetMouseUp() {
122
+ $(document).off('mouseup', rowOrWidgetMouseUp);
123
+
124
+ if (isFunction(_this2.props.onRowOrWidgetMouseUp)) {
125
+ _this2.props.onRowOrWidgetMouseUp();
126
+ }
127
+ };
128
+
129
+ $(document).on('mouseup', rowOrWidgetMouseUp);
130
+ };
131
+
132
+ this.builderView.on('row_added', function () {
133
+ _this2.builderView.$('.so-row-move').off('mousedown', rowOrWidgetMouseDown);
134
+
135
+ _this2.builderView.$('.so-row-move').on('mousedown', rowOrWidgetMouseDown);
136
+
137
+ _this2.builderView.$('.so-widget').off('mousedown', rowOrWidgetMouseDown);
138
+
139
+ _this2.builderView.$('.so-widget').on('mousedown', rowOrWidgetMouseDown);
140
+ });
141
+ this.builderView.on('widget_added', function () {
142
+ _this2.builderView.$('.so-widget').off('mousedown', rowOrWidgetMouseDown);
143
+
144
+ _this2.builderView.$('.so-widget').on('mousedown', rowOrWidgetMouseDown);
145
+ });
146
+ this.builderView.render().attach({
147
+ container: $panelsContainer
148
+ }).setData(panelsData);
149
+ this.builderView.trigger('builder_resize');
150
+ this.builderView.on('content_change', function () {
151
+ var newPanelsData = _this2.builderView.getData();
152
+
153
+ _this2.panelsDataChanged = !isEqual(panelsData, newPanelsData);
154
+
155
+ if (_this2.panelsDataChanged) {
156
+ if (_this2.props.onContentChange && isFunction(_this2.props.onContentChange)) {
157
+ _this2.props.onContentChange(newPanelsData);
158
+ }
159
+
160
+ _this2.setState({
161
+ loadingPreview: true,
162
+ previewHtml: ''
163
+ });
164
+ }
165
+ });
166
+ $(document).trigger('panels_setup', this.builderView);
167
+ this.panelsInitialized = true;
168
+ }
169
+ }, {
170
+ key: "fetchPreview",
171
+ value: function fetchPreview(props) {
172
+ var _this3 = this;
173
+
174
+ if (!this.isStillMounted) {
175
+ return;
176
+ }
177
+
178
+ this.previewInitialized = false;
179
+ var fetchRequest = this.currentFetchRequest = $.post({
180
+ url: soPanelsBlockEditorAdmin.previewUrl,
181
+ data: {
182
+ action: 'so_panels_layout_block_preview',
183
+ panelsData: JSON.stringify(props.panelsData)
184
+ }
185
+ }).then(function (preview) {
186
+ if (_this3.isStillMounted && fetchRequest === _this3.currentFetchRequest && preview) {
187
+ _this3.setState({
188
+ previewHtml: preview,
189
+ loadingPreview: false
190
+ });
191
+ }
192
+ });
193
+ return fetchRequest;
194
+ }
195
+ }, {
196
+ key: "render",
197
+ value: function render() {
198
+ var _this4 = this;
199
+
200
+ var panelsData = this.props.panelsData;
201
+
202
+ var switchToEditing = function switchToEditing() {
203
+ _this4.panelsInitialized = false;
204
+
205
+ _this4.setState({
206
+ editing: true
207
+ });
208
+ };
209
+
210
+ var switchToPreview = function switchToPreview() {
211
+ if (panelsData) {
212
+ _this4.setState({
213
+ editing: false
214
+ });
215
+ }
216
+ };
217
+
218
+ if (this.state.editing) {
219
+ return React.createElement(Fragment, null, React.createElement(BlockControls, null, React.createElement(Toolbar, null, React.createElement(IconButton, {
220
+ icon: "visibility",
221
+ className: "components-icon-button components-toolbar__control",
222
+ label: __('Preview layout.', 'siteorigin-panels'),
223
+ onClick: switchToPreview
224
+ }))), React.createElement("div", {
225
+ key: "layout-block",
226
+ className: "siteorigin-panels-layout-block-container",
227
+ ref: this.panelsContainer
228
+ }));
229
+ } else {
230
+ var loadingPreview = this.state.loadingPreview;
231
+ return React.createElement(Fragment, null, React.createElement(BlockControls, null, React.createElement(Toolbar, null, React.createElement(IconButton, {
232
+ icon: "edit",
233
+ className: "components-icon-button components-toolbar__control",
234
+ label: __('Edit layout.', 'siteorigin-panels'),
235
+ onClick: switchToEditing
236
+ }))), React.createElement("div", {
237
+ key: "preview",
238
+ className: "so-panels-block-layout-preview-container"
239
+ }, loadingPreview ? React.createElement("div", {
240
+ className: "so-panels-spinner-container"
241
+ }, React.createElement("span", null, React.createElement(Spinner, null))) : React.createElement("div", {
242
+ className: "so-panels-raw-html-container",
243
+ ref: this.previewContainer
244
+ }, React.createElement(RawHTML, null, this.state.previewHtml))));
245
+ }
246
+ }
247
+ }]);
248
+
249
+ return SiteOriginPanelsLayoutBlock;
250
+ }(Component);
251
+
252
+ registerBlockType('siteorigin-panels/layout-block', {
253
+ title: __('SiteOrigin Layout', 'siteorigin-panels'),
254
+ description: __("Build a layout using SiteOrigin's Page Builder.", 'siteorigin-panels'),
255
+ icon: function icon() {
256
+ return React.createElement("span", {
257
+ className: "siteorigin-panels-block-icon"
258
+ });
259
+ },
260
+ category: 'layout',
261
+ keywords: ['page builder', 'column,grid', 'panel'],
262
+ supports: {
263
+ html: false
264
+ },
265
+ attributes: {
266
+ panelsData: {
267
+ type: 'object'
268
+ }
269
+ },
270
+ edit: function edit(_ref) {
271
+ var attributes = _ref.attributes,
272
+ setAttributes = _ref.setAttributes,
273
+ toggleSelection = _ref.toggleSelection;
274
+
275
+ var onLayoutBlockContentChange = function onLayoutBlockContentChange(newPanelsData) {
276
+ if (!_.isEmpty(newPanelsData.widgets)) {
277
+ // Send panelsData to server for sanitization.
278
+ $.post(soPanelsBlockEditorAdmin.sanitizeUrl, {
279
+ action: 'so_panels_layout_block_sanitize',
280
+ panelsData: JSON.stringify(newPanelsData)
281
+ }, function (sanitizedPanelsData) {
282
+ if (sanitizedPanelsData !== '') {
283
+ setAttributes({
284
+ panelsData: sanitizedPanelsData
285
+ });
286
+ }
287
+ });
288
+ }
289
+ };
290
+
291
+ var disableSelection = function disableSelection() {
292
+ toggleSelection(false);
293
+ };
294
+
295
+ var enableSelection = function enableSelection() {
296
+ toggleSelection(true);
297
+ };
298
+
299
+ return React.createElement(SiteOriginPanelsLayoutBlock, {
300
+ panelsData: attributes.panelsData,
301
+ onContentChange: onLayoutBlockContentChange,
302
+ onRowOrWidgetMouseDown: disableSelection,
303
+ onRowOrWidgetMouseUp: enableSelection
304
+ });
305
+ },
306
+ save: function save() {
307
+ // Render in PHP
308
+ return null;
309
+ }
310
+ });
311
+
312
+ (function ($) {
313
+ if (soPanelsBlockEditorAdmin.showAddButton) {
314
+ $(function () {
315
+ setTimeout(function () {
316
+ var editorDispatch = wp.data.dispatch('core/editor');
317
+ var editorSelect = wp.data.select('core/editor');
318
+ var tmpl = $('#siteorigin-panels-add-layout-block-button').html();
319
+ var $addButton = $(tmpl).insertAfter('.editor-writing-flow > div:first');
320
+ $addButton.on('click', function () {
321
+ var layoutBlock = wp.blocks.createBlock('siteorigin-panels/layout-block', {});
322
+ var isEmpty = editorSelect.isEditedPostEmpty();
323
+
324
+ if (isEmpty) {
325
+ var blocks = editorSelect.getBlocks();
326
+
327
+ if (blocks.length) {
328
+ editorDispatch.replaceBlock(blocks[0].clientId, layoutBlock);
329
+ } else {
330
+ editorDispatch.insertBlock(layoutBlock);
331
+ }
332
+ } else {
333
+ editorDispatch.insertBlock(layoutBlock);
334
+ }
335
+ });
336
+
337
+ var hideButtonIfBlocks = function hideButtonIfBlocks() {
338
+ var isEmpty = wp.data.select('core/editor').isEditedPostEmpty();
339
+
340
+ if (isEmpty) {
341
+ $addButton.show();
342
+ } else {
343
+ $addButton.hide();
344
+ }
345
+ };
346
+
347
+ wp.data.subscribe(hideButtonIfBlocks);
348
+ hideButtonIfBlocks();
349
+ }, 100);
350
+ });
351
+ }
352
  })(jQuery);
inc/admin.php CHANGED
@@ -78,7 +78,9 @@ class SiteOrigin_Panels_Admin {
78
  add_filter( 'gutenberg_can_edit_post_type', array( $this, 'show_classic_editor_for_panels' ), 10, 2 );
79
  add_filter( 'use_block_editor_for_post_type', array( $this, 'show_classic_editor_for_panels' ), 10, 2 );
80
  add_action( 'admin_print_scripts-edit.php', array( $this, 'add_panels_add_new_button' ) );
81
- add_filter( 'display_post_states', array( $this, 'add_panels_post_state' ), 10, 2 );
 
 
82
  }
83
  }
84
 
78
  add_filter( 'gutenberg_can_edit_post_type', array( $this, 'show_classic_editor_for_panels' ), 10, 2 );
79
  add_filter( 'use_block_editor_for_post_type', array( $this, 'show_classic_editor_for_panels' ), 10, 2 );
80
  add_action( 'admin_print_scripts-edit.php', array( $this, 'add_panels_add_new_button' ) );
81
+ if( siteorigin_panels_setting( 'admin-post-state' ) ) {
82
+ add_filter( 'display_post_states', array( $this, 'add_panels_post_state' ), 10, 2 );
83
+ }
84
  }
85
  }
86
 
inc/css-builder.php CHANGED
@@ -109,7 +109,7 @@ class SiteOrigin_Panels_Css_Builder {
109
  $selector[] = '#pl-' . $li;
110
  }
111
  $selector[] = is_string( $ri ) ? ( '#' . $ri ) : '#pg-' . $li . '-' . $ri;
112
- $selector[] = '.panel-grid-cell';
113
  } elseif ( $ri !== false && $ci !== false ) {
114
  // This applies to a specific cell
115
  if ( $specify_layout ) {
109
  $selector[] = '#pl-' . $li;
110
  }
111
  $selector[] = is_string( $ri ) ? ( '#' . $ri ) : '#pg-' . $li . '-' . $ri;
112
+ $selector[] = '> .panel-grid-cell';
113
  } elseif ( $ri !== false && $ci !== false ) {
114
  // This applies to a specific cell
115
  if ( $specify_layout ) {
inc/live-editor.php CHANGED
@@ -9,7 +9,6 @@ class SiteOrigin_Panels_Live_Editor {
9
 
10
  function __construct() {
11
  add_action( 'template_redirect', array( $this, 'xss_headers' ) );
12
- add_action( 'get_post_metadata', array( $this, 'post_metadata' ), 10, 3 );
13
  add_action( 'wp_enqueue_scripts', array( $this, 'frontend_scripts' ) );
14
 
15
  // Don't display the admin bar when in live editor mode
@@ -33,40 +32,6 @@ class SiteOrigin_Panels_Live_Editor {
33
  }
34
  }
35
 
36
- /**
37
- * Edit the page builder data when we're viewing the live editor version
38
- *
39
- * @param $value
40
- * @param $post_id
41
- * @param $meta_key
42
- *
43
- * @return array
44
- */
45
- function post_metadata( $value, $post_id, $meta_key ) {
46
- if (
47
- $meta_key == 'panels_data' &&
48
- current_user_can( 'edit_post', $post_id ) &&
49
- ! empty( $_POST['live_editor_panels_data'] ) &&
50
- $_POST['live_editor_post_ID'] == $post_id
51
- ) {
52
- $data = json_decode( wp_unslash( $_POST['live_editor_panels_data'] ), true );
53
-
54
- if (
55
- ! empty( $data['widgets'] ) && (
56
- ! class_exists( 'SiteOrigin_Widget_Field_Class_Loader' ) ||
57
- method_exists( 'SiteOrigin_Widget_Field_Class_Loader', 'extend' )
58
- )
59
- ) {
60
- $data['widgets'] = SiteOrigin_Panels_Admin::single()->process_raw_widgets( $data['widgets'], false, false );
61
- }
62
-
63
- $value = array( $data );
64
- }
65
-
66
- return $value;
67
- }
68
-
69
-
70
  /**
71
  * Load the frontend scripts for the live editor
72
  */
9
 
10
  function __construct() {
11
  add_action( 'template_redirect', array( $this, 'xss_headers' ) );
 
12
  add_action( 'wp_enqueue_scripts', array( $this, 'frontend_scripts' ) );
13
 
14
  // Don't display the admin bar when in live editor mode
32
  }
33
  }
34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  /**
36
  * Load the frontend scripts for the live editor
37
  */
inc/renderer.php CHANGED
@@ -210,11 +210,12 @@ class SiteOrigin_Panels_Renderer {
210
  $css->add_cell_css( $post_id, $ri, false, '', array(
211
  'margin-right' => 0,
212
  ), $panels_mobile_width );
 
 
 
 
213
  }
214
 
215
- $css->add_cell_css( $post_id, $ri, false, '', array(
216
- 'width' => '100%',
217
- ), $panels_mobile_width );
218
 
219
  foreach ( $row['cells'] as $ci => $cell ) {
220
  if ( ( $collapse_order == 'left-top' && $ci != $cell_count - 1 ) || ( $collapse_order == 'right-top' && $ci !== 0 ) ) {
@@ -602,7 +603,19 @@ class SiteOrigin_Panels_Renderer {
602
  * @return array
603
  */
604
  private function get_panels_data_for_post( $post_id ) {
605
- if ( strpos( $post_id, 'prebuilt:' ) === 0 ) {
 
 
 
 
 
 
 
 
 
 
 
 
606
  list( $null, $prebuilt_id ) = explode( ':', $post_id, 2 );
607
  $layouts = apply_filters( 'siteorigin_panels_prebuilt_layouts', array() );
608
  $panels_data = ! empty( $layouts[ $prebuilt_id ] ) ? $layouts[ $prebuilt_id ] : array();
210
  $css->add_cell_css( $post_id, $ri, false, '', array(
211
  'margin-right' => 0,
212
  ), $panels_mobile_width );
213
+
214
+ $css->add_cell_css( $post_id, $ri, false, '', array(
215
+ 'width' => '100%',
216
+ ), $panels_mobile_width );
217
  }
218
 
 
 
 
219
 
220
  foreach ( $row['cells'] as $ci => $cell ) {
221
  if ( ( $collapse_order == 'left-top' && $ci != $cell_count - 1 ) || ( $collapse_order == 'right-top' && $ci !== 0 ) ) {
603
  * @return array
604
  */
605
  private function get_panels_data_for_post( $post_id ) {
606
+ if ( SiteOrigin_Panels::is_live_editor() ) {
607
+ if (
608
+ current_user_can( 'edit_post', $post_id ) &&
609
+ ! empty( $_POST['live_editor_panels_data'] ) &&
610
+ $_POST['live_editor_post_ID'] == $post_id
611
+ ) {
612
+ $panels_data = json_decode( wp_unslash( $_POST['live_editor_panels_data'] ), true );
613
+
614
+ if ( ! empty( $panels_data['widgets'] ) ) {
615
+ $panels_data['widgets'] = SiteOrigin_Panels_Admin::single()->process_raw_widgets( $panels_data['widgets'] );
616
+ }
617
+ }
618
+ } else if ( strpos( $post_id, 'prebuilt:' ) === 0 ) {
619
  list( $null, $prebuilt_id ) = explode( ':', $post_id, 2 );
620
  $layouts = apply_filters( 'siteorigin_panels_prebuilt_layouts', array() );
621
  $panels_data = ! empty( $layouts[ $prebuilt_id ] ) ? $layouts[ $prebuilt_id ] : array();
inc/settings.php CHANGED
@@ -119,6 +119,7 @@ class SiteOrigin_Panels_Settings {
119
  // The general fields
120
  $defaults['post-types'] = array( 'page', 'post' );
121
  $defaults['live-editor-quick-link'] = true;
 
122
  $defaults['admin-widget-count'] = false;
123
  $defaults['parallax-motion'] = '';
124
  $defaults['sidebars-emulator'] = true;
@@ -261,6 +262,16 @@ class SiteOrigin_Panels_Settings {
261
  'description' => __( 'Display a Live Editor button in the admin bar.', 'siteorigin-panels' ),
262
  );
263
 
 
 
 
 
 
 
 
 
 
 
264
  $fields['general']['fields']['admin-widget-count'] = array(
265
  'type' => 'checkbox',
266
  'label' => __( 'Display Widget Count', 'siteorigin-panels' ),
119
  // The general fields
120
  $defaults['post-types'] = array( 'page', 'post' );
121
  $defaults['live-editor-quick-link'] = true;
122
+ $defaults['admin-post-state'] = true;
123
  $defaults['admin-widget-count'] = false;
124
  $defaults['parallax-motion'] = '';
125
  $defaults['sidebars-emulator'] = true;
262
  'description' => __( 'Display a Live Editor button in the admin bar.', 'siteorigin-panels' ),
263
  );
264
 
265
+ $fields['general']['fields']['admin-post-state'] = array(
266
+ 'type' => 'checkbox',
267
+ 'label' => __( 'Display Post State', 'siteorigin-panels' ),
268
+ 'description' => sprintf(
269
+ __( "Display a %sSiteOrigin Page Builder%s post state in the admin lists of posts/pages to indicate Page Builder is active.", 'siteorigin-panels' ),
270
+ '<strong>',
271
+ '</strong>'
272
+ ),
273
+ );
274
+
275
  $fields['general']['fields']['admin-widget-count'] = array(
276
  'type' => 'checkbox',
277
  'label' => __( 'Display Widget Count', 'siteorigin-panels' ),
inc/widgets/layout.php CHANGED
@@ -58,14 +58,18 @@ class SiteOrigin_Panels_Widgets_Layout extends WP_Widget {
58
  $new['panels_data'] = json_decode( $new['panels_data'], true );
59
  }
60
 
61
- if ( ! empty( $new['panels_data'] ) && ! empty( $new['panels_data']['widgets'] ) ) {
62
- $new['panels_data']['widgets'] = SiteOrigin_Panels_Admin::single()->process_raw_widgets(
63
- $new['panels_data']['widgets'],
64
- ! empty( $old['panels_data']['widgets'] ) ? $old['panels_data']['widgets'] : false
65
- );
66
- foreach( $new['panels_data']['widgets'] as & $widget ) {
67
- $widget['panels_info']['class'] = str_replace( '\\', '&#92;', $widget['panels_info']['class'] );
 
 
68
  }
 
 
69
  }
70
 
71
  return $new;
58
  $new['panels_data'] = json_decode( $new['panels_data'], true );
59
  }
60
 
61
+ if ( ! empty( $new['panels_data'] ) ) {
62
+ if ( ! empty( $new['panels_data']['widgets'] ) ) {
63
+ $new['panels_data']['widgets'] = SiteOrigin_Panels_Admin::single()->process_raw_widgets(
64
+ $new['panels_data']['widgets'],
65
+ ! empty( $old['panels_data']['widgets'] ) ? $old['panels_data']['widgets'] : false
66
+ );
67
+ foreach( $new['panels_data']['widgets'] as & $widget ) {
68
+ $widget['panels_info']['class'] = str_replace( '\\', '&#92;', $widget['panels_info']['class'] );
69
+ }
70
  }
71
+
72
+ $new['panels_data'] = SiteOrigin_Panels_Styles_Admin::single()->sanitize_all( $new['panels_data'] );
73
  }
74
 
75
  return $new;
js/siteorigin-panels-2101.js DELETED
@@ -1,7438 +0,0 @@
1
- (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i<t.length;i++)o(t[i]);return o}return r})()({1:[function(require,module,exports){
2
- var panels = window.panels;
3
-
4
- module.exports = Backbone.Collection.extend( {
5
- model: panels.model.cell,
6
-
7
- initialize: function () {
8
- },
9
-
10
- /**
11
- * Get the total weight for the cells in this collection.
12
- * @returns {number}
13
- */
14
- totalWeight: function () {
15
- var totalWeight = 0;
16
- this.each( function ( cell ) {
17
- totalWeight += cell.get( 'weight' );
18
- } );
19
-
20
- return totalWeight;
21
- },
22
-
23
- } );
24
-
25
- },{}],2:[function(require,module,exports){
26
- var panels = window.panels;
27
-
28
- module.exports = Backbone.Collection.extend( {
29
- model: panels.model.historyEntry,
30
-
31
- /**
32
- * The builder model
33
- */
34
- builder: null,
35
-
36
- /**
37
- * The maximum number of items in the history
38
- */
39
- maxSize: 12,
40
-
41
- initialize: function () {
42
- this.on( 'add', this.onAddEntry, this );
43
- },
44
-
45
- /**
46
- * Add an entry to the collection.
47
- *
48
- * @param text The text that defines the action taken to get to this
49
- * @param data
50
- */
51
- addEntry: function ( text, data ) {
52
-
53
- if ( _.isEmpty( data ) ) {
54
- data = this.builder.getPanelsData();
55
- }
56
-
57
- var entry = new panels.model.historyEntry( {
58
- text: text,
59
- data: JSON.stringify( data ),
60
- time: parseInt( new Date().getTime() / 1000 ),
61
- collection: this
62
- } );
63
-
64
- this.add( entry );
65
- },
66
-
67
- /**
68
- * Resize the collection so it's not bigger than this.maxSize
69
- */
70
- onAddEntry: function ( entry ) {
71
-
72
- if ( this.models.length > 1 ) {
73
- var lastEntry = this.at( this.models.length - 2 );
74
-
75
- if (
76
- (
77
- entry.get( 'text' ) === lastEntry.get( 'text' ) && entry.get( 'time' ) - lastEntry.get( 'time' ) < 15
78
- ) ||
79
- (
80
- entry.get( 'data' ) === lastEntry.get( 'data' )
81
- )
82
- ) {
83
- // If both entries have the same text and are within 20 seconds of each other, or have the same data, then remove most recent
84
- this.remove( entry );
85
- lastEntry.set( 'count', lastEntry.get( 'count' ) + 1 );
86
- }
87
- }
88
-
89
- // Make sure that there are not to many entries in this collection
90
- while ( this.models.length > this.maxSize ) {
91
- this.shift();
92
- }
93
- }
94
- } );
95
-
96
- },{}],3:[function(require,module,exports){
97
- var panels = window.panels;
98
-
99
- module.exports = Backbone.Collection.extend( {
100
- model: panels.model.row,
101
-
102
- /**
103
- * Destroy all the rows in this collection
104
- */
105
- empty: function () {
106
- var model;
107
- do {
108
- model = this.collection.first();
109
- if ( ! model ) {
110
- break;
111
- }
112
-
113
- model.destroy();
114
- } while ( true );
115
- }
116
-
117
- } );
118
-
119
- },{}],4:[function(require,module,exports){
120
- var panels = window.panels;
121
-
122
- module.exports = Backbone.Collection.extend( {
123
- model: panels.model.widget,
124
-
125
- initialize: function () {
126
-
127
- }
128
-
129
- } );
130
-
131
- },{}],5:[function(require,module,exports){
132
- var panels = window.panels, $ = jQuery;
133
-
134
- module.exports = panels.view.dialog.extend( {
135
- dialogClass: 'so-panels-dialog-add-builder',
136
-
137
- render: function () {
138
- // Render the dialog and attach it to the builder interface
139
- this.renderDialog( this.parseDialogContent( $( '#siteorigin-panels-dialog-builder' ).html(), {} ) );
140
- this.$( '.so-content .siteorigin-panels-builder' ).append( this.builder.$el );
141
- },
142
-
143
- initializeDialog: function () {
144
- var thisView = this;
145
- this.once( 'open_dialog_complete', function () {
146
- thisView.builder.initSortable();
147
- } );
148
-
149
- this.on( 'open_dialog_complete', function () {
150
- thisView.builder.trigger( 'builder_resize' );
151
- } );
152
- }
153
- } );
154
-
155
- },{}],6:[function(require,module,exports){
156
- var panels = window.panels, $ = jQuery;
157
-
158
- module.exports = panels.view.dialog.extend( {
159
-
160
- historyEntryTemplate: _.template( panels.helpers.utils.processTemplate( $( '#siteorigin-panels-dialog-history-entry' ).html() ) ),
161
-
162
- entries: {},
163
- currentEntry: null,
164
- revertEntry: null,
165
- selectedEntry: null,
166
-
167
- previewScrollTop: null,
168
-
169
- dialogClass: 'so-panels-dialog-history',
170
- dialogIcon: 'history',
171
-
172
- events: {
173
- 'click .so-close': 'closeDialog',
174
- 'click .so-restore': 'restoreSelectedEntry'
175
- },
176
-
177
- initializeDialog: function () {
178
- this.entries = new panels.collection.historyEntries();
179
-
180
- this.on( 'open_dialog', this.setCurrentEntry, this );
181
- this.on( 'open_dialog', this.renderHistoryEntries, this );
182
- },
183
-
184
- render: function () {
185
- var thisView = this;
186
-
187
- // Render the dialog and attach it to the builder interface
188
- this.renderDialog( this.parseDialogContent( $( '#siteorigin-panels-dialog-history' ).html(), {} ) );
189
-
190
- this.$( 'iframe.siteorigin-panels-history-iframe' ).load( function () {
191
- var $$ = $( this );
192
- $$.show();
193
-
194
- $$.contents().scrollTop( thisView.previewScrollTop );
195
- } );
196
- },
197
-
198
- /**
199
- * Set the original entry. This should be set when creating the dialog.
200
- *
201
- * @param {panels.model.builder} builder
202
- */
203
- setRevertEntry: function ( builder ) {
204
- this.revertEntry = new panels.model.historyEntry( {
205
- data: JSON.stringify( builder.getPanelsData() ),
206
- time: parseInt( new Date().getTime() / 1000 )
207
- } );
208
- },
209
-
210
- /**
211
- * This is triggered when the dialog is opened.
212
- */
213
- setCurrentEntry: function () {
214
- this.currentEntry = new panels.model.historyEntry( {
215
- data: JSON.stringify( this.builder.model.getPanelsData() ),
216
- time: parseInt( new Date().getTime() / 1000 )
217
- } );
218
-
219
- this.selectedEntry = this.currentEntry;
220
- this.previewEntry( this.currentEntry );
221
- this.$( '.so-buttons .so-restore' ).addClass( 'disabled' );
222
- },
223
-
224
- /**
225
- * Render the history entries in the sidebar
226
- */
227
- renderHistoryEntries: function () {
228
- // Set up an interval that will display the time since every 10 seconds
229
- var thisView = this;
230
-
231
- var c = this.$( '.history-entries' ).empty();
232
-
233
- if ( this.currentEntry.get( 'data' ) !== this.revertEntry.get( 'data' ) || ! _.isEmpty( this.entries.models ) ) {
234
- $( this.historyEntryTemplate( {title: panelsOptions.loc.history.revert, count: 1} ) )
235
- .data( 'historyEntry', this.revertEntry )
236
- .prependTo( c );
237
- }
238
-
239
- // Now load all the entries in this.entries
240
- this.entries.each( function ( entry ) {
241
-
242
- var html = thisView.historyEntryTemplate( {
243
- title: panelsOptions.loc.history[entry.get( 'text' )],
244
- count: entry.get( 'count' )
245
- } );
246
-
247
- $( html )
248
- .data( 'historyEntry', entry )
249
- .prependTo( c );
250
- } );
251
-
252
-
253
- $( this.historyEntryTemplate( {title: panelsOptions.loc.history['current'], count: 1} ) )
254
- .data( 'historyEntry', this.currentEntry )
255
- .addClass( 'so-selected' )
256
- .prependTo( c );
257
-
258
- // Handle loading and selecting
259
- c.find( '.history-entry' ).click( function () {
260
- var $$ = jQuery( this );
261
- c.find( '.history-entry' ).not( $$ ).removeClass( 'so-selected' );
262
- $$.addClass( 'so-selected' );
263
-
264
- var entry = $$.data( 'historyEntry' );
265
-
266
- thisView.selectedEntry = entry;
267
-
268
- if ( thisView.selectedEntry.cid !== thisView.currentEntry.cid ) {
269
- thisView.$( '.so-buttons .so-restore' ).removeClass( 'disabled' );
270
- } else {
271
- thisView.$( '.so-buttons .so-restore' ).addClass( 'disabled' );
272
- }
273
-
274
- thisView.previewEntry( entry );
275
- } );
276
-
277
- this.updateEntryTimes();
278
- },
279
-
280
- /**
281
- * Preview an entry
282
- *
283
- * @param entry
284
- */
285
- previewEntry: function ( entry ) {
286
- var iframe = this.$( 'iframe.siteorigin-panels-history-iframe' );
287
- iframe.hide();
288
- this.previewScrollTop = iframe.contents().scrollTop();
289
-
290
- this.$( 'form.history-form input[name="live_editor_panels_data"]' ).val( entry.get( 'data' ) );
291
- this.$( 'form.history-form input[name="live_editor_post_ID"]' ).val( this.builder.config.postId );
292
- this.$( 'form.history-form' ).submit();
293
- },
294
-
295
- /**
296
- * Restore the current entry
297
- */
298
- restoreSelectedEntry: function () {
299
-
300
- if ( this.$( '.so-buttons .so-restore' ).hasClass( 'disabled' ) ) {
301
- return false;
302
- }
303
-
304
- if ( this.currentEntry.get( 'data' ) === this.selectedEntry.get( 'data' ) ) {
305
- this.closeDialog();
306
- return false;
307
- }
308
-
309
- // Add an entry for this restore event
310
- if ( this.selectedEntry.get( 'text' ) !== 'restore' ) {
311
- this.builder.addHistoryEntry( 'restore', this.builder.model.getPanelsData() );
312
- }
313
-
314
- this.builder.model.loadPanelsData( JSON.parse( this.selectedEntry.get( 'data' ) ) );
315
-
316
- this.closeDialog();
317
-
318
- return false;
319
- },
320
-
321
- /**
322
- * Update the entry times for the list of entries down the side
323
- */
324
- updateEntryTimes: function () {
325
- var thisView = this;
326
-
327
- this.$( '.history-entries .history-entry' ).each( function () {
328
- var $$ = jQuery( this );
329
-
330
- var time = $$.find( '.timesince' );
331
- var entry = $$.data( 'historyEntry' );
332
-
333
- time.html( thisView.timeSince( entry.get( 'time' ) ) );
334
- } );
335
- },
336
-
337
- /**
338
- * Gets the time since as a nice string.
339
- *
340
- * @param date
341
- */
342
- timeSince: function ( time ) {
343
- var diff = parseInt( new Date().getTime() / 1000 ) - time;
344
-
345
- var parts = [];
346
- var interval;
347
-
348
- // There are 3600 seconds in an hour
349
- if ( diff > 3600 ) {
350
- interval = Math.floor( diff / 3600 );
351
- if ( interval === 1 ) {
352
- parts.push( panelsOptions.loc.time.hour.replace( '%d', interval ) );
353
- } else {
354
- parts.push( panelsOptions.loc.time.hours.replace( '%d', interval ) );
355
- }
356
- diff -= interval * 3600;
357
- }
358
-
359
- // There are 60 seconds in a minute
360
- if ( diff > 60 ) {
361
- interval = Math.floor( diff / 60 );
362
- if ( interval === 1 ) {
363
- parts.push( panelsOptions.loc.time.minute.replace( '%d', interval ) );
364
- } else {
365
- parts.push( panelsOptions.loc.time.minutes.replace( '%d', interval ) );
366
- }
367
- diff -= interval * 60;
368
- }
369
-
370
- if ( diff > 0 ) {
371
- if ( diff === 1 ) {
372
- parts.push( panelsOptions.loc.time.second.replace( '%d', diff ) );
373
- } else {
374
- parts.push( panelsOptions.loc.time.seconds.replace( '%d', diff ) );
375
- }
376
- }
377
-
378
- // Return the amount of time ago
379
- return _.isEmpty( parts ) ? panelsOptions.loc.time.now : panelsOptions.loc.time.ago.replace( '%s', parts.slice( 0, 2 ).join( ', ' ) );
380
-
381
- }
382
-
383
- } );
384
-
385
- },{}],7:[function(require,module,exports){
386
- var panels = window.panels, $ = jQuery;
387
-
388
- module.exports = panels.view.dialog.extend( {
389
-
390
- directoryTemplate: _.template( panels.helpers.utils.processTemplate( $( '#siteorigin-panels-directory-items' ).html() ) ),
391
-
392
- builder: null,
393
- dialogClass: 'so-panels-dialog-prebuilt-layouts',
394
- dialogIcon: 'layouts',
395
-
396
- layoutCache: {},
397
- currentTab: false,
398
- directoryPage: 1,
399
-
400
- events: {
401
- 'click .so-close': 'closeDialog',
402
- 'click .so-sidebar-tabs li a': 'tabClickHandler',
403
- 'click .so-content .layout': 'layoutClickHandler',
404
- 'keyup .so-sidebar-search': 'searchHandler',
405
-
406
- // The directory items
407
- 'click .so-screenshot, .so-title': 'directoryItemClickHandler'
408
- },
409
-
410
- /**
411
- * Initialize the prebuilt dialog.
412
- */
413
- initializeDialog: function () {
414
- var thisView = this;
415
-
416
- this.on( 'open_dialog', function () {
417
- thisView.$( '.so-sidebar-tabs li a' ).first().click();
418
- thisView.$( '.so-status' ).removeClass( 'so-panels-loading' );
419
- } );
420
-
421
- this.on( 'button_click', this.toolbarButtonClick, this );
422
- },
423
-
424
- /**
425
- * Render the prebuilt layouts dialog
426
- */
427
- render: function () {
428
- this.renderDialog( this.parseDialogContent( $( '#siteorigin-panels-dialog-prebuilt' ).html(), {} ) );
429
-
430
- this.initToolbar();
431
- },
432
-
433
- /**
434
- *
435
- * @param e
436
- * @return {boolean}
437
- */
438
- tabClickHandler: function ( e ) {
439
- e.preventDefault();
440
- // Reset selected item state when changing tabs
441
- this.selectedLayoutItem = null;
442
- this.uploadedLayout = null;
443
- this.updateButtonState( false );
444
-
445
- this.$( '.so-sidebar-tabs li' ).removeClass( 'tab-active' );
446
-
447
- var $$ = $( e.target );
448
- var tab = $$.attr( 'href' ).split( '#' )[1];
449
- $$.parent().addClass( 'tab-active' );
450
-
451
- var thisView = this;
452
-
453
- // Empty everything
454
- this.$( '.so-content' ).empty();
455
-
456
- thisView.currentTab = tab;
457
- if ( tab == 'import' ) {
458
- this.displayImportExport();
459
- } else {
460
- this.displayLayoutDirectory( '', 1, tab );
461
- }
462
-
463
- thisView.$( '.so-sidebar-search' ).val( '' );
464
- },
465
-
466
- /**
467
- * Display and setup the import/export form
468
- */
469
- displayImportExport: function () {
470
- var c = this.$( '.so-content' ).empty().removeClass( 'so-panels-loading' );
471
- c.html( $( '#siteorigin-panels-dialog-prebuilt-importexport' ).html() );
472
-
473
- var thisView = this;
474
- var uploadUi = thisView.$( '.import-upload-ui' );
475
-
476
- // Create the uploader
477
- var uploader = new plupload.Uploader( {
478
- runtimes: 'html5,silverlight,flash,html4',
479
-
480
- browse_button: uploadUi.find( '.file-browse-button' ).get( 0 ),
481
- container: uploadUi.get( 0 ),
482
- drop_element: uploadUi.find( '.drag-upload-area' ).get( 0 ),
483
-
484
- file_data_name: 'panels_import_data',
485
- multiple_queues: false,
486
- max_file_size: panelsOptions.plupload.max_file_size,
487
- url: panelsOptions.plupload.url,
488
- flash_swf_url: panelsOptions.plupload.flash_swf_url,
489
- silverlight_xap_url: panelsOptions.plupload.silverlight_xap_url,
490
- filters: [
491
- {title: panelsOptions.plupload.filter_title, extensions: 'json'}
492
- ],
493
-
494
- multipart_params: {
495
- action: 'so_panels_import_layout'
496
- },
497
-
498
- init: {
499
- PostInit: function ( uploader ) {
500
- if ( uploader.features.dragdrop ) {
501
- uploadUi.addClass( 'has-drag-drop' );
502
- }
503
- uploadUi.find( '.progress-precent' ).css( 'width', '0%' );
504
- },
505
- FilesAdded: function ( uploader ) {
506
- uploadUi.find( '.file-browse-button' ).blur();
507
- uploadUi.find( '.drag-upload-area' ).removeClass( 'file-dragover' );
508
- uploadUi.find( '.progress-bar' ).fadeIn( 'fast' );
509
- thisView.$( '.js-so-selected-file' ).text( panelsOptions.loc.prebuilt_loading );
510
- uploader.start();
511
- },
512
- UploadProgress: function ( uploader, file ) {
513
- uploadUi.find( '.progress-precent' ).css( 'width', file.percent + '%' );
514
- },
515
- FileUploaded: function ( uploader, file, response ) {
516
- var layout = JSON.parse( response.response );
517
- if ( ! _.isUndefined( layout.widgets ) ) {
518
-
519
- thisView.uploadedLayout = layout;
520
- uploadUi.find( '.progress-bar' ).hide();
521
- thisView.$( '.js-so-selected-file' ).text(
522
- panelsOptions.loc.ready_to_insert.replace( '%s', file.name )
523
- );
524
- thisView.updateButtonState( true );
525
- } else {
526
- alert( panelsOptions.plupload.error_message );
527
- }
528
- },
529
- Error: function () {
530
- alert( panelsOptions.plupload.error_message );
531
- }
532
- }
533
- } );
534
- uploader.init();
535
-
536
- if ( /Edge\/\d./i.test(navigator.userAgent) ){
537
- // A very dirty fix for a Microsoft Edge issue.
538
- // TODO find a more elegant fix if Edge gains market share
539
- setTimeout( function(){
540
- uploader.refresh();
541
- }, 250 );
542
- }
543
-
544
- // This is
545
- uploadUi.find( '.drag-upload-area' )
546
- .on( 'dragover', function () {
547
- $( this ).addClass( 'file-dragover' );
548
- } )
549
- .on( 'dragleave', function () {
550
- $( this ).removeClass( 'file-dragover' );
551
- } );
552
-
553
- // Handle exporting the file
554
- c.find( '.so-export' ).submit( function ( e ) {
555
- var $$ = $( this );
556
- var panelsData = thisView.builder.model.getPanelsData();
557
- var postName = $('input[name="post_title"]').val();
558
- if ( ! postName ) {
559
- postName = $('input[name="post_ID"]').val();
560
- }
561
- panelsData.name = postName;
562
- $$.find( 'input[name="panels_export_data"]' ).val( JSON.stringify( panelsData ) );
563
- } );
564
-
565
- },
566
-
567
- /**
568
- * Display the layout directory tab.
569
- *
570
- * @param query
571
- */
572
- displayLayoutDirectory: function ( search, page, type ) {
573
- var thisView = this;
574
- var c = this.$( '.so-content' ).empty().addClass( 'so-panels-loading' );
575
-
576
- if ( search === undefined ) {
577
- search = '';
578
- }
579
- if ( page === undefined ) {
580
- page = 1;
581
- }
582
- if ( type === undefined ) {
583
- type = 'directory-siteorigin';
584
- }
585
-
586
- if ( type.match('^directory-') && ! panelsOptions.directory_enabled ) {
587
- // Display the button to enable the prebuilt layout
588
- c.removeClass( 'so-panels-loading' ).html( $( '#siteorigin-panels-directory-enable' ).html() );
589
- c.find( '.so-panels-enable-directory' ).click( function ( e ) {
590
- e.preventDefault();
591
- // Sent the query to enable the directory, then enable the directory
592
- $.get(
593
- panelsOptions.ajaxurl,
594
- {action: 'so_panels_directory_enable'},
595
- function () {
596
-
597
- }
598
- );
599
-
600
- // Enable the layout directory
601
- panelsOptions.directory_enabled = true;
602
- c.addClass( 'so-panels-loading' );
603
- thisView.displayLayoutDirectory( search, page, type );
604
- } );
605
- return;
606
- }
607
-
608
- // Get all the items for the current query
609
- $.get(
610
- panelsOptions.ajaxurl,
611
- {
612
- action: 'so_panels_layouts_query',
613
- search: search,
614
- page: page,
615
- type: type,
616
- },
617
- function ( data ) {
618
- // Skip this if we're no longer viewing the layout directory
619
- if ( thisView.currentTab !== type ) {
620
- return;
621
- }
622
-
623
- // Add the directory items
624
- c.removeClass( 'so-panels-loading' ).html( thisView.directoryTemplate( data ) );
625
-
626
- // Lets setup the next and previous buttons
627
- var prev = c.find( '.so-previous' ), next = c.find( '.so-next' );
628
-
629
- if ( page <= 1 ) {
630
- prev.addClass( 'button-disabled' );
631
- } else {
632
- prev.click( function ( e ) {
633
- e.preventDefault();
634
- thisView.displayLayoutDirectory( search, page - 1, thisView.currentTab );
635
- } );
636
- }
637
-
638
- if ( page === data.max_num_pages || data.max_num_pages === 0 ) {
639
- next.addClass( 'button-disabled' );
640
- } else {
641
- next.click( function ( e ) {
642
- e.preventDefault();
643
- thisView.displayLayoutDirectory( search, page + 1, thisView.currentTab );
644
- } );
645
- }
646
-
647
- // Handle nice preloading of the screenshots
648
- c.find( '.so-screenshot' ).each( function () {
649
- var $$ = $( this ), $a = $$.find( '.so-screenshot-wrapper' );
650
- $a.css( 'height', ( $a.width() / 4 * 3 ) + 'px' ).addClass( 'so-loading' );
651
-
652
- if ( $$.data( 'src' ) !== '' ) {
653
- // Set the initial height
654
- var $img = $( '<img/>' ).attr( 'src', $$.data( 'src' ) ).load( function () {
655
- $a.removeClass( 'so-loading' ).css( 'height', 'auto' );
656
- $img.appendTo( $a ).hide().fadeIn( 'fast' );
657
- } );
658
- } else {
659
- $( '<img/>' ).attr( 'src', panelsOptions.prebuiltDefaultScreenshot ).appendTo( $a ).hide().fadeIn( 'fast' );
660
- }
661
-
662
- } );
663
-
664
- // Set the title
665
- c.find( '.so-directory-browse' ).html( data.title );
666
- },
667
- 'json'
668
- );
669
- },
670
-
671
- /**
672
- * Set the selected state for the clicked layout directory item and remove previously selected item.
673
- * Enable the toolbar buttons.
674
- */
675
- directoryItemClickHandler: function ( e ) {
676
- var $directoryItem = this.$( e.target ).closest( '.so-directory-item' );
677
- this.$( '.so-directory-items' ).find( '.selected' ).removeClass( 'selected' );
678
- $directoryItem.addClass( 'selected' );
679
- this.selectedLayoutItem = {lid: $directoryItem.data( 'layout-id' ), type: $directoryItem.data( 'layout-type' )};
680
- this.updateButtonState( true );
681
-
682
- },
683
-
684
- /**
685
- * Load a particular layout into the builder.
686
- *
687
- * @param id
688
- */
689
- toolbarButtonClick: function ( $button ) {
690
- if ( ! this.canAddLayout() ) {
691
- return false;
692
- }
693
- var position = $button.data( 'value' );
694
- if ( _.isUndefined( position ) ) {
695
- return false;
696
- }
697
- this.updateButtonState( false );
698
-
699
- if ( $button.hasClass( 'so-needs-confirm' ) && ! $button.hasClass( 'so-confirmed' ) ) {
700
- this.updateButtonState( true );
701
- if ( $button.hasClass( 'so-confirming' ) ) {
702
- return;
703
- }
704
- $button.addClass( 'so-confirming' );
705
- var originalText = $button.html();
706
- $button.html( '<span class="dashicons dashicons-yes"></span>' + $button.data( 'confirm' ) );
707
- setTimeout( function () {
708
- $button.removeClass( 'so-confirmed' ).html( originalText );
709
- }, 2500 );
710
- setTimeout( function () {
711
- $button.removeClass( 'so-confirming' );
712
- $button.addClass( 'so-confirmed' );
713
- }, 200 );
714
- return false;
715
- }
716
- this.addingLayout = true;
717
- if ( this.currentTab === 'import' ) {
718
- this.addLayoutToBuilder( this.uploadedLayout, position );
719
- } else {
720
- this.loadSelectedLayout().then( function ( layout ) {
721
- this.addLayoutToBuilder( layout, position );
722
- }.bind( this ) );
723
- }
724
- },
725
-
726
- canAddLayout: function () {
727
- return (
728
- this.selectedLayoutItem || this.uploadedLayout
729
- ) && ! this.addingLayout;
730
- },
731
-
732
- /**
733
- * Load the layout according to selectedLayoutItem.
734
- */
735
- loadSelectedLayout: function () {
736
- this.setStatusMessage( panelsOptions.loc.prebuilt_loading, true );
737
-
738
- var args = _.extend( this.selectedLayoutItem, {action: 'so_panels_get_layout'} );
739
- var deferredLayout = new $.Deferred();
740
-
741
- $.get(
742
- panelsOptions.ajaxurl,
743
- args,
744
- function ( layout ) {
745
- var msg = '';
746
- if ( ! layout.success ) {
747
- msg = layout.data.message;
748
- deferredLayout.reject( layout.data );
749
- } else {
750
- deferredLayout.resolve( layout.data );
751
- }
752
- this.setStatusMessage( msg, false, ! layout.success );
753
- this.updateButtonState( true );
754
- }.bind( this )
755
- );
756
- return deferredLayout.promise();
757
- },
758
-
759
- /**
760
- * Handle an update to the search
761
- */
762
- searchHandler: function ( e ) {
763
- if ( e.keyCode === 13 ) {
764
- this.displayLayoutDirectory( $( e.currentTarget ).val(), 1, this.currentTab );
765
- }
766
- },
767
-
768
- /**
769
- * Attempt to set the 'Insert' button's state according to the `enabled` argument, also checking whether the
770
- * requirements for inserting a layout have valid values.
771
- */
772
- updateButtonState: function ( enabled ) {
773
- enabled = enabled && (
774
- this.selectedLayoutItem || this.uploadedLayout
775
- );
776
- var $button = this.$( '.so-import-layout' );
777
- $button.prop( "disabled", ! enabled );
778
- if ( enabled ) {
779
- $button.removeClass( 'disabled' );
780
- } else {
781
- $button.addClass( 'disabled' );
782
- }
783
- },
784
-
785
- addLayoutToBuilder: function ( layout, position ) {
786
- this.builder.addHistoryEntry( 'prebuilt_loaded' );
787
- this.builder.model.loadPanelsData( layout, position );
788
- this.addingLayout = false;
789
- this.closeDialog();
790
- }
791
- } );
792
-
793
- },{}],8:[function(require,module,exports){
794
- var panels = window.panels, $ = jQuery;
795
-
796
- module.exports = panels.view.dialog.extend({
797
-
798
- cellPreviewTemplate: _.template( panels.helpers.utils.processTemplate( $('#siteorigin-panels-dialog-row-cell-preview').html() ) ),
799
-
800
- editableLabel: true,
801
-
802
- events: {
803
- 'click .so-close': 'closeDialog',
804
-
805
- // Toolbar buttons
806
- 'click .so-toolbar .so-save': 'saveHandler',
807
- 'click .so-toolbar .so-insert': 'insertHandler',
808
- 'click .so-toolbar .so-delete': 'deleteHandler',
809
- 'click .so-toolbar .so-duplicate': 'duplicateHandler',
810
-
811
- // Changing the row
812
- 'change .row-set-form > *': 'setCellsFromForm',
813
- 'click .row-set-form button.set-row': 'setCellsFromForm',
814
- },
815
-
816
- rowView: null,
817
- dialogIcon: 'add-row',
818
- dialogClass: 'so-panels-dialog-row-edit',
819
- styleType: 'row',
820
-
821
- dialogType: 'edit',
822
-
823
- /**
824
- * The current settings, not yet saved to the model
825
- */
826
- row: {
827
- // This will be a clone of cells collection.
828
- cells: null,
829
- // The style settings of the row
830
- style: {}
831
- },
832
-
833
- cellStylesCache: [],
834
-
835
- initializeDialog: function () {
836
- this.on('open_dialog', function () {
837
- if (!_.isUndefined(this.model) && !_.isEmpty(this.model.get('cells'))) {
838
- this.setRowModel(this.model);
839
- } else {
840
- this.setRowModel(null);
841
- }
842
-
843
- this.regenerateRowPreview();
844
- this.renderStyles();
845
- }, this);
846
-
847
- // This is the default row layout
848
- this.row = {
849
- cells: new panels.collection.cells([{weight: 0.5}, {weight: 0.5}]),
850
- style: {}
851
- };
852
-
853
- // Refresh panels data after both dialog form components are loaded
854
- this.dialogFormsLoaded = 0;
855
- var thisView = this;
856
- this.on('form_loaded styles_loaded', function () {
857
- this.dialogFormsLoaded++;
858
- if (this.dialogFormsLoaded === 2) {
859
- thisView.updateModel({
860
- refreshArgs: {
861
- silent: true
862
- }
863
- });
864
- }
865
- });
866
-
867
- this.on('close_dialog', this.closeHandler);
868
-
869
- this.on( 'edit_label', function ( text ) {
870
- // If text is set to default values, just clear it.
871
- if ( text === panelsOptions.loc.row.add || text === panelsOptions.loc.row.edit ) {
872
- text = '';
873
- }
874
- this.model.set( 'label', text );
875
- if ( _.isEmpty( text ) ) {
876
- var title = this.dialogType === 'create' ? panelsOptions.loc.row.add : panelsOptions.loc.row.edit;
877
- this.$( '.so-title').text( title );
878
- }
879
- }.bind( this ) );
880
- },
881
-
882
- /**
883
- *
884
- * @param dialogType Either "edit" or "create"
885
- */
886
- setRowDialogType: function (dialogType) {
887
- this.dialogType = dialogType;
888
- },
889
-
890
- /**
891
- * Render the new row dialog
892
- */
893
- render: function () {
894
- var title = this.dialogType === 'create' ? panelsOptions.loc.row.add : panelsOptions.loc.row.edit;
895
- this.renderDialog( this.parseDialogContent( $( '#siteorigin-panels-dialog-row' ).html(), {
896
- title: title,
897
- dialogType: this.dialogType
898
- } ) );
899
-
900
- var titleElt = this.$( '.so-title' );
901
-
902
- if ( this.model.has( 'label' ) && ! _.isEmpty( this.model.get( 'label' ) ) ) {
903
- titleElt.text( this.model.get( 'label' ) );
904
- }
905
- this.$( '.so-edit-title' ).val( titleElt.text() );
906
-
907
- if (!this.builder.supports('addRow')) {
908
- this.$('.so-buttons .so-duplicate').remove();
909
- }
910
- if (!this.builder.supports('deleteRow')) {
911
- this.$('.so-buttons .so-delete').remove();
912
- }
913
-
914
- if (!_.isUndefined(this.model)) {
915
- // Set the initial value of the
916
- this.$( 'input[name="cells"].so-row-field' ).val( this.model.get( 'cells' ).length );
917
- if ( this.model.has( 'ratio' ) ) {
918
- this.$( 'select[name="ratio"].so-row-field' ).val( this.model.get( 'ratio' ) );
919
- }
920
- if ( this.model.has( 'ratio_direction' ) ) {
921
- this.$( 'select[name="ratio_direction"].so-row-field' ).val( this.model.get( 'ratio_direction' ) );
922
- }
923
- }
924
-
925
- this.$('input.so-row-field').keyup(function () {
926
- $(this).trigger('change');
927
- });
928
-
929
- return this;
930
- },
931
-
932
- renderStyles: function () {
933
- if ( this.styles ) {
934
- this.styles.off( 'styles_loaded' );
935
- this.styles.remove();
936
- }
937
-
938
- // Now we need to attach the style window
939
- this.styles = new panels.view.styles();
940
- this.styles.model = this.model;
941
- this.styles.render('row', this.builder.config.postId, {
942
- builderType: this.builder.config.builderType,
943
- dialog: this
944
- });
945
-
946
- var $rightSidebar = this.$('.so-sidebar.so-right-sidebar');
947
- this.styles.attach( $rightSidebar );
948
-
949
- // Handle the loading class
950
- this.styles.on('styles_loaded', function (hasStyles) {
951
- if ( ! hasStyles ) {
952
- // If we don't have styles remove the view.
953
- this.styles.remove();
954
-
955
- // If the sidebar is empty, hide it.
956
- if ( $rightSidebar.children().length === 0 ) {
957
- $rightSidebar.closest('.so-panels-dialog').removeClass('so-panels-dialog-has-right-sidebar');
958
- $rightSidebar.hide();
959
- }
960
- }
961
- }, this);
962
- },
963
-
964
- /**
965
- * Set the row model we'll be using for this dialog.
966
- *
967
- * @param model
968
- */
969
- setRowModel: function (model) {
970
- this.model = model;
971
-
972
- if (_.isEmpty(this.model)) {
973
- return this;
974
- }
975
-
976
- // Set the rows to be a copy of the model
977
- this.row = {
978
- cells: this.model.get('cells').clone(),
979
- style: {},
980
- ratio: this.model.get('ratio'),
981
- ratio_direction: this.model.get('ratio_direction'),
982
- };
983
-
984
- // Set the initial value of the cell field.
985
- this.$( 'input[name="cells"].so-row-field' ).val( this.model.get( 'cells' ).length );
986
- if ( this.model.has( 'ratio' ) ) {
987
- this.$( 'select[name="ratio"].so-row-field' ).val( this.model.get( 'ratio' ) );
988
- }
989
- if ( this.model.has( 'ratio_direction' ) ) {
990
- this.$( 'select[name="ratio_direction"].so-row-field' ).val( this.model.get( 'ratio_direction' ) );
991
- }
992
-
993
- this.clearCellStylesCache();
994
-
995
- return this;
996
- },
997
-
998
- /**
999
- * Regenerate the row preview and resizing interface.
1000
- */
1001
- regenerateRowPreview: function () {
1002
- var thisDialog = this;
1003
- var rowPreview = this.$('.row-preview');
1004
-
1005
- // If no selected cell, select the first cell.
1006
- var selectedIndex = this.getSelectedCellIndex();
1007
-
1008
- rowPreview.empty();
1009
-
1010
- var timeout;
1011
-
1012
- // Represent the cells
1013
- this.row.cells.each(function (cellModel, i) {
1014
- var newCell = $(this.cellPreviewTemplate({weight: cellModel.get('weight')}));
1015
- rowPreview.append(newCell);
1016
-
1017
- if(i == selectedIndex) {
1018
- newCell.find('.preview-cell-in').addClass('cell-selected');
1019
- }
1020
-
1021
- var prevCell = newCell.prev();
1022
- var handle;
1023
-
1024
- if (prevCell.length) {
1025
- handle = $('<div class="resize-handle"></div>');
1026
- handle
1027
- .appendTo(newCell)
1028
- .dblclick(function () {
1029
- var prevCellModel = thisDialog.row.cells.at(i - 1);
1030
- var t = cellModel.get('weight') + prevCellModel.get('weight');
1031
- cellModel.set('weight', t / 2);
1032
- prevCellModel.set('weight', t / 2);
1033
- thisDialog.scaleRowWidths();
1034
- });
1035
-
1036
- handle.draggable({
1037
- axis: 'x',
1038
- containment: rowPreview,
1039
- start: function (e, ui) {
1040
-
1041
- // Create the clone for the current cell
1042
- var newCellClone = newCell.clone().appendTo(ui.helper).css({
1043
- position: 'absolute',
1044
- top: '0',
1045
- width: newCell.outerWidth(),
1046
- left: 6,
1047
- height: newCell.outerHeight()
1048
- });
1049
- newCellClone.find('.resize-handle').remove();
1050
-
1051
- // Create the clone for the previous cell
1052
- var prevCellClone = prevCell.clone().appendTo(ui.helper).css({
1053
- position: 'absolute',
1054
- top: '0',
1055
- width: prevCell.outerWidth(),
1056
- right: 6,
1057
- height: prevCell.outerHeight()
1058
- });
1059
- prevCellClone.find('.resize-handle').remove();
1060
-
1061
- $(this).data({
1062
- 'newCellClone': newCellClone,
1063
- 'prevCellClone': prevCellClone
1064
- });
1065
-
1066
- // Hide the
1067
- newCell.find('> .preview-cell-in').css('visibility', 'hidden');
1068
- prevCell.find('> .preview-cell-in').css('visibility', 'hidden');
1069
- },
1070
- drag: function (e, ui) {
1071
- // Calculate the new cell and previous cell widths as a percent
1072
- var cellWeight = thisDialog.row.cells.at(i).get('weight');
1073
- var prevCellWeight = thisDialog.row.cells.at(i - 1).get('weight');
1074
- var ncw = cellWeight - (
1075
- (
1076
- ui.position.left + 6
1077
- ) / rowPreview.width()
1078
- );
1079
- var pcw = prevCellWeight + (
1080
- (
1081
- ui.position.left + 6
1082
- ) / rowPreview.width()
1083
- );
1084
-
1085
- var helperLeft = ui.helper.offset().left - rowPreview.offset().left - 6;
1086
-
1087
- $(this).data('newCellClone').css('width', rowPreview.width() * ncw)
1088
- .find('.preview-cell-weight').html(Math.round(ncw * 1000) / 10);
1089
-
1090
- $(this).data('prevCellClone').css('width', rowPreview.width() * pcw)
1091
- .find('.preview-cell-weight').html(Math.round(pcw * 1000) / 10);
1092
- },
1093
- stop: function (e, ui) {
1094
- // Remove the clones
1095
- $(this).data('newCellClone').remove();
1096
- $(this).data('prevCellClone').remove();
1097
-
1098
- // Reshow the main cells
1099
- newCell.find('.preview-cell-in').css('visibility', 'visible');
1100
- prevCell.find('.preview-cell-in').css('visibility', 'visible');
1101
-
1102
- // Calculate the new cell weights
1103
- var offset = ui.position.left + 6;
1104
- var percent = offset / rowPreview.width();
1105
-
1106
- // Ignore this if any of the cells are below 2% in width.
1107
- var cellModel = thisDialog.row.cells.at(i);
1108
- var prevCellModel = thisDialog.row.cells.at(i - 1);
1109
- if (cellModel.get('weight') - percent > 0.02 && prevCellModel.get('weight') + percent > 0.02) {
1110
- cellModel.set('weight', cellModel.get('weight') - percent);
1111
- prevCellModel.set('weight', prevCellModel.get('weight') + percent);
1112
- }
1113
-
1114
- thisDialog.scaleRowWidths();
1115
- ui.helper.css('left', -6);
1116
- }
1117
- });
1118
- }
1119
-
1120
- newCell.click(function (event) {
1121
-
1122
- if ( ! ( $(event.target).is('.preview-cell') || $(event.target).is('.preview-cell-in') ) ) {
1123
- return;
1124
- }
1125
-
1126
- var cell = $(event.target);
1127
- cell.closest('.row-preview').find('.preview-cell .preview-cell-in').removeClass('cell-selected');
1128
- cell.addClass('cell-selected');
1129
-
1130
- this.openSelectedCellStyles();
1131
-
1132
- }.bind(this));
1133
-
1134
- // Make this row weight click editable
1135
- newCell.find('.preview-cell-weight').click(function (ci) {
1136
-
1137
- // Disable the draggable while entering values
1138
- thisDialog.$('.resize-handle').css('pointer-event', 'none').draggable('disable');
1139
-
1140
- rowPreview.find('.preview-cell-weight').each(function () {
1141
- var $$ = jQuery(this).hide();
1142
- $('<input type="text" class="preview-cell-weight-input no-user-interacted" />')
1143
- .val(parseFloat($$.html())).insertAfter($$)
1144
- .focus(function () {
1145
- clearTimeout(timeout);
1146
- })
1147
- .keyup(function (e) {
1148
- if (e.keyCode !== 9) {
1149
- // Only register the interaction if the user didn't press tab
1150
- $(this).removeClass('no-user-interacted');
1151
- }
1152
-
1153
- // Enter is clicked
1154
- if (e.keyCode === 13) {
1155
- e.preventDefault();
1156
- $(this).blur();
1157
- }
1158
- })
1159
- .keydown(function (e) {
1160
- if (e.keyCode === 9) {
1161
- e.preventDefault();
1162
-
1163
- // Tab will always cycle around the row inputs
1164
- var inputs = rowPreview.find('.preview-cell-weight-input');
1165
- var i = inputs.index($(this));
1166
- if (i === inputs.length - 1) {
1167
- inputs.eq(0).focus().select();
1168
- } else {
1169
- inputs.eq(i + 1).focus().select();
1170
- }
1171
- }
1172
- })
1173
- .blur(function () {
1174
- rowPreview.find('.preview-cell-weight-input').each(function (i, el) {
1175
- if (isNaN(parseFloat($(el).val()))) {
1176
- $(el).val(Math.floor(thisDialog.row.cells.at(i).get('weight') * 1000) / 10);
1177
- }
1178
- });
1179
-
1180
- timeout = setTimeout(function () {
1181
- // If there are no weight inputs, then skip this
1182
- if (rowPreview.find('.preview-cell-weight-input').length === 0) {
1183
- return false;
1184
- }
1185
-
1186
- // Go through all the inputs
1187
- var rowWeights = [],
1188
- rowChanged = [],
1189
- changedSum = 0,
1190
- unchangedSum = 0;
1191
-
1192
- rowPreview.find('.preview-cell-weight-input').each(function (i, el) {
1193
- var val = parseFloat($(el).val());
1194
- if (isNaN(val)) {
1195
- val = 1 / thisDialog.row.cells.length;
1196
- } else {
1197
- val = Math.round(val * 10) / 1000;
1198
- }
1199
-
1200
- // Check within 3 decimal points
1201
- var changed = !$(el).hasClass('no-user-interacted');
1202
-
1203
- rowWeights.push(val);
1204
- rowChanged.push(changed);
1205
-
1206
- if (changed) {
1207
- changedSum += val;
1208
- } else {
1209
- unchangedSum += val;
1210
- }
1211
- });
1212
-
1213
- if (changedSum > 0 && unchangedSum > 0 && (
1214
- 1 - changedSum
1215
- ) > 0) {
1216
- // Balance out the unchanged rows to occupy the weight left over by the changed sum
1217
- for (var i = 0; i < rowWeights.length; i++) {
1218
- if (!rowChanged[i]) {
1219
- rowWeights[i] = (
1220
- rowWeights[i] / unchangedSum
1221
- ) * (
1222
- 1 - changedSum
1223
- );
1224
- }
1225
- }
1226
- }
1227
-
1228
- // Last check to ensure total weight is 1
1229
- var sum = _.reduce(rowWeights, function (memo, num) {
1230
- return memo + num;
1231
- });
1232
- rowWeights = rowWeights.map(function (w) {
1233
- return w / sum;
1234
- });
1235
-
1236
- // Set the new cell weights and regenerate the preview.
1237
- if (Math.min.apply(Math, rowWeights) > 0.01) {
1238
- thisDialog.row.cells.each(function (cell, i) {
1239
- cell.set('weight', rowWeights[i]);
1240
- });
1241
- }
1242
-
1243
- // Now lets animate the cells into their new widths
1244
- rowPreview.find('.preview-cell').each(function (i, el) {
1245
- var cellWeight = thisDialog.row.cells.at(i).get('weight');
1246
- $(el).animate({'width': Math.round(cellWeight * 1000) / 10 + "%"}, 250);
1247
- $(el).find('.preview-cell-weight-input').val(Math.round(cellWeight * 1000) / 10);
1248
- });
1249
-
1250
- // So the draggable handle is not hidden.
1251
- rowPreview.find('.preview-cell').css('overflow', 'visible');
1252
- setTimeout(thisDialog.regenerateRowPreview.bind(thisDialog), 260);
1253
-
1254
- }, 100);
1255
- })
1256
- .click(function () {
1257
- $(this).select();
1258
- });
1259
- });
1260
-
1261
- $(this).siblings('.preview-cell-weight-input').select();
1262
-
1263
- });
1264
-
1265
- }, this);
1266
-
1267
- this.openSelectedCellStyles();
1268
-
1269
- this.trigger('form_loaded', this);
1270
- },
1271
-
1272
- getSelectedCellIndex: function() {
1273
- var selectedIndex = -1;
1274
- this.$('.preview-cell .preview-cell-in').each(function(index, el) {
1275
- if($(el).is('.cell-selected')) {
1276
- selectedIndex = index;
1277
- }
1278
- });
1279
- return selectedIndex;
1280
- },
1281
-
1282
- openSelectedCellStyles: function() {
1283
- if (!_.isUndefined(this.cellStyles)) {
1284
- if (this.cellStyles.stylesLoaded) {
1285
- var style = {};
1286
- try {
1287
- style = this.getFormValues('.so-sidebar .so-visual-styles.so-cell-styles').style;
1288
- }
1289
- catch (err) {
1290
- console.log('Error retrieving cell styles - ' + err.message);
1291
- }
1292
-
1293
- this.cellStyles.model.set('style', style);
1294
- }
1295
- this.cellStyles.detach();
1296
- }
1297
-
1298
- this.cellStyles = this.getSelectedCellStyles();
1299
-
1300
- if ( this.cellStyles ) {
1301
- var $rightSidebar = this.$( '.so-sidebar.so-right-sidebar' );
1302
- this.cellStyles.attach( $rightSidebar );
1303
- this.cellStyles.on( 'styles_loaded', function ( hasStyles ) {
1304
- if ( hasStyles ) {
1305
- $rightSidebar.closest('.so-panels-dialog').addClass('so-panels-dialog-has-right-sidebar');
1306
- $rightSidebar.show();
1307
- }
1308
- } );
1309
- }
1310
- },
1311
-
1312
- getSelectedCellStyles: function () {
1313
- var cellIndex = this.getSelectedCellIndex();
1314
- if ( cellIndex > -1 ) {
1315
- var cellStyles = this.cellStylesCache[cellIndex];
1316
- if ( !cellStyles ) {
1317
- cellStyles = new panels.view.styles();
1318
- cellStyles.model = this.row.cells.at( cellIndex );
1319
- cellStyles.render( 'cell', this.builder.config.postId, {
1320
- builderType: this.builder.config.builderType,
1321
- dialog: this,
1322
- index: cellIndex,
1323
- } );
1324
- this.cellStylesCache[cellIndex] = cellStyles;
1325
- }
1326
- }
1327
-
1328
- return cellStyles;
1329
- },
1330
-
1331
- clearCellStylesCache: function () {
1332
- // Call remove() on all cell styles to remove data, event listeners etc.
1333
- this.cellStylesCache.forEach(function (cellStyles) {
1334
- cellStyles.remove();
1335
- });
1336
- this.cellStylesCache = [];
1337
- },
1338
-
1339
- /**
1340
- * Visually scale the row widths based on the cell weights
1341
- */
1342
- scaleRowWidths: function () {
1343
- var thisDialog = this;
1344
- this.$('.row-preview .preview-cell').each(function (i, el) {
1345
- var cell = thisDialog.row.cells.at(i);
1346
- $(el)
1347
- .css('width', cell.get('weight') * 100 + "%")
1348
- .find('.preview-cell-weight').html(Math.round(cell.get('weight') * 1000) / 10);
1349
- });
1350
- },
1351
-
1352
- /**
1353
- * Get the weights from the
1354
- */
1355
- setCellsFromForm: function () {
1356
-
1357
- try {
1358
- var f = {
1359
- 'cells': parseInt(this.$('.row-set-form input[name="cells"]').val()),
1360
- 'ratio': parseFloat(this.$('.row-set-form select[name="ratio"]').val()),
1361
- 'direction': this.$('.row-set-form select[name="ratio_direction"]').val()
1362
- };
1363
-
1364
- if (_.isNaN(f.cells)) {
1365
- f.cells = 1;
1366
- }
1367
- if (isNaN(f.ratio)) {
1368
- f.ratio = 1;
1369
- }
1370
- if (f.cells < 1) {
1371
- f.cells = 1;
1372
- this.$('.row-set-form input[name="cells"]').val(f.cells);
1373
- }
1374
- else if (f.cells > 12) {
1375
- f.cells = 12;
1376
- this.$('.row-set-form input[name="cells"]').val(f.cells);
1377
- }
1378
-
1379
- this.$('.row-set-form select[name="ratio"]').val(f.ratio);
1380
-
1381
- var cells = [];
1382
- var cellCountChanged = (
1383
- this.row.cells.length !== f.cells
1384
- );
1385
-
1386
- // Now, lets create some cells
1387
- var currentWeight = 1;
1388
- for (var i = 0; i < f.cells; i++) {
1389
- cells.push(currentWeight);
1390
- currentWeight *= f.ratio;
1391
- }
1392
-
1393
- // Now lets make sure that the row weights add up to 1
1394
-
1395
- var totalRowWeight = _.reduce(cells, function (memo, weight) {
1396
- return memo + weight;
1397
- });
1398
- cells = _.map(cells, function (cell) {
1399
- return cell / totalRowWeight;
1400
- });
1401
-
1402
- // Don't return cells that are too small
1403
- cells = _.filter(cells, function (cell) {
1404
- return cell > 0.01;
1405
- });
1406
-
1407
- if (f.direction === 'left') {
1408
- cells = cells.reverse();
1409
- }
1410
-
1411
- // Discard deleted cells.
1412
- this.row.cells = new panels.collection.cells(this.row.cells.first(cells.length));
1413
-
1414
- _.each(cells, function (cellWeight, index) {
1415
- var cell = this.row.cells.at(index);
1416
- if (!cell) {
1417
- cell = new panels.model.cell({weight: cellWeight, row: this.model});
1418
- this.row.cells.add(cell);
1419
- } else {
1420
- cell.set('weight', cellWeight);
1421
- }
1422
- }.bind(this));
1423
-
1424
- this.row.ratio = f.ratio;
1425
- this.row.ratio_direction = f.direction;
1426
-
1427
- if (cellCountChanged) {
1428
- this.regenerateRowPreview();
1429
- } else {
1430
- var thisDialog = this;
1431
-
1432
- // Now lets animate the cells into their new widths
1433
- this.$('.preview-cell').each(function (i, el) {
1434
- var cellWeight = thisDialog.row.cells.at(i).get('weight');
1435
- $(el).animate({'width': Math.round(cellWeight * 1000) / 10 + "%"}, 250);
1436
- $(el).find('.preview-cell-weight').html(Math.round(cellWeight * 1000) / 10);
1437
- });
1438
-
1439
- // So the draggable handle is not hidden.
1440
- this.$('.preview-cell').css('overflow', 'visible');
1441
-
1442
- setTimeout(thisDialog.regenerateRowPreview.bind(thisDialog), 260);
1443
- }
1444
- }
1445
- catch (err) {
1446
- console.log('Error setting cells - ' + err.message);
1447
- }
1448
-
1449
-
1450
- // Remove the button primary class
1451
- this.$('.row-set-form .so-button-row-set').removeClass('button-primary');
1452
- },
1453
-
1454
- /**
1455
- * Handle a click on the dialog left bar tab
1456
- */
1457
- tabClickHandler: function ($t) {
1458
- if ($t.attr('href') === '#row-layout') {
1459
- this.$('.so-panels-dialog').addClass('so-panels-dialog-has-right-sidebar');
1460
- } else {
1461
- this.$('.so-panels-dialog').removeClass('so-panels-dialog-has-right-sidebar');
1462
- }
1463
- },
1464
-
1465
- /**
1466
- * Update the current model with what we have in the dialog
1467
- */
1468
- updateModel: function (args) {
1469
- args = _.extend({
1470
- refresh: true,
1471
- refreshArgs: null
1472
- }, args);
1473
-
1474
- // Set the cells
1475
- if (!_.isEmpty(this.model)) {
1476
- this.model.setCells( this.row.cells );
1477
- this.model.set( 'ratio', this.row.ratio );
1478
- this.model.set( 'ratio_direction', this.row.ratio_direction );
1479
- }
1480
-
1481
- // Update the row styles if they've loaded
1482
- if (!_.isUndefined(this.styles) && this.styles.stylesLoaded) {
1483
- // This is an edit dialog, so there are styles
1484
- var style = {};
1485
- try {
1486
- style = this.getFormValues('.so-sidebar .so-visual-styles.so-row-styles').style;
1487
- }
1488
- catch (err) {
1489
- console.log('Error retrieving row styles - ' + err.message);
1490
- }
1491
-
1492
- this.model.set('style', style);
1493
- }
1494
-
1495
- // Update the cell styles if any are showing.
1496
- if (!_.isUndefined(this.cellStyles) && this.cellStyles.stylesLoaded) {
1497
-
1498
- var style = {};
1499
- try {
1500
- style = this.getFormValues('.so-sidebar .so-visual-styles.so-cell-styles').style;
1501
- }
1502
- catch (err) {
1503
- console.log('Error retrieving cell styles - ' + err.message);
1504
- }
1505
-
1506
- this.cellStyles.model.set('style', style);
1507
- }
1508
-
1509
- if (args.refresh) {
1510
- this.builder.model.refreshPanelsData(args.refreshArgs);
1511
- }
1512
- },
1513
-
1514
- /**
1515
- * Insert the new row
1516
- */
1517
- insertHandler: function () {
1518
- this.builder.addHistoryEntry('row_added');
1519
-
1520
- this.updateModel();
1521
-
1522
- var activeCell = this.builder.getActiveCell({
1523
- createCell: false,
1524
- });
1525
-
1526
- var options = {};
1527
- if (activeCell !== null) {
1528
- options.at = this.builder.model.get('rows').indexOf(activeCell.row) + 1;
1529
- }
1530
-
1531
- // Set up the model and add it to the builder
1532
- this.model.collection = this.builder.model.get('rows');
1533
- this.builder.model.get('rows').add(this.model, options);
1534
-
1535
- this.closeDialog();
1536
-
1537
- this.builder.model.refreshPanelsData();
1538
-
1539
- return false;
1540
- },
1541
-
1542
- /**
1543
- * We'll just save this model and close the dialog
1544
- */
1545
- saveHandler: function () {
1546
- this.builder.addHistoryEntry('row_edited');
1547
- this.updateModel();
1548
- this.closeDialog();
1549
-
1550
- this.builder.model.refreshPanelsData();
1551
-
1552
- return false;
1553
- },
1554
-
1555
- /**
1556
- * The user clicks delete, so trigger deletion on the row model
1557
- */
1558
- deleteHandler: function () {
1559
- // Trigger a destroy on the model that will happen with a visual indication to the user
1560
- this.rowView.visualDestroyModel();
1561
- this.closeDialog({silent: true});
1562
-
1563
- return false;
1564
- },
1565
-
1566
- /**
1567
- * Duplicate this row
1568
- */
1569
- duplicateHandler: function () {
1570
- this.builder.addHistoryEntry('row_duplicated');
1571
-
1572
- var duplicateRow = this.model.clone(this.builder.model);
1573
-
1574
- this.builder.model.get('rows').add( duplicateRow, {
1575
- at: this.builder.model.get('rows').indexOf(this.model) + 1
1576
- } );
1577
-
1578
- this.closeDialog({silent: true});
1579
-
1580
- return false;
1581
- },
1582
-
1583
- closeHandler: function() {
1584
- this.clearCellStylesCache();
1585
- if( ! _.isUndefined(this.cellStyles) ) {
1586
- this.cellStyles = undefined;
1587
- }
1588
- },
1589
-
1590
- });
1591
-
1592
- },{}],9:[function(require,module,exports){
1593
- var panels = window.panels, $ = jQuery;
1594
- var jsWidget = require( '../view/widgets/js-widget' );
1595
-
1596
- module.exports = panels.view.dialog.extend( {
1597
-
1598
- builder: null,
1599
- sidebarWidgetTemplate: _.template( panels.helpers.utils.processTemplate( $( '#siteorigin-panels-dialog-widget-sidebar-widget' ).html() ) ),
1600
-
1601
- dialogClass: 'so-panels-dialog-edit-widget',
1602
- dialogIcon: 'add-widget',
1603
-
1604
- widgetView: false,
1605
- savingWidget: false,
1606
- editableLabel: true,
1607
-
1608
- events: {
1609
- 'click .so-close': 'saveHandler',
1610
- 'click .so-nav.so-previous': 'navToPrevious',
1611
- 'click .so-nav.so-next': 'navToNext',
1612
-
1613
- // Action handlers
1614
- 'click .so-toolbar .so-delete': 'deleteHandler',
1615
- 'click .so-toolbar .so-duplicate': 'duplicateHandler'
1616
- },
1617
-
1618
- initializeDialog: function () {
1619
- var thisView = this;
1620
- this.listenTo( this.model, 'change:values', this.handleChangeValues );
1621
- this.listenTo( this.model, 'destroy', this.remove );
1622
-
1623
- // Refresh panels data after both dialog form components are loaded
1624
- this.dialogFormsLoaded = 0;
1625
- this.on( 'form_loaded styles_loaded', function () {
1626
- this.dialogFormsLoaded ++;
1627
- if ( this.dialogFormsLoaded === 2 ) {
1628
- thisView.updateModel( {
1629
- refreshArgs: {
1630
- silent: true
1631
- }
1632
- } );
1633
- }
1634
- } );
1635
-
1636
- this.on( 'edit_label', function ( text ) {
1637
- // If text is set to default value, just clear it.
1638
- if ( text === panelsOptions.widgets[ this.model.get( 'class' ) ][ 'title' ] ) {
1639
- text = '';
1640
- }
1641
- this.model.set( 'label', text );
1642
- if ( _.isEmpty( text ) ) {
1643
- this.$( '.so-title' ).text( this.model.getWidgetField( 'title' ) );
1644
- }
1645
- }.bind( this ) );
1646
- },
1647
-
1648
- /**
1649
- * Render the widget dialog.
1650
- */
1651
- render: function () {
1652
- // Render the dialog and attach it to the builder interface
1653
- this.renderDialog( this.parseDialogContent( $( '#siteorigin-panels-dialog-widget' ).html(), {} ) );
1654
- this.loadForm();
1655
-
1656
- var title = this.model.getWidgetField( 'title' );
1657
- this.$( '.so-title .widget-name' ).html( title );
1658
- this.$( '.so-edit-title' ).val( title );
1659
-
1660
- if( ! this.builder.supports( 'addWidget' ) ) {
1661
- this.$( '.so-buttons .so-duplicate' ).remove();
1662
- }
1663
- if( ! this.builder.supports( 'deleteWidget' ) ) {
1664
- this.$( '.so-buttons .so-delete' ).remove();
1665
- }
1666
-
1667
- // Now we need to attach the style window
1668
- this.styles = new panels.view.styles();
1669
- this.styles.model = this.model;
1670
- this.styles.render( 'widget', this.builder.config.postId, {
1671
- builderType: this.builder.config.builderType,
1672
- dialog: this
1673
- } );
1674
-
1675
- var $rightSidebar = this.$( '.so-sidebar.so-right-sidebar' );
1676
- this.styles.attach( $rightSidebar );
1677
-
1678
- // Handle the loading class
1679
- this.styles.on( 'styles_loaded', function ( hasStyles ) {
1680
- // If we don't have styles remove the empty sidebar.
1681
- if ( ! hasStyles ) {
1682
- $rightSidebar.closest( '.so-panels-dialog' ).removeClass( 'so-panels-dialog-has-right-sidebar' );
1683
- $rightSidebar.remove();
1684
- }
1685
- }, this );
1686
- },
1687
-
1688
- /**
1689
- * Get the previous widget editing dialog by looking at the dom.
1690
- * @returns {*}
1691
- */
1692
- getPrevDialog: function () {
1693
- var widgets = this.builder.$( '.so-cells .cell .so-widget' );
1694
- if ( widgets.length <= 1 ) {
1695
- return false;
1696
- }
1697
- var currentIndex = widgets.index( this.widgetView.$el );
1698
-
1699
- if ( currentIndex === 0 ) {
1700
- return false;
1701
- } else {
1702
- var widgetView;
1703
- do {
1704
- widgetView = widgets.eq( --currentIndex ).data( 'view' );
1705
- if ( ! _.isUndefined( widgetView ) && ! widgetView.model.get( 'read_only' ) ) {
1706
- return widgetView.getEditDialog();
1707
- }
1708
- } while( ! _.isUndefined( widgetView ) && currentIndex > 0 );
1709
- }
1710
-
1711
- return false;
1712
- },
1713
-
1714
- /**
1715
- * Get the next widget editing dialog by looking at the dom.
1716
- * @returns {*}
1717
- */
1718
- getNextDialog: function () {
1719
- var widgets = this.builder.$( '.so-cells .cell .so-widget' );
1720
- if ( widgets.length <= 1 ) {
1721
- return false;
1722
- }
1723
-
1724
- var currentIndex = widgets.index( this.widgetView.$el );
1725
-
1726
- if ( currentIndex === widgets.length - 1 ) {
1727
- return false;
1728
- } else {
1729
- var widgetView;
1730
- do {
1731
- widgetView = widgets.eq( ++currentIndex ).data( 'view' );
1732
- if ( ! _.isUndefined( widgetView ) && ! widgetView.model.get( 'read_only' ) ) {
1733
- return widgetView.getEditDialog();
1734
- }
1735
- } while( ! _.isUndefined( widgetView ) );
1736
- }
1737
-
1738
- return false;
1739
- },
1740
-
1741
- /**
1742
- * Load the widget form from the server.
1743
- * This is called when rendering the dialog for the first time.
1744
- */
1745
- loadForm: function () {
1746
- // don't load the form if this dialog hasn't been rendered yet
1747
- if ( ! this.$( '> *' ).length ) {
1748
- return;
1749
- }
1750
-
1751
- this.$( '.so-content' ).addClass( 'so-panels-loading' );
1752
-
1753
- var data = {
1754
- 'action': 'so_panels_widget_form',
1755
- 'widget': this.model.get( 'class' ),
1756
- 'instance': JSON.stringify( this.model.get( 'values' ) ),
1757
- 'raw': this.model.get( 'raw' )
1758
- };
1759
-
1760
- var $soContent = this.$( '.so-content' );
1761
-
1762
- $.post( panelsOptions.ajaxurl, data, null, 'html' )
1763
- .done( function ( result ) {
1764
- // Add in the CID of the widget model
1765
- var html = result.replace( /{\$id}/g, this.model.cid );
1766
-
1767
- // Load this content into the form
1768
- $soContent
1769
- .removeClass( 'so-panels-loading' )
1770
- .html( html );
1771
-
1772
- // Trigger all the necessary events
1773
- this.trigger( 'form_loaded', this );
1774
-
1775
- // For legacy compatibility, trigger a panelsopen event
1776
- this.$( '.panel-dialog' ).trigger( 'panelsopen' );
1777
-
1778
- // If the main dialog is closed from this point on, save the widget content
1779
- this.on( 'close_dialog', this.updateModel, this );
1780
-
1781
- var widgetContent = $soContent.find( '> .widget-content' );
1782
- // If there's a widget content wrapper, this is one of the new widgets in WP 4.8 which need some special
1783
- // handling in JS.
1784
- if ( widgetContent.length > 0 ) {
1785
- jsWidget.addWidget( $soContent, this.model.widget_id );
1786
- }
1787
-
1788
- }.bind( this ) )
1789
- .fail( function ( error ) {
1790
- var html;
1791
- if ( error && error.responseText ) {
1792
- html = error.responseText;
1793
- } else {
1794
- html = panelsOptions.forms.loadingFailed;
1795
- }
1796
-
1797
- $soContent
1798
- .removeClass( 'so-panels-loading' )
1799
- .html( html );
1800
- } );
1801
- },
1802
-
1803
- /**
1804
- * Save the widget from the form to the model
1805
- */
1806
- updateModel: function ( args ) {
1807
- args = _.extend( {
1808
- refresh: true,
1809
- refreshArgs: null
1810
- }, args );
1811
-
1812
- // Get the values from the form and assign the new values to the model
1813
- this.savingWidget = true;
1814
-
1815
- if ( ! this.model.get( 'missing' ) ) {
1816
- // Only get the values for non missing widgets.
1817
- var values = this.getFormValues();
1818
- if ( _.isUndefined( values.widgets ) ) {
1819
- values = {};
1820
- } else {
1821
- values = values.widgets;
1822
- values = values[Object.keys( values )[0]];
1823
- }
1824
-
1825
- this.model.setValues( values );
1826
- this.model.set( 'raw', true ); // We've saved from the widget form, so this is now raw
1827
- }
1828
-
1829
- if ( this.styles.stylesLoaded ) {
1830
- // If the styles view has loaded
1831
- var style = {};
1832
- try {
1833
- style = this.getFormValues( '.so-sidebar .so-visual-styles' ).style;
1834
- }
1835
- catch ( e ) {
1836
- }
1837
- this.model.set( 'style', style );
1838
- }
1839
-
1840
- this.savingWidget = false;
1841
-
1842
- if ( args.refresh ) {
1843
- this.builder.model.refreshPanelsData( args.refreshArgs );
1844
- }
1845
- },
1846
-
1847
- /**
1848
- *
1849
- */
1850
- handleChangeValues: function () {
1851
- if ( ! this.savingWidget ) {
1852
- // Reload the form when we've changed the model and we're not currently saving from the form
1853
- this.loadForm();
1854
- }
1855
- },
1856
-
1857
- /**
1858
- * Save a history entry for this widget. Called when the dialog is closed.
1859
- */
1860
- saveHandler: function () {
1861
- this.builder.addHistoryEntry( 'widget_edited' );
1862
- this.closeDialog();
1863
- },
1864
-
1865
- /**
1866
- * When the user clicks delete.
1867
- *
1868
- * @returns {boolean}
1869
- */
1870
- deleteHandler: function () {
1871
- this.widgetView.visualDestroyModel();
1872
- this.closeDialog( {silent: true} );
1873
- this.builder.model.refreshPanelsData();
1874
-
1875
- return false;
1876
- },
1877
-
1878
- duplicateHandler: function () {
1879
- // Call the widget duplicate handler directly
1880
- this.widgetView.duplicateHandler();
1881
-
1882
- this.closeDialog( {silent: true} );
1883
- this.builder.model.refreshPanelsData();
1884
-
1885
- return false;
1886
- }
1887
-
1888
- } );
1889
-
1890
- },{"../view/widgets/js-widget":31}],10:[function(require,module,exports){
1891
- var panels = window.panels, $ = jQuery;
1892
-
1893
- module.exports = panels.view.dialog.extend( {
1894
-
1895
- builder: null,
1896
- widgetTemplate: _.template( panels.helpers.utils.processTemplate( $( '#siteorigin-panels-dialog-widgets-widget' ).html() ) ),
1897
- filter: {},
1898
-
1899
- dialogClass: 'so-panels-dialog-add-widget',
1900
- dialogIcon: 'add-widget',
1901
-
1902
- events: {
1903
- 'click .so-close': 'closeDialog',
1904
- 'click .widget-type': 'widgetClickHandler',
1905
- 'keyup .so-sidebar-search': 'searchHandler'
1906
- },
1907
-
1908
- /**
1909
- * Initialize the widget adding dialog
1910
- */
1911
- initializeDialog: function () {
1912
-
1913
- this.on( 'open_dialog', function () {
1914
- this.filter.search = '';
1915
- this.filterWidgets( this.filter );
1916
- }, this );
1917
-
1918
- this.on( 'open_dialog_complete', function () {
1919
- // Clear the search and re-filter the widgets when we open the dialog
1920
- this.$( '.so-sidebar-search' ).val( '' ).focus();
1921
- this.balanceWidgetHeights();
1922
- } );
1923
-
1924
- // We'll implement a custom tab click handler
1925
- this.on( 'tab_click', this.tabClickHandler, this );
1926
- },
1927
-
1928
- render: function () {
1929
- // Render the dialog and attach it to the builder interface
1930
- this.renderDialog( this.parseDialogContent( $( '#siteorigin-panels-dialog-widgets' ).html(), {} ) );
1931
-
1932
- // Add all the widgets
1933
- _.each( panelsOptions.widgets, function ( widget ) {
1934
- var $w = $( this.widgetTemplate( {
1935
- title: widget.title,
1936
- description: widget.description
1937
- } ) );
1938
-
1939
- if ( _.isUndefined( widget.icon ) ) {
1940
- widget.icon = 'dashicons dashicons-admin-generic';
1941
- }
1942
-
1943
- $( '<span class="widget-icon" />' ).addClass( widget.icon ).prependTo( $w.find( '.widget-type-wrapper' ) );
1944
-
1945
- $w.data( 'class', widget.class ).appendTo( this.$( '.widget-type-list' ) );
1946
- }, this );
1947
-
1948
- // Add the sidebar tabs
1949
- var tabs = this.$( '.so-sidebar-tabs' );
1950
- _.each( panelsOptions.widget_dialog_tabs, function ( tab ) {
1951
- $( this.dialogTabTemplate( {'title': tab.title} ) ).data( {
1952
- 'message': tab.message,
1953
- 'filter': tab.filter
1954
- } ).appendTo( tabs );
1955
- }, this );
1956
-
1957
- // We'll be using tabs, so initialize them
1958
- this.initTabs();
1959
-
1960
- var thisDialog = this;
1961
- $( window ).resize( function () {
1962
- thisDialog.balanceWidgetHeights();
1963
- } );
1964
- },
1965
-
1966
- /**
1967
- * Handle a tab being clicked
1968
- */
1969
- tabClickHandler: function ( $t ) {
1970
- // Get the filter from the tab, and filter the widgets
1971
- this.filter = $t.parent().data( 'filter' );
1972
- this.filter.search = this.$( '.so-sidebar-search' ).val();
1973
-
1974
- var message = $t.parent().data( 'message' );
1975
- if ( _.isEmpty( message ) ) {
1976
- message = '';
1977
- }
1978
-
1979
- this.$( '.so-toolbar .so-status' ).html( message );
1980
-
1981
- this.filterWidgets( this.filter );
1982
-
1983
- return false;
1984
- },
1985
-
1986
- /**
1987
- * Handle changes to the search value
1988
- */
1989
- searchHandler: function ( e ) {
1990
- if( e.which === 13 ) {
1991
- var visibleWidgets = this.$( '.widget-type-list .widget-type:visible' );
1992
- if( visibleWidgets.length === 1 ) {
1993
- visibleWidgets.click();
1994
- }
1995
- }
1996
- else {
1997
- this.filter.search = $( e.target ).val().trim();
1998
- this.filterWidgets( this.filter );
1999
- }
2000
- },
2001
-
2002
- /**
2003
- * Filter the widgets that we're displaying
2004
- * @param filter
2005
- */
2006
- filterWidgets: function ( filter ) {
2007
- if ( _.isUndefined( filter ) ) {
2008
- filter = {};
2009
- }
2010
-
2011
- if ( _.isUndefined( filter.groups ) ) {
2012
- filter.groups = '';
2013
- }
2014
-
2015
- this.$( '.widget-type-list .widget-type' ).each( function () {
2016