fl-builder-ui-panel-content-library.js 28 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012
  1. (function($, FLBuilder) {
  2. /**
  3. * Panel tab controller. A controller object exists for each of the tabs
  4. * in the content panel.
  5. */
  6. var PanelTab = FLExtendableObject.create({
  7. handle: "",
  8. name: "",
  9. panel: null,
  10. shouldShowTabItem: true,
  11. isShowing: false,
  12. views: {},
  13. activeView: null,
  14. defaultView: null,
  15. categorySelector: null,
  16. /**
  17. * Initialize the tab
  18. *
  19. * @param {Object} args
  20. * @return void
  21. */
  22. init: function(args) {
  23. // Order matters here
  24. // Init category selector
  25. this.categorySelector = CategorySelector.create({
  26. handle: 'selector-' + this.handle,
  27. tab: this,
  28. items: []
  29. });
  30. this.categorySelector.init();
  31. $(this.categorySelector).on('categorySelected', this.onViewSelected.bind(this));
  32. // Init view object
  33. var views = args.views;
  34. this.initViews(args.views);
  35. // Ensure at least one view
  36. if (Object.keys(this.views).length === 0) {
  37. var view = {
  38. handle: "noViews",
  39. name: "No Views",
  40. templateName: "fl-content-panel-no-view"
  41. };
  42. this.addView(view);
  43. }
  44. // Ensure an Active View
  45. if (!this.activeView) {
  46. var key = Object.keys(this.views)[0];
  47. var view = this.views[key];
  48. this.activeView = view;
  49. }
  50. this.defaultView = this.activeView;
  51. $(this.panel).on('afterRender', this.renderView.bind(this, this.activeView ));
  52. $(this.panel).on('onShow onShowTab', this.initScroller.bind(this) );
  53. FLBuilder.addHook('contentItemsChanged', this.onLibraryDataChanged.bind(this));
  54. },
  55. /**
  56. * Initialize all views
  57. *
  58. * @param {Object} views
  59. * @return void
  60. */
  61. initViews: function(views) {
  62. for( var i in views) {
  63. var args = views[i];
  64. this.categorySelector.addItem(args);
  65. if ( 'separator' !== args.type ) {
  66. this.addView(args);
  67. }
  68. }
  69. },
  70. /**
  71. * Init a new view and add to this.views
  72. *
  73. * @param {Object} args
  74. * @return void
  75. */
  76. addView: function(args) {
  77. var viewType = PanelView;
  78. switch(this.handle) {
  79. case 'modules':
  80. viewType = ModulesPanelView;
  81. break;
  82. case 'rows':
  83. viewType = RowsPanelView;
  84. break;
  85. case 'templates':
  86. viewType = TemplatesPanelView;
  87. break;
  88. case 'saved':
  89. viewType = SavedPanelView;
  90. break;
  91. default:
  92. viewType = PanelView;
  93. }
  94. if (!_.isNull(this.viewController) && !_.isUndefined(this.viewController)) {
  95. viewType = window[this.viewController];
  96. }
  97. var view = viewType.create(args),
  98. handle = view.handle;
  99. view.init();
  100. this.views[handle] = view;
  101. if (view.isShowing) this.activeView = view;
  102. },
  103. /**
  104. * Render a view into the tab dom
  105. *
  106. * @param String | {Object} name
  107. * @return void
  108. */
  109. renderView: function(name) {
  110. this.$el = this.panel.$el.find('.fl-builder--panel-view[data-tab="' + this.handle + '"]');
  111. // Test if object was passed or string handle
  112. if (_.isObject(name)) {
  113. var view = name;
  114. } else {
  115. var view = this.views[name];
  116. }
  117. if (!_.isObject(view) || !_.isFunction(view.render)) return;
  118. var html = view.render();
  119. this.$el.find('.fl-nanoscroller-content').html(html);
  120. this.activeView = view;
  121. FLBuilder._initSortables();
  122. if ( this === this.panel.activeTab ) {
  123. this.renderGroupSelector();
  124. }
  125. this.initScroller();
  126. this.$el.find('.fl-nanoscroller-content').scrollTop(0);
  127. },
  128. /**
  129. * Setup nanoscroller on the current panel view.
  130. *
  131. * @return void
  132. */
  133. initScroller: function() {
  134. this.$el.nanoScroller({
  135. alwaysVisible: true,
  136. preventPageScrolling: true,
  137. paneClass: 'fl-nanoscroller-pane',
  138. sliderClass: 'fl-nanoscroller-slider',
  139. contentClass: 'fl-nanoscroller-content'
  140. });
  141. },
  142. /**
  143. * Show this tab
  144. *
  145. * @return void
  146. */
  147. show: function() {
  148. $(this.activeView).trigger('onBeforeShow');
  149. this.renderGroupSelector();
  150. this.isShowing = true;
  151. this.$el.addClass('is-showing');
  152. this.$el.find('.fl-nanoscroller-content').scrollTop(0);
  153. },
  154. /**
  155. * Hide the tab
  156. *
  157. * @return void
  158. */
  159. hide: function() {
  160. this.isShowing = false;
  161. this.$el.removeClass('is-showing');
  162. if ( this.activeView !== this.defaultView ) {
  163. this.renderView( this.defaultView );
  164. }
  165. if (_.isObject(this.categorySelector)) {
  166. this.categorySelector.close();
  167. }
  168. },
  169. /**
  170. * Render the group selector into the panel header if tab has multiple views.
  171. *
  172. * @return void
  173. */
  174. renderGroupSelector: function() {
  175. var $groupSelect = this.panel.$groupSelect,
  176. $search = this.panel.$el.find('.fl-builder-panel-search');
  177. if ( this.isSearchEnabled ) {
  178. $search.show();
  179. } else {
  180. $search.hide();
  181. }
  182. if ( Object.keys(this.views).length > 1 && !_.isUndefined( this.categorySelector ) ) {
  183. var html = this.categorySelector.render(),
  184. header = this.panel.$el.find('.fl-builder-content-group-select');
  185. $groupSelect.html(html);
  186. $groupSelect.show();
  187. this.panel.$el.removeClass('single-view');
  188. } else {
  189. $groupSelect.hide();
  190. $search.hide();
  191. this.panel.$el.addClass('single-view');
  192. }
  193. },
  194. /**
  195. * Handle a view being chosen
  196. *
  197. * @param {Event} e
  198. * @param {Object} viewName
  199. * @return void
  200. */
  201. onViewSelected: function(e, viewName) {
  202. this.renderView(viewName);
  203. this.categorySelector.close();
  204. },
  205. /**
  206. * Handle update of library data
  207. *
  208. * @return void
  209. */
  210. onLibraryDataChanged: function() {
  211. this.renderView( this.activeView );
  212. },
  213. });
  214. /**
  215. * Panel view controller prototype. See controllers for module,
  216. * row, template and saved views below.
  217. */
  218. var PanelView = FLExtendableObject.create({
  219. /**
  220. * The wp.template reference.
  221. */
  222. templateName: '',
  223. /**
  224. * String name of the view.
  225. */
  226. name: '',
  227. /**
  228. * String handle for the view.
  229. */
  230. handle: '',
  231. /**
  232. * Query to retrieve content items.
  233. */
  234. query: null,
  235. /**
  236. * Initialize view controller
  237. *
  238. * @return void
  239. */
  240. init: function() {
  241. this.template = wp.template(this.templateName);
  242. $(this).on('afterRender', this.bindEvents.bind(this));
  243. $(this).trigger('afterInit');
  244. },
  245. /**
  246. * Filter the data object before it's passed to the wp.template function
  247. *
  248. * @param {object} data
  249. * @return {object}
  250. */
  251. filterTemplateData: function(data) {
  252. if (!_.isNull(this.query) && !_.isUndefined(this.query)) {
  253. data.queryResults = FLBuilder.Search.byQuery(this.query);
  254. }
  255. return data;
  256. },
  257. /**
  258. * Render view html.
  259. *
  260. * @return jQuery DOM
  261. */
  262. render: function() {
  263. $(this).trigger('beforeRender');
  264. var data = this;
  265. data = this.filterTemplateData(data);
  266. var $html = $(this.template(data));
  267. this.$el = $html;
  268. $(this).trigger('afterRender');
  269. return $html;
  270. },
  271. /**
  272. * Setup event listeners. Fired after render.
  273. *
  274. * @return void
  275. */
  276. bindEvents: function() {},
  277. /**
  278. * Stub for child objects to extend
  279. *
  280. * @return void
  281. */
  282. transitionIn:function() {},
  283. /**
  284. * Stub for child objects to extend
  285. *
  286. * @return void
  287. */
  288. transitionOut: function() {},
  289. });
  290. /**
  291. * Panel view controller for module views.
  292. */
  293. var ModulesPanelView = PanelView.create({
  294. /**
  295. * The wp.template reference.
  296. */
  297. templateName: 'fl-content-panel-modules-view',
  298. /**
  299. * Bind Events
  300. *
  301. * @return void
  302. */
  303. bindEvents: function() {
  304. this.$sections = this.$el; // should really change this in the template.
  305. this.$items = this.$el.find('.fl-builder-block, .fl-builder-blocks-section-title');
  306. },
  307. });
  308. /**
  309. * Panel view controller for row views.
  310. */
  311. var RowsPanelView = PanelView.create({
  312. /**
  313. * The wp.template reference.
  314. */
  315. templateName: 'fl-content-panel-row-templates-view',
  316. /**
  317. * Bind events
  318. *
  319. * @return void
  320. */
  321. bindEvents: function() {
  322. this.$items = this.$el.find('.fl-builder-block, .fl-builder-blocks-section-title');
  323. },
  324. });
  325. /**
  326. * Panel view controller for template views
  327. */
  328. var TemplatesPanelView = PanelView.create({
  329. /**
  330. * The wp.template reference.
  331. */
  332. templateName: 'fl-content-panel-templates-view',
  333. /**
  334. * Bind event listeners. Fires after render.
  335. *
  336. * @return void
  337. */
  338. bindEvents: function() {
  339. this.$items = this.$el.find('.fl-builder--template-collection-item');
  340. this.$items.on('click', this.onTemplateClick.bind(this));
  341. this.$userTemplateSections = $('.fl-user-templates');
  342. this.$userTemplates = this.$el.find('.fl-user-template, .fl-builder--save-new-user-template');
  343. this.$saveNewTemplateInput = this.$el.find('.fl-save-control input[name="template-name"]');
  344. this.$saveNewTemplateCat = this.$el.find('.fl-save-control input[name="template-category"]');
  345. this.$saveNewTemplateBtn = this.$el.find('.fl-save-control button');
  346. this.$saveNewMask = this.$el.find('.fl-save-control-mask');
  347. this.$saveNewTemplateInput.on('focus', this.onSaveInputFocus.bind(this));
  348. this.$saveNewTemplateInput.on('keyup', this.onSaveInputKeyup.bind(this));
  349. this.$saveNewTemplateBtn.on('click', this.onSaveButtonClick.bind(this));
  350. this.$saveNewMask.on('click', this.resetSaveInput.bind(this));
  351. },
  352. /**
  353. * Handle input focus
  354. *
  355. * @return void
  356. */
  357. onSaveInputFocus: function() {
  358. this.resetSaveInput();
  359. this.$saveNewMask.show();
  360. },
  361. /**
  362. * Clear the input and collapse
  363. *
  364. * @return void
  365. */
  366. resetSaveInput: function() {
  367. this.$saveNewTemplateInput.val("");
  368. this.$saveNewTemplateBtn.hide();
  369. this.$saveNewMask.hide();
  370. },
  371. /**
  372. * Handle key up
  373. *
  374. * @param {Event} e
  375. * @return void
  376. */
  377. onSaveInputKeyup: function(e) {
  378. var input = $(e.currentTarget),
  379. value = input.val(),
  380. button = input.siblings('button');
  381. if (value !== '') {
  382. button.show();
  383. } else {
  384. button.hide();
  385. }
  386. },
  387. /**
  388. * Handle save button click
  389. *
  390. * @param {Event} e
  391. * @return void
  392. */
  393. onSaveButtonClick: function(e) {
  394. var button = $(e.currentTarget),
  395. value = button.siblings('input[name="template-name"]').val(),
  396. category = button.siblings('input[name="template-category"]').val(),
  397. settings = {
  398. name: value,
  399. category: category
  400. };
  401. if ("" !== value) {
  402. FLBuilder.ajax({
  403. action: 'save_user_template',
  404. settings: settings,
  405. }, FLBuilder._saveUserTemplateSettingsComplete);
  406. }
  407. },
  408. /**
  409. * Handle template clicked event
  410. * @return void
  411. */
  412. onTemplateClick: function(e) {
  413. var $item = $(e.currentTarget),
  414. id = $item.data('id'),
  415. type = $item.data('type');
  416. FLBuilder._requestTemplateInsert(id, type);
  417. },
  418. });
  419. /**
  420. * Panel view controller for saved modules and rows.
  421. */
  422. var SavedPanelView = PanelView.create({
  423. /**
  424. * The wp.template reference
  425. */
  426. templateName: 'fl-content-panel-saved-view',
  427. /**
  428. * Filter the data before it's given to the template function
  429. *
  430. * @param {object} data
  431. * @return {object}
  432. */
  433. filterTemplateData: function(data) {
  434. data.queryResults = FLBuilder.Search.byQuery({
  435. kind: "template",
  436. type: "user",
  437. content: ["module", "column", "row"]
  438. });
  439. return data;
  440. },
  441. });
  442. /**
  443. * Controller for category chooser.
  444. * One of these objects gets used by a PanelTab and rendered above the current panel view.
  445. */
  446. var CategorySelector = FLExtendableObject.create({
  447. /**
  448. * The wp.template reference.
  449. */
  450. templateName: 'fl-content-panel-category-selector',
  451. /**
  452. * Template function retreived by wp.template()
  453. */
  454. template: null,
  455. /**
  456. * Reference to the tab controller that owns this selector.
  457. */
  458. tab: null,
  459. /**
  460. * Whether or not the selector's menu is currently open.
  461. */
  462. isOpen: false,
  463. /**
  464. * The items to list in the menu
  465. */
  466. items: {},
  467. /**
  468. * Initial setup
  469. *
  470. * @return void
  471. */
  472. init: function() {
  473. this.template = wp.template(this.templateName);
  474. $(this).on('afterRender', this.bindEvents.bind(this));
  475. $(this.tab.panel).on('didShowSearchControls', this.close.bind(this) );
  476. },
  477. /**
  478. * Render the html for the selector. Requires this.tab to be set.
  479. *
  480. * @return jQuery DOM
  481. */
  482. render: function() {
  483. this.close();
  484. var $html = $(this.template(this));
  485. this.$el = $html;
  486. $(this).trigger('afterRender');
  487. return $html;
  488. },
  489. /**
  490. * Bind event listeners. Triggered after render.
  491. *
  492. * @return void
  493. */
  494. bindEvents: function() {
  495. this.$selectorTitle = this.$el.find('.fl-builder--selector-display');
  496. this.$selectorTitle.on('click', this.toggleOpenClose.bind(this));
  497. this.$categories = this.$el.find('.fl-builder--selector-menu .fl-builder--menu-item');
  498. this.$categories.on('click', this.onCategoryClick.bind(this));
  499. },
  500. /**
  501. * Add an item to the menu
  502. *
  503. * @param {object} item
  504. * @return void
  505. */
  506. addItem: function(item) {
  507. var handle;
  508. if( _.isUndefined(item.handle)) {
  509. handle = _.uniqueId('sep_');
  510. } else {
  511. handle = item.handle;
  512. }
  513. this.items[handle] = item;
  514. },
  515. /**
  516. * Open the menu.
  517. *
  518. * @return void
  519. */
  520. open: function() {
  521. if (this.isOpen) return;
  522. this.$el.addClass('is-showing');
  523. this.isOpen = true;
  524. },
  525. /**
  526. * Close the menu.
  527. *
  528. * @return void
  529. */
  530. close: function() {
  531. if (!this.isOpen) return;
  532. this.$el.removeClass('is-showing');
  533. this.isOpen = false;
  534. this.$selectorTitle.find("button").focus();
  535. },
  536. /**
  537. * Toggle the menu between open and closed states.
  538. *
  539. * @return void
  540. */
  541. toggleOpenClose: function() {
  542. if (this.isOpen) {
  543. this.close();
  544. } else {
  545. this.open();
  546. }
  547. },
  548. /**
  549. * Fired one would of the menu items is clicked.
  550. *
  551. * {Event} e
  552. * @return void
  553. */
  554. onCategoryClick: function(e) {
  555. var viewName = $(e.target).data('view');
  556. $(this).trigger('categorySelected', viewName);
  557. },
  558. });
  559. /**
  560. * Panel housing all the draggable content items and templates for the builder.
  561. */
  562. FLBuilder.ContentPanel = FLExtendableObject.create({
  563. /**
  564. * Name of the js template for the panel.
  565. */
  566. templateName: 'fl-content-panel-base',
  567. /**
  568. * wp.template function to render the panel
  569. */
  570. template: null,
  571. /**
  572. * Tab section controller objects.
  573. */
  574. tabs: {},
  575. /**
  576. * A reference to the active tab controller object.
  577. */
  578. activeTab: null,
  579. /**
  580. * Whether or not the panel is currently visible.
  581. */
  582. isShowing: false,
  583. /**
  584. * Initialize and render the panel.
  585. *
  586. * @return void
  587. */
  588. init: function() {
  589. if (!FLBuilderConfig.panelData) return;
  590. var items = FLBuilderConfig.panelData.tabs;
  591. for( var i in items) {
  592. var item = items[i];
  593. tab = PanelTab.create(item);
  594. tab.panel = this;
  595. tab.views = {};
  596. tab.init(item);
  597. this.tabs[i] = tab;
  598. if (tab.isShowing) {
  599. this.activeTab = tab;
  600. }
  601. if (!this.activeTab) {
  602. var firstTab = Object.keys(this.tabs)[0];
  603. var tab = this.tabs[firstTab];
  604. tab.isShowing = true;
  605. this.activeTab = tab;
  606. }
  607. }
  608. // Render panel
  609. this.template = wp.template(this.templateName);
  610. this.render();
  611. this.renderSearchResults = wp.template('fl-search-results-panel');
  612. this.renderNoResults = wp.template('fl-search-no-results');
  613. FLBuilder.triggerHook('contentPanelDidInit');
  614. },
  615. /**
  616. * Render the base HTML for the panel
  617. *
  618. * @return void
  619. */
  620. render: function() {
  621. $('body').prepend(this.template(this));
  622. this.$el = $(".fl-builder--content-library-panel");
  623. this.bindEvents();
  624. this.$groupSelect = this.$el.find('.fl-builder-content-group-select');
  625. $(this).trigger('afterRender');
  626. },
  627. /**
  628. * Setup event listeners for the base panel.
  629. *
  630. * @return void
  631. */
  632. bindEvents: function() {
  633. this.$tabs = this.$el.find('.fl-builder--tabs [data-tab]');
  634. this.$tabs.on('mouseup', this.onTabItemMouseUp.bind( this ));
  635. this.$tabs.on('click', this.onTabItemClick.bind( this ));
  636. this.$search = this.$el.find('.fl-builder-panel-search');
  637. this.$searchBtn = this.$search.find('.fl-builder-toggle-panel-search');
  638. this.$searchInput = this.$search.find('input[name="search-term"]');
  639. this.$searchBtn.on('click', this.onSearchButtonClicked.bind(this) );
  640. this.$search.find('.fl-builder-dismiss-panel-search').on('click', this.onDismissButtonClicked.bind(this) );
  641. this.$searchInput.on('keyup', this.onSearchTermChanged.bind(this) );
  642. this.$searchPanel = this.$el.find('.fl-builder--search-results-panel');
  643. FLBuilder.addHook('showContentPanel', this.show.bind( this ));
  644. FLBuilder.addHook('showModules', this.show.bind( this, 'modules' ));
  645. FLBuilder.addHook('showRows', this.show.bind( this, 'rows' ));
  646. FLBuilder.addHook('showTemplates', this.show.bind( this, 'templates' ));
  647. FLBuilder.addHook('showSaved', this.show.bind( this, 'saved' ));
  648. FLBuilder.addHook('showSearch', this.goToSearch.bind(this) );
  649. var hide = this.hide.bind(this);
  650. FLBuilder.addHook('hideContentPanel', hide );
  651. FLBuilder.addHook('didShowLightbox', hide );
  652. FLBuilder.addHook('didShowPublishActions', hide );
  653. FLBuilder.addHook('didBeginSearch', hide );
  654. FLBuilder.addHook('didInitDrag', hide );
  655. FLBuilder.addHook('didOpenMainMenu', hide );
  656. FLBuilder.addHook('didApplyTemplate', hide );
  657. var toggle = this.toggleShowHide.bind( this );
  658. FLBuilder.addHook('toggleContentPanel', toggle );
  659. FLBuilder.addHook('didStopDrag', this.hideSearchControls.bind(this) );
  660. },
  661. /**
  662. * Align the panel arrow with the + button
  663. */
  664. alignPanelArrow: function() {
  665. var $panel = this.$el,
  666. panelOffset = null,
  667. $arrow = this.$el.find('.fl-builder--panel-arrow'),
  668. $button = $('.fl-builder-content-panel-button'),
  669. arrowOffset,
  670. arrowX,
  671. animationDuration = this.$el.css('animation-duration');
  672. if ( $button.length == 0 ) return;
  673. this.$el.css('animation-duration', '0s');
  674. this.show();
  675. panelOffset = $panel[0].getBoundingClientRect();
  676. arrowOffset = $arrow[0].getBoundingClientRect();
  677. this.hide();
  678. this.$el.css('animation-duration', animationDuration );
  679. buttonOffset = $button[0].getBoundingClientRect();
  680. var buttonCenterX = buttonOffset.x + ( buttonOffset.width / 2 );
  681. if ( buttonCenterX < panelOffset.x ) {
  682. // move the panel & the arrow
  683. arrowX = 20;
  684. } else {
  685. arrowX = ( buttonCenterX - panelOffset.x ) - ( arrowOffset.width / 2 );
  686. }
  687. /* Position the arrow */
  688. $arrow.css({
  689. right: 'auto',
  690. left: arrowX + 'px'
  691. });
  692. },
  693. /**
  694. * Show content panel
  695. *
  696. * @param String tabName
  697. * @return void
  698. */
  699. show: function(tabName) {
  700. if ( 'module' === FLBuilderConfig.userTemplateType || FLBuilderConfig.simpleUi ) {
  701. return;
  702. }
  703. FLBuilder.triggerHook('willShowContentPanel');
  704. if (typeof tabName !== 'undefined') {
  705. this.showTab(tabName);
  706. }
  707. if (this.isShowing) return;
  708. // Save existing settings first if any exist. Don't proceed if it fails.
  709. if ( ! FLBuilder._triggerSettingsSave( false, true ) ) {
  710. return;
  711. }
  712. $('body').addClass('fl-builder-content-panel-is-showing');
  713. this.isShowing = true;
  714. $(this).trigger('onShow');
  715. FLBuilder.triggerHook('didShowContentPanel');
  716. },
  717. /**
  718. * Hide content panel
  719. *
  720. * @return void
  721. */
  722. hide: function() {
  723. if ( ! this.isShowing ) {
  724. return;
  725. } else if ( this.$el.hasClass( 'fl-builder-ui-pinned' ) ) {
  726. return;
  727. }
  728. $('body').removeClass('fl-builder-content-panel-is-showing');
  729. this.isShowing = false;
  730. $(this).trigger('onHide');
  731. FLBuilder.triggerHook('didHideContentPanel');
  732. },
  733. /**
  734. * Toggle between show and hide states.
  735. *
  736. * @return void
  737. */
  738. toggleShowHide: function() {
  739. if (this.isShowing) {
  740. this.hide();
  741. } else {
  742. this.show();
  743. }
  744. },
  745. /**
  746. * Display one of the panel tabs
  747. *
  748. * @param String handle
  749. * @return void
  750. */
  751. showTab: function(handle) {
  752. var tab = this.tabs[handle];
  753. if (!_.isObject(tab)) return;
  754. if (_.isObject(this.activeTab)) {
  755. this.activeTab.hide();
  756. this.$tabs.filter('.is-showing').removeClass('is-showing');
  757. }
  758. this.hideSearchControls();
  759. tab.show();
  760. this.$tabs.filter('[data-tab="' + tab.handle + '"]').addClass('is-showing');
  761. this.activeTab = tab;
  762. $(this).trigger('onShowTab');
  763. },
  764. goToSearch: function() {
  765. this.show('modules');
  766. this.$el.find('.fl-builder-toggle-panel-search').trigger('click');
  767. },
  768. onTabItemMouseUp: function(e) {
  769. $(e.currentTarget).blur();
  770. },
  771. /**
  772. * Handle tab clicks.
  773. *
  774. * @param {Event} e
  775. * @return void
  776. */
  777. onTabItemClick: function(e) {
  778. var el = $(e.target),
  779. name = el.data('tab');
  780. this.showTab(name);
  781. },
  782. /**
  783. * Handle search icon click
  784. */
  785. onSearchButtonClicked: function() {
  786. this.showSearchControls();
  787. },
  788. onDismissButtonClicked: function() {
  789. this.hideSearchControls();
  790. this.$searchBtn.focus();
  791. },
  792. showSearchControls: function() {
  793. this.$search.addClass('is-showing-input');
  794. this.$search.find('input[name="search-term"]').focus();
  795. $('.fl-builder--selector-display-label').attr('tabindex', -1 );
  796. this.$searchBtn.attr('tabindex', -1 );
  797. $(this).trigger('didShowSearchControls');
  798. },
  799. hideSearchControls: function() {
  800. this.$search.removeClass('is-showing-input');
  801. this.clearSearchInput();
  802. this.hideSearchResults();
  803. $('.fl-builder--selector-display-label').attr('tabindex', null );
  804. this.$searchBtn.attr('tabindex', null );
  805. },
  806. onSearchTermChanged: function(e) {
  807. var value = this.$searchInput.val();
  808. if ( "" !== value ) {
  809. var results = FLBuilder.Search.byTerm(value);
  810. if (results.term != "") {
  811. this.showSearchResults(results);
  812. } else {
  813. this.hideSearchResults();
  814. }
  815. } else {
  816. this.hideSearchResults();
  817. }
  818. },
  819. clearSearchInput: function() {
  820. this.$searchInput.val("");
  821. this.hideSearchResults();
  822. },
  823. /**
  824. * Display the found results in the results panel.
  825. * @var Object - the found results
  826. * @return void
  827. */
  828. showSearchResults: function(data) {
  829. if (data.total > 0) {
  830. var $html = $(this.renderSearchResults(data));
  831. this.$searchPanel.html($html);
  832. FLBuilder._initSortables();
  833. } else {
  834. var $html = $(this.renderNoResults(data));
  835. this.$searchPanel.html($html);
  836. }
  837. $('body').addClass('fl-builder-search-results-panel-is-showing');
  838. },
  839. /**
  840. * Hide the search results panel
  841. * @return void
  842. */
  843. hideSearchResults: function() {
  844. $('body').removeClass('fl-builder-search-results-panel-is-showing');
  845. },
  846. });
  847. })(jQuery, FLBuilder);