1/**
2* jQuery.fn.sortElements
3* --------------
4* @author James Padolsey (http://james.padolsey.com)
5* @version 0.11
6* @updated 18-MAR-2010
7* --------------
8* @param Function comparator:
9*   Exactly the same behaviour as [1,2,3].sort(comparator)
10*
11* @param Function getSortable
12*   A function that should return the element that is
13*   to be sorted. The comparator will run on the
14*   current collection, but you may want the actual
15*   resulting sort to occur on a parent or another
16*   associated element.
17*
18*   E.g. $('td').sortElements(comparator, function(){
19*      return this.parentNode;
20*   })
21*
22*   The <td>'s parent (<tr>) will be sorted instead
23*   of the <td> itself.
24*/
25jQuery.fn.sortElements = (function() {
26   var sort = [].sort;
27   return function(comparator, getSortable) {
28       getSortable = getSortable || function() { return this; };
29       var placements = this.map(function() {
30           var sortElement = getSortable.call(this),
31               parentNode = sortElement.parentNode,
32
33               // Since the element itself will change position, we have
34               // to have some way of storing it's original position in
35               // the DOM. The easiest way is to have a 'flag' node:
36               nextSibling = parentNode.insertBefore(
37                   document.createTextNode(''),
38                   sortElement.nextSibling
39               );
40
41           return function() {
42               if (parentNode === this) {
43                   throw new Error(
44                       "You can't sort elements if any one is a descendant of another."
45                   );
46               }
47               // Insert before flag:
48               parentNode.insertBefore(this, nextSibling);
49               // Remove flag:
50               parentNode.removeChild(nextSibling);
51           };
52       });
53       return sort.call(this, comparator).each(function(i) {
54           placements[i].call(getSortable.call(this));
55       });
56   };
57})();
58
59(function() {
60// natural compare
61var natcmp = function(s1, s2) {
62    // 'normalize' the values we're sorting
63    s1 = s1.replace( /<.*?>/g, "" ).replace('&gt;','<').replace('&lt;','>').replace('&amp;','&');
64    s2 = s2.replace( /<.*?>/g, "" ).replace('&gt;','<').replace('&lt;','>').replace('&amp;','&');
65
66    // do the actual sorting
67    var n = /^(\d+)(.*)$/;
68    while (true) {
69        if (s1 == s2) { return 0; }
70        if (s1 == '') { return -1; }
71        if (s2 == '') { return 1; }
72        var n1 = n.exec(s1);
73        var n2 = n.exec(s2);
74        if ( (n1 != null) && (n2 != null) ) {
75            if (n1[1] != n2[1]) { return n1[1] - n2[1]; }
76            s1 = n1[2];
77            s2 = n2[2];
78        } else {
79            n1 = s1.charCodeAt(0);
80            n2 = s2.charCodeAt(0);
81            if (n1 != n2) { return n1 - n2; }
82            s1 = s1.substr(1);
83            s2 = s2.substr(1);
84        }
85    }
86};
87// natural compare right to left (numbers still left to right)
88var natcmp_rtl = function(s1, s2) {
89    // 'normalize' the values we're sorting
90    s1 = s1.replace( /<.*?>/g, "" ).replace('&gt;','<').replace('&lt;','>').replace('&amp;','&');
91    s2 = s2.replace( /<.*?>/g, "" ).replace('&gt;','<').replace('&lt;','>').replace('&amp;','&');
92
93    // do the actual sorting
94    var n = /^(.*?)(\d+)$/;
95    while (true) {
96        if (s1 == s2) { return 0; }
97        if (s1 == '') { return -1; }
98        if (s2 == '') { return 1; }
99        var n1 = n.exec(s1);
100        var n2 = n.exec(s2);
101        if ( (n1 != null) && (n2 != null) ) {
102            if (n1[2] != n2[2]) { return n1[2] - n2[2]; }
103            s1 = n1[1];
104            s2 = n2[1];
105        } else {
106            n1 = s1.charCodeAt(s1.length - 1);
107            n2 = s2.charCodeAt(s2.length - 1);
108            if (n1 != n2) { return n1 - n2; }
109            s1 = s1.substr(0, s1.length - 1);
110            s2 = s2.substr(0, s2.length - 1);
111        }
112    }
113};
114
115// generic stable unique function
116var unique = function(es) {
117    var temp = {};
118    var result = [];
119    for(var i = 0; i < es.length; i++) {
120        var e = es[i];
121        if(! (e in temp)) {
122            result.push(e);
123            temp[e]=true;
124        }
125    }
126
127    return result;
128};
129
130// multi field compare
131var create_item_compare = function(fields, isAscending, sortType) {
132    return function(item1, item2) {
133        var valueMap1 = jQuery(item1).data('strata-item-values');
134        var valueMap2 = jQuery(item2).data('strata-item-values');
135        for (var i = 0; i < fields.length; i++) {
136            var d = isAscending[i] ? 1 : -1;
137            var cmp = (sortType[i] == 'r' ? natcmp_rtl : natcmp);
138            var values1 = valueMap1[fields[i]];
139            var values2 = valueMap2[fields[i]];
140            var length = Math.min(values1.length, values2.length);
141            for (var j = 0; j < length; j++) {
142                var c = cmp(values1[j], values2[j]);
143                if (c != 0) {
144                    return d * c;
145                }
146            }
147            if (values1.length > values2.length) {
148                return d * 1;
149            } else if (values1.length < values2.length) {
150                return d * -1;
151            }
152        }
153        return parseInt(jQuery(item1).attr('data-strata-order')) - parseInt(jQuery(item2).attr('data-strata-order'));
154    }
155};
156
157// Create a filter field of the given type and add it to the given filterElement
158var createFilterFieldAndSort = function(filterElement, filterType, filterId, field, sortType, fieldSelector, containerElement, caption, minWidth) {
159    createItemFilterAndSort(containerElement, filterId, field, fieldSelector, filterType);
160    if (filterType == 't') {
161        var input = createFilterTextField(containerElement, filterId, caption);
162        if (minWidth != undefined) {
163            jQuery(input).css('min-width', minWidth + 'px');
164        }
165        jQuery(filterElement).append(input);
166    } else if (filterType == 's' || filterType == 'p' || filterType == 'e') {
167        var cmp = (sortType == 'r' ? natcmp_rtl : natcmp);
168        var select = createFilterSelect(containerElement, filterId, fieldSelector, caption, cmp);
169        jQuery(filterElement).append(select);
170    }
171};
172
173// Returns a text input which filters the field belonging to the given filterId
174var createFilterTextField = function(element, filterId, caption) {
175    var input = document.createElement('input');
176    input.type = 'text';
177    input.size = 1;
178    input.title = 'Filter on ' + caption;
179    jQuery(input).keyup(function() {
180        var val = jQuery(this).val();
181        if(val == '') {
182            delete jQuery(element).data('strata-search')[filterId];
183        } else {
184            jQuery(element).data('strata-search')[filterId] = val.toLowerCase();
185        }
186        strataFilter(element);
187        toggleFiltered(this);
188    });
189    return input;
190};
191
192// Returns a select input which filters the field belonging to the given filterId
193var createFilterSelect = function(element, filterId, fieldSelector, caption, cmp) {
194    var select = document.createElement('select');
195    jQuery(select).append('<option data-filter="none" class="strata-filter-special"></option>');
196    var values = [];
197    jQuery(fieldSelector, element).each(function(_,es) {
198        var vs = jQuery('*.strata-value', es);
199        if (vs.length) {
200            vs.each(function(i, v) {
201                if (values.indexOf(v.textContent) == -1) {
202                    values.push(v.textContent);
203                }
204            });
205        } else if (values.indexOf('') == -1) {
206            values.push('');
207        }
208    });
209    values.sort(cmp);
210
211    jQuery.each(values, function(_,v) {
212        var option = document.createElement('option');
213        option.value = v;
214        option.textContent = v==''?'<no value>':v;
215        if (v == '') {
216            option.className = 'strata-filter-special';
217        }
218        jQuery(select).append(option);
219    });
220
221    jQuery(select).change(function() {
222        var $option = jQuery(this).find(':selected');
223        if($option.attr('data-filter') == 'none') {
224            delete jQuery(element).data('strata-search')[filterId];
225        } else {
226            jQuery(element).data('strata-search')[filterId] = jQuery(this).val().toLowerCase();
227        }
228        strataFilter(element);
229        toggleFiltered(this);
230    });
231    return select;
232};
233
234// Create a filter for every item of the field belonging to the given filterId
235var createItemFilterAndSort = function(element, filterId, field, fieldSelector, filterType) {
236    jQuery('*.strata-item', element).each(function(i, item) {
237        var values = getValues(item, fieldSelector);
238
239        // Create filter
240        var filter;
241        if (filterType == 't') { // substring
242            // must match at least one value
243            filter = function(search) {
244                var result = false;
245                for (var k = 0; !result && k < values.length; k++) {
246                    result = values[k].indexOf(search) != -1;
247                }
248                return result;
249            };
250        } else if (filterType == 'p') { // prefix
251            // must match at least one value
252            filter = function(search) {
253                if (search == '') return jQuery.inArray('', values) != -1; // Filtering for empty prefix is useless, so do exact match
254                var result = false;
255                for (var k = 0; !result && k < values.length; k++) {
256                    result = values[k].substr(0, search.length) == search;
257                }
258                return result;
259            };
260        } else if (filterType == 'e') { // ending a.k.a. suffix
261            // must match at least one value
262            filter = function(search) {
263                if (search == '') return jQuery.inArray('', values) != -1; // Filtering for empty suffix is useless, so do exact match
264                var result = false;
265                for (var k = 0; !result && k < values.length; k++) {
266                    result = values[k].substr(values[k].length - search.length, search.length) == search;
267                }
268                return result;
269            };
270        } else { // exact
271            // must match at least one value
272            filter = function(search) {
273                return jQuery.inArray(search, values) != -1;
274            };
275        }
276        addToItemMap(item, 'strata-item-values', field, values);
277        addToItemMap(item, 'strata-item-filter', filterId, filter);
278    });
279};
280
281// Get all values for the fields selected by fieldSelector within the given item
282function getValues(item, fieldSelector) {
283	// Return all values of each field and the empty string for fields without values
284    return jQuery(fieldSelector, item).map(function(_, es) {
285        var vs = jQuery('*.strata-value', es);
286        if (vs.length) {
287            return jQuery.makeArray(vs.map(function(_, v) {
288                return v.textContent.toLowerCase();
289            }));
290        } else {
291            return '';
292        }
293    });
294}
295
296// Store data of the given field for the given item
297var addToItemMap = function(item, key, id, values) {
298    var valueMap = jQuery(item).data(key);
299    if (valueMap == undefined) {
300        valueMap = {};
301        jQuery(item).data(key, valueMap);
302    }
303    valueMap[id] = values;
304};
305
306var sortGeneric = function(element, fieldlist) {
307    var fields = [];
308    var isAscending = [];
309    var sortType = [];
310    var items = jQuery('li', fieldlist);
311    for (var i = 0; i < items.length && jQuery(items[i]).attr('data-field') != undefined; i++) {
312        fields.push(jQuery(items[i]).attr('data-field'));
313        isAscending.push(jQuery('.strata-ui-sort-direction', items[i]).attr('data-strata-sort-direction') == 'asc');
314        sortType.push(jQuery(items[i]).data('strata-sort-type'));
315    }
316    jQuery('.strata-item', element).sortElements(create_item_compare(fields, isAscending, sortType));
317};
318
319var sortTable = function(element, field, isAdditional) {
320    var fields = jQuery(element).data('strata-sort-fields');
321    var isAscending = jQuery(element).data('strata-sort-directions');
322    var sortType = [];
323    if (fields[0] == field) {
324        if (isAscending[0]) { // Change sort direction
325            isAscending[0] = false;
326        } else { // Remove from sort
327            fields.splice(0, 1);
328            isAscending.splice(0, 1);
329        }
330    } else if (isAdditional) { // Add as sort field
331        var i = fields.indexOf(field);
332        if (i >= 0) {
333            fields.splice(i, 1);
334            isAscending.splice(i, 1);
335        }
336        fields.unshift(field);
337        isAscending.unshift(true);
338    } else { // Replace sort with given field
339        fields.splice(0, fields.length, field);
340        isAscending.splice(0, fields.length, true);
341    }
342    var sort = jQuery(element).attr('data-strata-ui-sort');
343    jQuery('th', element).removeAttr('data-strata-sort').removeAttr('data-strata-sort-direction');
344    jQuery('td', element).removeAttr('data-strata-sort').removeAttr('data-strata-sort-direction');
345    for (var i = 0; i < fields.length; i++) {
346        var col = fields[i];
347        jQuery('.col' + col, element).attr('data-strata-sort', i);
348        jQuery('.col' + col, element).attr('data-strata-sort-direction', isAscending[i] ? 'asc' : 'desc');
349        sortType.push(sort[col]);
350    }
351    jQuery('.strata-item', element).sortElements(create_item_compare(fields, isAscending, sortType));
352};
353
354// UI initialization
355jQuery(document).ready(function() {
356    // Table UI initialization
357    jQuery('div.strata-container-table[data-strata-ui-ui="table"]').each(function(i, div) {
358        // Do not make this a dataTable if a colspan is used somewhere (Colspans are only generated by strata when errors occur)
359        if (jQuery('table tbody td[colspan][colspan != 1]', div).length > 0) {
360            return;
361        }
362
363        // Set filter to empty set
364        jQuery(div).data('strata-search', {});
365
366        var filterColumns = jQuery(div).attr('data-strata-ui-filter');
367        var sortColumns = jQuery(div).attr('data-strata-ui-sort');
368
369        // Create sort and filter fields for each column
370        var tr = document.createElement('tr');
371        jQuery(tr).addClass('filter');
372        var thead = jQuery('thead', div);
373        var headers = jQuery('tr.row0 th', thead);
374        headers.each(function(i, td) {
375            var field = jQuery('.strata-caption', td).attr('data-field');
376            var th = document.createElement('th'); // Filter field
377            if (field != undefined) { // Is there a field to sort/filter on?
378                // Create sort
379                if (sortColumns.charAt(i) != 'n') {
380                    jQuery(td).addClass('sorting');
381                    jQuery(td).click(function(e) {
382                        sortTable(div, i, e.shiftKey);
383                    });
384                }
385                // Create filter
386                var fieldSelector = '.col' + i + ' *.strata-field';
387                createFilterFieldAndSort(th, filterColumns.charAt(i), i, i, sortColumns.charAt(i), fieldSelector, div, td.textContent);
388            }
389            jQuery(tr).append(th);
390        });
391        jQuery(thead).append(tr);
392
393        // Set column widths
394        jQuery('thead tr.row0 th', div).each(
395            function(i, th) {
396                // Set the width of a column to its initial width, which is the width of the widest row.
397                // This avoids resizing when filtering hides long rows in the table.
398                var width = jQuery(th).width();
399                jQuery(th).css('min-width', width + 'px');
400            }
401        );
402
403        // Set data for sort
404        jQuery(div).data('strata-sort-fields', []);
405        jQuery(div).data('strata-sort-directions', []);
406
407        // Allow switching to alternate table view with the meta key
408        jQuery(thead).click(function(e) {
409            if (e.metaKey) {
410                jQuery(div).toggleClass('strata-ui-filter');
411            }
412        });
413    });
414
415    // Generic UI initialization
416    jQuery('div.strata-container[data-strata-ui-ui="generic"]').each(function(i, div) {
417        // Set filter to empty set
418        jQuery(div).data('strata-search', {});
419
420        var filterColumns = jQuery(div).attr('data-strata-ui-filter');
421        var sortColumns = jQuery(div).attr('data-strata-ui-sort');
422
423        // Create sort and filter fields for each column
424        var list = document.createElement('ul');
425        jQuery(list).addClass('filter')
426            .mouseenter(function(){ jQuery(div).toggleClass('section_highlight', true); })
427            .mouseleave(function(){ jQuery(div).toggleClass('section_highlight', false); });
428
429        var li = document.createElement('li');
430        jQuery(li).addClass('ui-state-highlight strata-ui-eos');
431        jQuery(li).append(document.createTextNode('End of sort order'));
432        jQuery(list).append(li);
433        var lastSortable = li;
434
435		// Collect all sort and filter fields
436		var fields = {};
437		var fieldOrder = [];
438        jQuery('.strata-caption', div).each(function(i, captionElement) {
439            if (sortColumns.charAt(i) != 'n' || filterColumns.charAt(i) != 'n') {
440                var field = jQuery(captionElement).attr('data-field');
441                var minWidth = Math.max.apply(Math, jQuery('*.strata-field[data-field="' + field + '"] .strata-value', div).map(function(_, v) {
442                    return jQuery(v).width();
443                }));
444                var f;
445                if (field in fields) {
446                    f = fields[field];
447                    f.caption.push(captionElement.textContent);
448                    if (f.sortType == 'n') {
449                        f.sortType = sortColumns.charAt(i);
450                    }
451                    f.minWidth = Math.max(f.minWidth, minWidth);
452                } else {
453                    f = {
454                        'field': field,
455                        'caption': [captionElement.textContent],
456                        'sortType': sortColumns.charAt(i),
457                        'minWidth': minWidth,
458                        'filters': []
459                    };
460                    fields[field] = f;
461                    fieldOrder.push(f);
462                }
463                if (filterColumns.charAt(i) != 'n') {
464                    f.filters.push(filterColumns.charAt(i));
465                }
466            }
467        });
468        // Create the collected fields
469        for (var i = 0; i < fieldOrder.length; i++) {
470            var f = fieldOrder[i];
471            var caption = unique(f.caption).join(' / ');
472            var li = document.createElement('li');
473            jQuery(li).addClass('ui-state-default');
474            jQuery(li).attr('data-field', f.field);
475            jQuery(li).append(document.createTextNode(caption));
476            var fieldSelector = '*.strata-field[data-field="' + f.field + '"]';
477            if (f.filters.length) {
478                jQuery(li).append('&nbsp;');
479            }
480            for (var j = 0; j < f.filters.length; j++) {
481                createFilterFieldAndSort(li, f.filters[j], i + '_' + j, f.field, f.sortType, fieldSelector, div, caption, f.minWidth);
482            }
483            if (f.sortType != 'n') {
484                jQuery(li).data('strata-sort-type', f.sortType);
485                var span = document.createElement('span');
486                jQuery(span).addClass('strata-ui-sort-direction');
487                jQuery(span).attr('data-strata-sort-direction', 'asc');
488                jQuery(span).append('&nbsp;');
489                jQuery(li).append(span);
490                jQuery(span).click(function(e) {
491                    var dir = jQuery(this).attr('data-strata-sort-direction') == 'asc' ? 'desc' : 'asc';
492                    jQuery(this).attr('data-strata-sort-direction', dir);
493                    sortGeneric(div, list);
494                });
495                if (f.filters.length == 0) { // No sort data was stored yet, do it now
496                    jQuery('*.strata-item', div).each(function(i, item) {
497                        addToItemMap(item, 'strata-item-values', f.field, getValues(item, fieldSelector));
498                    });
499                }
500            } else {
501                jQuery(li).append(' ');
502            }
503            if (f.sortType == 'n') {
504                jQuery(li).addClass('strata-no-sort');
505                jQuery(list).append(li);
506            } else {
507                jQuery(lastSortable).after(li);
508                lastSortable = li;
509            }
510        }
511        jQuery(div).prepend(list);
512
513        // Set data for sort
514        jQuery(div).data('strata-sort-fields', []);
515        jQuery(div).data('strata-sort-directions', []);
516
517        jQuery(list).sortable({
518            items: "li:not(.strata-no-sort)",
519            placeholder: "ui-state-default ui-state-disabled ui-drop-target",
520            start: function(e, ui) {
521                jQuery(ui.placeholder).css('min-width', jQuery(ui.item).width() + 'px');
522            },
523            update: function(e, ui) {
524                sortGeneric(div, list);
525            }
526        });
527    });
528});
529
530// Filter every strata-item in the given element based on its filter
531var strataFilter = function(element) {
532    var search = jQuery(element).data('strata-search');
533    // Traverse all items (rows) that can be filtered
534    jQuery('*.strata-item', element).each(function(_, item) {
535        // Traverse all fields on which a filter is applied, filter must match all fields
536        var filterMap = jQuery(item).data('strata-item-filter');
537        var matchesAllFields = true;
538        for (filterId in search) {
539            var filter = filterMap[filterId];
540            if (!filter(search[filterId])) {
541                matchesAllFields = false;
542                break;
543            }
544        }
545        jQuery(item).toggleClass('hidden', !matchesAllFields);
546    });
547};
548
549var toggleFiltered = function(tableElement) {
550    var tr = jQuery(tableElement).closest('tr.filter');
551    //console.log(Object.keys(...).length);
552    var isFiltered = false;
553    tr.find('input').each(function(_, input) {
554        isFiltered = isFiltered || (input.value != '');
555    });
556    tr.find('select').each(function(_, select) {
557        isFiltered = isFiltered || (jQuery(select).val() != '');
558    });
559    tr.toggleClass('isFiltered', isFiltered);
560};
561
562})();
563
564/* DOKUWIKI:include plotly-2.25.2.min.js */
565