From 9fd7af7733833644fef331802f34847ce5dc9710 Mon Sep 17 00:00:00 2001
From: Anton Bagliy <taccessviolation@gmail.com>
Date: Thu, 14 Nov 2019 20:33:34 +0300
Subject: [PATCH] ADD: example of download widget for discipline tablesorter
 #519

---
 .../jquery.tablesorter.widget-output.min.js   |  4 +
 media/js/office/disciplines.js                | 76 ++++++++++++++++++-
 .../views/office/disciplines/index.twig       |  4 +-
 3 files changed, 82 insertions(+), 2 deletions(-)
 create mode 100644 media/js/libs/jquery.tablesorter.widget-output.min.js

diff --git a/media/js/libs/jquery.tablesorter.widget-output.min.js b/media/js/libs/jquery.tablesorter.widget-output.min.js
new file mode 100644
index 000000000..5c9dde2a8
--- /dev/null
+++ b/media/js/libs/jquery.tablesorter.widget-output.min.js
@@ -0,0 +1,4 @@
+(function(factory){if (typeof define === 'function' && define.amd){define(['jquery'], factory);} else if (typeof module === 'object' && typeof module.exports === 'object'){module.exports = factory(require('jquery'));} else {factory(jQuery);}}(function(jQuery){
+
+/*! Widget: output - updated 9/27/2017 (v2.29.0) */
+!function(b){"use strict";var y=b.tablesorter,S=y.output={event:"outputTable",noDblClick:600,lastEvent:0,busy:!1,regexQuote:/([\n\t\x09\x0d\x0a]|<[^<]+>)/,regexBR:/(<br([\s\/])?>|\n)/g,regexIMG:/<img[^>]+alt\s*=\s*['"]([^'"]+)['"][^>]*>/i,regexHTML:/<[^<]+>/g,replaceCR:"\r\n",replaceTab:"\t",popupTitle:"Output",popupStyle:"width:100%;height:100%;margin:0;resize:none;",message:"Your device does not support downloading. Please try again in desktop browser.",init:function(e){e.$table.off(S.event).on(S.event,function(t){t.stopPropagation(),!S.busy&&t.timeStamp-S.lastEvent>S.noDblClick&&(S.lastEvent=t.timeStamp,S.busy=!0,S.process(e,e.widgetOptions))})},processRow:function(t,e,o,u){var n,r,a,p,i,l,s,c,d,f,_=t.widgetOptions,w=[],m=_.output_duplicateSpans,g=o&&u&&_.output_headerRows&&b.isFunction(_.output_callbackJSON),h=0,v=e.length;for(p=0;p<v;p++)for(w[p]||(w[p]=[]),h=0,a=(r=e.eq(p).children()).length,s=0;s<a;s++){if((n=r.eq(s)).filter("[rowspan]").length)for(c=parseInt(n.attr("rowspan"),10)-1,f=S.formatData(t,_,n,o,s),i=1;i<=c;i++)w[p+i]||(w[p+i]=[]),w[p+i][h]=o?f:m?f:"";if(n.filter("[colspan]").length)for(d=parseInt(n.attr("colspan"),10)-1,f=S.formatData(t,_,n,o,s),l=0;l<d;l++)if(n.filter("[rowspan]").length)for(c=parseInt(n.attr("rowspan"),10),i=0;i<c;i++)w[p+i]||(w[p+i]=[]),w[p+i][h+l]=g?_.output_callbackJSON(n,f,h+l)||f+"("+(h+l)+")":o?f:m?f:"";else w[p][h+l]=g?_.output_callbackJSON(n,f,h+l)||f+"("+(h+l)+")":o?f:m?f:"";for(;void 0!==w[p][h];)h++;w[p][h]=w[p][h]||S.formatData(t,_,n,o,h),h++}return y.output.removeColumns(t,_,w)},removeColumns:function(t,e,o){var u,n,r,a=[],p=o.length;for(u=0;u<p;u++)for(n=o[u],a[u]=[],r=0;r<t.columns;r++)e.output_hiddenColumnArray[r]||a[u].push(n[r]);return a},process:function(t,e,o,u){var n,r,a,p,i,l,s,c=window.JSON&&JSON.hasOwnProperty("stringify"),d=0,f=(e.output_separator||",").toLowerCase(),_="json"===f,w="array"===f,m=_||w?",":e.output_separator,g=e.output_saveRows,h=t.$table;for(e.output_regex=new RegExp("("+(/\\/.test(m)?"\\":"")+m+")"),e.output_hiddenColumnArray=[],d=0;d<t.columns;d++)e.output_hiddenColumnArray[d]=-1<b.inArray(d,e.output_ignoreColumns)||!e.output_hiddenColumns&&"none"===t.$headerIndexed[d].css("display")&&!t.$headerIndexed[d].hasClass("tablesorter-scroller-hidden-column");if(r=h.children("thead").children("tr").not("."+(y.css.filterRow||"tablesorter-filter-row")).filter(function(){return e.output_hiddenColumns||"none"!==b(this).css("display")}),a=S.processRow(t,r,!0,_),o||(o=h.children("tbody").children("tr").not(t.selectorRemove)),o="function"==typeof g?o.filter(g):/^f/.test(g)?o.not("."+(e.filter_filteredRow||"filtered")):/^v/.test(g)?o.filter(":visible"):/^[.#:\[]/.test(g)?o.filter(g):o,p=S.processRow(t,o),e.output_includeFooter&&(p=p.concat(S.processRow(t,h.children("tfoot").children("tr:visible")))),i=a.length,_){for(f=[],l=p.length,d=0;d<l;d++)s=a[1<i&&e.output_headerRows?d%i:i-1],f.push(S.row2Hash(s,p[d]));n=c?JSON.stringify(f):f}else f=e.output_includeHeader?(s=[a[1<i&&e.output_headerRows?d%i:i-1]],S.row2CSV(e,e.output_headerRows?a:s,w).concat(S.row2CSV(e,p,w))):S.row2CSV(e,p,w),n=w&&c?JSON.stringify(f):f.join("\n");if(u)return n;if(b.isFunction(e.output_callback)){if(!1===(s=e.output_callback(t,n,t.pager&&t.pager.ajaxObject.url||null)))return void(S.busy=!1);"string"==typeof s&&(n=s)}/p/i.test(e.output_delivery||"")?S.popup(n,e.output_popupStyle,_||w):S.download(t,e,n),S.busy=!1},row2CSV:function(t,e,o){var u,n,r=[],a=e.length;for(n=0;n<a;n++)u=(e[n]||[]).join("").replace(/\"/g,""),0<(e[n]||[]).length&&""!==u&&(r[r.length]=o?e[n]:e[n].join(t.output_separator));return r},row2Hash:function(t,e){var o,u={},n=e.length;for(o=0;o<n;o++)o<t.length&&(u[t[o]]=e[o]);return u},formatData:function(t,e,o,u,n){var r=o.attr(e.output_dataAttrib),a=void 0!==r?r:o.html(),p=(e.output_separator||",").toLowerCase(),i="json"===p||"array"===p,l=a.replace(/\"/g,e.output_replaceQuote||"“");return a=(l=e.output_trimSpaces?l.replace(S.regexBR,""):l.replace(S.regexBR,S.replaceCR).replace(/\t/g,S.replaceTab)).match(S.regexIMG),e.output_includeHTML||null===a||(l=a[1]),l=e.output_includeHTML&&!u?l:l.replace(S.regexHTML,""),l=e.output_trimSpaces||u?b.trim(l):l,l=(p=!i&&(e.output_wrapQuotes||e.output_regex.test(l)||S.regexQuote.test(l)))?'"'+l+'"':l,"function"==typeof e.output_formatContent?e.output_formatContent(t,e,{isHeader:u||!1,$cell:o,content:l,columnIndex:n,parsed:t.parsers[n].format(l,t.table,o[0],n)}):l},popup:function(e,o,u){var n=window.open("",S.popupTitle,o);try{n.document.write("<html><head><title>"+S.popupTitle+'</title></head><body><textarea wrap="'+(u?"on":"off")+'" style="'+S.popupStyle+'">'+e+"\n</textarea></body></html>"),n.document.close(),n.focus()}catch(t){return n.close(),S.popup(e,o,u)}return!0},download:function(t,e,o){if("function"==typeof e.output_savePlugin)return e.output_savePlugin(t,e,o);var u,n,r,a,p=window.navigator,i=document.createElement("a");if(/(iP)/g.test(p.userAgent))return alert(S.message),!1;try{r=!!new Blob}catch(t){r=!1}return r?(window.URL=window.URL||window.webkitURL,a=/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(e.output_encoding)?["\ufeff",o]:[o],n=new Blob(a,{type:e.output_encoding}),p.msSaveBlob?p.msSaveBlob(n,e.output_saveFileName):(i.href=window.URL.createObjectURL(n),i.download=e.output_saveFileName,document.createEvent&&((u=document.createEvent("MouseEvents")).initMouseEvent("click",!0,!0,window,0,0,0,0,0,!1,!1,!1,!1,0,null),i.dispatchEvent(u))),!1):(window.open(e.output_encoding+encodeURIComponent(o)+"?download","_self"),!0)},remove:function(t){t.$table.off(S.event)}};y.addWidget({id:"output",options:{output_separator:",",output_ignoreColumns:[],output_hiddenColumns:!1,output_includeFooter:!1,output_includeHeader:!0,output_headerRows:!1,output_dataAttrib:"data-name",output_delivery:"popup",output_saveRows:"filtered",output_duplicateSpans:!0,output_replaceQuote:"“;",output_includeHTML:!1,output_trimSpaces:!0,output_wrapQuotes:!1,output_popupStyle:"width=500,height=300",output_saveFileName:"mytable.csv",output_formatContent:null,output_callback:function(){return!0},output_callbackJSON:function(t,e,o){return e+"("+o+")"},output_encoding:"data:application/octet-stream;charset=utf8,",output_savePlugin:null},init:function(t,e,o){S.init(o)},remove:function(t,e){S.remove(e)}})}(jQuery);return jQuery;}));
diff --git a/media/js/office/disciplines.js b/media/js/office/disciplines.js
index f931b7f5a..f215258d0 100644
--- a/media/js/office/disciplines.js
+++ b/media/js/office/disciplines.js
@@ -41,7 +41,7 @@ $(() => {
         // table class name template to match to include a widget
         widgetClass: 'widget-{name}',
         // initialize zebra striping and column styling of the table
-        widgets : ["zebra", "filter"],
+        widgets : ["zebra", "filter", "output"],
         // использование фильтров в tablesorter
         // https://mottie.github.io/tablesorter/docs/example-widget-filter.html
         widgetOptions: {
@@ -59,11 +59,85 @@ $(() => {
             // disabled by using -> headers: { 1: { filter: false } } OR add class="filter-false"
             // if you set this to false, make sure you perform a search using the second method below
             filter_columnFilters : true,
+            output_separator     : ',',         // ',' 'json', 'array' or separator (e.g. ';')
+            output_ignoreColumns : [0],         // columns to ignore [0, 1,... ] (zero-based index)
+            output_hiddenColumns : false,       // include hidden columns in the output
+            output_includeFooter : true,        // include footer rows in the output
+            output_includeHeader : true,        // include header rows in the output
+            output_headerRows    : false,       // output all header rows (if multiple rows)
+            output_dataAttrib    : 'data-name', // data-attribute containing alternate cell text
+            output_delivery      : 'd',         // (p)opup, (d)ownload
+            output_saveRows      : 'f',         // (a)ll, (v)isible, (f)iltered, jQuery filter selector (string only) or filter function
+            output_duplicateSpans: true,        // duplicate output data in tbody colspan/rowspan
+            output_replaceQuote  : '\u201c;',   // change quote to left double quote
+            output_includeHTML   : true,        // output includes all cell HTML (except the header cells)
+            output_trimSpaces    : false,       // remove extra white-space characters from beginning & end
+            output_wrapQuotes    : false,       // wrap every cell output in quotes
+            output_popupStyle    : 'width=580,height=310',
+            output_saveFileName  : 'disciplines.csv',
+            // callback executed after the content of the table has been processed
+            output_formatContent : function(config, widgetOptions, data) {
+                // data.isHeader (boolean) = true if processing a header cell
+                // data.$cell = jQuery object of the cell currently being processed
+                // data.content = processed cell content (spaces trimmed, quotes added/replaced, etc)
+                // data.columnIndex = column in which the cell is contained
+                // data.parsed = cell content parsed by the associated column parser
+                return data.content;
+            },
+            // callback executed when processing completes
+            output_callback      : function(config, data, url) {
+                // return false to stop delivery & do something else with the data
+                // return true OR modified data (v2.25.1) to continue download/output
+                return true;
+            },
+            // callbackJSON used when outputting JSON & any header cells has a colspan - unique names required
+            output_callbackJSON  : function($cell, txt, cellIndex) {
+                return txt + '(' + cellIndex + ')';
+            },
+            // the need to modify this for Excel no longer exists
+            output_encoding      : 'data:application/octet-stream;charset=utf8,',
+            // override internal save file code and use an external plugin such as
+            // https://github.com/eligrey/FileSaver.js
+            output_savePlugin    : null /* function(config, widgetOptions, data) {
+                var blob = new Blob([data], {type: widgetOptions.output_encoding});
+                saveAs(blob, widgetOptions.output_saveFileName);
+            } */
 
         }
 
         });
 
+    // taken from https://mottie.github.io/tablesorter/docs/example-widget-output.html
+    $("#tableGroup").find('.download').click(function() {
+        $this = $("#tableGroup");
+        var typ,
+            $table = $this.find('table'),
+            wo = $table[0].config.widgetOptions,
+            val = $this.find('.output-filter-all :checked').attr('class');
+        wo.output_saveRows     = val === 'output-filter' ? 'f' :
+            val === 'output-visible' ? 'v' :
+                // checked class name, see table.config.checkboxClass
+                val === 'output-selected' ? '.checked' :
+                    val === 'output-sel-vis' ? '.checked:visible' :
+                        'a';
+        val = $this.find('.output-download-popup :checked').attr('class');
+        wo.output_delivery     = val === 'output-download' ? 'd' : 'p';
+        wo.output_separator    = $this.find('.output-separator-input').val();
+        wo.output_replaceQuote = $this.find('.output-replacequotes').val();
+        wo.output_trimSpaces   = $this.find('.output-trim').is(':checked');
+        wo.output_includeHTML  = $this.find('.output-html').is(':checked');
+        wo.output_wrapQuotes   = $this.find('.output-wrap').is(':checked');
+        wo.output_saveFileName = $this.find('.output-filename').val();
+
+        // first example buttons, second has radio buttons
+        wo.output_includeHeader = $this.find('button.output-header').is(".active");
+        // footer not included in second example
+        wo.output_includeFooter = $this.find('.output-footer').is(".active");
+
+        $table.trigger('outputTable');
+        return false;
+    });
+
     $('.disciplineInactiveSwitch').click(function () {
 
         let idString = $(this).attr('id');
diff --git a/~dev_rating/application/views/office/disciplines/index.twig b/~dev_rating/application/views/office/disciplines/index.twig
index 0b73f08e6..3ae76b51c 100644
--- a/~dev_rating/application/views/office/disciplines/index.twig
+++ b/~dev_rating/application/views/office/disciplines/index.twig
@@ -7,6 +7,7 @@
 
     {{ HTML.script('static/js/libs/jquery.tablesorter.min.js')|raw }}
     {{ HTML.script('static/js/libs/jquery.tablesorter.widgets.min.js')|raw }}
+    {{ HTML.script('static/js/libs/jquery.tablesorter.widget-output.min.js')|raw }}
     {{ HTML.script('static/js/office/disciplines.js')|raw }}
 
 
@@ -16,7 +17,8 @@
 
     <p>Всего: {{ Disciplines | length }}</p>
 
-    <div class="officeList">
+    <div id="tableGroup" class="officeList">
+        <button type="button" class="btn btn-default download">Output</button>
         <table id="disciplineTable" class="tablesorter" style="table-layout: fixed; width: 100%;">
             <thead>
             <tr>
-- 
GitLab