PageRenderTime 55ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 1ms

/wp-content/themes/baton/js/baton-conductor.js

https://gitlab.com/diegoduncan21/geogrim
JavaScript | 433 lines | 268 code | 73 blank | 92 comment | 33 complexity | a67b344301177e9616bd6da1545d28a7 MD5 | raw file
  1. /**
  2. * Baton Conductor
  3. */
  4. var baton_conductor = baton_conductor || {};
  5. ( function ( wp, $ ) {
  6. "use strict";
  7. var api = wp.customize;
  8. /**
  9. * Document Ready
  10. */
  11. $( function() {
  12. var $baton_conductor_number = $( '.baton-conductor-number' ),
  13. $baton_conductor_flexbox_range = $( '.baton-conductor-flexbox-columns-range' ),
  14. $baton_conductor_output = $( '.baton-conductor-output' ),
  15. baton_conductor_section = api.section( 'baton_conductor' ),
  16. baton_conductor_posts_per_page_setting = api( 'baton_conductor[posts_per_page]' ),
  17. baton_conductor_posts_per_page_setting_changed = false,
  18. baton_conductor_disabled_control = api.control( 'baton_conductor[disabled]' ),
  19. baton_conductor_enhanced_display_disabled_setting = api( 'baton_conductor[enhanced_display_disabled]' ),
  20. baton_conductor_enhanced_display_disabled_control = api.control( 'baton_conductor[enhanced_display_disabled]' );
  21. // Baton Conductor Section
  22. if ( baton_conductor_section ) {
  23. // Append <sup> label to titles
  24. baton_conductor_section.container.find( '.accordion-section-title, .customize-section-title h3' ).append( '<sup>' + baton_conductor.customizer.section_sup_label + '</sup>' );
  25. }
  26. // Baton Conductor numbers, only allow numerical characters into input boxes
  27. $baton_conductor_number.on( 'keyup', function( e ) {
  28. var $this = $( this ), numeric_value = $this.val().replace( /[^0-9]/g, '' );
  29. if ( $this.val() != numeric_value ) {
  30. $this.val( numeric_value );
  31. }
  32. } );
  33. // On flexbox column change (jQuery "input" event)
  34. $baton_conductor_flexbox_range.on( 'input', function() {
  35. var $this = $( this );
  36. // Adjust the value
  37. $this.next( '.baton-conductor-flexbox-columns-value' ).html( $this.val() );
  38. } );
  39. // Initialize Backbone Views on Conductor Widgets (on initial page load)
  40. // Create a new output view and store it in widget data
  41. $baton_conductor_output.data( 'baton-conductor-output', new baton_conductor.views.output( {
  42. el: $baton_conductor_output, // Attach this view to the widgets output list
  43. collection: new baton_conductor.collections.output() // New collection
  44. } ) );
  45. // Baton Conductor Disabled Control
  46. if ( baton_conductor_disabled_control ) {
  47. // Append <sup> label to title
  48. baton_conductor_disabled_control.container.find( '.customize-control-title' ).append( ' <sup>' + baton_conductor.customizer.section_sup_label + '</sup>' + ' ' + baton_conductor.customizer.control_enabled_label );
  49. }
  50. // Baton Conductor Enhanced Display Disabled Control
  51. if ( baton_conductor_enhanced_display_disabled_control ) {
  52. // Append <sup> label to title
  53. baton_conductor_enhanced_display_disabled_control.container.find( '.customize-control-title' ).append( ' <sup>' + baton_conductor.customizer.section_sup_label + '</sup>' + ' ' + baton_conductor.customizer.control_enabled_label );
  54. }
  55. // Baton Conductor Posts Per Page Setting
  56. if ( baton_conductor_posts_per_page_setting && baton_conductor_enhanced_display_disabled_setting ) {
  57. // Baton Conductor Enhanced Display Disabled Setting
  58. baton_conductor_enhanced_display_disabled_setting.bind( function( setting ) {
  59. // If the enhanced display is disabled
  60. if ( setting && baton_conductor_posts_per_page_setting() === baton_conductor.defaults.posts_per_page ) {
  61. baton_conductor_posts_per_page_setting( ( baton_conductor.defaults.posts_per_page - baton_conductor.defaults.enhanced_display_posts_per_page_offset ) );
  62. }
  63. // Otherwise if it is enabled
  64. else if ( baton_conductor_posts_per_page_setting() === ( baton_conductor.defaults.posts_per_page - baton_conductor.defaults.enhanced_display_posts_per_page_offset ) ) {
  65. baton_conductor_posts_per_page_setting( baton_conductor.defaults.posts_per_page );
  66. }
  67. } );
  68. }
  69. } );
  70. /*******************
  71. * Backbone Models *
  72. *******************/
  73. baton_conductor.models = {
  74. output: Backbone.Model.extend( {
  75. // Model defaults
  76. defaults: {
  77. priority: baton_conductor.output.priority_step_size, // Default is 10
  78. id: false,
  79. label: false,
  80. type: false,
  81. visible: true
  82. }
  83. } )
  84. };
  85. /************************
  86. * Backbone Collections *
  87. ************************/
  88. baton_conductor.collections = {
  89. output: Backbone.Collection.extend( {
  90. model: baton_conductor.models.output
  91. } )
  92. };
  93. /******************
  94. * Backbone Views *
  95. ******************/
  96. // Baton Conductor Output
  97. baton_conductor.views = {
  98. output: Backbone.View.extend( {
  99. el: '.baton-conductor-output',
  100. $output_list: false,
  101. $output_list_items: false,
  102. $widget: false,
  103. collection: new baton_conductor.collections.output(),
  104. events: {
  105. // Labels (editable input elements)
  106. 'click .baton-conductor-output-element-label-editable.editable-input': 'editElementLabel',
  107. 'keypress .baton-conductor-output-element-label-editable.editable-input input': 'saveElementLabel',
  108. 'click .baton-conductor-output-element-label-editable.editable-input .baton-conductor-save': 'saveElementLabel', // Save
  109. 'click .baton-conductor-output-element-label-editable.editable-input .baton-conductor-discard': 'saveElementLabel', // Discard
  110. // Options (editable select elements)
  111. 'click .baton-conductor-output-element-label-editable.editable-select': 'editElementOption',
  112. 'click .baton-conductor-output-element-label-editable.editable-select .baton-conductor-save': 'saveElementOption', // Save
  113. 'click .baton-conductor-output-element-label-editable.editable-select .baton-conductor-discard': 'saveElementOption', // Discard
  114. // Link
  115. 'click .baton-conductor-link' : 'toggleLink', // Link
  116. // Visibility
  117. 'click .baton-conductor-output-element-controls .baton-conductor-visibility' : 'toggleVisibility', // Visibility
  118. // jQuery Sortable
  119. 'sortstop .baton-conductor-output-list' : 'sortableStop' // jQuery Sortable Stop
  120. },
  121. // jQuery Sortable options
  122. sortable_options: {
  123. handle: '.dashicons-sort',
  124. axis: 'y', // Vertically
  125. cursor: 'move',
  126. placeholder: 'ui-state-placeholder'
  127. },
  128. initialize: function() {
  129. var self = this;
  130. // Bind "this" to all functions/callbacks
  131. _.bindAll( this,
  132. 'render',
  133. 'editElementLabel',
  134. 'saveElementLabel',
  135. 'editElementOption',
  136. 'saveElementOption',
  137. 'toggleLink',
  138. 'toggleVisibility',
  139. 'sortableStop',
  140. 'destroy' );
  141. // Store a reference to the widget
  142. this.$widget = this.$el.parents( '.widget' );
  143. // Store a reference to the output element list
  144. this.$output_list = this.$el.find( '.baton-conductor-output-list' );
  145. // Store a reference to the output element list
  146. this.$output_list_items = this.$output_list.find( 'li' );
  147. /*
  148. * Backbone Models
  149. */
  150. this.$output_list_items.each( function() {
  151. var $self = $( this ), model = new baton_conductor.models.output( {
  152. priority: ( ( $self.index() + 1 ) * baton_conductor.output.priority_step_size ), // Default is 10
  153. id: $self.attr( 'data-id' ),
  154. label: $self.attr( 'data-label' ),
  155. type: $self.attr( 'data-type' ),
  156. link: ( $self.attr( 'data-link' ) === 'true' ),
  157. visible: ( $self.attr( 'data-visible' ) === 'true' )
  158. } );
  159. // Add the new model to the collection
  160. self.collection.add( model );
  161. } );
  162. /*
  163. * jQuery Sortable - Initialize jQuery Sortable
  164. */
  165. this.$output_list.sortable( this.sortable_options );
  166. },
  167. editElementLabel: function( event ) {
  168. var $el = $( event.currentTarget );
  169. $el.addClass( 'editing' );
  170. $el.find( 'input' ).focus().attr( 'data-current', $el.find( 'input').val() );
  171. },
  172. editElementOption: function( event ) {
  173. var $el = $( event.currentTarget );
  174. $el.addClass( 'editing' );
  175. $el.find( 'select' ).attr( 'data-current', $el.find( 'select' ).val() );
  176. },
  177. saveElementLabel: function( event ) {
  178. var $el = $( event.currentTarget ),
  179. $output_element,
  180. escaped_val,
  181. original,
  182. $input;
  183. // Enter (save)
  184. if ( event.type === 'keypress' && event.which === 13 ) {
  185. $output_element = $el.closest( '.baton-conductor-output-element' );
  186. escaped_val = _.escape( $el.val() );
  187. // Remove editing class from label wrapper
  188. $el.parents( '.baton-conductor-output-element-label' ).removeClass( 'editing' );
  189. // Set the current label value
  190. if ( escaped_val.length ) {
  191. $output_element.attr( 'data-label', escaped_val ).find( '.label' ).html( escaped_val );
  192. }
  193. // No label entered, revert back to original
  194. else {
  195. original = $el.attr( 'data-original' );
  196. // Reset back to the original value
  197. $el.val( '' );
  198. $output_element.attr( 'data-label', original ).find( '.label' ).html( original );
  199. }
  200. // Update the sortable data
  201. this.sortableStop( false, false );
  202. //event.preventDefault();
  203. }
  204. // Click (save or discard)
  205. if ( event.type === 'click' ) {
  206. $output_element = $el.closest( '.baton-conductor-output-element' );
  207. $input = $el.parent().find( 'input' );
  208. escaped_val = _.escape( $input.val() );
  209. // Remove editing class from label wrapper
  210. $el.parents( '.baton-conductor-output-element-label' ).removeClass( 'editing' );
  211. // Save
  212. if ( $el.hasClass( 'baton-conductor-save' ) ) {
  213. // Set the current label value
  214. if ( escaped_val.length ) {
  215. $output_element.attr( 'data-label', escaped_val ).find( '.label' ).html( escaped_val );
  216. }
  217. // No label entered, revert back to original
  218. else {
  219. original = $input.attr( 'data-original' );
  220. // Reset back to the original value
  221. $el.val( '' );
  222. $output_element.attr( 'data-label', original ).find( '.label' ).html( original );
  223. }
  224. // Update the sortable data
  225. this.sortableStop( false, false );
  226. }
  227. // Discard
  228. if ( $el.hasClass( 'baton-conductor-discard' ) ) {
  229. // Reset back to the original value
  230. $input.val( $input.attr( 'data-current' ) );
  231. }
  232. // Prevent Default and Propagation
  233. event.preventDefault();
  234. event.stopPropagation();
  235. }
  236. },
  237. saveElementOption: function( event ) {
  238. var $el = $( event.currentTarget ),
  239. $output_element = $el.closest( '.baton-conductor-output-element' ),
  240. $select = $el.parent().find( 'select' ),
  241. escaped_val = _.escape( $select.val()),
  242. $selected = $select.find( ':selected' );
  243. // Remove editing class from label wrapper
  244. $el.parents( '.baton-conductor-output-element-label' ).removeClass( 'editing' );
  245. // Save
  246. if ( $el.hasClass( 'baton-conductor-save' ) ) {
  247. // Set the current value
  248. if ( escaped_val.length ) {
  249. $output_element.attr( 'data-value', escaped_val ).attr( 'data-label', $selected.attr( 'data-label' ) ).find( '.label' ).html( $selected.attr( 'data-label' ) );
  250. }
  251. // No value entered, revert back to original
  252. else {
  253. var original = $select.attr( 'data-original' );
  254. // Reset back to the original value
  255. $select.val( original );
  256. $output_element.attr( 'data-value', original );
  257. }
  258. // Update the sortable data
  259. this.sortableStop( false, false );
  260. }
  261. // Discard
  262. if ( $el.hasClass( 'baton-conductor-discard' ) ) {
  263. // Reset back to the original value
  264. $select.attr( 'data-current', $output_element.attr( 'data-value' ) );
  265. $select.val( $select.attr( 'data-current' ) );
  266. }
  267. // Prevent Default and Propagation
  268. event.preventDefault();
  269. event.stopPropagation();
  270. },
  271. toggleLink: function( event ) {
  272. var $el = $( event.currentTarget ), $parent = $el.parents( '.baton-conductor-output-element' ),
  273. model = this.collection.findWhere( { id: $parent.attr( 'data-id' ) } );
  274. // Toggle the link class
  275. $parent.toggleClass( 'link' );
  276. // Update the model and the data-visible attr
  277. if ( $parent.hasClass( 'link' ) ) {
  278. $parent.attr( 'data-link', 'true' );
  279. model.set( 'link', true );
  280. }
  281. else {
  282. $parent.attr( 'data-link', 'false' );
  283. model.set( 'link', false );
  284. }
  285. // Update the sortable data
  286. this.sortableStop( false, false );
  287. },
  288. toggleVisibility: function( event ) {
  289. var $el = $( event.currentTarget ), $parent = $el.parents( '.baton-conductor-output-element' ),
  290. model = this.collection.findWhere( { id: $parent.attr( 'data-id' ) } );
  291. // Toggle the visible class
  292. $parent.toggleClass( 'visible' );
  293. // Update the model and the data-visible attr
  294. if ( $parent.hasClass( 'visible' ) ) {
  295. $parent.attr( 'data-visible', 'true' );
  296. model.set( 'visible', true );
  297. }
  298. else {
  299. $parent.attr( 'data-visible', 'false' );
  300. model.set( 'visible', false );
  301. }
  302. // Update the sortable data
  303. this.sortableStop( false, false );
  304. },
  305. // When jQuery Sortable has stopped
  306. sortableStop: function( event, ui ) {
  307. var $baton_conductor_output_data = this.$el.find( '.baton-conductor-output-data' ),
  308. data = {}, self = this;
  309. // Clear the collection
  310. this.collection.reset();
  311. // Reset the output list items
  312. this.$output_list_items = this.$output_list.find( 'li' );
  313. // Each output element
  314. this.$output_list_items.each( function() {
  315. var $self = $( this ), model = new baton_conductor.models.output( {
  316. priority: ( ( $self.index() + 1 ) * baton_conductor.output.priority_step_size ), // Default is 10
  317. id: $self.attr( 'data-id' ),
  318. label: $self.attr( 'data-label' ),
  319. type: $self.attr( 'data-type' ),
  320. visible: ( $self.attr( 'data-visible' ) === 'true' )
  321. } ),
  322. index = $self.index(), priority = $self.attr( 'data-priority' ),
  323. new_priority = ( ( index + 1 ) * baton_conductor.output.priority_step_size ),
  324. value = $self.attr( 'data-value' ),
  325. link = $self.attr( 'data-link' );
  326. // Adjust priority
  327. $self.attr( 'data-priority', new_priority );
  328. model.set( 'priority', new_priority );
  329. // Store data in array
  330. data[model.get( 'priority' ).toString()] = {
  331. 'id': model.get( 'id' ),
  332. 'priority': model.get( 'priority' ),
  333. 'label': model.get( 'label' ),
  334. 'type': model.get( 'type' ),
  335. 'visible': model.get( 'visible' )
  336. };
  337. // Add value data
  338. if ( typeof value !== undefined && value !== false ) {
  339. model.set( 'value', value );
  340. data[model.get( 'priority' ).toString()].value = value;
  341. }
  342. // Add link data
  343. if ( typeof link !== undefined && link !== false ) {
  344. model.set( 'link', ( link !== 'false' ) ? link : false );
  345. data[model.get( 'priority' ).toString()].link = ( link !== 'false' ) ? link : false;
  346. }
  347. // Add the new model to the collection
  348. self.collection.add( model );
  349. } );
  350. // Add data string to widget (hidden input elements do not automatically trigger the "change" method)
  351. $baton_conductor_output_data.val( JSON.stringify( data ) ).trigger( 'change' );
  352. },
  353. // Completely destroy this view and all event handlers
  354. destroy: function() {
  355. this.undelegateEvents();
  356. this.remove();
  357. }
  358. } )
  359. };
  360. }( wp, jQuery ) );