PageRenderTime 49ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/p/pretty-plugins/themes/3-eight/theme.js

https://bitbucket.org/matthewselby/wpdev
JavaScript | 663 lines | 356 code | 131 blank | 176 comment | 58 complexity | 5dae2f49f49d074192298566123bee64 MD5 | raw file
Possible License(s): Apache-2.0, GPL-2.0, LGPL-3.0, LGPL-2.1, AGPL-1.0, BSD-3-Clause, MIT, GPL-3.0, MPL-2.0-no-copyleft-exception
  1. /* global _wpPluginSettings, confirm */
  2. window.wp = window.wp || {};
  3. ( function($) {
  4. // Set up our namespace...
  5. var plugins, l10n;
  6. plugins = wp.plugins = wp.plugins || {};
  7. // Store the plugin data and settings for organized and quick access
  8. // plugins.data.settings, plugins.data.plugins, plugins.data.l10n
  9. plugins.data = _wpPluginsSettings;
  10. l10n = plugins.data.l10n;
  11. categories = plugins.data.categories;
  12. // Setup app structure
  13. _.extend( plugins, { model: {}, view: {}, routes: {}, router: {}, template: wp.template });
  14. plugins.model = Backbone.Model.extend({});
  15. // Main view controller for plugins.php
  16. // Unifies and renders all available views
  17. plugins.view.Appearance = wp.Backbone.View.extend({
  18. el: '#wpbody-content .wrap .plugin-browser',
  19. window: $( window ),
  20. // Pagination instance
  21. page: 0,
  22. // Sets up a throttler for binding to 'scroll'
  23. initialize: function() {
  24. // Scroller checks how far the scroll position is
  25. _.bindAll( this, 'scroller' );
  26. // Bind to the scroll event and throttle
  27. // the results from this.scroller
  28. this.window.bind( 'scroll', _.throttle( this.scroller, 300 ) );
  29. },
  30. // Main render control
  31. render: function() {
  32. // Setup the main plugin view
  33. // with the current plugin collection
  34. this.view = new plugins.view.Plugins({
  35. collection: this.collection,
  36. parent: this
  37. });
  38. // Render categories form.
  39. this.categories();
  40. // Render search form.
  41. this.search();
  42. // Render and append
  43. this.view.render();
  44. this.$el.empty().append( this.view.el ).addClass('rendered');
  45. this.$el.append( '<br class="clear"/>' );
  46. },
  47. // Search input and view
  48. // for current plugin collection
  49. categories: function() {
  50. var view,
  51. self = this;
  52. // Don't render the categories if there is only one plugin
  53. if ( plugins.data.plugins.length === 1 ) {
  54. return;
  55. }
  56. view = new plugins.view.Categories({ collection: self.collection });
  57. view_wp_menu = new plugins.view.CategoriesWpMenu({ collection: self.collection });
  58. // Render and append before plugins list
  59. view.render();
  60. $('#wpbody .plugin-browser')
  61. .before( view.el );
  62. },
  63. // Search input and view
  64. // for current plugin collection
  65. search: function() {
  66. var view,
  67. self = this;
  68. // Don't render the search if there is only one plugin
  69. if ( plugins.data.plugins.length === 1 ) {
  70. return;
  71. }
  72. view = new plugins.view.Search({ collection: self.collection });
  73. view.render();
  74. $('.wp-filter')
  75. .append( '<div class="search-form"></div>' );
  76. $('.wp-filter .search-form')
  77. .append( view.el );
  78. },
  79. // Checks when the user gets close to the bottom
  80. // of the mage and triggers a plugin:scroll event
  81. scroller: function() {
  82. var self = this,
  83. bottom, threshold;
  84. bottom = this.window.scrollTop() + self.window.height();
  85. threshold = self.$el.offset().top + self.$el.outerHeight( false ) - self.window.height();
  86. threshold = Math.round( threshold * 0.9 );
  87. if ( bottom > threshold ) {
  88. this.trigger( 'plugin:scroll' );
  89. }
  90. }
  91. });
  92. // Set up the Collection for our plugin data
  93. plugins.Collection = Backbone.Collection.extend({
  94. model: plugins.model,
  95. // Search terms
  96. terms: '',
  97. // Controls searching on the current plugin collection
  98. // and triggers an update event
  99. doSearch: function( value ) {
  100. //reset category to "all" on search
  101. $('.plugin-categories .plugin-category').removeClass('current');
  102. $('[data-category="all"]').addClass('current');
  103. // Don't do anything if we've already done this search
  104. // Useful because the Search handler fires multiple times per keystroke
  105. if ( this.terms === value ) {
  106. return;
  107. }
  108. // Updates terms with the value passed
  109. this.terms = value;
  110. // If we have terms, run a search...
  111. if ( this.terms.length > 0 ) {
  112. this.search( this.terms );
  113. }
  114. // If search is blank, show all plugins
  115. // Useful for resetting the views when you clean the input
  116. if ( this.terms === '' ) {
  117. this.reset( plugins.data.plugins );
  118. }
  119. // Trigger an 'update' event
  120. this.trigger( 'update' );
  121. },
  122. // Controls viewing plugins from category on the current plugin collection
  123. // and triggers an update event
  124. doCategory: function( value ) {
  125. //Sets up class for active category button
  126. $('#plugin-search-input').val('');
  127. $('.plugin-categories .plugin-category').removeClass('current');
  128. $('[data-category="'+ value + '"]').addClass('current');
  129. $('#toplevel_page_pretty-plugins li').removeClass('current');
  130. // If we have terms, run a search...
  131. if ( value.length > 0 ) {
  132. this.category( value );
  133. if(value == 'all')
  134. $('#toplevel_page_pretty-plugins li a.current').parent().addClass('current');
  135. else
  136. $('#toplevel_page_pretty-plugins li a[href$="category\\='+value+'"]').parent().addClass('current');
  137. }
  138. // If search is blank, show all plugins
  139. // Useful for resetting the views when you clean the input
  140. if ( value === '' ) {
  141. this.reset( plugins.data.plugins );
  142. }
  143. // Trigger an 'update' event
  144. this.trigger( 'update' );
  145. },
  146. // Performs a search within the collection
  147. // @uses RegExp
  148. search: function( term ) {
  149. var match, results, haystack, name, description;
  150. // Start with a full collection
  151. this.reset( plugins.data.plugins, { silent: true } );
  152. // Escape the term string for RegExp meta characters
  153. term = term.replace( /[-\/\\^$*+?.()|[\]{}]/g, '\\$&' );
  154. // Consider spaces as word delimiters and match the whole string
  155. // so matching terms can be combined
  156. term = term.replace( / /g, ')(?=.*' );
  157. match = new RegExp( '^(?=.*' + term + ').+', 'i' );
  158. // Find results
  159. // _.filter and .test
  160. results = this.filter( function( data ) {
  161. name = data.get( 'Name' ).replace( /(<([^>]+)>)/ig, '' );
  162. description = data.get( 'Description' ).replace( /(<([^>]+)>)/ig, '' );
  163. haystack = _.union( [ name, description ] );
  164. return match.test( haystack );
  165. });
  166. this.reset( results );
  167. },
  168. // Picks categories within the collection
  169. category: function( term ) {
  170. var results;
  171. // Start with a full collection
  172. this.reset( plugins.data.plugins, { silent: true } );
  173. // Find results
  174. // _.filter and .test
  175. results = this.filter( function( data ) {
  176. if(term == 'all' || term == 'active' || term == 'inactive') {
  177. if(term == 'all')
  178. return data;
  179. else if(term == 'active' && data.attributes.isActive == true)
  180. return data;
  181. else if(term == 'inactive' && data.attributes.isActive == false)
  182. return data;
  183. }
  184. else if($.inArray( term, data.attributes.Categories ) !== -1)
  185. return data;
  186. });
  187. this.reset( results );
  188. },
  189. // Paginates the collection with a helper method
  190. // that slices the collection
  191. paginate: function( instance ) {
  192. var collection = this;
  193. instance = instance || 0;
  194. // Plugins per instance are set at 15
  195. collection = _( collection.rest( 15 * instance ) );
  196. collection = _( collection.first( 15 ) );
  197. return collection;
  198. }
  199. });
  200. // This is the view that controls each plugin item
  201. // that will be displayed on the screen
  202. plugins.view.Plugin = wp.Backbone.View.extend({
  203. // Wrap plugin data on a div.plugin element
  204. className: 'plugin-card',
  205. events: {
  206. 'click .show-more-button': 'showMore'
  207. },
  208. // Reflects which plugin view we have
  209. // 'grid' (default) or 'detail'
  210. state: 'grid',
  211. // The HTML template for each element to be rendered
  212. html: plugins.template( 'plugin' ),
  213. touchDrag: false,
  214. render: function() {
  215. var data = this.model.toJSON();
  216. // Render plugins using the html template
  217. this.$el.html( this.html( data ) ).attr({
  218. tabindex: 0,
  219. 'aria-describedby' : data.id + '-action ' + data.id + '-name'
  220. });
  221. // Renders active plugin styles
  222. this.activePlugin();
  223. if ( this.model.get( 'displayAuthor' ) ) {
  224. this.$el.addClass( 'display-author' );
  225. }
  226. },
  227. // Adds a class to the currently active plugin
  228. // and to the overlay in detailed view mode
  229. activePlugin: function() {
  230. if ( this.model.get( 'active' ) ) {
  231. this.$el.addClass( 'active' );
  232. }
  233. },
  234. showMore: function( event ) {
  235. event.preventDefault();
  236. this.$el.find('.plugin-content').removeClass('plugin-show-more');
  237. },
  238. });
  239. // Controls the rendering of div.plugins,
  240. // a wrapper that will hold all the plugin elements
  241. plugins.view.Plugins = wp.Backbone.View.extend({
  242. className: 'plugins',
  243. $overlay: $( 'div.plugin-overlay' ),
  244. // Number to keep track of scroll position
  245. // while in plugin-overlay mode
  246. index: 0,
  247. // The plugin count element
  248. count: $( '.plugin-count' ),
  249. initialize: function( options ) {
  250. var self = this;
  251. // Set up parent
  252. this.parent = options.parent;
  253. // Set current view to [grid]
  254. this.setView( 'grid' );
  255. // Move the active plugin to the beginning of the collection
  256. self.currentPlugin();
  257. // When the collection is updated by user input...
  258. this.listenTo( self.collection, 'update', function() {
  259. self.parent.page = 0;
  260. self.currentPlugin();
  261. self.render( this );
  262. });
  263. this.listenTo( this.parent, 'plugin:scroll', function() {
  264. self.renderPlugins( self.parent.page );
  265. });
  266. // Bind keyboard events.
  267. $('body').on( 'keyup', function( event ) {
  268. if ( ! self.overlay ) {
  269. return;
  270. }
  271. // Pressing the right arrow key fires a plugin:next event
  272. if ( event.keyCode === 39 ) {
  273. self.overlay.nextPlugin();
  274. }
  275. // Pressing the left arrow key fires a plugin:previous event
  276. if ( event.keyCode === 37 ) {
  277. self.overlay.previousPlugin();
  278. }
  279. // Pressing the escape key fires a plugin:collapse event
  280. if ( event.keyCode === 27 ) {
  281. self.overlay.collapse( event );
  282. }
  283. });
  284. },
  285. // Manages rendering of plugin pages
  286. // and keeping plugin count in sync
  287. render: function() {
  288. // Clear the DOM, please
  289. this.$el.html( '' );
  290. // Generate the plugins
  291. // Using page instance
  292. this.renderPlugins( this.parent.page );
  293. // Display a live plugin count for the collection
  294. this.count.text( this.collection.length );
  295. },
  296. // Iterates through each instance of the collection
  297. // and renders each plugin module
  298. renderPlugins: function( page ) {
  299. var self = this;
  300. self.instance = self.collection.paginate( page );
  301. // If we have no more plugins bail
  302. if ( self.instance.length === 0 ) {
  303. return;
  304. }
  305. // Loop through the plugins and setup each plugin view
  306. self.instance.each( function( plugin ) {
  307. self.plugin = new plugins.view.Plugin({
  308. model: plugin
  309. });
  310. // Render the views...
  311. self.plugin.render();
  312. // and append them to div.plugins
  313. self.$el.append( self.plugin.el );
  314. });
  315. this.parent.page++;
  316. },
  317. // Grabs current plugin and puts it at the beginning of the collection
  318. currentPlugin: function() {
  319. var self = this,
  320. current;
  321. current = self.collection.findWhere({ active: true });
  322. // Move the active plugin to the beginning of the collection
  323. if ( current ) {
  324. self.collection.remove( current );
  325. self.collection.add( current, { at:0 } );
  326. }
  327. },
  328. // Sets current view
  329. setView: function( view ) {
  330. return view;
  331. },
  332. });
  333. // Search input view controller.
  334. plugins.view.Search = wp.Backbone.View.extend({
  335. tagName: 'input',
  336. className: 'plugin-search',
  337. id: 'plugin-search-input',
  338. attributes: {
  339. placeholder: l10n.searchPlaceholder,
  340. type: 'search'
  341. },
  342. events: {
  343. 'input': 'search',
  344. 'keyup': 'search',
  345. 'change': 'search',
  346. 'search': 'search'
  347. },
  348. // Runs a search on the plugin collection.
  349. search: function( event ) {
  350. // Clear on escape.
  351. if ( event.type === 'keyup' && event.which === 27 ) {
  352. event.target.value = '';
  353. }
  354. this.collection.doSearch( event.target.value );
  355. // Update the URL hash
  356. if ( event.target.value ) {
  357. plugins.router.navigate( plugins.router.baseUrl( '&search=' + event.target.value ), { replace: true } );
  358. } else {
  359. plugins.router.navigate( plugins.router.baseUrl( '' ), { replace: true } );
  360. }
  361. }
  362. });
  363. // Categories input view controller.
  364. plugins.view.Categories = wp.Backbone.View.extend({
  365. tagName: 'div',
  366. className: 'wp-filter plugin-categories',
  367. events: {
  368. 'click a': 'categories'
  369. },
  370. render: function() {
  371. var el = $(this.el);
  372. el.append( $( '.filter-count' ) );
  373. el.append( $.parseHTML( '<span style="display:none;" class="plugin-categories-label">' + l10n.categories + '</span>' ) );
  374. el.append( '<ul class="filter-links"></ul>' );
  375. var el_ul = el.find('.filter-links');
  376. $.each(categories, function( index, value ) {
  377. var add_class = '';
  378. if(index == 'all')
  379. add_class = ' current';
  380. el_ul.append( $.parseHTML( '<li><a data-category="'+ index +'" class="plugin-category '+ add_class +'" href="#">' + value + '</a></li>' ) );
  381. });
  382. },
  383. // Runs a search on the plugin collection.
  384. categories: function( event ) {
  385. event.preventDefault();
  386. // Update the URL hash
  387. if ( event.target.dataset.category ) {
  388. this.collection.doCategory( event.target.dataset.category );
  389. plugins.router.navigate( plugins.router.baseUrl( '&category=' + event.target.dataset.category ), { replace: true } );
  390. }
  391. }
  392. });
  393. // Categories input view controller.
  394. plugins.view.CategoriesWpMenu = wp.Backbone.View.extend({
  395. el: 'li.toplevel_page_pretty-plugins',
  396. events: {
  397. 'click a': 'categories'
  398. },
  399. // Runs a search on the plugin collection.
  400. categories: function( event ) {
  401. event.preventDefault();
  402. // Update the URL hash
  403. if ( event.target.href ) {
  404. var category = this.getUrlParameter(event.target.href);
  405. if('category' in category)
  406. category = category['category'];
  407. else
  408. category = 'all';
  409. if ( category ) {
  410. this.collection.doCategory( category );
  411. plugins.router.navigate( plugins.router.baseUrl( '&category=' + category ), { replace: true } );
  412. }
  413. }
  414. },
  415. getUrlParameter: function(url){
  416. var vars = [], hash;
  417. var hashes = url.slice(url.indexOf('?') + 1).split('&');
  418. for(var i = 0; i < hashes.length; i++)
  419. {
  420. hash = hashes[i].split('=');
  421. vars.push(hash[0]);
  422. vars[hash[0]] = hash[1];
  423. }
  424. return vars;
  425. }
  426. });
  427. // Sets up the routes events for relevant url queries
  428. // Listens to [plugin] and [search] params
  429. plugins.routes = Backbone.Router.extend({
  430. initialize: function() {
  431. this.routes = _.object([
  432. ]);
  433. },
  434. baseUrl: function( url ) {
  435. return plugins.data.settings.root + url;
  436. }
  437. });
  438. // Execute and setup the application
  439. plugins.Run = {
  440. init: function() {
  441. // Initializes the blog's plugin library view
  442. // Create a new collection with data
  443. this.plugins = new plugins.Collection( plugins.data.plugins );
  444. // Set up the view
  445. this.view = new plugins.view.Appearance({
  446. collection: this.plugins
  447. });
  448. this.render();
  449. },
  450. render: function() {
  451. // Render results
  452. this.view.render();
  453. this.routes();
  454. // Set the initial search
  455. if ( 'undefined' !== typeof plugins.data.settings.search && '' !== plugins.data.settings.search ){
  456. $( '.plugin-search' ).val( plugins.data.settings.search );
  457. this.plugins.doSearch( plugins.data.settings.search );
  458. }
  459. // Set the initial category
  460. if ( 'undefined' !== typeof plugins.data.settings.category && '' !== plugins.data.settings.category ){
  461. this.plugins.doCategory( plugins.data.settings.category );
  462. }
  463. // Start the router if browser supports History API
  464. if ( window.history && window.history.pushState ) {
  465. // Calls the routes functionality
  466. Backbone.history.start({ pushState: true, silent: true });
  467. }
  468. },
  469. routes: function() {
  470. // Bind to our global thx object
  471. // so that the object is available to sub-views
  472. plugins.router = new plugins.routes();
  473. }
  474. };
  475. // Ready...
  476. jQuery( document ).ready(
  477. // Bring on the plugins
  478. _.bind( plugins.Run.init, plugins.Run )
  479. );
  480. })( jQuery );
  481. /*
  482. jQuery(document).ready( function($) {
  483. // Integrate with WP submenu
  484. $('#toplevel_page_pretty-plugins li a').click(function(e) {
  485. e.preventDefault();
  486. var category = get_url_parameter($(this).attr('href'));
  487. if(typeof category['category'] === "undefined")
  488. category = 'all';
  489. else
  490. category = category['category'];
  491. console.log(plugins);
  492. // Update the URL hash
  493. if ( category ) {
  494. _.bind( plugins.doCategory, category );
  495. //wp.plugins.view.Categories.collection.doCategory( category );
  496. wp.plugins.router.navigate( wp.plugins.router.baseUrl( '&category=' + category ), { replace: true } );
  497. }
  498. return false;
  499. });
  500. });
  501. // Align plugin browser thickbox
  502. var tb_position;
  503. jQuery(document).ready( function($) {
  504. tb_position = function() {
  505. var tbWindow = $('#TB_window'),
  506. width = $(window).width(),
  507. H = $(window).height(),
  508. W = ( 1040 < width ) ? 1040 : width,
  509. adminbar_height = 0;
  510. if ( $('body.admin-bar').length ) {
  511. adminbar_height = parseInt( jQuery('#wpadminbar').css('height'), 10 );
  512. }
  513. if ( tbWindow.size() ) {
  514. tbWindow.width( W - 50 ).height( H - 45 - adminbar_height );
  515. $('#TB_iframeContent').width( W - 50 ).height( H - 75 - adminbar_height );
  516. tbWindow.css({'margin-left': '-' + parseInt( ( ( W - 50 ) / 2 ), 10 ) + 'px'});
  517. if ( typeof document.body.style.maxWidth !== 'undefined' ) {
  518. tbWindow.css({'top': 20 + adminbar_height + 'px', 'margin-top': '0'});
  519. }
  520. }
  521. };
  522. $(window).resize(function(){ tb_position(); });
  523. });
  524. */