PageRenderTime 74ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 1ms

/Wordpress-Project/wp-content/plugins/siteorigin-panels/js/siteorigin-panels-2516.js

https://bitbucket.org/hinzanhilmy/hinzan-sample-works
JavaScript | 7235 lines | 4846 code | 1214 blank | 1175 comment | 663 complexity | c0c96c6ae32c83abe5891ef5a5f779af MD5 | raw file
Possible License(s): GPL-3.0, 0BSD, Apache-2.0, BSD-2-Clause, MPL-2.0-no-copyleft-exception, LGPL-2.1, GPL-2.0, BSD-3-Clause, MIT

Large files files are truncated, but you can click here to view the full file

  1. (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
  2. var panels = window.panels;
  3. module.exports = Backbone.Collection.extend( {
  4. model: panels.model.cell,
  5. initialize: function () {
  6. },
  7. /**
  8. * Get the total weight for the cells in this collection.
  9. * @returns {number}
  10. */
  11. totalWeight: function () {
  12. var totalWeight = 0;
  13. this.each( function ( cell ) {
  14. totalWeight += cell.get( 'weight' );
  15. } );
  16. return totalWeight;
  17. },
  18. } );
  19. },{}],2:[function(require,module,exports){
  20. var panels = window.panels;
  21. module.exports = Backbone.Collection.extend( {
  22. model: panels.model.historyEntry,
  23. /**
  24. * The builder model
  25. */
  26. builder: null,
  27. /**
  28. * The maximum number of items in the history
  29. */
  30. maxSize: 12,
  31. initialize: function () {
  32. this.on( 'add', this.onAddEntry, this );
  33. },
  34. /**
  35. * Add an entry to the collection.
  36. *
  37. * @param text The text that defines the action taken to get to this
  38. * @param data
  39. */
  40. addEntry: function ( text, data ) {
  41. if ( _.isEmpty( data ) ) {
  42. data = this.builder.getPanelsData();
  43. }
  44. var entry = new panels.model.historyEntry( {
  45. text: text,
  46. data: JSON.stringify( data ),
  47. time: parseInt( new Date().getTime() / 1000 ),
  48. collection: this
  49. } );
  50. this.add( entry );
  51. },
  52. /**
  53. * Resize the collection so it's not bigger than this.maxSize
  54. */
  55. onAddEntry: function ( entry ) {
  56. if ( this.models.length > 1 ) {
  57. var lastEntry = this.at( this.models.length - 2 );
  58. if (
  59. (
  60. entry.get( 'text' ) === lastEntry.get( 'text' ) && entry.get( 'time' ) - lastEntry.get( 'time' ) < 15
  61. ) ||
  62. (
  63. entry.get( 'data' ) === lastEntry.get( 'data' )
  64. )
  65. ) {
  66. // If both entries have the same text and are within 20 seconds of each other, or have the same data, then remove most recent
  67. this.remove( entry );
  68. lastEntry.set( 'count', lastEntry.get( 'count' ) + 1 );
  69. }
  70. }
  71. // Make sure that there are not to many entries in this collection
  72. while ( this.models.length > this.maxSize ) {
  73. this.shift();
  74. }
  75. }
  76. } );
  77. },{}],3:[function(require,module,exports){
  78. var panels = window.panels;
  79. module.exports = Backbone.Collection.extend( {
  80. model: panels.model.row,
  81. /**
  82. * Destroy all the rows in this collection
  83. */
  84. empty: function () {
  85. var model;
  86. do {
  87. model = this.collection.first();
  88. if ( ! model ) {
  89. break;
  90. }
  91. model.destroy();
  92. } while ( true );
  93. }
  94. } );
  95. },{}],4:[function(require,module,exports){
  96. var panels = window.panels;
  97. module.exports = Backbone.Collection.extend( {
  98. model: panels.model.widget,
  99. initialize: function () {
  100. }
  101. } );
  102. },{}],5:[function(require,module,exports){
  103. var panels = window.panels, $ = jQuery;
  104. module.exports = panels.view.dialog.extend( {
  105. dialogClass: 'so-panels-dialog-add-builder',
  106. render: function () {
  107. // Render the dialog and attach it to the builder interface
  108. this.renderDialog( this.parseDialogContent( $( '#siteorigin-panels-dialog-builder' ).html(), {} ) );
  109. this.$( '.so-content .siteorigin-panels-builder' ).append( this.builder.$el );
  110. },
  111. initializeDialog: function () {
  112. var thisView = this;
  113. this.once( 'open_dialog_complete', function () {
  114. thisView.builder.initSortable();
  115. } );
  116. this.on( 'open_dialog_complete', function () {
  117. thisView.builder.trigger( 'builder_resize' );
  118. } );
  119. }
  120. } );
  121. },{}],6:[function(require,module,exports){
  122. var panels = window.panels, $ = jQuery;
  123. module.exports = panels.view.dialog.extend( {
  124. historyEntryTemplate: _.template( panels.helpers.utils.processTemplate( $( '#siteorigin-panels-dialog-history-entry' ).html() ) ),
  125. entries: {},
  126. currentEntry: null,
  127. revertEntry: null,
  128. selectedEntry: null,
  129. previewScrollTop: null,
  130. dialogClass: 'so-panels-dialog-history',
  131. dialogIcon: 'history',
  132. events: {
  133. 'click .so-close': 'closeDialog',
  134. 'click .so-restore': 'restoreSelectedEntry'
  135. },
  136. initializeDialog: function () {
  137. this.entries = new panels.collection.historyEntries();
  138. this.on( 'open_dialog', this.setCurrentEntry, this );
  139. this.on( 'open_dialog', this.renderHistoryEntries, this );
  140. },
  141. render: function () {
  142. var thisView = this;
  143. // Render the dialog and attach it to the builder interface
  144. this.renderDialog( this.parseDialogContent( $( '#siteorigin-panels-dialog-history' ).html(), {} ) );
  145. this.$( 'iframe.siteorigin-panels-history-iframe' ).load( function () {
  146. var $$ = $( this );
  147. $$.show();
  148. $$.contents().scrollTop( thisView.previewScrollTop );
  149. } );
  150. },
  151. /**
  152. * Set the original entry. This should be set when creating the dialog.
  153. *
  154. * @param {panels.model.builder} builder
  155. */
  156. setRevertEntry: function ( builder ) {
  157. this.revertEntry = new panels.model.historyEntry( {
  158. data: JSON.stringify( builder.getPanelsData() ),
  159. time: parseInt( new Date().getTime() / 1000 )
  160. } );
  161. },
  162. /**
  163. * This is triggered when the dialog is opened.
  164. */
  165. setCurrentEntry: function () {
  166. this.currentEntry = new panels.model.historyEntry( {
  167. data: JSON.stringify( this.builder.model.getPanelsData() ),
  168. time: parseInt( new Date().getTime() / 1000 )
  169. } );
  170. this.selectedEntry = this.currentEntry;
  171. this.previewEntry( this.currentEntry );
  172. this.$( '.so-buttons .so-restore' ).addClass( 'disabled' );
  173. },
  174. /**
  175. * Render the history entries in the sidebar
  176. */
  177. renderHistoryEntries: function () {
  178. // Set up an interval that will display the time since every 10 seconds
  179. var thisView = this;
  180. var c = this.$( '.history-entries' ).empty();
  181. if ( this.currentEntry.get( 'data' ) !== this.revertEntry.get( 'data' ) || ! _.isEmpty( this.entries.models ) ) {
  182. $( this.historyEntryTemplate( {title: panelsOptions.loc.history.revert, count: 1} ) )
  183. .data( 'historyEntry', this.revertEntry )
  184. .prependTo( c );
  185. }
  186. // Now load all the entries in this.entries
  187. this.entries.each( function ( entry ) {
  188. var html = thisView.historyEntryTemplate( {
  189. title: panelsOptions.loc.history[entry.get( 'text' )],
  190. count: entry.get( 'count' )
  191. } );
  192. $( html )
  193. .data( 'historyEntry', entry )
  194. .prependTo( c );
  195. } );
  196. $( this.historyEntryTemplate( {title: panelsOptions.loc.history['current'], count: 1} ) )
  197. .data( 'historyEntry', this.currentEntry )
  198. .addClass( 'so-selected' )
  199. .prependTo( c );
  200. // Handle loading and selecting
  201. c.find( '.history-entry' ).click( function () {
  202. var $$ = jQuery( this );
  203. c.find( '.history-entry' ).not( $$ ).removeClass( 'so-selected' );
  204. $$.addClass( 'so-selected' );
  205. var entry = $$.data( 'historyEntry' );
  206. thisView.selectedEntry = entry;
  207. if ( thisView.selectedEntry.cid !== thisView.currentEntry.cid ) {
  208. thisView.$( '.so-buttons .so-restore' ).removeClass( 'disabled' );
  209. } else {
  210. thisView.$( '.so-buttons .so-restore' ).addClass( 'disabled' );
  211. }
  212. thisView.previewEntry( entry );
  213. } );
  214. this.updateEntryTimes();
  215. },
  216. /**
  217. * Preview an entry
  218. *
  219. * @param entry
  220. */
  221. previewEntry: function ( entry ) {
  222. var iframe = this.$( 'iframe.siteorigin-panels-history-iframe' );
  223. iframe.hide();
  224. this.previewScrollTop = iframe.contents().scrollTop();
  225. this.$( 'form.history-form input[name="live_editor_panels_data"]' ).val( entry.get( 'data' ) );
  226. this.$( 'form.history-form input[name="live_editor_post_ID"]' ).val( this.builder.config.postId );
  227. this.$( 'form.history-form' ).submit();
  228. },
  229. /**
  230. * Restore the current entry
  231. */
  232. restoreSelectedEntry: function () {
  233. if ( this.$( '.so-buttons .so-restore' ).hasClass( 'disabled' ) ) {
  234. return false;
  235. }
  236. if ( this.currentEntry.get( 'data' ) === this.selectedEntry.get( 'data' ) ) {
  237. this.closeDialog();
  238. return false;
  239. }
  240. // Add an entry for this restore event
  241. if ( this.selectedEntry.get( 'text' ) !== 'restore' ) {
  242. this.builder.addHistoryEntry( 'restore', this.builder.model.getPanelsData() );
  243. }
  244. this.builder.model.loadPanelsData( JSON.parse( this.selectedEntry.get( 'data' ) ) );
  245. this.closeDialog();
  246. return false;
  247. },
  248. /**
  249. * Update the entry times for the list of entries down the side
  250. */
  251. updateEntryTimes: function () {
  252. var thisView = this;
  253. this.$( '.history-entries .history-entry' ).each( function () {
  254. var $$ = jQuery( this );
  255. var time = $$.find( '.timesince' );
  256. var entry = $$.data( 'historyEntry' );
  257. time.html( thisView.timeSince( entry.get( 'time' ) ) );
  258. } );
  259. },
  260. /**
  261. * Gets the time since as a nice string.
  262. *
  263. * @param date
  264. */
  265. timeSince: function ( time ) {
  266. var diff = parseInt( new Date().getTime() / 1000 ) - time;
  267. var parts = [];
  268. var interval;
  269. // There are 3600 seconds in an hour
  270. if ( diff > 3600 ) {
  271. interval = Math.floor( diff / 3600 );
  272. if ( interval === 1 ) {
  273. parts.push( panelsOptions.loc.time.hour.replace( '%d', interval ) );
  274. } else {
  275. parts.push( panelsOptions.loc.time.hours.replace( '%d', interval ) );
  276. }
  277. diff -= interval * 3600;
  278. }
  279. // There are 60 seconds in a minute
  280. if ( diff > 60 ) {
  281. interval = Math.floor( diff / 60 );
  282. if ( interval === 1 ) {
  283. parts.push( panelsOptions.loc.time.minute.replace( '%d', interval ) );
  284. } else {
  285. parts.push( panelsOptions.loc.time.minutes.replace( '%d', interval ) );
  286. }
  287. diff -= interval * 60;
  288. }
  289. if ( diff > 0 ) {
  290. if ( diff === 1 ) {
  291. parts.push( panelsOptions.loc.time.second.replace( '%d', diff ) );
  292. } else {
  293. parts.push( panelsOptions.loc.time.seconds.replace( '%d', diff ) );
  294. }
  295. }
  296. // Return the amount of time ago
  297. return _.isEmpty( parts ) ? panelsOptions.loc.time.now : panelsOptions.loc.time.ago.replace( '%s', parts.slice( 0, 2 ).join( ', ' ) );
  298. }
  299. } );
  300. },{}],7:[function(require,module,exports){
  301. var panels = window.panels, $ = jQuery;
  302. module.exports = panels.view.dialog.extend( {
  303. directoryTemplate: _.template( panels.helpers.utils.processTemplate( $( '#siteorigin-panels-directory-items' ).html() ) ),
  304. builder: null,
  305. dialogClass: 'so-panels-dialog-prebuilt-layouts',
  306. dialogIcon: 'layouts',
  307. layoutCache: {},
  308. currentTab: false,
  309. directoryPage: 1,
  310. events: {
  311. 'click .so-close': 'closeDialog',
  312. 'click .so-sidebar-tabs li a': 'tabClickHandler',
  313. 'click .so-content .layout': 'layoutClickHandler',
  314. 'keyup .so-sidebar-search': 'searchHandler',
  315. // The directory items
  316. 'click .so-screenshot, .so-title': 'directoryItemClickHandler'
  317. },
  318. /**
  319. * Initialize the prebuilt dialog.
  320. */
  321. initializeDialog: function () {
  322. var thisView = this;
  323. this.on( 'open_dialog', function () {
  324. thisView.$( '.so-sidebar-tabs li a' ).first().click();
  325. thisView.$( '.so-status' ).removeClass( 'so-panels-loading' );
  326. } );
  327. this.on( 'button_click', this.toolbarButtonClick, this );
  328. },
  329. /**
  330. * Render the prebuilt layouts dialog
  331. */
  332. render: function () {
  333. this.renderDialog( this.parseDialogContent( $( '#siteorigin-panels-dialog-prebuilt' ).html(), {} ) );
  334. this.initToolbar();
  335. },
  336. /**
  337. *
  338. * @param e
  339. * @return {boolean}
  340. */
  341. tabClickHandler: function ( e ) {
  342. e.preventDefault();
  343. // Reset selected item state when changing tabs
  344. this.selectedLayoutItem = null;
  345. this.uploadedLayout = null;
  346. this.updateButtonState( false );
  347. this.$( '.so-sidebar-tabs li' ).removeClass( 'tab-active' );
  348. var $$ = $( e.target );
  349. var tab = $$.attr( 'href' ).split( '#' )[1];
  350. $$.parent().addClass( 'tab-active' );
  351. var thisView = this;
  352. // Empty everything
  353. this.$( '.so-content' ).empty();
  354. thisView.currentTab = tab;
  355. if ( tab == 'import' ) {
  356. this.displayImportExport();
  357. } else {
  358. this.displayLayoutDirectory( '', 1, tab );
  359. }
  360. thisView.$( '.so-sidebar-search' ).val( '' );
  361. },
  362. /**
  363. * Display and setup the import/export form
  364. */
  365. displayImportExport: function () {
  366. var c = this.$( '.so-content' ).empty().removeClass( 'so-panels-loading' );
  367. c.html( $( '#siteorigin-panels-dialog-prebuilt-importexport' ).html() );
  368. var thisView = this;
  369. var uploadUi = thisView.$( '.import-upload-ui' ).hide();
  370. // Create the uploader
  371. var uploader = new plupload.Uploader( {
  372. runtimes: 'html5,silverlight,flash,html4',
  373. browse_button: uploadUi.find( '.file-browse-button' ).get( 0 ),
  374. container: uploadUi.get( 0 ),
  375. drop_element: uploadUi.find( '.drag-upload-area' ).get( 0 ),
  376. file_data_name: 'panels_import_data',
  377. multiple_queues: false,
  378. max_file_size: panelsOptions.plupload.max_file_size,
  379. url: panelsOptions.plupload.url,
  380. flash_swf_url: panelsOptions.plupload.flash_swf_url,
  381. silverlight_xap_url: panelsOptions.plupload.silverlight_xap_url,
  382. filters: [
  383. {title: panelsOptions.plupload.filter_title, extensions: 'json'}
  384. ],
  385. multipart_params: {
  386. action: 'so_panels_import_layout'
  387. },
  388. init: {
  389. PostInit: function ( uploader ) {
  390. if ( uploader.features.dragdrop ) {
  391. uploadUi.addClass( 'has-drag-drop' );
  392. }
  393. uploadUi.show().find( '.progress-precent' ).css( 'width', '0%' );
  394. },
  395. FilesAdded: function ( uploader ) {
  396. uploadUi.find( '.file-browse-button' ).blur();
  397. uploadUi.find( '.drag-upload-area' ).removeClass( 'file-dragover' );
  398. uploadUi.find( '.progress-bar' ).fadeIn( 'fast' );
  399. thisView.$( '.js-so-selected-file' ).text( panelsOptions.loc.prebuilt_loading );
  400. uploader.start();
  401. },
  402. UploadProgress: function ( uploader, file ) {
  403. uploadUi.find( '.progress-precent' ).css( 'width', file.percent + '%' );
  404. },
  405. FileUploaded: function ( uploader, file, response ) {
  406. var layout = JSON.parse( response.response );
  407. if ( ! _.isUndefined( layout.widgets ) ) {
  408. thisView.uploadedLayout = layout;
  409. uploadUi.find( '.progress-bar' ).hide();
  410. thisView.$( '.js-so-selected-file' ).text(
  411. panelsOptions.loc.ready_to_insert.replace( '%s', file.name )
  412. );
  413. thisView.updateButtonState( true );
  414. } else {
  415. alert( panelsOptions.plupload.error_message );
  416. }
  417. },
  418. Error: function () {
  419. alert( panelsOptions.plupload.error_message );
  420. }
  421. }
  422. } );
  423. uploader.init();
  424. // This is
  425. uploadUi.find( '.drag-upload-area' )
  426. .on( 'dragover', function () {
  427. $( this ).addClass( 'file-dragover' );
  428. } )
  429. .on( 'dragleave', function () {
  430. $( this ).removeClass( 'file-dragover' );
  431. } );
  432. // Handle exporting the file
  433. c.find( '.so-export' ).submit( function ( e ) {
  434. var $$ = $( this );
  435. $$.find( 'input[name="panels_export_data"]' ).val( JSON.stringify( thisView.builder.model.getPanelsData() ) );
  436. } );
  437. },
  438. /**
  439. * Display the layout directory tab.
  440. *
  441. * @param query
  442. */
  443. displayLayoutDirectory: function ( search, page, type ) {
  444. var thisView = this;
  445. var c = this.$( '.so-content' ).empty().addClass( 'so-panels-loading' );
  446. if ( search === undefined ) {
  447. search = '';
  448. }
  449. if ( page === undefined ) {
  450. page = 1;
  451. }
  452. if ( type === undefined ) {
  453. type = 'directory-siteorigin';
  454. }
  455. if ( type.match('^directory-') && ! panelsOptions.directory_enabled ) {
  456. // Display the button to enable the prebuilt layout
  457. c.removeClass( 'so-panels-loading' ).html( $( '#siteorigin-panels-directory-enable' ).html() );
  458. c.find( '.so-panels-enable-directory' ).click( function ( e ) {
  459. e.preventDefault();
  460. // Sent the query to enable the directory, then enable the directory
  461. $.get(
  462. panelsOptions.ajaxurl,
  463. {action: 'so_panels_directory_enable'},
  464. function () {
  465. }
  466. );
  467. // Enable the layout directory
  468. panelsOptions.directory_enabled = true;
  469. c.addClass( 'so-panels-loading' );
  470. thisView.displayLayoutDirectory( search, page, type );
  471. } );
  472. return;
  473. }
  474. // Get all the items for the current query
  475. $.get(
  476. panelsOptions.ajaxurl,
  477. {
  478. action: 'so_panels_layouts_query',
  479. search: search,
  480. page: page,
  481. type: type,
  482. },
  483. function ( data ) {
  484. // Skip this if we're no longer viewing the layout directory
  485. if ( thisView.currentTab !== type ) {
  486. return;
  487. }
  488. // Add the directory items
  489. c.removeClass( 'so-panels-loading' ).html( thisView.directoryTemplate( data ) );
  490. // Lets setup the next and previous buttons
  491. var prev = c.find( '.so-previous' ), next = c.find( '.so-next' );
  492. if ( page <= 1 ) {
  493. prev.addClass( 'button-disabled' );
  494. } else {
  495. prev.click( function ( e ) {
  496. e.preventDefault();
  497. thisView.displayLayoutDirectory( search, page - 1, thisView.currentTab );
  498. } );
  499. }
  500. if ( page === data.max_num_pages || data.max_num_pages === 0 ) {
  501. next.addClass( 'button-disabled' );
  502. } else {
  503. next.click( function ( e ) {
  504. e.preventDefault();
  505. thisView.displayLayoutDirectory( search, page + 1, thisView.currentTab );
  506. } );
  507. }
  508. // Handle nice preloading of the screenshots
  509. c.find( '.so-screenshot' ).each( function () {
  510. var $$ = $( this ), $a = $$.find( '.so-screenshot-wrapper' );
  511. $a.css( 'height', ( $a.width() / 4 * 3 ) + 'px' ).addClass( 'so-loading' );
  512. if ( $$.data( 'src' ) !== '' ) {
  513. // Set the initial height
  514. var $img = $( '<img/>' ).attr( 'src', $$.data( 'src' ) ).load( function () {
  515. $a.removeClass( 'so-loading' ).css( 'height', 'auto' );
  516. $img.appendTo( $a ).hide().fadeIn( 'fast' );
  517. } );
  518. } else {
  519. $( '<img/>' ).attr( 'src', panelsOptions.prebuiltDefaultScreenshot ).appendTo( $a ).hide().fadeIn( 'fast' );
  520. }
  521. } );
  522. // Set the title
  523. c.find( '.so-directory-browse' ).html( data.title );
  524. },
  525. 'json'
  526. );
  527. },
  528. /**
  529. * Set the selected state for the clicked layout directory item and remove previously selected item.
  530. * Enable the toolbar buttons.
  531. */
  532. directoryItemClickHandler: function ( e ) {
  533. var $directoryItem = this.$( e.target ).closest( '.so-directory-item' );
  534. this.$( '.so-directory-items' ).find( '.selected' ).removeClass( 'selected' );
  535. $directoryItem.addClass( 'selected' );
  536. this.selectedLayoutItem = {lid: $directoryItem.data( 'layout-id' ), type: $directoryItem.data( 'layout-type' )};
  537. this.updateButtonState( true );
  538. },
  539. /**
  540. * Load a particular layout into the builder.
  541. *
  542. * @param id
  543. */
  544. toolbarButtonClick: function ( $button ) {
  545. if ( ! this.canAddLayout() ) {
  546. return false;
  547. }
  548. var position = $button.data( 'value' );
  549. if ( _.isUndefined( position ) ) {
  550. return false;
  551. }
  552. this.updateButtonState( false );
  553. if ( $button.hasClass( 'so-needs-confirm' ) && ! $button.hasClass( 'so-confirmed' ) ) {
  554. this.updateButtonState( true );
  555. if ( $button.hasClass( 'so-confirming' ) ) {
  556. return;
  557. }
  558. $button.addClass( 'so-confirming' );
  559. var originalText = $button.html();
  560. $button.html( '<span class="dashicons dashicons-yes"></span>' + $button.data( 'confirm' ) );
  561. setTimeout( function () {
  562. $button.removeClass( 'so-confirmed' ).html( originalText );
  563. }, 2500 );
  564. setTimeout( function () {
  565. $button.removeClass( 'so-confirming' );
  566. $button.addClass( 'so-confirmed' );
  567. }, 200 );
  568. return false;
  569. }
  570. this.addingLayout = true;
  571. if ( this.currentTab === 'import' ) {
  572. this.addLayoutToBuilder( this.uploadedLayout, position );
  573. } else {
  574. this.loadSelectedLayout().then( function ( layout ) {
  575. this.addLayoutToBuilder( layout, position );
  576. }.bind( this ) );
  577. }
  578. },
  579. canAddLayout: function () {
  580. return (
  581. this.selectedLayoutItem || this.uploadedLayout
  582. ) && ! this.addingLayout;
  583. },
  584. /**
  585. * Load the layout according to selectedLayoutItem.
  586. */
  587. loadSelectedLayout: function () {
  588. this.setStatusMessage( panelsOptions.loc.prebuilt_loading, true );
  589. var args = _.extend( this.selectedLayoutItem, {action: 'so_panels_get_layout'} );
  590. var deferredLayout = new $.Deferred();
  591. $.get(
  592. panelsOptions.ajaxurl,
  593. args,
  594. function ( layout ) {
  595. if ( layout.error !== undefined ) {
  596. // There was an error
  597. alert( layout.error );
  598. deferredLayout.reject( layout );
  599. } else {
  600. this.setStatusMessage( '', false );
  601. deferredLayout.resolve( layout );
  602. }
  603. }.bind( this )
  604. );
  605. return deferredLayout.promise();
  606. },
  607. /**
  608. * Handle an update to the search
  609. */
  610. searchHandler: function ( e ) {
  611. if ( e.keyCode === 13 ) {
  612. this.displayLayoutDirectory( $( e.currentTarget ).val(), 1, this.currentTab );
  613. }
  614. },
  615. /**
  616. * Attempt to set the 'Insert' button's state according to the `enabled` argument, also checking whether the
  617. * requirements for inserting a layout have valid values.
  618. */
  619. updateButtonState: function ( enabled ) {
  620. enabled = enabled && (
  621. this.selectedLayoutItem || this.uploadedLayout
  622. );
  623. var $button = this.$( '.so-import-layout' );
  624. $button.prop( "disabled", ! enabled );
  625. if ( enabled ) {
  626. $button.removeClass( 'disabled' );
  627. } else {
  628. $button.addClass( 'disabled' );
  629. }
  630. },
  631. addLayoutToBuilder: function ( layout, position ) {
  632. this.builder.addHistoryEntry( 'prebuilt_loaded' );
  633. this.builder.model.loadPanelsData( layout, position );
  634. this.addingLayout = false;
  635. this.closeDialog();
  636. }
  637. } );
  638. },{}],8:[function(require,module,exports){
  639. var panels = window.panels, $ = jQuery;
  640. module.exports = panels.view.dialog.extend({
  641. cellPreviewTemplate: _.template( panels.helpers.utils.processTemplate( $('#siteorigin-panels-dialog-row-cell-preview').html() ) ),
  642. editableLabel: true,
  643. events: {
  644. 'click .so-close': 'closeDialog',
  645. // Toolbar buttons
  646. 'click .so-toolbar .so-save': 'saveHandler',
  647. 'click .so-toolbar .so-insert': 'insertHandler',
  648. 'click .so-toolbar .so-delete': 'deleteHandler',
  649. 'click .so-toolbar .so-duplicate': 'duplicateHandler',
  650. // Changing the row
  651. 'change .row-set-form > *': 'setCellsFromForm',
  652. 'click .row-set-form button.set-row': 'setCellsFromForm',
  653. },
  654. dialogIcon: 'add-row',
  655. dialogClass: 'so-panels-dialog-row-edit',
  656. styleType: 'row',
  657. dialogType: 'edit',
  658. /**
  659. * The current settings, not yet saved to the model
  660. */
  661. row: {
  662. // This will be a clone of cells collection.
  663. cells: null,
  664. // The style settings of the row
  665. style: {}
  666. },
  667. cellStylesCache: [],
  668. initializeDialog: function () {
  669. this.on('open_dialog', function () {
  670. if (!_.isUndefined(this.model) && !_.isEmpty(this.model.get('cells'))) {
  671. this.setRowModel(this.model);
  672. } else {
  673. this.setRowModel(null);
  674. }
  675. this.regenerateRowPreview();
  676. }, this);
  677. // This is the default row layout
  678. this.row = {
  679. cells: new panels.collection.cells([{weight: 0.5}, {weight: 0.5}]),
  680. style: {}
  681. };
  682. // Refresh panels data after both dialog form components are loaded
  683. this.dialogFormsLoaded = 0;
  684. var thisView = this;
  685. this.on('form_loaded styles_loaded', function () {
  686. this.dialogFormsLoaded++;
  687. if (this.dialogFormsLoaded === 2) {
  688. thisView.updateModel({
  689. refreshArgs: {
  690. silent: true
  691. }
  692. });
  693. }
  694. });
  695. this.on('close_dialog', this.closeHandler);
  696. this.on( 'edit_label', function ( text ) {
  697. // If text is set to default values, just clear it.
  698. if ( text === panelsOptions.loc.row.add || text === panelsOptions.loc.row.edit ) {
  699. text = '';
  700. }
  701. this.model.set( 'label', text );
  702. if ( _.isEmpty( text ) ) {
  703. var title = this.dialogType === 'create' ? panelsOptions.loc.row.add : panelsOptions.loc.row.edit;
  704. this.$( '.so-title').text( title );
  705. }
  706. }.bind( this ) );
  707. },
  708. /**
  709. *
  710. * @param dialogType Either "edit" or "create"
  711. */
  712. setRowDialogType: function (dialogType) {
  713. this.dialogType = dialogType;
  714. },
  715. /**
  716. * Render the new row dialog
  717. */
  718. render: function () {
  719. var title = this.dialogType === 'create' ? panelsOptions.loc.row.add : panelsOptions.loc.row.edit;
  720. this.renderDialog( this.parseDialogContent( $( '#siteorigin-panels-dialog-row' ).html(), {
  721. title: title,
  722. dialogType: this.dialogType
  723. } ) );
  724. var titleElt = this.$( '.so-title' );
  725. if ( this.model.has( 'label' ) && ! _.isEmpty( this.model.get( 'label' ) ) ) {
  726. titleElt.text( this.model.get( 'label' ) );
  727. }
  728. this.$( '.so-edit-title' ).val( titleElt.text() );
  729. // Now we need to attach the style window
  730. this.styles = new panels.view.styles();
  731. this.styles.model = this.model;
  732. this.styles.render('row', this.builder.config.postId, {
  733. builderType: this.builder.config.builderType,
  734. dialog: this
  735. });
  736. if (!this.builder.supports('addRow')) {
  737. this.$('.so-buttons .so-duplicate').remove();
  738. }
  739. if (!this.builder.supports('deleteRow')) {
  740. this.$('.so-buttons .so-delete').remove();
  741. }
  742. var $rightSidebar = this.$('.so-sidebar.so-right-sidebar');
  743. this.styles.attach($rightSidebar);
  744. // Handle the loading class
  745. this.styles.on('styles_loaded', function (hasStyles) {
  746. // If we have styles remove the loading spinner, else remove the whole empty sidebar.
  747. if (hasStyles) {
  748. $rightSidebar.removeClass('so-panels-loading');
  749. } else {
  750. $rightSidebar.closest('.so-panels-dialog').removeClass('so-panels-dialog-has-right-sidebar');
  751. $rightSidebar.remove();
  752. }
  753. }, this);
  754. $rightSidebar.addClass('so-panels-loading');
  755. if (!_.isUndefined(this.model)) {
  756. // Set the initial value of the
  757. this.$( 'input[name="cells"].so-row-field' ).val( this.model.get( 'cells' ).length );
  758. if ( this.model.has( 'ratio' ) ) {
  759. this.$( 'select[name="ratio"].so-row-field' ).val( this.model.get( 'ratio' ) );
  760. }
  761. if ( this.model.has( 'ratio_direction' ) ) {
  762. this.$( 'select[name="ratio_direction"].so-row-field' ).val( this.model.get( 'ratio_direction' ) );
  763. }
  764. }
  765. this.$('input.so-row-field').keyup(function () {
  766. $(this).trigger('change');
  767. });
  768. return this;
  769. },
  770. /**
  771. * Set the row model we'll be using for this dialog.
  772. *
  773. * @param model
  774. */
  775. setRowModel: function (model) {
  776. this.model = model;
  777. if (_.isEmpty(this.model)) {
  778. return this;
  779. }
  780. // Set the rows to be a copy of the model
  781. this.row = {
  782. cells: this.model.get('cells').clone(),
  783. style: {},
  784. ratio: this.model.get('ratio'),
  785. ratio_direction: this.model.get('ratio_direction'),
  786. };
  787. // Set the initial value of the cell field.
  788. this.$( 'input[name="cells"].so-row-field' ).val( this.model.get( 'cells' ).length );
  789. if ( this.model.has( 'ratio' ) ) {
  790. this.$( 'select[name="ratio"].so-row-field' ).val( this.model.get( 'ratio' ) );
  791. }
  792. if ( this.model.has( 'ratio_direction' ) ) {
  793. this.$( 'select[name="ratio_direction"].so-row-field' ).val( this.model.get( 'ratio_direction' ) );
  794. }
  795. this.clearCellStylesCache();
  796. return this;
  797. },
  798. /**
  799. * Regenerate the row preview and resizing interface.
  800. */
  801. regenerateRowPreview: function () {
  802. var thisDialog = this;
  803. var rowPreview = this.$('.row-preview');
  804. // If no selected cell, select the first cell.
  805. var selectedIndex = this.getSelectedCellIndex();
  806. rowPreview.empty();
  807. var timeout;
  808. // Represent the cells
  809. this.row.cells.each(function (cellModel, i) {
  810. var newCell = $(this.cellPreviewTemplate({weight: cellModel.get('weight')}));
  811. rowPreview.append(newCell);
  812. if(i == selectedIndex) {
  813. newCell.find('.preview-cell-in').addClass('cell-selected');
  814. }
  815. var prevCell = newCell.prev();
  816. var handle;
  817. if (prevCell.length) {
  818. handle = $('<div class="resize-handle"></div>');
  819. handle
  820. .appendTo(newCell)
  821. .dblclick(function () {
  822. var prevCellModel = thisDialog.row.cells.at(i - 1);
  823. var t = cellModel.get('weight') + prevCellModel.get('weight');
  824. cellModel.set('weight', t / 2);
  825. prevCellModel.set('weight', t / 2);
  826. thisDialog.scaleRowWidths();
  827. });
  828. handle.draggable({
  829. axis: 'x',
  830. containment: rowPreview,
  831. start: function (e, ui) {
  832. // Create the clone for the current cell
  833. var newCellClone = newCell.clone().appendTo(ui.helper).css({
  834. position: 'absolute',
  835. top: '0',
  836. width: newCell.outerWidth(),
  837. left: 6,
  838. height: newCell.outerHeight()
  839. });
  840. newCellClone.find('.resize-handle').remove();
  841. // Create the clone for the previous cell
  842. var prevCellClone = prevCell.clone().appendTo(ui.helper).css({
  843. position: 'absolute',
  844. top: '0',
  845. width: prevCell.outerWidth(),
  846. right: 6,
  847. height: prevCell.outerHeight()
  848. });
  849. prevCellClone.find('.resize-handle').remove();
  850. $(this).data({
  851. 'newCellClone': newCellClone,
  852. 'prevCellClone': prevCellClone
  853. });
  854. // Hide the
  855. newCell.find('> .preview-cell-in').css('visibility', 'hidden');
  856. prevCell.find('> .preview-cell-in').css('visibility', 'hidden');
  857. },
  858. drag: function (e, ui) {
  859. // Calculate the new cell and previous cell widths as a percent
  860. var cellWeight = thisDialog.row.cells.at(i).get('weight');
  861. var prevCellWeight = thisDialog.row.cells.at(i - 1).get('weight');
  862. var ncw = cellWeight - (
  863. (
  864. ui.position.left + 6
  865. ) / rowPreview.width()
  866. );
  867. var pcw = prevCellWeight + (
  868. (
  869. ui.position.left + 6
  870. ) / rowPreview.width()
  871. );
  872. var helperLeft = ui.helper.offset().left - rowPreview.offset().left - 6;
  873. $(this).data('newCellClone').css('width', rowPreview.width() * ncw)
  874. .find('.preview-cell-weight').html(Math.round(ncw * 1000) / 10);
  875. $(this).data('prevCellClone').css('width', rowPreview.width() * pcw)
  876. .find('.preview-cell-weight').html(Math.round(pcw * 1000) / 10);
  877. },
  878. stop: function (e, ui) {
  879. // Remove the clones
  880. $(this).data('newCellClone').remove();
  881. $(this).data('prevCellClone').remove();
  882. // Reshow the main cells
  883. newCell.find('.preview-cell-in').css('visibility', 'visible');
  884. prevCell.find('.preview-cell-in').css('visibility', 'visible');
  885. // Calculate the new cell weights
  886. var offset = ui.position.left + 6;
  887. var percent = offset / rowPreview.width();
  888. // Ignore this if any of the cells are below 2% in width.
  889. var cellModel = thisDialog.row.cells.at(i);
  890. var prevCellModel = thisDialog.row.cells.at(i - 1);
  891. if (cellModel.get('weight') - percent > 0.02 && prevCellModel.get('weight') + percent > 0.02) {
  892. cellModel.set('weight', cellModel.get('weight') - percent);
  893. prevCellModel.set('weight', prevCellModel.get('weight') + percent);
  894. }
  895. thisDialog.scaleRowWidths();
  896. ui.helper.css('left', -6);
  897. }
  898. });
  899. }
  900. newCell.click(function (event) {
  901. if ( ! ( $(event.target).is('.preview-cell') || $(event.target).is('.preview-cell-in') ) ) {
  902. return;
  903. }
  904. var cell = $(event.target);
  905. cell.closest('.row-preview').find('.preview-cell .preview-cell-in').removeClass('cell-selected');
  906. cell.addClass('cell-selected');
  907. this.openSelectedCellStyles();
  908. }.bind(this));
  909. // Make this row weight click editable
  910. newCell.find('.preview-cell-weight').click(function (ci) {
  911. // Disable the draggable while entering values
  912. thisDialog.$('.resize-handle').css('pointer-event', 'none').draggable('disable');
  913. rowPreview.find('.preview-cell-weight').each(function () {
  914. var $$ = jQuery(this).hide();
  915. $('<input type="text" class="preview-cell-weight-input no-user-interacted" />')
  916. .val(parseFloat($$.html())).insertAfter($$)
  917. .focus(function () {
  918. clearTimeout(timeout);
  919. })
  920. .keyup(function (e) {
  921. if (e.keyCode !== 9) {
  922. // Only register the interaction if the user didn't press tab
  923. $(this).removeClass('no-user-interacted');
  924. }
  925. // Enter is clicked
  926. if (e.keyCode === 13) {
  927. e.preventDefault();
  928. $(this).blur();
  929. }
  930. })
  931. .keydown(function (e) {
  932. if (e.keyCode === 9) {
  933. e.preventDefault();
  934. // Tab will always cycle around the row inputs
  935. var inputs = rowPreview.find('.preview-cell-weight-input');
  936. var i = inputs.index($(this));
  937. if (i === inputs.length - 1) {
  938. inputs.eq(0).focus().select();
  939. } else {
  940. inputs.eq(i + 1).focus().select();
  941. }
  942. }
  943. })
  944. .blur(function () {
  945. rowPreview.find('.preview-cell-weight-input').each(function (i, el) {
  946. if (isNaN(parseFloat($(el).val()))) {
  947. $(el).val(Math.floor(thisDialog.row.cells.at(i).get('weight') * 1000) / 10);
  948. }
  949. });
  950. timeout = setTimeout(function () {
  951. // If there are no weight inputs, then skip this
  952. if (rowPreview.find('.preview-cell-weight-input').length === 0) {
  953. return false;
  954. }
  955. // Go through all the inputs
  956. var rowWeights = [],
  957. rowChanged = [],
  958. changedSum = 0,
  959. unchangedSum = 0;
  960. rowPreview.find('.preview-cell-weight-input').each(function (i, el) {
  961. var val = parseFloat($(el).val());
  962. if (isNaN(val)) {
  963. val = 1 / thisDialog.row.cells.length;
  964. } else {
  965. val = Math.round(val * 10) / 1000;
  966. }
  967. // Check within 3 decimal points
  968. var changed = !$(el).hasClass('no-user-interacted');
  969. rowWeights.push(val);
  970. rowChanged.push(changed);
  971. if (changed) {
  972. changedSum += val;
  973. } else {
  974. unchangedSum += val;
  975. }
  976. });
  977. if (changedSum > 0 && unchangedSum > 0 && (
  978. 1 - changedSum
  979. ) > 0) {
  980. // Balance out the unchanged rows to occupy the weight left over by the changed sum
  981. for (var i = 0; i < rowWeights.length; i++) {
  982. if (!rowChanged[i]) {
  983. rowWeights[i] = (
  984. rowWeights[i] / unchangedSum
  985. ) * (
  986. 1 - changedSum
  987. );
  988. }
  989. }
  990. }
  991. // Last check to ensure total weight is 1
  992. var sum = _.reduce(rowWeights, function (memo, num) {
  993. return memo + num;
  994. });
  995. rowWeights = rowWeights.map(function (w) {
  996. return w / sum;
  997. });
  998. // Set the new cell weights and regenerate the preview.
  999. if (Math.min.apply(Math, rowWeights) > 0.01) {
  1000. thisDialog.row.cells.each(function (cell, i) {
  1001. cell.set('weight', rowWeights[i]);
  1002. });
  1003. }
  1004. // Now lets animate the cells into their new widths
  1005. rowPreview.find('.preview-cell').each(function (i, el) {
  1006. var cellWeight = thisDialog.row.cells.at(i).get('weight');
  1007. $(el).animate({'width': Math.round(cellWeight * 1000) / 10 + "%"}, 250);
  1008. $(el).find('.preview-cell-weight-input').val(Math.round(cellWeight * 1000) / 10);
  1009. });
  1010. // So the draggable handle is not hidden.
  1011. rowPreview.find('.preview-cell').css('overflow', 'visible');
  1012. setTimeout(function () {
  1013. thisDialog.regenerateRowPreview();
  1014. }, 260);
  1015. }, 100);
  1016. })
  1017. .click(function () {
  1018. $(this).select();
  1019. });
  1020. });
  1021. $(this).siblings('.preview-cell-weight-input').select();
  1022. });
  1023. }, this);
  1024. this.openSelectedCellStyles();
  1025. this.trigger('form_loaded', this);
  1026. },
  1027. getSelectedCellIndex: function() {
  1028. var selectedIndex = -1;
  1029. this.$('.preview-cell .preview-cell-in').each(function(index, el) {
  1030. if($(el).is('.cell-selected')) {
  1031. selectedIndex = index;
  1032. }
  1033. });
  1034. return selectedIndex;
  1035. },
  1036. openSelectedCellStyles: function() {
  1037. if (!_.isUndefined(this.cellStyles)) {
  1038. if (this.cellStyles.stylesLoaded) {
  1039. var style = {};
  1040. try {
  1041. style = this.getFormValues('.so-sidebar .so-visual-styles.so-cell-styles').style;
  1042. }
  1043. catch (err) {
  1044. console.log('Error retrieving cell styles - ' + err.message);
  1045. }
  1046. this.cellStyles.model.set('style', style);
  1047. }
  1048. this.cellStyles.detach();
  1049. }
  1050. this.cellStyles = this.getSelectedCellStyles();
  1051. if ( this.cellStyles ) {
  1052. var $rightSidebar = this.$( '.so-sidebar.so-right-sidebar' );
  1053. this.cellStyles.attach( $rightSidebar );
  1054. if ( !this.cellStyles.stylesLoaded ) {
  1055. this.cellStyles.on( 'styles_loaded', function () {
  1056. $rightSidebar.removeClass( 'so-panels-loading' );
  1057. }, this );
  1058. $rightSidebar.addClass( 'so-panels-loading' );
  1059. }
  1060. }
  1061. },
  1062. getSelectedCellStyles: function () {
  1063. var cellIndex = this.getSelectedCellIndex();
  1064. if ( cellIndex > -1 ) {
  1065. var cellStyles = this.cellStylesCache[cellIndex];
  1066. if ( !cellStyles ) {
  1067. cellStyles = new panels.view.styles();
  1068. cellStyles.model = this.row.cells.at( cellIndex );
  1069. cellStyles.render( 'cell', this.builder.config.postId, {
  1070. builderType: this.builder.config.builderType,
  1071. dialog: this,
  1072. index: cellIndex,
  1073. } );
  1074. this.cellStylesCache[cellIndex] = cellStyles;
  1075. }
  1076. }
  1077. return cellStyles;
  1078. },
  1079. clearCellStylesCache: function () {
  1080. // Call remove() on all cell styles to remove data, event listeners etc.
  1081. this.cellStylesCache.forEach(function (cellStyles) {
  1082. cellStyles.remove();
  1083. });
  1084. this.cellStylesCache = [];
  1085. },
  1086. /**
  1087. * Visually scale the row widths based on the cell weights
  1088. */
  1089. scaleRowWidths: function () {
  1090. var thisDialog = this;
  1091. this.$('.row-preview .preview-cell').each(function (i, el) {
  1092. var cell = thisDialog.row.cells.at(i);
  1093. $(el)
  1094. .css('width', cell.get('weight') * 100 + "%")
  1095. .find('.preview-cell-weight').html(Math.round(cell.get('weight') * 1000) / 10);
  1096. });
  1097. },
  1098. /**
  1099. * Get the weights from the
  1100. */
  1101. setCellsFromForm: function () {
  1102. try {
  1103. var f = {
  1104. 'cells': parseInt(this.$('.row-set-form input[name="cells"]').val()),
  1105. 'ratio': parseFloat(this.$('.row-set-form select[name="ratio"]').val()),
  1106. 'direction': this.$('.row-set-form select[name="ratio_direction"]').val()
  1107. };
  1108. if (_.isNaN(f.cells)) {
  1109. f.cells = 1;
  1110. }
  1111. if (isNaN(f.ratio)) {
  1112. f.ratio = 1;
  1113. }
  1114. if (f.cells < 1) {
  1115. f.cells = 1;
  1116. this.$('.row-set-form input[name="cells"]').val(f.cells);
  1117. }
  1118. else if (f.cells > 12) {
  1119. f.cells = 12;
  1120. this.$('.row-set-form input[name="cells"]').val(f.cells);
  1121. }
  1122. this.$('.row-set-form select[name="ratio"]').val(f.ratio);
  1123. var cells = [];
  1124. var cellCountChanged = (
  1125. this.row.cells.length !== f.cells
  1126. );
  1127. // Now, lets create some cells
  1128. var currentWeight = 1;
  1129. for (var i = 0; i < f.cells; i++) {
  1130. cells.push(currentWeight);
  1131. currentWeight *= f.ratio;
  1132. }
  1133. // Now lets make sure that the row weights add up to 1
  1134. var totalRowWeight = _.reduce(cells, function (memo, weight) {
  1135. return memo + weight;
  1136. });
  1137. cells = _.map(cells, function (cell) {
  1138. return cell / totalRowWeight;
  1139. });
  1140. // Don't return cells that are too small
  1141. cells = _.filter(cells, function (cell) {
  1142. return cell > 0.01;
  1143. });
  1144. if (f.direction === 'left') {
  1145. cells = cells.reverse();
  1146. }
  1147. // Discard deleted cells.
  1148. this.row.cells = new panels.collection.cells(this.row.cells.first(cells.length));
  1149. _.each(cells, function (cellWeight, index) {
  1150. var cell = this.row.cells.at(index);
  1151. if (!cell) {
  1152. cell = new panels.model.cell({weight: cellWeight, row: this.model});
  1153. this.row.cells.add(cell);
  1154. } else {
  1155. cell.set('weight', cellWeight);
  1156. }
  1157. }.bind(this));
  1158. this.row.ratio = f.ratio;
  1159. this.row.ratio_direction = f.direction;
  1160. if (cellCountChanged) {
  1161. this.regenerateRowPreview();
  1162. } else {
  1163. var thisDialog = this;
  1164. // Now lets animate the cells into their new widths
  1165. this.$('.preview-cell').each(function (i, el) {
  1166. var cellWeight = thisDialog.row.cells.at(i).get('weight');
  1167. $(el).animate({'width': Math.round(cellWeight * 1000) / 10 + "%"}, 250);
  1168. $(el).find('.preview-cell-weight').html(Math.round(cellWeight * 1000) / 10);
  1169. });
  1170. // So the draggable handle is not hidden.
  1171. this.$('.preview-cell').css('overflow', 'visible');
  1172. setTimeout(function () {
  1173. thisDialog.regenerateRowPreview();
  1174. }, 260);
  1175. }
  1176. }
  1177. catch (err) {
  1178. console.log('Error setting cells - ' + err.message);
  1179. }
  1180. // Remove the button primary class
  1181. this.$('.row-set-form .so-button-row-set').removeClass('button-primary');
  1182. },
  1183. /**
  1184. * Handle a click on the dialog left bar tab
  1185. */
  1186. tabClickHandler: function ($t) {
  1187. if ($t.attr('href') === '#row-layout') {
  1188. this.$('.so-panels-dialog').addClass('so-panels-dialog-has-right-sidebar');
  1189. } else {
  1190. this.$('.so-panels-dialog').removeClass('so-panels-dialog-has-right-sidebar');
  1191. }
  1192. },
  1193. /**
  1194. * Update the current model with what we have in the dialog
  1195. */
  1196. updateModel: function (args) {
  1197. args = _.extend({
  1198. refresh: true,
  1199. refreshArgs: null
  1200. }, args);
  1201. // Set the cells
  1202. if (!_.isEmpty(this.model)) {
  1203. this.model.setCells( this.row.cells );
  1204. this.model.set( 'ratio', this.row.ratio );
  1205. this.model.set( 'ratio_direction', this.row.ratio_direction );
  1206. }
  1207. // Update the row styles if they've loaded
  1208. if (!_.isUndefined(this.styles) && this.styles.stylesLoaded) {
  1209. // This is an edit dialog, so there are styles
  1210. var style = {};
  1211. try {
  1212. style = this.getFormValues('.so-sidebar .so-visual-styles.so-row-styles').style;
  1213. }
  1214. catch (err) {
  1215. console.log('Error retrieving row styles - ' + err.message);
  1216. }
  1217. this.model.set('style', style);
  1218. }
  1219. // Update the cell styles if any are showing.
  1220. if (!_.isUndefined(this.cellStyles) && this.cellStyles.stylesLoaded) {
  1221. var style = {};
  1222. try {
  1223. style = this.getFormValues('.so-sidebar .so-visual-styles.so-cell-styles').style;
  1224. }
  1225. catch (err) {
  1226. console.log('Error retrieving cell styles - ' + err.message);
  1227. }
  1228. this.cellStyles.model.set('style', style);
  1229. }
  1230. if (args.refresh) {
  1231. this.builder.model.refreshPanelsData(args.refreshArgs);
  1232. }
  1233. },
  1234. /**
  1235. * Insert the new row
  1236. */
  1237. insertHandler: function () {
  1238. this.builder.addHistoryEntry('row_added');
  1239. this.updateModel();
  1240. var activeCell = this.builder.getActiveCell({
  1241. createCell: false,
  1242. });
  1243. var options = {};
  1244. if (activeCell !== null) {
  1245. options.at = this.builder.model.get('rows').indexOf(activeCell.row) + 1;
  1246. }
  1247. // Set up the model and add it to the builder
  1248. this.model.collection = this.builder.model.get('rows');
  1249. this.builder.model.get('rows').add(this.model, options);
  1250. this.closeDialog();
  1251. this.builder.model.refreshPanelsData();
  1252. return false;
  1253. },
  1254. /**
  1255. * We'll just save this model and close the dialog
  1256. */
  1257. saveHandler: function () {
  1258. this.builder.addHistoryEntry('row_edited');
  1259. this.updateModel();
  1260. this.closeDialog();
  1261. this.builder.model.refreshPanelsData();
  1262. return false;
  1263. },
  1264. /**
  1265. * The user clicks delete, so trigger deletion on the row model
  1266. */
  1267. deleteHandler: function () {
  1268. // Trigger a destroy on the model that will happen with a visual indication to the user
  1269. this.model.trigger('visual_destroy');
  1270. this.closeDialog({silent: true});
  1271. return false;
  1272. },
  1273. /**
  1274. * Duplicate this row
  1275. */
  1276. duplicateHandler: function () {
  1277. this.builder.addHistoryEntry('row_duplicated');
  1278. var duplicateRow = this.model.clone(this.builder.model);
  1279. this.builder.model.get('rows').add( duplicateRow, {
  1280. at: this.builder.model.get('rows').indexOf(this.model) + 1
  1281. } );
  1282. this.closeDialog({silent: true});
  1283. return false;
  1284. },
  1285. closeHandler: function() {
  1286. this.clearCellStylesCache();
  1287. if( ! _.isUndefined(this.cellStyles) ) {
  1288. this.cellStyles = undefined;
  1289. }
  1290. },
  1291. });
  1292. },{}],9:[function(require,module,exports){
  1293. var panels = window.panels, $ = jQuery;
  1294. var jsWidget = require( '../view/widgets/js-widget' );
  1295. module.exports = panels.view.dialog.extend( {
  1296. builder: null,
  1297. sidebarWidgetTemplate: _.template( panels.helpers.utils.processTemplate( $( '#siteorigin-panels-dialog-widget-sidebar-widget' ).html() ) ),
  1298. dialogClass: 'so-panels-dialog-edit-widget',
  1299. dialogIcon: 'add-widget',
  1300. widgetView: false,
  1301. savingWidget: false,
  1302. editableLabel: true,
  1303. events: {
  1304. 'click .so-close': 'saveHandler',
  1305. 'click .so-nav.so-previous': 'navToPrevious',
  1306. 'click .so-nav.so-next': 'navToNext',
  1307. // Action handlers
  1308. 'click .so-toolbar .so-delete': 'deleteHandler',
  1309. 'click .so-toolbar .so-duplicate': 'duplicateHandler'
  1310. },
  1311. initializeDialog: function () {
  1312. var thisView = this;
  1313. this.model.on( 'change:values', this.handleChangeValues, this );
  1314. this.model.on( 'destroy', this.remove, this );
  1315. // Refresh panels data after both dialog form components are loaded
  1316. this.dialogFormsLoaded = 0;
  1317. this.on( 'form_loaded styles_loaded', function () {
  1318. this.dialogFormsLoaded ++;
  1319. if ( this.dialogFormsLoaded === 2 ) {
  1320. thisView.updateModel( {
  1321. refreshArgs: {
  1322. silent: true
  1323. }
  1324. } );
  1325. }
  1326. } );
  1327. this.on( 'edit_label', function ( text ) {
  1328. // If text is set to default value, just clear it.
  1329. if ( text === panelsOptions.widgets[ this.model.get( 'class' ) ][ 'title' ] ) {
  1330. text = '';
  1331. }
  1332. this.model.set( 'label', text );
  1333. if ( _.isEmpty( text ) ) {
  1334. this.$( '.so-title' ).text( this.model.getWidgetField( 'title' ) );
  1335. }
  1336. }.bind( this ) );
  1337. },
  1338. /**
  1339. * Render the widget dialog.
  1340. */
  1341. render: function () {
  1342. // Render the dialog and attach it to the builder interface
  1343. this.renderDialog( this.parseDialogContent( $( '#siteorigin-panels-dialog-widget' ).html(), {} ) );
  1344. this.loadForm();
  1345. var title = this.model.getWidgetField( 'title' );
  1346. this.$( '.so-title .widget-name' ).html( title );
  1347. this.$( '.so-edit-title' ).val( title );
  1348. if( ! this.builder.supports( 'addWidget' ) ) {
  1349. this.$( '.so-buttons .so-duplicate' ).remove();
  1350. }
  1351. if( ! this.builder.supports( 'deleteWidget' ) ) {
  1352. this.$( '.so-buttons .so-delete' ).remove();
  1353. }
  1354. // Now we need to attach the style window
  1355. this.styles = new panels.view.styles();
  1356. this.styles.model = this.model;
  1357. this.styles.render( 'widget', this.builder.config.postId, {
  1358. builderType: this.builder.config.builderType,
  1359. dialog: this
  1360. } );
  1361. var $rightSidebar = this.$( '.so-sidebar.so-right-sidebar' );
  1362. this.styles.attach( $rightSidebar );
  1363. // Handle the loading class
  1364. this.styles.on( 'styles_loaded', function ( hasStyles ) {
  1365. // If we have styles remove the loading spinner, else remove the whole empty sidebar.
  1366. if ( hasStyles ) {
  1367. $rightSidebar.removeClass( 'so-panels-loading' );
  1368. } else {
  1369. $rightSidebar.closest( '.so-panels-dialog' ).removeClass( 'so-panels-dialog-has-right-sidebar' );
  1370. $rightSidebar.remove();
  1371. }
  1372. }, this );
  1373. $rightSidebar.addClass( 'so-panels-loading' );
  1374. },
  1375. /**
  1376. * Get the previous widget editing dialog by looking at the dom.
  1377. * @returns {*}
  1378. */
  1379. getPrevDialog: function () {
  1380. var widgets = this.builder.$( '.so-cells .cell .so-widget' );
  1381. if ( widgets.length <= 1 ) {
  1382. return false;
  1383. }
  1384. var currentIndex = widgets.index( this.widgetView.$el );
  1385. if ( currentIndex === 0 ) {
  1386. return false;
  1387. } else {
  1388. do {
  1389. widgetView = widgets.eq( --currentIndex ).data( 'view' );
  1390. if ( ! _.isUndefined( widgetView ) && ! widgetView.model.get( 'read_only' ) ) {
  1391. return widgetView.getEditDialog();
  1392. }
  1393. } while( ! _.isUndefined( widgetView ) && currentIndex > 0 );
  1394. }
  1395. return false;
  1396. },
  1397. /**
  1398. * Get the next widget editing dialog by looking at the dom.
  1399. * @returns {*}
  1400. */
  1401. getNextDialog: function () {
  1402. var widgets = this.builder.$( '.so-cells .cell .so-widget' );
  1403. if ( widgets.length <= 1 ) {
  1404. return false;
  1405. }
  1406. var currentIndex = widgets.index( this.widgetView.$el ), widgetView;
  1407. if ( currentIndex === widgets.length - 1 ) {
  1408. return false;
  1409. } else {
  1410. do {
  1411. widgetView = widgets.eq( ++currentIndex ).data( 'view' );
  1412. if ( ! _.isUndefined( widgetView ) && ! widgetView.model.get( 'read_only' ) ) {
  1413. return widgetView.getEditDialog();
  1414. }
  1415. } while( ! _.isUndefined( widgetView ) );
  1416. }
  1417. return false;
  1418. },
  1419. /**
  1420. * Load the widget form from the server.
  1421. * This is called when rendering the dialog for the first time.
  1422. */
  1423. loadForm: function () {
  1424. // don't load the form if this dialog hasn't been rendered yet
  1425. if ( ! this.$( '> *' ).length ) {
  1426. return;
  1427. }
  1428. this.$( '.so-content' ).addClass( 'so-panels-loading' );
  1429. var data = {
  1430. 'action': 'so_panels_widget_form',
  1431. 'widget': this.model.get( 'class' ),
  1432. 'instance': JSON.stringify( this.model.get( 'values' ) ),
  1433. 'raw': this.model.get( 'raw' )
  1434. };
  1435. $.post(
  1436. panelsOptions.ajaxurl,
  1437. data,
  1438. function ( result ) {
  1439. // Add in the CID of the widget model
  1440. var html = result.replace( /{\$id}/g, this.model.cid );
  1441. // Load this content into the form
  1442. var $soContent = this.$( '.so-content' );
  1443. $soContent
  1444. .removeClass( 'so-panels-loading' )
  1445. .html( html );
  1446. // Trigger all the necessary events
  1447. this.trigger( 'form_loaded', this );
  1448. // For legacy compatibility, trigger a panelsopen event
  1449. this.$( '.panel-dialog' ).trigger( 'panelsopen' );
  1450. // If the main dialog is closed from this point on, save the widget content
  1451. this.on( 'close_dialog', this.updateModel, this );
  1452. var widgetContent = $soContent.find( '> .widget-content' );
  1453. // If there's a widget content wrapper, this is one of the new widgets in WP 4.8 which need some special
  1454. // handling in JS.
  1455. if ( widgetContent.length > 0 ) {
  1456. jsWidget.addWidget( $soContent, this.model.widget_id );
  1457. }
  1458. }.bind( this ),
  1459. 'html'
  1460. );
  1461. },
  1462. /**
  1463. * Save the widget from the form to the model
  1464. */
  1465. updateModel: function ( args ) {
  1466. args = _.extend( {
  1467. refresh: true,
  1468. refreshArgs: null
  1469. }, args );
  1470. // Get the values from the form and assign the new values to the model
  1471. this.savingWidget = true;
  1472. if ( ! this.model.get( 'missing' ) ) {
  1473. // Only get the values for non missing widgets.
  1474. var values = this.getFormValues();
  1475. if ( _.isUndefined( values.widgets ) ) {
  1476. values = {};
  1477. } else {
  1478. values = values.widgets;
  1479. values = values[Object.keys( values )[0]];
  1480. }
  1481. this.model.setValues( values );
  1482. this.model.set( 'raw', true ); // We've saved from the widget form, so this is now raw
  1483. }
  1484. if ( this.styles.stylesLoaded ) {
  1485. // If the styles view has loaded
  1486. var style = {};
  1487. try {
  1488. style = this.getFormValues( '.so-sidebar .so-visual-styles' ).style;
  1489. }
  1490. catch ( e ) {
  1491. }
  1492. this.model.set( 'style', style );
  1493. }
  1494. this.savingWidget = false;
  1495. if ( args.refresh ) {
  1496. this.builder.model.refreshPanelsData(

Large files files are truncated, but you can click here to view the full file