(function ($) { "use strict"; var Filelisting = function(element, options) { this.$capiton = $(element).find('.plugin__filelisting_capiton'); this.$collapsible = $(element).find('.plugin__filelisting_collapsible'); this.$content = $(element).find('.plugin__filelisting_content'); this.$headertable = $(element).find('.plugin__filelisting_headertable'); this.$bodytable = $(element).find('.plugin__filelisting_bodytable'); this.$footer = $(element).find('.plugin__filelisting_footer'); this.options = $.extend({}, $.fn.dokuwiki_plugin_filelisting.defaults, options); this.storageKey = 'plugin_filelisting'; if (this.options.remember_state_per_page) { this.storageKey += '/' + this.options.pageId; } this.initToggleButton(); this.initAjaxDirectoryExpand(); this.initFilter(); this.initSorting(); this.initDelete(); }; Filelisting.prototype.getToggleStatus = function () { if (!localStorage.getItem(this.storageKey)) { return this.options.defaultToggle; } return localStorage.getItem(this.storageKey); }; Filelisting.prototype.setToggleStatus = function (status) { if (status !== 'visible' && status !== 'hidden') { throw 'status must be "visible" or "hidden"'; } localStorage.setItem(this.storageKey, status); }; Filelisting.prototype.initToggleButton = function() { //toggle button var $toggleButton = $('
').text(this.options.toggleVisible) .css({ float: 'right', cursor: 'pointer' }).addClass('plugin__filelisting_toggle').appendTo(this.$capiton); //by default filelisting is visible if (this.getToggleStatus() === 'hidden') { this.$collapsible.hide(); $toggleButton.text(this.options.toggleHidden); } $toggleButton.click($.proxy(function () { if (this.$collapsible.is(':hidden')) { this.$collapsible.slideDown(); $toggleButton.text(this.options.toggleVisible); this.setToggleStatus('visible'); } else { this.$collapsible.slideUp(); $toggleButton.text(this.options.toggleHidden); this.setToggleStatus('hidden'); } }, this)); }; Filelisting.prototype.initAjaxDirectoryExpand = function() { //allow click on link this.$content.find('tbody').on('click', 'tr[data-namespace] a', $.proxy(function (event) { event.preventDefault(); //row and namespace are used in $.post var $row = $(event.target).closest('tr'), namespace = $row.data('namespace'); //get all siblings and subsiblings var $children = $row.nextAll('[data-childOf="' + namespace + '"]'), $descendants = $row.nextAll('[data-childOf^="' + namespace + '"]'); //namespace is expanded - hide it if ($row.data('isExpanded')) { //set icon $row.children('.plugin__filelisting_cell_icon').html(this.options.dirClosedIcon); //save the state of all expanded sub namespaces to restore it as it was $descendants.each(function () { if ($(this).is(':visible')) { $(this).data('reopenAs', 'visible'); } else { $(this).data('reopenAs', 'hidden'); } }).hide(); $row.data('isExpanded', false); //namespace is hidden and is loaded } else if ($row.data('isLoaded')) { $row.children('.plugin__filelisting_cell_icon').html(this.options.dirOpenedIcon); //always open children $children.show(); //check if we should open any descendents $descendants.each(function() { if ($(this).data('reopenAs') === 'visible') { $(this).show(); } }); $row.data('isExpanded', true); this.$content.trigger('expand', namespace); //namespace isn't loaded } else { //loading $row.children('.plugin__filelisting_cell_icon').html(this.options.loadingIcon); var data = {}; data['call'] = 'plugin_filelisting'; data['namespace'] = namespace; data['baseNamespace'] = this.options.baseNamespace; $.post(DOKU_BASE + 'lib/exe/ajax.php', data, $.proxy(function(html) { $row.children('.plugin__filelisting_cell_icon').html(this.options.dirOpenedIcon); $row.after(html); $row.data('isLoaded', true); this.$content.trigger('nsload', namespace); $row.data('isExpanded', true); this.$content.trigger('expand', namespace); }, this), 'html') .fail($.proxy(function () { $row.children('.plugin__filelisting_cell_icon').html(this.options.dirClosedIcon); }, this)); } }, this)); this.$content.on('namespaceFilesChanged', $.proxy(function (event, namespace) { var $row = $('tr[data-namespace="'+namespace+'"]'); if ($row.length && !$row.data('isLoaded')) { return; } var data = {}; data['call'] = 'plugin_filelisting'; data['namespace'] = namespace; data['baseNamespace'] = this.options.baseNamespace; data['filesOnly'] = true; $.post(DOKU_BASE + 'lib/exe/ajax.php', data, $.proxy(function(html) { var fileRows = $(html); if ($row.length && !$row.data('isExpanded')) { fileRows.hide(); } var $filesInNamespace = $('tr[data-childOf="'+namespace+'"]').not('[data-namespace]'); if ($filesInNamespace.length) { // there are already files in the namespace: replace them $filesInNamespace.first().replaceWith(fileRows); $filesInNamespace.remove(); } else if ($('tr[data-childOf="'+namespace+'"]').length) { // there are no files, but other folders in the namespace: insert after them $('tr[data-childOf="'+namespace+'"]').last().after(fileRows); } else { // tbody is currently empty: append to it this.$content.find('tbody').append(fileRows); } this.$content.trigger('nsload', namespace); }, this), 'html'); }, this)) }; Filelisting.prototype.initFilter = function() { this.$filter = $('').appendTo(this.$footer); var $input = this.$filter.find('input'); //filter has changed, update content $input.on('keyup', $.proxy(this.applyFilter, this)); //prevent deleting files on pressing enter $input.on('keydown', function (event) { if(event.keyCode === 13) { event.preventDefault(); return false; } }); //bind filtering to content update event this.$content.on('expand', $.proxy(this.applyFilter, this)); }; Filelisting.prototype.applyFilter = function() { var filter = this.$filter.find('input').val(), //escape regex //https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Using_Special_Characters escaped = filter.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), // $& means the whole matched string globbing = escaped.replace(/\\\*/g, '.*').replace(/\\\?/g, '.'), regex = new RegExp(globbing), $rows = this.$content.find('tbody tr'), $files = $rows.not('[data-namespace]'), $dirs = $rows.not($files), filterCallback = function() { //text in second column var $row = $(this), text = $row.find('td.plugin__filelisting_cell_name a').text(); if (text.match(regex)) { $row.show(); } else { $row.hide(); } }; //files in base namespace are always visible $files.filter('[data-childOf="' + this.options.baseNamespace + '"]').each(filterCallback); //get namespaces $dirs.filter(function() { //only expanded return $(this).data('isExpanded'); }).each(function () { var namespace = $(this).data('namespace'); $files.filter('[data-childOf="' + namespace + '"]').each(filterCallback); }); }; Filelisting.prototype.initSorting = function() { //global: current sort header //not defined by default this.$sortHeader = []; //create sort links (for styling purposes) this.$content.find('thead th').wrapInner(''); //sorting indicator this.$content.find('thead th a').prepend(''); //options for click this.$content.find('thead th a').on('click', $.proxy(function(event) { //prevent from scrolling to top event.preventDefault(); this.$sortHeader = $(event.target).closest('th'); var $order = this.$sortHeader.find('span'); //clear other sorting indicators this.$content.find('thead th').not(this.$sortHeader).find('span').text(''); //toggle sort ordering if ($order.text() === '' || $order.text() === this.options.sortDesc) { $order.text(this.options.sortAsc); } else { $order.text(this.options.sortDesc); } //perform sorting this.sortBy(); }, this)); //bind sorting to content update event this.$content.on('nsload', $.proxy(function(event, namespace) { this.sortBy(namespace); }, this)); }; Filelisting.prototype.sortBy = function(namespace) { //don't sort where sortHeader not defined if (this.$sortHeader.length === 0) return; //by default sort starts from the base namespace if (namespace === undefined) { namespace = this.options.baseNamespace; } var $root = this.$content.find('tbody tr[data-namespace="' + namespace + '"]'), $rows = this.$content.find('tbody tr[data-childOf="' + namespace + '"]'), $files = $rows.not('[data-namespace]'), $dirs = $rows.not($files), sortCallback = $.proxy(function (a, b) { //remember about first th colspan var colspan = this.$headertable.find('th').first().attr('colspan'), index = this.$sortHeader.index() + (colspan - 1), order = 1; //1 ascending order, -1 descending order //check for desc sorting if (this.$sortHeader.find('span').text() === this.options.sortDesc) { order = -1; } var dataA = $(a).find('td').eq(index).data('sort'), dataB = $(b).find('td').eq(index).data('sort'); //$.data automatically converts string to integer when possible if (dataA < dataB) { return -order; } else if (dataA > dataB) { return order; } return 0; }, this); //sort dirs $dirs.sort(sortCallback); //sort files $files.sort(sortCallback); //we are on top level if ($root.length === 0) { this.$content.find('tbody').append($dirs, $files); } else { $root.after($dirs, $files); } //attach files to corresponding dirs $dirs.each($.proxy(function(index, element) { var namespace = $(element).data('namespace'), $descendants = $(element).siblings('[data-childOf^="' + namespace + '"]'); $descendants.insertAfter(element); //sort sub namespaces this.sortBy(namespace); }, this)); }; Filelisting.prototype.initDelete = function() { var $deleteButton = this.$collapsible.find('button[name="do[plugin_filelisting_delete]"]'); $deleteButton.on('click', $.proxy(function (event) { var deleteFiles = window.confirm(this.options.deleteConfirm); if (!deleteFiles) { event.preventDefault(); } }, this)); this.toggleDeleteButton(); //show/hide delete button this.$content.on('change', 'input[type=checkbox]', $.proxy(this.toggleDeleteButton, this)); }; Filelisting.prototype.toggleDeleteButton = function() { var $deleteButton = this.$collapsible.find('button[name="do[plugin_filelisting_delete]"]'); if (this.$content.find('input[type=checkbox]:checked').length === 0) { $deleteButton.hide(); } else { $deleteButton.show(); } }; $.fn.dokuwiki_plugin_filelisting = function (options) { //return jquery object return this.each(function() { new Filelisting(this, options); }); }; $.fn.dokuwiki_plugin_filelisting.defaults = { //label for visible list toggleVisible: '▼', //label for hidden list toggleHidden: '▲', //id of the current wiki page pageId: '', defaultToggle: 'visible', //html used as dir open icon dirOpenedIcon: '', //html used as dir close icon dirClosedIcon: '', //html used as loading icon for ajax call loadingIcon: '', //namespace of the current wiki page baseNamespace: '', //label of filter input filterLabel: 'Filter', //sort ascending label sortAsc: '↓', //sort descending label sortDesc: '↑', //confirm file deletion deleteConfirm: LANG.plugins.filelisting.delete_confirm }; }(window.jQuery)); jQuery(function() { //read JSINFO and LANG if (JSINFO === undefined || LANG === undefined) { console.log('filelisting: JSINFO or LANG undefined'); return; } var options = {}; options.pageId = JSINFO.id; var defaulttoggle = JSINFO.plugin.filelisting.defaulttoggle; if (defaulttoggle === '1') { options.defaultToggle = 'visible'; } else { options.defaultToggle = 'hidden'; } options.remember_state_per_page = JSINFO.plugin.filelisting.remember_state_per_page; options.dirOpenedIcon = JSINFO.plugin.filelisting.dirOpenedIcon; options.dirClosedIcon = JSINFO.plugin.filelisting.dirClosedIcon; options.loadingIcon = JSINFO.plugin.filelisting.loadingIcon; var $plugin__filelisting = jQuery('.plugin__filelisting'); // if base namespace is not properly set, sorting and filtering wont work var ns = $plugin__filelisting.data("namespace"); if (ns !== undefined) { options.baseNamespace = ns; } else { options.baseNamespace = JSINFO.namespace; } options.filterLabel = LANG.plugins.filelisting.filter_label; options.deleteConfirm = LANG.plugins.filelisting.delete_confirm; $plugin__filelisting.dokuwiki_plugin_filelisting(options); /** * Fixes the tablewidths so that the table columns in header and body are exactly aligned * * This is necessary, because browsers have different widths for scrollbars */ $plugin__filelisting.each(function adjustTableWidthForScrollbar(index, container) { var $bodyTable = jQuery(container).find('.plugin__filelisting_bodytable table'); var $headerWrapper = jQuery(container).find('.plugin__filelisting_headertable'); var tablediff = $bodyTable.width() - $headerWrapper.find('table').width(); var originalPaddingRight = parseInt($headerWrapper.css('padding-right'), 10); var newPaddingRight = originalPaddingRight - tablediff; $headerWrapper.css('padding-right', newPaddingRight + 'px'); }); });