Commit 3543a79115a018255c66b3df3921c99acac291a7

Authored by Zambom
1 parent d01cc219

Adding goals answered reports csv download

amadeus/static/css/dataTables.buttons.bootstrap.css 0 → 100644
... ... @@ -0,0 +1,102 @@
  1 +div.dt-button-info {
  2 + position: fixed;
  3 + top: 50%;
  4 + left: 50%;
  5 + width: 400px;
  6 + margin-top: -100px;
  7 + margin-left: -200px;
  8 + background-color: white;
  9 + border: 2px solid #111;
  10 + box-shadow: 3px 3px 8px rgba(0, 0, 0, 0.3);
  11 + border-radius: 3px;
  12 + text-align: center;
  13 + z-index: 21;
  14 +}
  15 +div.dt-button-info h2 {
  16 + padding: 0.5em;
  17 + margin: 0;
  18 + font-weight: normal;
  19 + border-bottom: 1px solid #ddd;
  20 + background-color: #f3f3f3;
  21 +}
  22 +div.dt-button-info > div {
  23 + padding: 1em;
  24 +}
  25 +
  26 +ul.dt-button-collection.dropdown-menu {
  27 + display: block;
  28 + z-index: 2002;
  29 + -webkit-column-gap: 8px;
  30 + -moz-column-gap: 8px;
  31 + -ms-column-gap: 8px;
  32 + -o-column-gap: 8px;
  33 + column-gap: 8px;
  34 +}
  35 +ul.dt-button-collection.dropdown-menu.fixed {
  36 + position: fixed;
  37 + top: 50%;
  38 + left: 50%;
  39 + margin-left: -75px;
  40 + border-radius: 0;
  41 +}
  42 +ul.dt-button-collection.dropdown-menu.fixed.two-column {
  43 + margin-left: -150px;
  44 +}
  45 +ul.dt-button-collection.dropdown-menu.fixed.three-column {
  46 + margin-left: -225px;
  47 +}
  48 +ul.dt-button-collection.dropdown-menu.fixed.four-column {
  49 + margin-left: -300px;
  50 +}
  51 +ul.dt-button-collection.dropdown-menu > * {
  52 + -webkit-column-break-inside: avoid;
  53 + break-inside: avoid;
  54 +}
  55 +ul.dt-button-collection.dropdown-menu.two-column {
  56 + width: 300px;
  57 + padding-bottom: 1px;
  58 + -webkit-column-count: 2;
  59 + -moz-column-count: 2;
  60 + -ms-column-count: 2;
  61 + -o-column-count: 2;
  62 + column-count: 2;
  63 +}
  64 +ul.dt-button-collection.dropdown-menu.three-column {
  65 + width: 450px;
  66 + padding-bottom: 1px;
  67 + -webkit-column-count: 3;
  68 + -moz-column-count: 3;
  69 + -ms-column-count: 3;
  70 + -o-column-count: 3;
  71 + column-count: 3;
  72 +}
  73 +ul.dt-button-collection.dropdown-menu.four-column {
  74 + width: 600px;
  75 + padding-bottom: 1px;
  76 + -webkit-column-count: 4;
  77 + -moz-column-count: 4;
  78 + -ms-column-count: 4;
  79 + -o-column-count: 4;
  80 + column-count: 4;
  81 +}
  82 +
  83 +div.dt-button-background {
  84 + position: fixed;
  85 + top: 0;
  86 + left: 0;
  87 + width: 100%;
  88 + height: 100%;
  89 + z-index: 2001;
  90 +}
  91 +
  92 +@media screen and (max-width: 767px) {
  93 + div.dt-buttons {
  94 + float: none;
  95 + width: 100%;
  96 + text-align: center;
  97 + margin-bottom: 0.5em;
  98 + }
  99 + div.dt-buttons a.btn {
  100 + float: none;
  101 + }
  102 +}
... ...
amadeus/static/js/dataTables.buttons.html5.js 0 → 100644
... ... @@ -0,0 +1,1340 @@
  1 +/*!
  2 + * HTML5 export buttons for Buttons and DataTables.
  3 + * 2016 SpryMedia Ltd - datatables.net/license
  4 + *
  5 + * FileSaver.js (1.3.3) - MIT license
  6 + * Copyright © 2016 Eli Grey - http://eligrey.com
  7 + */
  8 +
  9 +(function( factory ){
  10 + if ( typeof define === 'function' && define.amd ) {
  11 + // AMD
  12 + define( ['jquery', 'datatables.net', 'datatables.net-buttons'], function ( $ ) {
  13 + return factory( $, window, document );
  14 + } );
  15 + }
  16 + else if ( typeof exports === 'object' ) {
  17 + // CommonJS
  18 + module.exports = function (root, $, jszip, pdfmake) {
  19 + if ( ! root ) {
  20 + root = window;
  21 + }
  22 +
  23 + if ( ! $ || ! $.fn.dataTable ) {
  24 + $ = require('datatables.net')(root, $).$;
  25 + }
  26 +
  27 + if ( ! $.fn.dataTable.Buttons ) {
  28 + require('datatables.net-buttons')(root, $);
  29 + }
  30 +
  31 + return factory( $, root, root.document, jszip, pdfmake );
  32 + };
  33 + }
  34 + else {
  35 + // Browser
  36 + factory( jQuery, window, document );
  37 + }
  38 +}(function( $, window, document, jszip, pdfmake, undefined ) {
  39 +'use strict';
  40 +var DataTable = $.fn.dataTable;
  41 +
  42 +// Allow the constructor to pass in JSZip and PDFMake from external requires.
  43 +// Otherwise, use globally defined variables, if they are available.
  44 +function _jsZip () {
  45 + return jszip || window.JSZip;
  46 +}
  47 +function _pdfMake () {
  48 + return pdfmake || window.pdfMake;
  49 +}
  50 +
  51 +
  52 +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  53 + * FileSaver.js dependency
  54 + */
  55 +
  56 +/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
  57 +
  58 +var _saveAs = (function(view) {
  59 + "use strict";
  60 + // IE <10 is explicitly unsupported
  61 + if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
  62 + return;
  63 + }
  64 + var
  65 + doc = view.document
  66 + // only get URL when necessary in case Blob.js hasn't overridden it yet
  67 + , get_URL = function() {
  68 + return view.URL || view.webkitURL || view;
  69 + }
  70 + , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
  71 + , can_use_save_link = "download" in save_link
  72 + , click = function(node) {
  73 + var event = new MouseEvent("click");
  74 + node.dispatchEvent(event);
  75 + }
  76 + , is_safari = /constructor/i.test(view.HTMLElement) || view.safari
  77 + , is_chrome_ios =/CriOS\/[\d]+/.test(navigator.userAgent)
  78 + , throw_outside = function(ex) {
  79 + (view.setImmediate || view.setTimeout)(function() {
  80 + throw ex;
  81 + }, 0);
  82 + }
  83 + , force_saveable_type = "application/octet-stream"
  84 + // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
  85 + , arbitrary_revoke_timeout = 1000 * 40 // in ms
  86 + , revoke = function(file) {
  87 + var revoker = function() {
  88 + if (typeof file === "string") { // file is an object URL
  89 + get_URL().revokeObjectURL(file);
  90 + } else { // file is a File
  91 + file.remove();
  92 + }
  93 + };
  94 + setTimeout(revoker, arbitrary_revoke_timeout);
  95 + }
  96 + , dispatch = function(filesaver, event_types, event) {
  97 + event_types = [].concat(event_types);
  98 + var i = event_types.length;
  99 + while (i--) {
  100 + var listener = filesaver["on" + event_types[i]];
  101 + if (typeof listener === "function") {
  102 + try {
  103 + listener.call(filesaver, event || filesaver);
  104 + } catch (ex) {
  105 + throw_outside(ex);
  106 + }
  107 + }
  108 + }
  109 + }
  110 + , auto_bom = function(blob) {
  111 + // prepend BOM for UTF-8 XML and text/* types (including HTML)
  112 + // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
  113 + if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
  114 + return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type});
  115 + }
  116 + return blob;
  117 + }
  118 + , FileSaver = function(blob, name, no_auto_bom) {
  119 + if (!no_auto_bom) {
  120 + blob = auto_bom(blob);
  121 + }
  122 + // First try a.download, then web filesystem, then object URLs
  123 + var
  124 + filesaver = this
  125 + , type = blob.type
  126 + , force = type === force_saveable_type
  127 + , object_url
  128 + , dispatch_all = function() {
  129 + dispatch(filesaver, "writestart progress write writeend".split(" "));
  130 + }
  131 + // on any filesys errors revert to saving with object URLs
  132 + , fs_error = function() {
  133 + if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
  134 + // Safari doesn't allow downloading of blob urls
  135 + var reader = new FileReader();
  136 + reader.onloadend = function() {
  137 + var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');
  138 + var popup = view.open(url, '_blank');
  139 + if(!popup) view.location.href = url;
  140 + url=undefined; // release reference before dispatching
  141 + filesaver.readyState = filesaver.DONE;
  142 + dispatch_all();
  143 + };
  144 + reader.readAsDataURL(blob);
  145 + filesaver.readyState = filesaver.INIT;
  146 + return;
  147 + }
  148 + // don't create more object URLs than needed
  149 + if (!object_url) {
  150 + object_url = get_URL().createObjectURL(blob);
  151 + }
  152 + if (force) {
  153 + view.location.href = object_url;
  154 + } else {
  155 + var opened = view.open(object_url, "_blank");
  156 + if (!opened) {
  157 + // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
  158 + view.location.href = object_url;
  159 + }
  160 + }
  161 + filesaver.readyState = filesaver.DONE;
  162 + dispatch_all();
  163 + revoke(object_url);
  164 + }
  165 + ;
  166 + filesaver.readyState = filesaver.INIT;
  167 +
  168 + if (can_use_save_link) {
  169 + object_url = get_URL().createObjectURL(blob);
  170 + setTimeout(function() {
  171 + save_link.href = object_url;
  172 + save_link.download = name;
  173 + click(save_link);
  174 + dispatch_all();
  175 + revoke(object_url);
  176 + filesaver.readyState = filesaver.DONE;
  177 + });
  178 + return;
  179 + }
  180 +
  181 + fs_error();
  182 + }
  183 + , FS_proto = FileSaver.prototype
  184 + , saveAs = function(blob, name, no_auto_bom) {
  185 + return new FileSaver(blob, name || blob.name || "download", no_auto_bom);
  186 + }
  187 + ;
  188 + // IE 10+ (native saveAs)
  189 + if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
  190 + return function(blob, name, no_auto_bom) {
  191 + name = name || blob.name || "download";
  192 +
  193 + if (!no_auto_bom) {
  194 + blob = auto_bom(blob);
  195 + }
  196 + return navigator.msSaveOrOpenBlob(blob, name);
  197 + };
  198 + }
  199 +
  200 + FS_proto.abort = function(){};
  201 + FS_proto.readyState = FS_proto.INIT = 0;
  202 + FS_proto.WRITING = 1;
  203 + FS_proto.DONE = 2;
  204 +
  205 + FS_proto.error =
  206 + FS_proto.onwritestart =
  207 + FS_proto.onprogress =
  208 + FS_proto.onwrite =
  209 + FS_proto.onabort =
  210 + FS_proto.onerror =
  211 + FS_proto.onwriteend =
  212 + null;
  213 +
  214 + return saveAs;
  215 +}(
  216 + typeof self !== "undefined" && self
  217 + || typeof window !== "undefined" && window
  218 + || this.content
  219 +));
  220 +
  221 +
  222 +// Expose file saver on the DataTables API. Can't attach to `DataTables.Buttons`
  223 +// since this file can be loaded before Button's core!
  224 +DataTable.fileSave = _saveAs;
  225 +
  226 +
  227 +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  228 + * Local (private) functions
  229 + */
  230 +
  231 +/**
  232 + * Get the file name for an exported file.
  233 + *
  234 + * @param {object} config Button configuration
  235 + * @param {boolean} incExtension Include the file name extension
  236 + */
  237 +var _filename = function ( config, incExtension )
  238 +{
  239 + // Backwards compatibility
  240 + var filename = config.filename === '*' && config.title !== '*' && config.title !== undefined ?
  241 + config.title :
  242 + config.filename;
  243 +
  244 + if ( typeof filename === 'function' ) {
  245 + filename = filename();
  246 + }
  247 +
  248 + if ( filename.indexOf( '*' ) !== -1 ) {
  249 + filename = $.trim( filename.replace( '*', $('title').text() ) );
  250 + }
  251 +
  252 + // Strip characters which the OS will object to
  253 + filename = filename.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, "");
  254 +
  255 + return incExtension === undefined || incExtension === true ?
  256 + filename+config.extension :
  257 + filename;
  258 +};
  259 +
  260 +/**
  261 + * Get the sheet name for Excel exports.
  262 + *
  263 + * @param {object} config Button configuration
  264 + */
  265 +var _sheetname = function ( config )
  266 +{
  267 + var sheetName = 'Sheet1';
  268 +
  269 + if ( config.sheetName ) {
  270 + sheetName = config.sheetName.replace(/[\[\]\*\/\\\?\:]/g, '');
  271 + }
  272 +
  273 +return sheetName;
  274 +};
  275 +
  276 +/**
  277 + * Get the title for an exported file.
  278 + *
  279 + * @param {object} config Button configuration
  280 + */
  281 +var _title = function ( config )
  282 +{
  283 + var title = config.title;
  284 +
  285 + if ( typeof title === 'function' ) {
  286 + title = title();
  287 + }
  288 +
  289 + return title.indexOf( '*' ) !== -1 ?
  290 + title.replace( '*', $('title').text() || 'Exported data' ) :
  291 + title;
  292 +};
  293 +
  294 +/**
  295 + * Get the newline character(s)
  296 + *
  297 + * @param {object} config Button configuration
  298 + * @return {string} Newline character
  299 + */
  300 +var _newLine = function ( config )
  301 +{
  302 + return config.newline ?
  303 + config.newline :
  304 + navigator.userAgent.match(/Windows/) ?
  305 + '\r\n' :
  306 + '\n';
  307 +};
  308 +
  309 +/**
  310 + * Combine the data from the `buttons.exportData` method into a string that
  311 + * will be used in the export file.
  312 + *
  313 + * @param {DataTable.Api} dt DataTables API instance
  314 + * @param {object} config Button configuration
  315 + * @return {object} The data to export
  316 + */
  317 +var _exportData = function ( dt, config )
  318 +{
  319 + var newLine = _newLine( config );
  320 + var data = dt.buttons.exportData( config.exportOptions );
  321 + var boundary = config.fieldBoundary;
  322 + var separator = config.fieldSeparator;
  323 + var reBoundary = new RegExp( boundary, 'g' );
  324 + var escapeChar = config.escapeChar !== undefined ?
  325 + config.escapeChar :
  326 + '\\';
  327 + var join = function ( a ) {
  328 + var s = '';
  329 +
  330 + // If there is a field boundary, then we might need to escape it in
  331 + // the source data
  332 + for ( var i=0, ien=a.length ; i<ien ; i++ ) {
  333 + if ( i > 0 ) {
  334 + s += separator;
  335 + }
  336 +
  337 + s += boundary ?
  338 + boundary + ('' + a[i]).replace( reBoundary, escapeChar+boundary ) + boundary :
  339 + a[i];
  340 + }
  341 +
  342 + return s;
  343 + };
  344 +
  345 + var header = config.header ? join( data.header )+newLine : '';
  346 + var footer = config.footer && data.footer ? newLine+join( data.footer ) : '';
  347 + var body = [];
  348 +
  349 + for ( var i=0, ien=data.body.length ; i<ien ; i++ ) {
  350 + body.push( join( data.body[i] ) );
  351 + }
  352 +
  353 + return {
  354 + str: header + body.join( newLine ) + footer,
  355 + rows: body.length
  356 + };
  357 +};
  358 +
  359 +/**
  360 + * Older versions of Safari (prior to tech preview 18) don't support the
  361 + * download option required.
  362 + *
  363 + * @return {Boolean} `true` if old Safari
  364 + */
  365 +var _isDuffSafari = function ()
  366 +{
  367 + var safari = navigator.userAgent.indexOf('Safari') !== -1 &&
  368 + navigator.userAgent.indexOf('Chrome') === -1 &&
  369 + navigator.userAgent.indexOf('Opera') === -1;
  370 +
  371 + if ( ! safari ) {
  372 + return false;
  373 + }
  374 +
  375 + var version = navigator.userAgent.match( /AppleWebKit\/(\d+\.\d+)/ );
  376 + if ( version && version.length > 1 && version[1]*1 < 603.1 ) {
  377 + return true;
  378 + }
  379 +
  380 + return false;
  381 +};
  382 +
  383 +/**
  384 + * Convert from numeric position to letter for column names in Excel
  385 + * @param {int} n Column number
  386 + * @return {string} Column letter(s) name
  387 + */
  388 +function createCellPos( n ){
  389 + var ordA = 'A'.charCodeAt(0);
  390 + var ordZ = 'Z'.charCodeAt(0);
  391 + var len = ordZ - ordA + 1;
  392 + var s = "";
  393 +
  394 + while( n >= 0 ) {
  395 + s = String.fromCharCode(n % len + ordA) + s;
  396 + n = Math.floor(n / len) - 1;
  397 + }
  398 +
  399 + return s;
  400 +}
  401 +
  402 +try {
  403 + var _serialiser = new XMLSerializer();
  404 + var _ieExcel;
  405 +}
  406 +catch (t) {}
  407 +
  408 +/**
  409 + * Recursively add XML files from an object's structure to a ZIP file. This
  410 + * allows the XSLX file to be easily defined with an object's structure matching
  411 + * the files structure.
  412 + *
  413 + * @param {JSZip} zip ZIP package
  414 + * @param {object} obj Object to add (recursive)
  415 + */
  416 +function _addToZip( zip, obj ) {
  417 + if ( _ieExcel === undefined ) {
  418 + // Detect if we are dealing with IE's _awful_ serialiser by seeing if it
  419 + // drop attributes
  420 + _ieExcel = _serialiser
  421 + .serializeToString(
  422 + $.parseXML( excelStrings['xl/worksheets/sheet1.xml'] )
  423 + )
  424 + .indexOf( 'xmlns:r' ) === -1;
  425 + }
  426 +
  427 + $.each( obj, function ( name, val ) {
  428 + if ( $.isPlainObject( val ) ) {
  429 + var newDir = zip.folder( name );
  430 + _addToZip( newDir, val );
  431 + }
  432 + else {
  433 + if ( _ieExcel ) {
  434 + // IE's XML serialiser will drop some name space attributes from
  435 + // from the root node, so we need to save them. Do this by
  436 + // replacing the namespace nodes with a regular attribute that
  437 + // we convert back when serialised. Edge does not have this
  438 + // issue
  439 + var worksheet = val.childNodes[0];
  440 + var i, ien;
  441 + var attrs = [];
  442 +
  443 + for ( i=worksheet.attributes.length-1 ; i>=0 ; i-- ) {
  444 + var attrName = worksheet.attributes[i].nodeName;
  445 + var attrValue = worksheet.attributes[i].nodeValue;
  446 +
  447 + if ( attrName.indexOf( ':' ) !== -1 ) {
  448 + attrs.push( { name: attrName, value: attrValue } );
  449 +
  450 + worksheet.removeAttribute( attrName );
  451 + }
  452 + }
  453 +
  454 + for ( i=0, ien=attrs.length ; i<ien ; i++ ) {
  455 + var attr = val.createAttribute( attrs[i].name.replace( ':', '_dt_b_namespace_token_' ) );
  456 + attr.value = attrs[i].value;
  457 + worksheet.setAttributeNode( attr );
  458 + }
  459 + }
  460 +
  461 + var str = _serialiser.serializeToString(val);
  462 +
  463 + // Fix IE's XML
  464 + if ( _ieExcel ) {
  465 + // IE doesn't include the XML declaration
  466 + if ( str.indexOf( '<?xml' ) === -1 ) {
  467 + str = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+str;
  468 + }
  469 +
  470 + // Return namespace attributes to being as such
  471 + str = str.replace( /_dt_b_namespace_token_/g, ':' );
  472 + }
  473 +
  474 + // Safari, IE and Edge will put empty name space attributes onto
  475 + // various elements making them useless. This strips them out
  476 + str = str.replace( /<(.*?) xmlns=""(.*?)>/g, '<$1 $2>' );
  477 +
  478 + zip.file( name, str );
  479 + }
  480 + } );
  481 +}
  482 +
  483 +/**
  484 + * Create an XML node and add any children, attributes, etc without needing to
  485 + * be verbose in the DOM.
  486 + *
  487 + * @param {object} doc XML document
  488 + * @param {string} nodeName Node name
  489 + * @param {object} opts Options - can be `attr` (attributes), `children`
  490 + * (child nodes) and `text` (text content)
  491 + * @return {node} Created node
  492 + */
  493 +function _createNode( doc, nodeName, opts ) {
  494 + var tempNode = doc.createElement( nodeName );
  495 +
  496 + if ( opts ) {
  497 + if ( opts.attr ) {
  498 + $(tempNode).attr( opts.attr );
  499 + }
  500 +
  501 + if( opts.children ) {
  502 + $.each( opts.children, function ( key, value ) {
  503 + tempNode.appendChild( value );
  504 + });
  505 + }
  506 +
  507 + if( opts.text ) {
  508 + tempNode.appendChild( doc.createTextNode( opts.text ) );
  509 + }
  510 + }
  511 +
  512 + return tempNode;
  513 +}
  514 +
  515 +/**
  516 + * Get the width for an Excel column based on the contents of that column
  517 + * @param {object} data Data for export
  518 + * @param {int} col Column index
  519 + * @return {int} Column width
  520 + */
  521 +function _excelColWidth( data, col ) {
  522 + var max = data.header[col].length;
  523 + var len, lineSplit, str;
  524 +
  525 + if ( data.footer && data.footer[col].length > max ) {
  526 + max = data.footer[col].length;
  527 + }
  528 +
  529 + for ( var i=0, ien=data.body.length ; i<ien ; i++ ) {
  530 + str = data.body[i][col].toString();
  531 +
  532 + // If there is a newline character, workout the width of the column
  533 + // based on the longest line in the string
  534 + if ( str.indexOf('\n') !== -1 ) {
  535 + lineSplit = str.split('\n');
  536 + lineSplit.sort( function (a, b) {
  537 + return b.length - a.length;
  538 + } );
  539 +
  540 + len = lineSplit[0].length;
  541 + }
  542 + else {
  543 + len = str.length;
  544 + }
  545 +
  546 + if ( len > max ) {
  547 + max = len;
  548 + }
  549 +
  550 + // Max width rather than having potentially massive column widths
  551 + if ( max > 40 ) {
  552 + break;
  553 + }
  554 + }
  555 +
  556 + max *= 1.3;
  557 +
  558 + // And a min width
  559 + return max > 6 ? max : 6;
  560 +}
  561 +
  562 +// Excel - Pre-defined strings to build a basic XLSX file
  563 +var excelStrings = {
  564 + "_rels/.rels":
  565 + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+
  566 + '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'+
  567 + '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>'+
  568 + '</Relationships>',
  569 +
  570 + "xl/_rels/workbook.xml.rels":
  571 + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+
  572 + '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'+
  573 + '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>'+
  574 + '<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>'+
  575 + '</Relationships>',
  576 +
  577 + "[Content_Types].xml":
  578 + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+
  579 + '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'+
  580 + '<Default Extension="xml" ContentType="application/xml" />'+
  581 + '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml" />'+
  582 + '<Default Extension="jpeg" ContentType="image/jpeg" />'+
  583 + '<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" />'+
  584 + '<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" />'+
  585 + '<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml" />'+
  586 + '</Types>',
  587 +
  588 + "xl/workbook.xml":
  589 + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+
  590 + '<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">'+
  591 + '<fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="24816"/>'+
  592 + '<workbookPr showInkAnnotation="0" autoCompressPictures="0"/>'+
  593 + '<bookViews>'+
  594 + '<workbookView xWindow="0" yWindow="0" windowWidth="25600" windowHeight="19020" tabRatio="500"/>'+
  595 + '</bookViews>'+
  596 + '<sheets>'+
  597 + '<sheet name="" sheetId="1" r:id="rId1"/>'+
  598 + '</sheets>'+
  599 + '</workbook>',
  600 +
  601 + "xl/worksheets/sheet1.xml":
  602 + '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+
  603 + '<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">'+
  604 + '<sheetData/>'+
  605 + '</worksheet>',
  606 +
  607 + "xl/styles.xml":
  608 + '<?xml version="1.0" encoding="UTF-8"?>'+
  609 + '<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">'+
  610 + '<numFmts count="6">'+
  611 + '<numFmt numFmtId="164" formatCode="#,##0.00_-\ [$$-45C]"/>'+
  612 + '<numFmt numFmtId="165" formatCode="&quot;£&quot;#,##0.00"/>'+
  613 + '<numFmt numFmtId="166" formatCode="[$€-2]\ #,##0.00"/>'+
  614 + '<numFmt numFmtId="167" formatCode="0.0%"/>'+
  615 + '<numFmt numFmtId="168" formatCode="#,##0;(#,##0)"/>'+
  616 + '<numFmt numFmtId="169" formatCode="#,##0.00;(#,##0.00)"/>'+
  617 + '</numFmts>'+
  618 + '<fonts count="5" x14ac:knownFonts="1">'+
  619 + '<font>'+
  620 + '<sz val="11" />'+
  621 + '<name val="Calibri" />'+
  622 + '</font>'+
  623 + '<font>'+
  624 + '<sz val="11" />'+
  625 + '<name val="Calibri" />'+
  626 + '<color rgb="FFFFFFFF" />'+
  627 + '</font>'+
  628 + '<font>'+
  629 + '<sz val="11" />'+
  630 + '<name val="Calibri" />'+
  631 + '<b />'+
  632 + '</font>'+
  633 + '<font>'+
  634 + '<sz val="11" />'+
  635 + '<name val="Calibri" />'+
  636 + '<i />'+
  637 + '</font>'+
  638 + '<font>'+
  639 + '<sz val="11" />'+
  640 + '<name val="Calibri" />'+
  641 + '<u />'+
  642 + '</font>'+
  643 + '</fonts>'+
  644 + '<fills count="6">'+
  645 + '<fill>'+
  646 + '<patternFill patternType="none" />'+
  647 + '</fill>'+
  648 + '<fill/>'+ // Excel appears to use this as a dotted background regardless of values
  649 + '<fill>'+
  650 + '<patternFill patternType="solid">'+
  651 + '<fgColor rgb="FFD9D9D9" />'+
  652 + '<bgColor indexed="64" />'+
  653 + '</patternFill>'+
  654 + '</fill>'+
  655 + '<fill>'+
  656 + '<patternFill patternType="solid">'+
  657 + '<fgColor rgb="FFD99795" />'+
  658 + '<bgColor indexed="64" />'+
  659 + '</patternFill>'+
  660 + '</fill>'+
  661 + '<fill>'+
  662 + '<patternFill patternType="solid">'+
  663 + '<fgColor rgb="ffc6efce" />'+
  664 + '<bgColor indexed="64" />'+
  665 + '</patternFill>'+
  666 + '</fill>'+
  667 + '<fill>'+
  668 + '<patternFill patternType="solid">'+
  669 + '<fgColor rgb="ffc6cfef" />'+
  670 + '<bgColor indexed="64" />'+
  671 + '</patternFill>'+
  672 + '</fill>'+
  673 + '</fills>'+
  674 + '<borders count="2">'+
  675 + '<border>'+
  676 + '<left />'+
  677 + '<right />'+
  678 + '<top />'+
  679 + '<bottom />'+
  680 + '<diagonal />'+
  681 + '</border>'+
  682 + '<border diagonalUp="false" diagonalDown="false">'+
  683 + '<left style="thin">'+
  684 + '<color auto="1" />'+
  685 + '</left>'+
  686 + '<right style="thin">'+
  687 + '<color auto="1" />'+
  688 + '</right>'+
  689 + '<top style="thin">'+
  690 + '<color auto="1" />'+
  691 + '</top>'+
  692 + '<bottom style="thin">'+
  693 + '<color auto="1" />'+
  694 + '</bottom>'+
  695 + '<diagonal />'+
  696 + '</border>'+
  697 + '</borders>'+
  698 + '<cellStyleXfs count="1">'+
  699 + '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" />'+
  700 + '</cellStyleXfs>'+
  701 + '<cellXfs count="65">'+
  702 + '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  703 + '<xf numFmtId="0" fontId="1" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  704 + '<xf numFmtId="0" fontId="2" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  705 + '<xf numFmtId="0" fontId="3" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  706 + '<xf numFmtId="0" fontId="4" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  707 + '<xf numFmtId="0" fontId="0" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  708 + '<xf numFmtId="0" fontId="1" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  709 + '<xf numFmtId="0" fontId="2" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  710 + '<xf numFmtId="0" fontId="3" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  711 + '<xf numFmtId="0" fontId="4" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  712 + '<xf numFmtId="0" fontId="0" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  713 + '<xf numFmtId="0" fontId="1" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  714 + '<xf numFmtId="0" fontId="2" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  715 + '<xf numFmtId="0" fontId="3" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  716 + '<xf numFmtId="0" fontId="4" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  717 + '<xf numFmtId="0" fontId="0" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  718 + '<xf numFmtId="0" fontId="1" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  719 + '<xf numFmtId="0" fontId="2" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  720 + '<xf numFmtId="0" fontId="3" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  721 + '<xf numFmtId="0" fontId="4" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  722 + '<xf numFmtId="0" fontId="0" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  723 + '<xf numFmtId="0" fontId="1" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  724 + '<xf numFmtId="0" fontId="2" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  725 + '<xf numFmtId="0" fontId="3" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  726 + '<xf numFmtId="0" fontId="4" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
  727 + '<xf numFmtId="0" fontId="0" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  728 + '<xf numFmtId="0" fontId="1" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  729 + '<xf numFmtId="0" fontId="2" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  730 + '<xf numFmtId="0" fontId="3" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  731 + '<xf numFmtId="0" fontId="4" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  732 + '<xf numFmtId="0" fontId="0" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  733 + '<xf numFmtId="0" fontId="1" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  734 + '<xf numFmtId="0" fontId="2" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  735 + '<xf numFmtId="0" fontId="3" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  736 + '<xf numFmtId="0" fontId="4" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  737 + '<xf numFmtId="0" fontId="0" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  738 + '<xf numFmtId="0" fontId="1" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  739 + '<xf numFmtId="0" fontId="2" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  740 + '<xf numFmtId="0" fontId="3" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  741 + '<xf numFmtId="0" fontId="4" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  742 + '<xf numFmtId="0" fontId="0" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  743 + '<xf numFmtId="0" fontId="1" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  744 + '<xf numFmtId="0" fontId="2" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  745 + '<xf numFmtId="0" fontId="3" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  746 + '<xf numFmtId="0" fontId="4" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  747 + '<xf numFmtId="0" fontId="0" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  748 + '<xf numFmtId="0" fontId="1" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  749 + '<xf numFmtId="0" fontId="2" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  750 + '<xf numFmtId="0" fontId="3" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  751 + '<xf numFmtId="0" fontId="4" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
  752 + '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
  753 + '<alignment horizontal="left"/>'+
  754 + '</xf>'+
  755 + '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
  756 + '<alignment horizontal="center"/>'+
  757 + '</xf>'+
  758 + '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
  759 + '<alignment horizontal="right"/>'+
  760 + '</xf>'+
  761 + '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
  762 + '<alignment horizontal="fill"/>'+
  763 + '</xf>'+
  764 + '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
  765 + '<alignment textRotation="90"/>'+
  766 + '</xf>'+
  767 + '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
  768 + '<alignment wrapText="1"/>'+
  769 + '</xf>'+
  770 + '<xf numFmtId="9" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
  771 + '<xf numFmtId="164" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
  772 + '<xf numFmtId="165" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
  773 + '<xf numFmtId="166" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
  774 + '<xf numFmtId="167" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
  775 + '<xf numFmtId="168" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
  776 + '<xf numFmtId="169" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
  777 + '<xf numFmtId="3" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
  778 + '<xf numFmtId="4" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
  779 + '</cellXfs>'+
  780 + '<cellStyles count="1">'+
  781 + '<cellStyle name="Normal" xfId="0" builtinId="0" />'+
  782 + '</cellStyles>'+
  783 + '<dxfs count="0" />'+
  784 + '<tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleMedium4" />'+
  785 + '</styleSheet>'
  786 +};
  787 +// Note we could use 3 `for` loops for the styles, but when gzipped there is
  788 +// virtually no difference in size, since the above can be easily compressed
  789 +
  790 +// Pattern matching for special number formats. Perhaps this should be exposed
  791 +// via an API in future?
  792 +// Ref: section 3.8.30 - built in formatters in open spreadsheet
  793 +// https://www.ecma-international.org/news/TC45_current_work/Office%20Open%20XML%20Part%204%20-%20Markup%20Language%20Reference.pdf
  794 +var _excelSpecials = [
  795 + { match: /^\-?\d+\.\d%$/, style: 60, fmt: function (d) { return d/100; } }, // Precent with d.p.
  796 + { match: /^\-?\d+\.?\d*%$/, style: 56, fmt: function (d) { return d/100; } }, // Percent
  797 + { match: /^\-?\$[\d,]+.?\d*$/, style: 57 }, // Dollars
  798 + { match: /^\-?£[\d,]+.?\d*$/, style: 58 }, // Pounds
  799 + { match: /^\-?€[\d,]+.?\d*$/, style: 59 }, // Euros
  800 + { match: /^\([\d,]+\)$/, style: 61, fmt: function (d) { return -1 * d.replace(/[\(\)]/g, ''); } }, // Negative numbers indicated by brackets
  801 + { match: /^\([\d,]+\.\d{2}\)$/, style: 62, fmt: function (d) { return -1 * d.replace(/[\(\)]/g, ''); } }, // Negative numbers indicated by brackets - 2d.p.
  802 + { match: /^[\d,]+$/, style: 63 }, // Numbers with thousand separators
  803 + { match: /^[\d,]+\.\d{2}$/, style: 64 } // Numbers with 2d.p. and thousands separators
  804 +];
  805 +
  806 +
  807 +
  808 +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  809 + * Buttons
  810 + */
  811 +
  812 +//
  813 +// Copy to clipboard
  814 +//
  815 +DataTable.ext.buttons.copyHtml5 = {
  816 + className: 'buttons-copy buttons-html5',
  817 +
  818 + text: function ( dt ) {
  819 + return dt.i18n( 'buttons.copy', 'Copy' );
  820 + },
  821 +
  822 + action: function ( e, dt, button, config ) {
  823 + var exportData = _exportData( dt, config );
  824 + var output = exportData.str;
  825 + var hiddenDiv = $('<div/>')
  826 + .css( {
  827 + height: 1,
  828 + width: 1,
  829 + overflow: 'hidden',
  830 + position: 'fixed',
  831 + top: 0,
  832 + left: 0
  833 + } );
  834 +
  835 + if ( config.customize ) {
  836 + output = config.customize( output, config );
  837 + }
  838 +
  839 + var textarea = $('<textarea readonly/>')
  840 + .val( output )
  841 + .appendTo( hiddenDiv );
  842 +
  843 + // For browsers that support the copy execCommand, try to use it
  844 + if ( document.queryCommandSupported('copy') ) {
  845 + hiddenDiv.appendTo( dt.table().container() );
  846 + textarea[0].focus();
  847 + textarea[0].select();
  848 +
  849 + try {
  850 + var successful = document.execCommand( 'copy' );
  851 + hiddenDiv.remove();
  852 +
  853 + if (successful) {
  854 + dt.buttons.info(
  855 + dt.i18n( 'buttons.copyTitle', 'Copy to clipboard' ),
  856 + dt.i18n( 'buttons.copySuccess', {
  857 + 1: 'Copied one row to clipboard',
  858 + _: 'Copied %d rows to clipboard'
  859 + }, exportData.rows ),
  860 + 2000
  861 + );
  862 + return;
  863 + }
  864 + }
  865 + catch (t) {}
  866 + }
  867 +
  868 + // Otherwise we show the text box and instruct the user to use it
  869 + var message = $('<span>'+dt.i18n( 'buttons.copyKeys',
  870 + 'Press <i>ctrl</i> or <i>\u2318</i> + <i>C</i> to copy the table data<br>to your system clipboard.<br><br>'+
  871 + 'To cancel, click this message or press escape.' )+'</span>'
  872 + )
  873 + .append( hiddenDiv );
  874 +
  875 + dt.buttons.info( dt.i18n( 'buttons.copyTitle', 'Copy to clipboard' ), message, 0 );
  876 +
  877 + // Select the text so when the user activates their system clipboard
  878 + // it will copy that text
  879 + textarea[0].focus();
  880 + textarea[0].select();
  881 +
  882 + // Event to hide the message when the user is done
  883 + var container = $(message).closest('.dt-button-info');
  884 + var close = function () {
  885 + container.off( 'click.buttons-copy' );
  886 + $(document).off( '.buttons-copy' );
  887 + dt.buttons.info( false );
  888 + };
  889 +
  890 + container.on( 'click.buttons-copy', close );
  891 + $(document)
  892 + .on( 'keydown.buttons-copy', function (e) {
  893 + if ( e.keyCode === 27 ) { // esc
  894 + close();
  895 + }
  896 + } )
  897 + .on( 'copy.buttons-copy cut.buttons-copy', function () {
  898 + close();
  899 + } );
  900 + },
  901 +
  902 + exportOptions: {},
  903 +
  904 + fieldSeparator: '\t',
  905 +
  906 + fieldBoundary: '',
  907 +
  908 + header: true,
  909 +
  910 + footer: false
  911 +};
  912 +
  913 +//
  914 +// CSV export
  915 +//
  916 +DataTable.ext.buttons.csvHtml5 = {
  917 + bom: false,
  918 +
  919 + className: 'buttons-csv buttons-html5',
  920 +
  921 + available: function () {
  922 + return window.FileReader !== undefined && window.Blob;
  923 + },
  924 +
  925 + text: function ( dt ) {
  926 + return dt.i18n( 'buttons.csv', 'CSV' );
  927 + },
  928 +
  929 + action: function ( e, dt, button, config ) {
  930 + // Set the text
  931 + var output = _exportData( dt, config ).str;
  932 + var charset = config.charset;
  933 +
  934 + if ( config.customize ) {
  935 + output = config.customize( output, config );
  936 + }
  937 +
  938 + if ( charset !== false ) {
  939 + if ( ! charset ) {
  940 + charset = document.characterSet || document.charset;
  941 + }
  942 +
  943 + if ( charset ) {
  944 + charset = ';charset='+charset;
  945 + }
  946 + }
  947 + else {
  948 + charset = '';
  949 + }
  950 +
  951 + if ( config.bom ) {
  952 + output = '\ufeff' + output;
  953 + }
  954 +
  955 + _saveAs(
  956 + new Blob( [output], {type: 'text/csv'+charset} ),
  957 + _filename( config ),
  958 + true
  959 + );
  960 + },
  961 +
  962 + filename: '*',
  963 +
  964 + extension: '.csv',
  965 +
  966 + exportOptions: {},
  967 +
  968 + fieldSeparator: ',',
  969 +
  970 + fieldBoundary: '"',
  971 +
  972 + escapeChar: '"',
  973 +
  974 + charset: null,
  975 +
  976 + header: true,
  977 +
  978 + footer: false
  979 +};
  980 +
  981 +//
  982 +// Excel (xlsx) export
  983 +//
  984 +DataTable.ext.buttons.excelHtml5 = {
  985 + className: 'buttons-excel buttons-html5',
  986 +
  987 + available: function () {
  988 + return window.FileReader !== undefined && _jsZip() !== undefined && ! _isDuffSafari() && _serialiser;
  989 + },
  990 +
  991 + text: function ( dt ) {
  992 + return dt.i18n( 'buttons.excel', 'Excel' );
  993 + },
  994 +
  995 + action: function ( e, dt, button, config ) {
  996 + var rowPos = 0;
  997 + var getXml = function ( type ) {
  998 + var str = excelStrings[ type ];
  999 +
  1000 + //str = str.replace( /xmlns:/g, 'xmlns_' ).replace( /mc:/g, 'mc_' );
  1001 +
  1002 + return $.parseXML( str );
  1003 + };
  1004 + var rels = getXml('xl/worksheets/sheet1.xml');
  1005 + var relsGet = rels.getElementsByTagName( "sheetData" )[0];
  1006 +
  1007 + var xlsx = {
  1008 + _rels: {
  1009 + ".rels": getXml('_rels/.rels')
  1010 + },
  1011 + xl: {
  1012 + _rels: {
  1013 + "workbook.xml.rels": getXml('xl/_rels/workbook.xml.rels')
  1014 + },
  1015 + "workbook.xml": getXml('xl/workbook.xml'),
  1016 + "styles.xml": getXml('xl/styles.xml'),
  1017 + "worksheets": {
  1018 + "sheet1.xml": rels
  1019 + }
  1020 +
  1021 + },
  1022 + "[Content_Types].xml": getXml('[Content_Types].xml')
  1023 + };
  1024 +
  1025 + var data = dt.buttons.exportData( config.exportOptions );
  1026 + var currentRow, rowNode;
  1027 + var addRow = function ( row ) {
  1028 + currentRow = rowPos+1;
  1029 + rowNode = _createNode( rels, "row", { attr: {r:currentRow} } );
  1030 +
  1031 + for ( var i=0, ien=row.length ; i<ien ; i++ ) {
  1032 + // Concat both the Cell Columns as a letter and the Row of the cell.
  1033 + var cellId = createCellPos(i) + '' + currentRow;
  1034 + var cell = null;
  1035 +
  1036 + // For null, undefined of blank cell, continue so it doesn't create the _createNode
  1037 + if ( row[i] === null || row[i] === undefined || row[i] === '' ) {
  1038 + continue;
  1039 + }
  1040 +
  1041 + row[i] = $.trim( row[i] );
  1042 +
  1043 + // Special number formatting options
  1044 + for ( var j=0, jen=_excelSpecials.length ; j<jen ; j++ ) {
  1045 + var special = _excelSpecials[j];
  1046 +
  1047 + if ( row[i].match && row[i].match( special.match ) ) {
  1048 + var val = row[i].replace(/[^\d\.\-]/g, '');
  1049 +
  1050 + if ( special.fmt ) {
  1051 + val = special.fmt( val );
  1052 + }
  1053 +
  1054 + cell = _createNode( rels, 'c', {
  1055 + attr: {
  1056 + r: cellId,
  1057 + s: special.style
  1058 + },
  1059 + children: [
  1060 + _createNode( rels, 'v', { text: val } )
  1061 + ]
  1062 + } );
  1063 +
  1064 + break;
  1065 + }
  1066 + }
  1067 +
  1068 + if ( ! cell ) {
  1069 + if ( typeof row[i] === 'number' || (
  1070 + row[i].match &&
  1071 + row[i].match(/^-?\d+(\.\d+)?$/) &&
  1072 + ! row[i].match(/^0\d+/) )
  1073 + ) {
  1074 + // Detect numbers - don't match numbers with leading zeros
  1075 + // or a negative anywhere but the start
  1076 + cell = _createNode( rels, 'c', {
  1077 + attr: {
  1078 + t: 'n',
  1079 + r: cellId
  1080 + },
  1081 + children: [
  1082 + _createNode( rels, 'v', { text: row[i] } )
  1083 + ]
  1084 + } );
  1085 + }
  1086 + else {
  1087 + // String output - replace non standard characters for text output
  1088 + var text = ! row[i].replace ?
  1089 + row[i] :
  1090 + row[i].replace(/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, '');
  1091 +
  1092 + cell = _createNode( rels, 'c', {
  1093 + attr: {
  1094 + t: 'inlineStr',
  1095 + r: cellId
  1096 + },
  1097 + children:{
  1098 + row: _createNode( rels, 'is', {
  1099 + children: {
  1100 + row: _createNode( rels, 't', {
  1101 + text: text
  1102 + } )
  1103 + }
  1104 + } )
  1105 + }
  1106 + } );
  1107 + }
  1108 + }
  1109 +
  1110 + rowNode.appendChild( cell );
  1111 + }
  1112 +
  1113 + relsGet.appendChild(rowNode);
  1114 + rowPos++;
  1115 + };
  1116 +
  1117 + $( 'sheets sheet', xlsx.xl['workbook.xml'] ).attr( 'name', _sheetname( config ) );
  1118 +
  1119 + if ( config.customizeData ) {
  1120 + config.customizeData( data );
  1121 + }
  1122 +
  1123 + if ( config.header ) {
  1124 + addRow( data.header, rowPos );
  1125 + $('row c', rels).attr( 's', '2' ); // bold
  1126 + }
  1127 +
  1128 + for ( var n=0, ie=data.body.length ; n<ie ; n++ ) {
  1129 + addRow( data.body[n], rowPos );
  1130 + }
  1131 +
  1132 + if ( config.footer && data.footer ) {
  1133 + addRow( data.footer, rowPos);
  1134 + $('row:last c', rels).attr( 's', '2' ); // bold
  1135 + }
  1136 +
  1137 + // Set column widths
  1138 + var cols = _createNode( rels, 'cols' );
  1139 + $('worksheet', rels).prepend( cols );
  1140 +
  1141 + for ( var i=0, ien=data.header.length ; i<ien ; i++ ) {
  1142 + cols.appendChild( _createNode( rels, 'col', {
  1143 + attr: {
  1144 + min: i+1,
  1145 + max: i+1,
  1146 + width: _excelColWidth( data, i ),
  1147 + customWidth: 1
  1148 + }
  1149 + } ) );
  1150 + }
  1151 +
  1152 + // Let the developer customise the document if they want to
  1153 + if ( config.customize ) {
  1154 + config.customize( xlsx );
  1155 + }
  1156 +
  1157 + var jszip = _jsZip();
  1158 + var zip = new jszip();
  1159 + var zipConfig = {
  1160 + type: 'blob',
  1161 + mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
  1162 + };
  1163 +
  1164 + _addToZip( zip, xlsx );
  1165 +
  1166 + if ( zip.generateAsync ) {
  1167 + // JSZip 3+
  1168 + zip
  1169 + .generateAsync( zipConfig )
  1170 + .then( function ( blob ) {
  1171 + _saveAs( blob, _filename( config ) );
  1172 + } );
  1173 + }
  1174 + else {
  1175 + // JSZip 2.5
  1176 + _saveAs(
  1177 + zip.generate( zipConfig ),
  1178 + _filename( config )
  1179 + );
  1180 + }
  1181 + },
  1182 +
  1183 + filename: '*',
  1184 +
  1185 + extension: '.xlsx',
  1186 +
  1187 + exportOptions: {},
  1188 +
  1189 + header: true,
  1190 +
  1191 + footer: false
  1192 +};
  1193 +
  1194 +//
  1195 +// PDF export - using pdfMake - http://pdfmake.org
  1196 +//
  1197 +DataTable.ext.buttons.pdfHtml5 = {
  1198 + className: 'buttons-pdf buttons-html5',
  1199 +
  1200 + available: function () {
  1201 + return window.FileReader !== undefined && _pdfMake();
  1202 + },
  1203 +
  1204 + text: function ( dt ) {
  1205 + return dt.i18n( 'buttons.pdf', 'PDF' );
  1206 + },
  1207 +
  1208 + action: function ( e, dt, button, config ) {
  1209 + var newLine = _newLine( config );
  1210 + var data = dt.buttons.exportData( config.exportOptions );
  1211 + var rows = [];
  1212 +
  1213 + if ( config.header ) {
  1214 + rows.push( $.map( data.header, function ( d ) {
  1215 + return {
  1216 + text: typeof d === 'string' ? d : d+'',
  1217 + style: 'tableHeader'
  1218 + };
  1219 + } ) );
  1220 + }
  1221 +
  1222 + for ( var i=0, ien=data.body.length ; i<ien ; i++ ) {
  1223 + rows.push( $.map( data.body[i], function ( d ) {
  1224 + return {
  1225 + text: typeof d === 'string' ? d : d+'',
  1226 + style: i % 2 ? 'tableBodyEven' : 'tableBodyOdd'
  1227 + };
  1228 + } ) );
  1229 + }
  1230 +
  1231 + if ( config.footer && data.footer) {
  1232 + rows.push( $.map( data.footer, function ( d ) {
  1233 + return {
  1234 + text: typeof d === 'string' ? d : d+'',
  1235 + style: 'tableFooter'
  1236 + };
  1237 + } ) );
  1238 + }
  1239 +
  1240 + var doc = {
  1241 + pageSize: config.pageSize,
  1242 + pageOrientation: config.orientation,
  1243 + content: [
  1244 + {
  1245 + table: {
  1246 + headerRows: 1,
  1247 + body: rows
  1248 + },
  1249 + layout: 'noBorders'
  1250 + }
  1251 + ],
  1252 + styles: {
  1253 + tableHeader: {
  1254 + bold: true,
  1255 + fontSize: 11,
  1256 + color: 'white',
  1257 + fillColor: '#2d4154',
  1258 + alignment: 'center'
  1259 + },
  1260 + tableBodyEven: {},
  1261 + tableBodyOdd: {
  1262 + fillColor: '#f3f3f3'
  1263 + },
  1264 + tableFooter: {
  1265 + bold: true,
  1266 + fontSize: 11,
  1267 + color: 'white',
  1268 + fillColor: '#2d4154'
  1269 + },
  1270 + title: {
  1271 + alignment: 'center',
  1272 + fontSize: 15
  1273 + },
  1274 + message: {}
  1275 + },
  1276 + defaultStyle: {
  1277 + fontSize: 10
  1278 + }
  1279 + };
  1280 +
  1281 + if ( config.message ) {
  1282 + doc.content.unshift( {
  1283 + text: typeof config.message == 'function' ? config.message(dt, button, config) : config.message,
  1284 + style: 'message',
  1285 + margin: [ 0, 0, 0, 12 ]
  1286 + } );
  1287 + }
  1288 +
  1289 + if ( config.title ) {
  1290 + doc.content.unshift( {
  1291 + text: _title( config, false ),
  1292 + style: 'title',
  1293 + margin: [ 0, 0, 0, 12 ]
  1294 + } );
  1295 + }
  1296 +
  1297 + if ( config.customize ) {
  1298 + config.customize( doc, config );
  1299 + }
  1300 +
  1301 + var pdf = _pdfMake().createPdf( doc );
  1302 +
  1303 + if ( config.download === 'open' && ! _isDuffSafari() ) {
  1304 + pdf.open();
  1305 + }
  1306 + else {
  1307 + pdf.getBuffer( function (buffer) {
  1308 + var blob = new Blob( [buffer], {type:'application/pdf'} );
  1309 +
  1310 + _saveAs( blob, _filename( config ) );
  1311 + } );
  1312 + }
  1313 + },
  1314 +
  1315 + title: '*',
  1316 +
  1317 + filename: '*',
  1318 +
  1319 + extension: '.pdf',
  1320 +
  1321 + exportOptions: {},
  1322 +
  1323 + orientation: 'portrait',
  1324 +
  1325 + pageSize: 'A4',
  1326 +
  1327 + header: true,
  1328 +
  1329 + footer: false,
  1330 +
  1331 + message: null,
  1332 +
  1333 + customize: null,
  1334 +
  1335 + download: 'download'
  1336 +};
  1337 +
  1338 +
  1339 +return DataTable.Buttons;
  1340 +}));
... ...
amadeus/static/js/dataTables.buttons.js 0 → 100644
... ... @@ -0,0 +1,1664 @@
  1 +/*! Buttons for DataTables 1.2.3
  2 + * ©2016 SpryMedia Ltd - datatables.net/license
  3 + */
  4 +
  5 +(function( factory ){
  6 + if ( typeof define === 'function' && define.amd ) {
  7 + // AMD
  8 + define( ['jquery', 'datatables.net'], function ( $ ) {
  9 + return factory( $, window, document );
  10 + } );
  11 + }
  12 + else if ( typeof exports === 'object' ) {
  13 + // CommonJS
  14 + module.exports = function (root, $) {
  15 + if ( ! root ) {
  16 + root = window;
  17 + }
  18 +
  19 + if ( ! $ || ! $.fn.dataTable ) {
  20 + $ = require('datatables.net')(root, $).$;
  21 + }
  22 +
  23 + return factory( $, root, root.document );
  24 + };
  25 + }
  26 + else {
  27 + // Browser
  28 + factory( jQuery, window, document );
  29 + }
  30 +}(function( $, window, document, undefined ) {
  31 +'use strict';
  32 +var DataTable = $.fn.dataTable;
  33 +
  34 +
  35 +// Used for namespacing events added to the document by each instance, so they
  36 +// can be removed on destroy
  37 +var _instCounter = 0;
  38 +
  39 +// Button namespacing counter for namespacing events on individual buttons
  40 +var _buttonCounter = 0;
  41 +
  42 +var _dtButtons = DataTable.ext.buttons;
  43 +
  44 +/**
  45 + * [Buttons description]
  46 + * @param {[type]}
  47 + * @param {[type]}
  48 + */
  49 +var Buttons = function( dt, config )
  50 +{
  51 + // Allow a boolean true for defaults
  52 + if ( config === true ) {
  53 + config = {};
  54 + }
  55 +
  56 + // For easy configuration of buttons an array can be given
  57 + if ( $.isArray( config ) ) {
  58 + config = { buttons: config };
  59 + }
  60 +
  61 + this.c = $.extend( true, {}, Buttons.defaults, config );
  62 +
  63 + // Don't want a deep copy for the buttons
  64 + if ( config.buttons ) {
  65 + this.c.buttons = config.buttons;
  66 + }
  67 +
  68 + this.s = {
  69 + dt: new DataTable.Api( dt ),
  70 + buttons: [],
  71 + listenKeys: '',
  72 + namespace: 'dtb'+(_instCounter++)
  73 + };
  74 +
  75 + this.dom = {
  76 + container: $('<'+this.c.dom.container.tag+'/>')
  77 + .addClass( this.c.dom.container.className )
  78 + };
  79 +
  80 + this._constructor();
  81 +};
  82 +
  83 +
  84 +$.extend( Buttons.prototype, {
  85 + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  86 + * Public methods
  87 + */
  88 +
  89 + /**
  90 + * Get the action of a button
  91 + * @param {int|string} Button index
  92 + * @return {function}
  93 + *//**
  94 + * Set the action of a button
  95 + * @param {node} node Button element
  96 + * @param {function} action Function to set
  97 + * @return {Buttons} Self for chaining
  98 + */
  99 + action: function ( node, action )
  100 + {
  101 + var button = this._nodeToButton( node );
  102 +
  103 + if ( action === undefined ) {
  104 + return button.conf.action;
  105 + }
  106 +
  107 + button.conf.action = action;
  108 +
  109 + return this;
  110 + },
  111 +
  112 + /**
  113 + * Add an active class to the button to make to look active or get current
  114 + * active state.
  115 + * @param {node} node Button element
  116 + * @param {boolean} [flag] Enable / disable flag
  117 + * @return {Buttons} Self for chaining or boolean for getter
  118 + */
  119 + active: function ( node, flag ) {
  120 + var button = this._nodeToButton( node );
  121 + var klass = this.c.dom.button.active;
  122 + var jqNode = $(button.node);
  123 +
  124 + if ( flag === undefined ) {
  125 + return jqNode.hasClass( klass );
  126 + }
  127 +
  128 + jqNode.toggleClass( klass, flag === undefined ? true : flag );
  129 +
  130 + return this;
  131 + },
  132 +
  133 + /**
  134 + * Add a new button
  135 + * @param {object} config Button configuration object, base string name or function
  136 + * @param {int|string} [idx] Button index for where to insert the button
  137 + * @return {Buttons} Self for chaining
  138 + */
  139 + add: function ( config, idx )
  140 + {
  141 + var buttons = this.s.buttons;
  142 +
  143 + if ( typeof idx === 'string' ) {
  144 + var split = idx.split('-');
  145 + var base = this.s;
  146 +
  147 + for ( var i=0, ien=split.length-1 ; i<ien ; i++ ) {
  148 + base = base.buttons[ split[i]*1 ];
  149 + }
  150 +
  151 + buttons = base.buttons;
  152 + idx = split[ split.length-1 ]*1;
  153 + }
  154 +
  155 + this._expandButton( buttons, config, false, idx );
  156 + this._draw();
  157 +
  158 + return this;
  159 + },
  160 +
  161 + /**
  162 + * Get the container node for the buttons
  163 + * @return {jQuery} Buttons node
  164 + */
  165 + container: function ()
  166 + {
  167 + return this.dom.container;
  168 + },
  169 +
  170 + /**
  171 + * Disable a button
  172 + * @param {node} node Button node
  173 + * @return {Buttons} Self for chaining
  174 + */
  175 + disable: function ( node ) {
  176 + var button = this._nodeToButton( node );
  177 +
  178 + $(button.node).addClass( this.c.dom.button.disabled );
  179 +
  180 + return this;
  181 + },
  182 +
  183 + /**
  184 + * Destroy the instance, cleaning up event handlers and removing DOM
  185 + * elements
  186 + * @return {Buttons} Self for chaining
  187 + */
  188 + destroy: function ()
  189 + {
  190 + // Key event listener
  191 + $('body').off( 'keyup.'+this.s.namespace );
  192 +
  193 + // Individual button destroy (so they can remove their own events if
  194 + // needed). Take a copy as the array is modified by `remove`
  195 + var buttons = this.s.buttons.slice();
  196 + var i, ien;
  197 +
  198 + for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
  199 + this.remove( buttons[i].node );
  200 + }
  201 +
  202 + // Container
  203 + this.dom.container.remove();
  204 +
  205 + // Remove from the settings object collection
  206 + var buttonInsts = this.s.dt.settings()[0];
  207 +
  208 + for ( i=0, ien=buttonInsts.length ; i<ien ; i++ ) {
  209 + if ( buttonInsts.inst === this ) {
  210 + buttonInsts.splice( i, 1 );
  211 + break;
  212 + }
  213 + }
  214 +
  215 + return this;
  216 + },
  217 +
  218 + /**
  219 + * Enable / disable a button
  220 + * @param {node} node Button node
  221 + * @param {boolean} [flag=true] Enable / disable flag
  222 + * @return {Buttons} Self for chaining
  223 + */
  224 + enable: function ( node, flag )
  225 + {
  226 + if ( flag === false ) {
  227 + return this.disable( node );
  228 + }
  229 +
  230 + var button = this._nodeToButton( node );
  231 + $(button.node).removeClass( this.c.dom.button.disabled );
  232 +
  233 + return this;
  234 + },
  235 +
  236 + /**
  237 + * Get the instance name for the button set selector
  238 + * @return {string} Instance name
  239 + */
  240 + name: function ()
  241 + {
  242 + return this.c.name;
  243 + },
  244 +
  245 + /**
  246 + * Get a button's node
  247 + * @param {node} node Button node
  248 + * @return {jQuery} Button element
  249 + */
  250 + node: function ( node )
  251 + {
  252 + var button = this._nodeToButton( node );
  253 + return $(button.node);
  254 + },
  255 +
  256 + /**
  257 + * Remove a button.
  258 + * @param {node} node Button node
  259 + * @return {Buttons} Self for chaining
  260 + */
  261 + remove: function ( node )
  262 + {
  263 + var button = this._nodeToButton( node );
  264 + var host = this._nodeToHost( node );
  265 + var dt = this.s.dt;
  266 +
  267 + // Remove any child buttons first
  268 + if ( button.buttons.length ) {
  269 + for ( var i=button.buttons.length-1 ; i>=0 ; i-- ) {
  270 + this.remove( button.buttons[i].node );
  271 + }
  272 + }
  273 +
  274 + // Allow the button to remove event handlers, etc
  275 + if ( button.conf.destroy ) {
  276 + button.conf.destroy.call( dt.button(node), dt, $(node), button.conf );
  277 + }
  278 +
  279 + this._removeKey( button.conf );
  280 +
  281 + $(button.node).remove();
  282 +
  283 + var idx = $.inArray( button, host );
  284 + host.splice( idx, 1 );
  285 +
  286 + return this;
  287 + },
  288 +
  289 + /**
  290 + * Get the text for a button
  291 + * @param {int|string} node Button index
  292 + * @return {string} Button text
  293 + *//**
  294 + * Set the text for a button
  295 + * @param {int|string|function} node Button index
  296 + * @param {string} label Text
  297 + * @return {Buttons} Self for chaining
  298 + */
  299 + text: function ( node, label )
  300 + {
  301 + var button = this._nodeToButton( node );
  302 + var buttonLiner = this.c.dom.collection.buttonLiner;
  303 + var linerTag = button.inCollection && buttonLiner && buttonLiner.tag ?
  304 + buttonLiner.tag :
  305 + this.c.dom.buttonLiner.tag;
  306 + var dt = this.s.dt;
  307 + var jqNode = $(button.node);
  308 + var text = function ( opt ) {
  309 + return typeof opt === 'function' ?
  310 + opt( dt, jqNode, button.conf ) :
  311 + opt;
  312 + };
  313 +
  314 + if ( label === undefined ) {
  315 + return text( button.conf.text );
  316 + }
  317 +
  318 + button.conf.text = label;
  319 +
  320 + if ( linerTag ) {
  321 + jqNode.children( linerTag ).html( text(label) );
  322 + }
  323 + else {
  324 + jqNode.html( text(label) );
  325 + }
  326 +
  327 + return this;
  328 + },
  329 +
  330 +
  331 + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  332 + * Constructor
  333 + */
  334 +
  335 + /**
  336 + * Buttons constructor
  337 + * @private
  338 + */
  339 + _constructor: function ()
  340 + {
  341 + var that = this;
  342 + var dt = this.s.dt;
  343 + var dtSettings = dt.settings()[0];
  344 + var buttons = this.c.buttons;
  345 +
  346 + if ( ! dtSettings._buttons ) {
  347 + dtSettings._buttons = [];
  348 + }
  349 +
  350 + dtSettings._buttons.push( {
  351 + inst: this,
  352 + name: this.c.name
  353 + } );
  354 +
  355 + for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
  356 + this.add( buttons[i] );
  357 + }
  358 +
  359 + dt.on( 'destroy', function () {
  360 + that.destroy();
  361 + } );
  362 +
  363 + // Global key event binding to listen for button keys
  364 + $('body').on( 'keyup.'+this.s.namespace, function ( e ) {
  365 + if ( ! document.activeElement || document.activeElement === document.body ) {
  366 + // SUse a string of characters for fast lookup of if we need to
  367 + // handle this
  368 + var character = String.fromCharCode(e.keyCode).toLowerCase();
  369 +
  370 + if ( that.s.listenKeys.toLowerCase().indexOf( character ) !== -1 ) {
  371 + that._keypress( character, e );
  372 + }
  373 + }
  374 + } );
  375 + },
  376 +
  377 +
  378 + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  379 + * Private methods
  380 + */
  381 +
  382 + /**
  383 + * Add a new button to the key press listener
  384 + * @param {object} conf Resolved button configuration object
  385 + * @private
  386 + */
  387 + _addKey: function ( conf )
  388 + {
  389 + if ( conf.key ) {
  390 + this.s.listenKeys += $.isPlainObject( conf.key ) ?
  391 + conf.key.key :
  392 + conf.key;
  393 + }
  394 + },
  395 +
  396 + /**
  397 + * Insert the buttons into the container. Call without parameters!
  398 + * @param {node} [container] Recursive only - Insert point
  399 + * @param {array} [buttons] Recursive only - Buttons array
  400 + * @private
  401 + */
  402 + _draw: function ( container, buttons )
  403 + {
  404 + if ( ! container ) {
  405 + container = this.dom.container;
  406 + buttons = this.s.buttons;
  407 + }
  408 +
  409 + container.children().detach();
  410 +
  411 + for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
  412 + container.append( buttons[i].inserter );
  413 +
  414 + if ( buttons[i].buttons && buttons[i].buttons.length ) {
  415 + this._draw( buttons[i].collection, buttons[i].buttons );
  416 + }
  417 + }
  418 + },
  419 +
  420 + /**
  421 + * Create buttons from an array of buttons
  422 + * @param {array} attachTo Buttons array to attach to
  423 + * @param {object} button Button definition
  424 + * @param {boolean} inCollection true if the button is in a collection
  425 + * @private
  426 + */
  427 + _expandButton: function ( attachTo, button, inCollection, attachPoint )
  428 + {
  429 + var dt = this.s.dt;
  430 + var buttonCounter = 0;
  431 + var buttons = ! $.isArray( button ) ?
  432 + [ button ] :
  433 + button;
  434 +
  435 + for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
  436 + var conf = this._resolveExtends( buttons[i] );
  437 +
  438 + if ( ! conf ) {
  439 + continue;
  440 + }
  441 +
  442 + // If the configuration is an array, then expand the buttons at this
  443 + // point
  444 + if ( $.isArray( conf ) ) {
  445 + this._expandButton( attachTo, conf, inCollection, attachPoint );
  446 + continue;
  447 + }
  448 +
  449 + var built = this._buildButton( conf, inCollection );
  450 + if ( ! built ) {
  451 + continue;
  452 + }
  453 +
  454 + if ( attachPoint !== undefined ) {
  455 + attachTo.splice( attachPoint, 0, built );
  456 + attachPoint++;
  457 + }
  458 + else {
  459 + attachTo.push( built );
  460 + }
  461 +
  462 + if ( built.conf.buttons ) {
  463 + var collectionDom = this.c.dom.collection;
  464 + built.collection = $('<'+collectionDom.tag+'/>')
  465 + .addClass( collectionDom.className );
  466 + built.conf._collection = built.collection;
  467 +
  468 + this._expandButton( built.buttons, built.conf.buttons, true, attachPoint );
  469 + }
  470 +
  471 + // init call is made here, rather than buildButton as it needs to
  472 + // be selectable, and for that it needs to be in the buttons array
  473 + if ( conf.init ) {
  474 + conf.init.call( dt.button( built.node ), dt, $(built.node), conf );
  475 + }
  476 +
  477 + buttonCounter++;
  478 + }
  479 + },
  480 +
  481 + /**
  482 + * Create an individual button
  483 + * @param {object} config Resolved button configuration
  484 + * @param {boolean} inCollection `true` if a collection button
  485 + * @return {jQuery} Created button node (jQuery)
  486 + * @private
  487 + */
  488 + _buildButton: function ( config, inCollection )
  489 + {
  490 + var buttonDom = this.c.dom.button;
  491 + var linerDom = this.c.dom.buttonLiner;
  492 + var collectionDom = this.c.dom.collection;
  493 + var dt = this.s.dt;
  494 + var text = function ( opt ) {
  495 + return typeof opt === 'function' ?
  496 + opt( dt, button, config ) :
  497 + opt;
  498 + };
  499 +
  500 + if ( inCollection && collectionDom.button ) {
  501 + buttonDom = collectionDom.button;
  502 + }
  503 +
  504 + if ( inCollection && collectionDom.buttonLiner ) {
  505 + linerDom = collectionDom.buttonLiner;
  506 + }
  507 +
  508 + // Make sure that the button is available based on whatever requirements
  509 + // it has. For example, Flash buttons require Flash
  510 + if ( config.available && ! config.available( dt, config ) ) {
  511 + return false;
  512 + }
  513 +
  514 + var action = function ( e, dt, button, config ) {
  515 + config.action.call( dt.button( button ), e, dt, button, config );
  516 +
  517 + $(dt.table().node()).triggerHandler( 'buttons-action.dt', [
  518 + dt.button( button ), dt, button, config
  519 + ] );
  520 + };
  521 +
  522 + var button = $('<'+buttonDom.tag+'/>')
  523 + .addClass( buttonDom.className )
  524 + .attr( 'tabindex', this.s.dt.settings()[0].iTabIndex )
  525 + .attr( 'aria-controls', this.s.dt.table().node().id )
  526 + .on( 'click.dtb', function (e) {
  527 + e.preventDefault();
  528 +
  529 + if ( ! button.hasClass( buttonDom.disabled ) && config.action ) {
  530 + action( e, dt, button, config );
  531 + }
  532 +
  533 + button.blur();
  534 + } )
  535 + .on( 'keyup.dtb', function (e) {
  536 + if ( e.keyCode === 13 ) {
  537 + if ( ! button.hasClass( buttonDom.disabled ) && config.action ) {
  538 + action( e, dt, button, config );
  539 + }
  540 + }
  541 + } );
  542 +
  543 + // Make `a` tags act like a link
  544 + if ( buttonDom.tag.toLowerCase() === 'a' ) {
  545 + button.attr( 'href', '#' );
  546 + }
  547 +
  548 + if ( linerDom.tag ) {
  549 + var liner = $('<'+linerDom.tag+'/>')
  550 + .html( text( config.text ) )
  551 + .addClass( linerDom.className );
  552 +
  553 + if ( linerDom.tag.toLowerCase() === 'a' ) {
  554 + liner.attr( 'href', '#' );
  555 + }
  556 +
  557 + button.append( liner );
  558 + }
  559 + else {
  560 + button.html( text( config.text ) );
  561 + }
  562 +
  563 + if ( config.enabled === false ) {
  564 + button.addClass( buttonDom.disabled );
  565 + }
  566 +
  567 + if ( config.className ) {
  568 + button.addClass( config.className );
  569 + }
  570 +
  571 + if ( config.titleAttr ) {
  572 + button.attr( 'title', config.titleAttr );
  573 + }
  574 +
  575 + if ( ! config.namespace ) {
  576 + config.namespace = '.dt-button-'+(_buttonCounter++);
  577 + }
  578 +
  579 + var buttonContainer = this.c.dom.buttonContainer;
  580 + var inserter;
  581 + if ( buttonContainer && buttonContainer.tag ) {
  582 + inserter = $('<'+buttonContainer.tag+'/>')
  583 + .addClass( buttonContainer.className )
  584 + .append( button );
  585 + }
  586 + else {
  587 + inserter = button;
  588 + }
  589 +
  590 + this._addKey( config );
  591 +
  592 + return {
  593 + conf: config,
  594 + node: button.get(0),
  595 + inserter: inserter,
  596 + buttons: [],
  597 + inCollection: inCollection,
  598 + collection: null
  599 + };
  600 + },
  601 +
  602 + /**
  603 + * Get the button object from a node (recursive)
  604 + * @param {node} node Button node
  605 + * @param {array} [buttons] Button array, uses base if not defined
  606 + * @return {object} Button object
  607 + * @private
  608 + */
  609 + _nodeToButton: function ( node, buttons )
  610 + {
  611 + if ( ! buttons ) {
  612 + buttons = this.s.buttons;
  613 + }
  614 +
  615 + for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
  616 + if ( buttons[i].node === node ) {
  617 + return buttons[i];
  618 + }
  619 +
  620 + if ( buttons[i].buttons.length ) {
  621 + var ret = this._nodeToButton( node, buttons[i].buttons );
  622 +
  623 + if ( ret ) {
  624 + return ret;
  625 + }
  626 + }
  627 + }
  628 + },
  629 +
  630 + /**
  631 + * Get container array for a button from a button node (recursive)
  632 + * @param {node} node Button node
  633 + * @param {array} [buttons] Button array, uses base if not defined
  634 + * @return {array} Button's host array
  635 + * @private
  636 + */
  637 + _nodeToHost: function ( node, buttons )
  638 + {
  639 + if ( ! buttons ) {
  640 + buttons = this.s.buttons;
  641 + }
  642 +
  643 + for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
  644 + if ( buttons[i].node === node ) {
  645 + return buttons;
  646 + }
  647 +
  648 + if ( buttons[i].buttons.length ) {
  649 + var ret = this._nodeToHost( node, buttons[i].buttons );
  650 +
  651 + if ( ret ) {
  652 + return ret;
  653 + }
  654 + }
  655 + }
  656 + },
  657 +
  658 + /**
  659 + * Handle a key press - determine if any button's key configured matches
  660 + * what was typed and trigger the action if so.
  661 + * @param {string} character The character pressed
  662 + * @param {object} e Key event that triggered this call
  663 + * @private
  664 + */
  665 + _keypress: function ( character, e )
  666 + {
  667 + var run = function ( conf, node ) {
  668 + if ( ! conf.key ) {
  669 + return;
  670 + }
  671 +
  672 + if ( conf.key === character ) {
  673 + $(node).click();
  674 + }
  675 + else if ( $.isPlainObject( conf.key ) ) {
  676 + if ( conf.key.key !== character ) {
  677 + return;
  678 + }
  679 +
  680 + if ( conf.key.shiftKey && ! e.shiftKey ) {
  681 + return;
  682 + }
  683 +
  684 + if ( conf.key.altKey && ! e.altKey ) {
  685 + return;
  686 + }
  687 +
  688 + if ( conf.key.ctrlKey && ! e.ctrlKey ) {
  689 + return;
  690 + }
  691 +
  692 + if ( conf.key.metaKey && ! e.metaKey ) {
  693 + return;
  694 + }
  695 +
  696 + // Made it this far - it is good
  697 + $(node).click();
  698 + }
  699 + };
  700 +
  701 + var recurse = function ( a ) {
  702 + for ( var i=0, ien=a.length ; i<ien ; i++ ) {
  703 + run( a[i].conf, a[i].node );
  704 +
  705 + if ( a[i].buttons.length ) {
  706 + recurse( a[i].buttons );
  707 + }
  708 + }
  709 + };
  710 +
  711 + recurse( this.s.buttons );
  712 + },
  713 +
  714 + /**
  715 + * Remove a key from the key listener for this instance (to be used when a
  716 + * button is removed)
  717 + * @param {object} conf Button configuration
  718 + * @private
  719 + */
  720 + _removeKey: function ( conf )
  721 + {
  722 + if ( conf.key ) {
  723 + var character = $.isPlainObject( conf.key ) ?
  724 + conf.key.key :
  725 + conf.key;
  726 +
  727 + // Remove only one character, as multiple buttons could have the
  728 + // same listening key
  729 + var a = this.s.listenKeys.split('');
  730 + var idx = $.inArray( character, a );
  731 + a.splice( idx, 1 );
  732 + this.s.listenKeys = a.join('');
  733 + }
  734 + },
  735 +
  736 + /**
  737 + * Resolve a button configuration
  738 + * @param {string|function|object} conf Button config to resolve
  739 + * @return {object} Button configuration
  740 + * @private
  741 + */
  742 + _resolveExtends: function ( conf )
  743 + {
  744 + var dt = this.s.dt;
  745 + var i, ien;
  746 + var toConfObject = function ( base ) {
  747 + var loop = 0;
  748 +
  749 + // Loop until we have resolved to a button configuration, or an
  750 + // array of button configurations (which will be iterated
  751 + // separately)
  752 + while ( ! $.isPlainObject(base) && ! $.isArray(base) ) {
  753 + if ( base === undefined ) {
  754 + return;
  755 + }
  756 +
  757 + if ( typeof base === 'function' ) {
  758 + base = base( dt, conf );
  759 +
  760 + if ( ! base ) {
  761 + return false;
  762 + }
  763 + }
  764 + else if ( typeof base === 'string' ) {
  765 + if ( ! _dtButtons[ base ] ) {
  766 + throw 'Unknown button type: '+base;
  767 + }
  768 +
  769 + base = _dtButtons[ base ];
  770 + }
  771 +
  772 + loop++;
  773 + if ( loop > 30 ) {
  774 + // Protect against misconfiguration killing the browser
  775 + throw 'Buttons: Too many iterations';
  776 + }
  777 + }
  778 +
  779 + return $.isArray( base ) ?
  780 + base :
  781 + $.extend( {}, base );
  782 + };
  783 +
  784 + conf = toConfObject( conf );
  785 +
  786 + while ( conf && conf.extend ) {
  787 + // Use `toConfObject` in case the button definition being extended
  788 + // is itself a string or a function
  789 + if ( ! _dtButtons[ conf.extend ] ) {
  790 + throw 'Cannot extend unknown button type: '+conf.extend;
  791 + }
  792 +
  793 + var objArray = toConfObject( _dtButtons[ conf.extend ] );
  794 + if ( $.isArray( objArray ) ) {
  795 + return objArray;
  796 + }
  797 + else if ( ! objArray ) {
  798 + // This is a little brutal as it might be possible to have a
  799 + // valid button without the extend, but if there is no extend
  800 + // then the host button would be acting in an undefined state
  801 + return false;
  802 + }
  803 +
  804 + // Stash the current class name
  805 + var originalClassName = objArray.className;
  806 +
  807 + conf = $.extend( {}, objArray, conf );
  808 +
  809 + // The extend will have overwritten the original class name if the
  810 + // `conf` object also assigned a class, but we want to concatenate
  811 + // them so they are list that is combined from all extended buttons
  812 + if ( originalClassName && conf.className !== originalClassName ) {
  813 + conf.className = originalClassName+' '+conf.className;
  814 + }
  815 +
  816 + // Buttons to be added to a collection -gives the ability to define
  817 + // if buttons should be added to the start or end of a collection
  818 + var postfixButtons = conf.postfixButtons;
  819 + if ( postfixButtons ) {
  820 + if ( ! conf.buttons ) {
  821 + conf.buttons = [];
  822 + }
  823 +
  824 + for ( i=0, ien=postfixButtons.length ; i<ien ; i++ ) {
  825 + conf.buttons.push( postfixButtons[i] );
  826 + }
  827 +
  828 + conf.postfixButtons = null;
  829 + }
  830 +
  831 + var prefixButtons = conf.prefixButtons;
  832 + if ( prefixButtons ) {
  833 + if ( ! conf.buttons ) {
  834 + conf.buttons = [];
  835 + }
  836 +
  837 + for ( i=0, ien=prefixButtons.length ; i<ien ; i++ ) {
  838 + conf.buttons.splice( i, 0, prefixButtons[i] );
  839 + }
  840 +
  841 + conf.prefixButtons = null;
  842 + }
  843 +
  844 + // Although we want the `conf` object to overwrite almost all of
  845 + // the properties of the object being extended, the `extend`
  846 + // property should come from the object being extended
  847 + conf.extend = objArray.extend;
  848 + }
  849 +
  850 + return conf;
  851 + }
  852 +} );
  853 +
  854 +
  855 +
  856 +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  857 + * Statics
  858 + */
  859 +
  860 +/**
  861 + * Show / hide a background layer behind a collection
  862 + * @param {boolean} Flag to indicate if the background should be shown or
  863 + * hidden
  864 + * @param {string} Class to assign to the background
  865 + * @static
  866 + */
  867 +Buttons.background = function ( show, className, fade ) {
  868 + if ( fade === undefined ) {
  869 + fade = 400;
  870 + }
  871 +
  872 + if ( show ) {
  873 + $('<div/>')
  874 + .addClass( className )
  875 + .css( 'display', 'none' )
  876 + .appendTo( 'body' )
  877 + .fadeIn( fade );
  878 + }
  879 + else {
  880 + $('body > div.'+className)
  881 + .fadeOut( fade, function () {
  882 + $(this)
  883 + .removeClass( className )
  884 + .remove();
  885 + } );
  886 + }
  887 +};
  888 +
  889 +/**
  890 + * Instance selector - select Buttons instances based on an instance selector
  891 + * value from the buttons assigned to a DataTable. This is only useful if
  892 + * multiple instances are attached to a DataTable.
  893 + * @param {string|int|array} Instance selector - see `instance-selector`
  894 + * documentation on the DataTables site
  895 + * @param {array} Button instance array that was attached to the DataTables
  896 + * settings object
  897 + * @return {array} Buttons instances
  898 + * @static
  899 + */
  900 +Buttons.instanceSelector = function ( group, buttons )
  901 +{
  902 + if ( ! group ) {
  903 + return $.map( buttons, function ( v ) {
  904 + return v.inst;
  905 + } );
  906 + }
  907 +
  908 + var ret = [];
  909 + var names = $.map( buttons, function ( v ) {
  910 + return v.name;
  911 + } );
  912 +
  913 + // Flatten the group selector into an array of single options
  914 + var process = function ( input ) {
  915 + if ( $.isArray( input ) ) {
  916 + for ( var i=0, ien=input.length ; i<ien ; i++ ) {
  917 + process( input[i] );
  918 + }
  919 + return;
  920 + }
  921 +
  922 + if ( typeof input === 'string' ) {
  923 + if ( input.indexOf( ',' ) !== -1 ) {
  924 + // String selector, list of names
  925 + process( input.split(',') );
  926 + }
  927 + else {
  928 + // String selector individual name
  929 + var idx = $.inArray( $.trim(input), names );
  930 +
  931 + if ( idx !== -1 ) {
  932 + ret.push( buttons[ idx ].inst );
  933 + }
  934 + }
  935 + }
  936 + else if ( typeof input === 'number' ) {
  937 + // Index selector
  938 + ret.push( buttons[ input ].inst );
  939 + }
  940 + };
  941 +
  942 + process( group );
  943 +
  944 + return ret;
  945 +};
  946 +
  947 +/**
  948 + * Button selector - select one or more buttons from a selector input so some
  949 + * operation can be performed on them.
  950 + * @param {array} Button instances array that the selector should operate on
  951 + * @param {string|int|node|jQuery|array} Button selector - see
  952 + * `button-selector` documentation on the DataTables site
  953 + * @return {array} Array of objects containing `inst` and `idx` properties of
  954 + * the selected buttons so you know which instance each button belongs to.
  955 + * @static
  956 + */
  957 +Buttons.buttonSelector = function ( insts, selector )
  958 +{
  959 + var ret = [];
  960 + var nodeBuilder = function ( a, buttons, baseIdx ) {
  961 + var button;
  962 + var idx;
  963 +
  964 + for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
  965 + button = buttons[i];
  966 +
  967 + if ( button ) {
  968 + idx = baseIdx !== undefined ?
  969 + baseIdx+i :
  970 + i+'';
  971 +
  972 + a.push( {
  973 + node: button.node,
  974 + name: button.conf.name,
  975 + idx: idx
  976 + } );
  977 +
  978 + if ( button.buttons ) {
  979 + nodeBuilder( a, button.buttons, idx+'-' );
  980 + }
  981 + }
  982 + }
  983 + };
  984 +
  985 + var run = function ( selector, inst ) {
  986 + var i, ien;
  987 + var buttons = [];
  988 + nodeBuilder( buttons, inst.s.buttons );
  989 +
  990 + var nodes = $.map( buttons, function (v) {
  991 + return v.node;
  992 + } );
  993 +
  994 + if ( $.isArray( selector ) || selector instanceof $ ) {
  995 + for ( i=0, ien=selector.length ; i<ien ; i++ ) {
  996 + run( selector[i], inst );
  997 + }
  998 + return;
  999 + }
  1000 +
  1001 + if ( selector === null || selector === undefined || selector === '*' ) {
  1002 + // Select all
  1003 + for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
  1004 + ret.push( {
  1005 + inst: inst,
  1006 + node: buttons[i].node
  1007 + } );
  1008 + }
  1009 + }
  1010 + else if ( typeof selector === 'number' ) {
  1011 + // Main button index selector
  1012 + ret.push( {
  1013 + inst: inst,
  1014 + node: inst.s.buttons[ selector ].node
  1015 + } );
  1016 + }
  1017 + else if ( typeof selector === 'string' ) {
  1018 + if ( selector.indexOf( ',' ) !== -1 ) {
  1019 + // Split
  1020 + var a = selector.split(',');
  1021 +
  1022 + for ( i=0, ien=a.length ; i<ien ; i++ ) {
  1023 + run( $.trim(a[i]), inst );
  1024 + }
  1025 + }
  1026 + else if ( selector.match( /^\d+(\-\d+)*$/ ) ) {
  1027 + // Sub-button index selector
  1028 + var indexes = $.map( buttons, function (v) {
  1029 + return v.idx;
  1030 + } );
  1031 +
  1032 + ret.push( {
  1033 + inst: inst,
  1034 + node: buttons[ $.inArray( selector, indexes ) ].node
  1035 + } );
  1036 + }
  1037 + else if ( selector.indexOf( ':name' ) !== -1 ) {
  1038 + // Button name selector
  1039 + var name = selector.replace( ':name', '' );
  1040 +
  1041 + for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
  1042 + if ( buttons[i].name === name ) {
  1043 + ret.push( {
  1044 + inst: inst,
  1045 + node: buttons[i].node
  1046 + } );
  1047 + }
  1048 + }
  1049 + }
  1050 + else {
  1051 + // jQuery selector on the nodes
  1052 + $( nodes ).filter( selector ).each( function () {
  1053 + ret.push( {
  1054 + inst: inst,
  1055 + node: this
  1056 + } );
  1057 + } );
  1058 + }
  1059 + }
  1060 + else if ( typeof selector === 'object' && selector.nodeName ) {
  1061 + // Node selector
  1062 + var idx = $.inArray( selector, nodes );
  1063 +
  1064 + if ( idx !== -1 ) {
  1065 + ret.push( {
  1066 + inst: inst,
  1067 + node: nodes[ idx ]
  1068 + } );
  1069 + }
  1070 + }
  1071 + };
  1072 +
  1073 +
  1074 + for ( var i=0, ien=insts.length ; i<ien ; i++ ) {
  1075 + var inst = insts[i];
  1076 +
  1077 + run( selector, inst );
  1078 + }
  1079 +
  1080 + return ret;
  1081 +};
  1082 +
  1083 +
  1084 +/**
  1085 + * Buttons defaults. For full documentation, please refer to the docs/option
  1086 + * directory or the DataTables site.
  1087 + * @type {Object}
  1088 + * @static
  1089 + */
  1090 +Buttons.defaults = {
  1091 + buttons: [ 'copy', 'excel', 'csv', 'pdf', 'print' ],
  1092 + name: 'main',
  1093 + tabIndex: 0,
  1094 + dom: {
  1095 + container: {
  1096 + tag: 'div',
  1097 + className: 'dt-buttons'
  1098 + },
  1099 + collection: {
  1100 + tag: 'div',
  1101 + className: 'dt-button-collection'
  1102 + },
  1103 + button: {
  1104 + tag: 'a',
  1105 + className: 'dt-button',
  1106 + active: 'active',
  1107 + disabled: 'disabled'
  1108 + },
  1109 + buttonLiner: {
  1110 + tag: 'span',
  1111 + className: ''
  1112 + }
  1113 + }
  1114 +};
  1115 +
  1116 +/**
  1117 + * Version information
  1118 + * @type {string}
  1119 + * @static
  1120 + */
  1121 +Buttons.version = '1.2.3';
  1122 +
  1123 +
  1124 +$.extend( _dtButtons, {
  1125 + collection: {
  1126 + text: function ( dt ) {
  1127 + return dt.i18n( 'buttons.collection', 'Collection' );
  1128 + },
  1129 + className: 'buttons-collection',
  1130 + action: function ( e, dt, button, config ) {
  1131 + var host = button;
  1132 + var hostOffset = host.offset();
  1133 + var tableContainer = $( dt.table().container() );
  1134 + var multiLevel = false;
  1135 +
  1136 + // Remove any old collection
  1137 + if ( $('div.dt-button-background').length ) {
  1138 + multiLevel = $('.dt-button-collection').offset();
  1139 + $('body').trigger( 'click.dtb-collection' );
  1140 + }
  1141 +
  1142 + config._collection
  1143 + .addClass( config.collectionLayout )
  1144 + .css( 'display', 'none' )
  1145 + .appendTo( 'body' )
  1146 + .fadeIn( config.fade );
  1147 +
  1148 + var position = config._collection.css( 'position' );
  1149 +
  1150 + if ( multiLevel && position === 'absolute' ) {
  1151 + config._collection.css( {
  1152 + top: multiLevel.top,
  1153 + left: multiLevel.left
  1154 + } );
  1155 + }
  1156 + else if ( position === 'absolute' ) {
  1157 + config._collection.css( {
  1158 + top: hostOffset.top + host.outerHeight(),
  1159 + left: hostOffset.left
  1160 + } );
  1161 +
  1162 + var listRight = hostOffset.left + config._collection.outerWidth();
  1163 + var tableRight = tableContainer.offset().left + tableContainer.width();
  1164 + if ( listRight > tableRight ) {
  1165 + config._collection.css( 'left', hostOffset.left - ( listRight - tableRight ) );
  1166 + }
  1167 + }
  1168 + else {
  1169 + // Fix position - centre on screen
  1170 + var top = config._collection.height() / 2;
  1171 + if ( top > $(window).height() / 2 ) {
  1172 + top = $(window).height() / 2;
  1173 + }
  1174 +
  1175 + config._collection.css( 'marginTop', top*-1 );
  1176 + }
  1177 +
  1178 + if ( config.background ) {
  1179 + Buttons.background( true, config.backgroundClassName, config.fade );
  1180 + }
  1181 +
  1182 + // Need to break the 'thread' for the collection button being
  1183 + // activated by a click - it would also trigger this event
  1184 + setTimeout( function () {
  1185 + // This is bonkers, but if we don't have a click listener on the
  1186 + // background element, iOS Safari will ignore the body click
  1187 + // listener below. An empty function here is all that is
  1188 + // required to make it work...
  1189 + $('div.dt-button-background').on( 'click.dtb-collection', function () {} );
  1190 +
  1191 + $('body').on( 'click.dtb-collection', function (e) {
  1192 + // andSelf is deprecated in jQ1.8, but we want 1.7 compat
  1193 + var back = $.fn.addBack ? 'addBack' : 'andSelf';
  1194 +
  1195 + if ( ! $(e.target).parents()[back]().filter( config._collection ).length ) {
  1196 + config._collection
  1197 + .fadeOut( config.fade, function () {
  1198 + config._collection.detach();
  1199 + } );
  1200 +
  1201 + $('div.dt-button-background').off( 'click.dtb-collection' );
  1202 + Buttons.background( false, config.backgroundClassName, config.fade );
  1203 +
  1204 + $('body').off( 'click.dtb-collection' );
  1205 + dt.off( 'buttons-action.b-internal' );
  1206 + }
  1207 + } );
  1208 + }, 10 );
  1209 +
  1210 + if ( config.autoClose ) {
  1211 + dt.on( 'buttons-action.b-internal', function () {
  1212 + $('div.dt-button-background').click();
  1213 + } );
  1214 + }
  1215 + },
  1216 + background: true,
  1217 + collectionLayout: '',
  1218 + backgroundClassName: 'dt-button-background',
  1219 + autoClose: false,
  1220 + fade: 400
  1221 + },
  1222 + copy: function ( dt, conf ) {
  1223 + if ( _dtButtons.copyHtml5 ) {
  1224 + return 'copyHtml5';
  1225 + }
  1226 + if ( _dtButtons.copyFlash && _dtButtons.copyFlash.available( dt, conf ) ) {
  1227 + return 'copyFlash';
  1228 + }
  1229 + },
  1230 + csv: function ( dt, conf ) {
  1231 + // Common option that will use the HTML5 or Flash export buttons
  1232 + if ( _dtButtons.csvHtml5 && _dtButtons.csvHtml5.available( dt, conf ) ) {
  1233 + return 'csvHtml5';
  1234 + }
  1235 + if ( _dtButtons.csvFlash && _dtButtons.csvFlash.available( dt, conf ) ) {
  1236 + return 'csvFlash';
  1237 + }
  1238 + },
  1239 + excel: function ( dt, conf ) {
  1240 + // Common option that will use the HTML5 or Flash export buttons
  1241 + if ( _dtButtons.excelHtml5 && _dtButtons.excelHtml5.available( dt, conf ) ) {
  1242 + return 'excelHtml5';
  1243 + }
  1244 + if ( _dtButtons.excelFlash && _dtButtons.excelFlash.available( dt, conf ) ) {
  1245 + return 'excelFlash';
  1246 + }
  1247 + },
  1248 + pdf: function ( dt, conf ) {
  1249 + // Common option that will use the HTML5 or Flash export buttons
  1250 + if ( _dtButtons.pdfHtml5 && _dtButtons.pdfHtml5.available( dt, conf ) ) {
  1251 + return 'pdfHtml5';
  1252 + }
  1253 + if ( _dtButtons.pdfFlash && _dtButtons.pdfFlash.available( dt, conf ) ) {
  1254 + return 'pdfFlash';
  1255 + }
  1256 + },
  1257 + pageLength: function ( dt ) {
  1258 + var lengthMenu = dt.settings()[0].aLengthMenu;
  1259 + var vals = $.isArray( lengthMenu[0] ) ? lengthMenu[0] : lengthMenu;
  1260 + var lang = $.isArray( lengthMenu[0] ) ? lengthMenu[1] : lengthMenu;
  1261 + var text = function ( dt ) {
  1262 + return dt.i18n( 'buttons.pageLength', {
  1263 + "-1": 'Show all rows',
  1264 + _: 'Show %d rows'
  1265 + }, dt.page.len() );
  1266 + };
  1267 +
  1268 + return {
  1269 + extend: 'collection',
  1270 + text: text,
  1271 + className: 'buttons-page-length',
  1272 + autoClose: true,
  1273 + buttons: $.map( vals, function ( val, i ) {
  1274 + return {
  1275 + text: lang[i],
  1276 + className: 'button-page-length',
  1277 + action: function ( e, dt ) {
  1278 + dt.page.len( val ).draw();
  1279 + },
  1280 + init: function ( dt, node, conf ) {
  1281 + var that = this;
  1282 + var fn = function () {
  1283 + that.active( dt.page.len() === val );
  1284 + };
  1285 +
  1286 + dt.on( 'length.dt'+conf.namespace, fn );
  1287 + fn();
  1288 + },
  1289 + destroy: function ( dt, node, conf ) {
  1290 + dt.off( 'length.dt'+conf.namespace );
  1291 + }
  1292 + };
  1293 + } ),
  1294 + init: function ( dt, node, conf ) {
  1295 + var that = this;
  1296 + dt.on( 'length.dt'+conf.namespace, function () {
  1297 + that.text( text( dt ) );
  1298 + } );
  1299 + },
  1300 + destroy: function ( dt, node, conf ) {
  1301 + dt.off( 'length.dt'+conf.namespace );
  1302 + }
  1303 + };
  1304 + }
  1305 +} );
  1306 +
  1307 +
  1308 +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1309 + * DataTables API
  1310 + *
  1311 + * For complete documentation, please refer to the docs/api directory or the
  1312 + * DataTables site
  1313 + */
  1314 +
  1315 +// Buttons group and individual button selector
  1316 +DataTable.Api.register( 'buttons()', function ( group, selector ) {
  1317 + // Argument shifting
  1318 + if ( selector === undefined ) {
  1319 + selector = group;
  1320 + group = undefined;
  1321 + }
  1322 +
  1323 + this.selector.buttonGroup = group;
  1324 +
  1325 + var res = this.iterator( true, 'table', function ( ctx ) {
  1326 + if ( ctx._buttons ) {
  1327 + return Buttons.buttonSelector(
  1328 + Buttons.instanceSelector( group, ctx._buttons ),
  1329 + selector
  1330 + );
  1331 + }
  1332 + }, true );
  1333 +
  1334 + res._groupSelector = group;
  1335 + return res;
  1336 +} );
  1337 +
  1338 +// Individual button selector
  1339 +DataTable.Api.register( 'button()', function ( group, selector ) {
  1340 + // just run buttons() and truncate
  1341 + var buttons = this.buttons( group, selector );
  1342 +
  1343 + if ( buttons.length > 1 ) {
  1344 + buttons.splice( 1, buttons.length );
  1345 + }
  1346 +
  1347 + return buttons;
  1348 +} );
  1349 +
  1350 +// Active buttons
  1351 +DataTable.Api.registerPlural( 'buttons().active()', 'button().active()', function ( flag ) {
  1352 + if ( flag === undefined ) {
  1353 + return this.map( function ( set ) {
  1354 + return set.inst.active( set.node );
  1355 + } );
  1356 + }
  1357 +
  1358 + return this.each( function ( set ) {
  1359 + set.inst.active( set.node, flag );
  1360 + } );
  1361 +} );
  1362 +
  1363 +// Get / set button action
  1364 +DataTable.Api.registerPlural( 'buttons().action()', 'button().action()', function ( action ) {
  1365 + if ( action === undefined ) {
  1366 + return this.map( function ( set ) {
  1367 + return set.inst.action( set.node );
  1368 + } );
  1369 + }
  1370 +
  1371 + return this.each( function ( set ) {
  1372 + set.inst.action( set.node, action );
  1373 + } );
  1374 +} );
  1375 +
  1376 +// Enable / disable buttons
  1377 +DataTable.Api.register( ['buttons().enable()', 'button().enable()'], function ( flag ) {
  1378 + return this.each( function ( set ) {
  1379 + set.inst.enable( set.node, flag );
  1380 + } );
  1381 +} );
  1382 +
  1383 +// Disable buttons
  1384 +DataTable.Api.register( ['buttons().disable()', 'button().disable()'], function () {
  1385 + return this.each( function ( set ) {
  1386 + set.inst.disable( set.node );
  1387 + } );
  1388 +} );
  1389 +
  1390 +// Get button nodes
  1391 +DataTable.Api.registerPlural( 'buttons().nodes()', 'button().node()', function () {
  1392 + var jq = $();
  1393 +
  1394 + // jQuery will automatically reduce duplicates to a single entry
  1395 + $( this.each( function ( set ) {
  1396 + jq = jq.add( set.inst.node( set.node ) );
  1397 + } ) );
  1398 +
  1399 + return jq;
  1400 +} );
  1401 +
  1402 +// Get / set button text (i.e. the button labels)
  1403 +DataTable.Api.registerPlural( 'buttons().text()', 'button().text()', function ( label ) {
  1404 + if ( label === undefined ) {
  1405 + return this.map( function ( set ) {
  1406 + return set.inst.text( set.node );
  1407 + } );
  1408 + }
  1409 +
  1410 + return this.each( function ( set ) {
  1411 + set.inst.text( set.node, label );
  1412 + } );
  1413 +} );
  1414 +
  1415 +// Trigger a button's action
  1416 +DataTable.Api.registerPlural( 'buttons().trigger()', 'button().trigger()', function () {
  1417 + return this.each( function ( set ) {
  1418 + set.inst.node( set.node ).trigger( 'click' );
  1419 + } );
  1420 +} );
  1421 +
  1422 +// Get the container elements
  1423 +DataTable.Api.registerPlural( 'buttons().containers()', 'buttons().container()', function () {
  1424 + var jq = $();
  1425 + var groupSelector = this._groupSelector;
  1426 +
  1427 + // We need to use the group selector directly, since if there are no buttons
  1428 + // the result set will be empty
  1429 + this.iterator( true, 'table', function ( ctx ) {
  1430 + if ( ctx._buttons ) {
  1431 + var insts = Buttons.instanceSelector( groupSelector, ctx._buttons );
  1432 +
  1433 + for ( var i=0, ien=insts.length ; i<ien ; i++ ) {
  1434 + jq = jq.add( insts[i].container() );
  1435 + }
  1436 + }
  1437 + } );
  1438 +
  1439 + return jq;
  1440 +} );
  1441 +
  1442 +// Add a new button
  1443 +DataTable.Api.register( 'button().add()', function ( idx, conf ) {
  1444 + var ctx = this.context;
  1445 +
  1446 + // Don't use `this` as it could be empty - select the instances directly
  1447 + if ( ctx.length ) {
  1448 + var inst = Buttons.instanceSelector( this._groupSelector, ctx[0]._buttons );
  1449 +
  1450 + if ( inst.length ) {
  1451 + inst[0].add( conf, idx );
  1452 + }
  1453 + }
  1454 +
  1455 + return this.button( this._groupSelector, idx );
  1456 +} );
  1457 +
  1458 +// Destroy the button sets selected
  1459 +DataTable.Api.register( 'buttons().destroy()', function () {
  1460 + this.pluck( 'inst' ).unique().each( function ( inst ) {
  1461 + inst.destroy();
  1462 + } );
  1463 +
  1464 + return this;
  1465 +} );
  1466 +
  1467 +// Remove a button
  1468 +DataTable.Api.registerPlural( 'buttons().remove()', 'buttons().remove()', function () {
  1469 + this.each( function ( set ) {
  1470 + set.inst.remove( set.node );
  1471 + } );
  1472 +
  1473 + return this;
  1474 +} );
  1475 +
  1476 +// Information box that can be used by buttons
  1477 +var _infoTimer;
  1478 +DataTable.Api.register( 'buttons.info()', function ( title, message, time ) {
  1479 + var that = this;
  1480 +
  1481 + if ( title === false ) {
  1482 + $('#datatables_buttons_info').fadeOut( function () {
  1483 + $(this).remove();
  1484 + } );
  1485 + clearTimeout( _infoTimer );
  1486 + _infoTimer = null;
  1487 +
  1488 + return this;
  1489 + }
  1490 +
  1491 + if ( _infoTimer ) {
  1492 + clearTimeout( _infoTimer );
  1493 + }
  1494 +
  1495 + if ( $('#datatables_buttons_info').length ) {
  1496 + $('#datatables_buttons_info').remove();
  1497 + }
  1498 +
  1499 + title = title ? '<h2>'+title+'</h2>' : '';
  1500 +
  1501 + $('<div id="datatables_buttons_info" class="dt-button-info"/>')
  1502 + .html( title )
  1503 + .append( $('<div/>')[ typeof message === 'string' ? 'html' : 'append' ]( message ) )
  1504 + .css( 'display', 'none' )
  1505 + .appendTo( 'body' )
  1506 + .fadeIn();
  1507 +
  1508 + if ( time !== undefined && time !== 0 ) {
  1509 + _infoTimer = setTimeout( function () {
  1510 + that.buttons.info( false );
  1511 + }, time );
  1512 + }
  1513 +
  1514 + return this;
  1515 +} );
  1516 +
  1517 +// Get data from the table for export - this is common to a number of plug-in
  1518 +// buttons so it is included in the Buttons core library
  1519 +DataTable.Api.register( 'buttons.exportData()', function ( options ) {
  1520 + if ( this.context.length ) {
  1521 + return _exportData( new DataTable.Api( this.context[0] ), options );
  1522 + }
  1523 +} );
  1524 +
  1525 +
  1526 +var _exportTextarea = $('<textarea/>')[0];
  1527 +var _exportData = function ( dt, inOpts )
  1528 +{
  1529 + var config = $.extend( true, {}, {
  1530 + rows: null,
  1531 + columns: '',
  1532 + modifier: {
  1533 + search: 'applied',
  1534 + order: 'applied'
  1535 + },
  1536 + orthogonal: 'display',
  1537 + stripHtml: true,
  1538 + stripNewlines: true,
  1539 + decodeEntities: true,
  1540 + trim: true,
  1541 + format: {
  1542 + header: function ( d ) {
  1543 + return strip( d );
  1544 + },
  1545 + footer: function ( d ) {
  1546 + return strip( d );
  1547 + },
  1548 + body: function ( d ) {
  1549 + return strip( d );
  1550 + }
  1551 + }
  1552 + }, inOpts );
  1553 +
  1554 + var strip = function ( str ) {
  1555 + if ( typeof str !== 'string' ) {
  1556 + return str;
  1557 + }
  1558 +
  1559 + if ( config.stripHtml ) {
  1560 + str = str.replace( /<[^>]*>/g, '' );
  1561 + }
  1562 +
  1563 + if ( config.trim ) {
  1564 + str = str.replace( /^\s+|\s+$/g, '' );
  1565 + }
  1566 +
  1567 + if ( config.stripNewlines ) {
  1568 + str = str.replace( /\n/g, ' ' );
  1569 + }
  1570 +
  1571 + if ( config.decodeEntities ) {
  1572 + _exportTextarea.innerHTML = str;
  1573 + str = _exportTextarea.value;
  1574 + }
  1575 +
  1576 + return str;
  1577 + };
  1578 +
  1579 +
  1580 + var header = dt.columns( config.columns ).indexes().map( function (idx) {
  1581 + var el = dt.column( idx ).header();
  1582 + return config.format.header( el.innerHTML, idx, el );
  1583 + } ).toArray();
  1584 +
  1585 + var footer = dt.table().footer() ?
  1586 + dt.columns( config.columns ).indexes().map( function (idx) {
  1587 + var el = dt.column( idx ).footer();
  1588 + return config.format.footer( el ? el.innerHTML : '', idx, el );
  1589 + } ).toArray() :
  1590 + null;
  1591 +
  1592 + var selectedCells = dt.cells( config.rows, config.columns, config.modifier );
  1593 + var cells = selectedCells
  1594 + .render( config.orthogonal )
  1595 + .toArray();
  1596 + var cellNodes = selectedCells
  1597 + .nodes()
  1598 + .toArray();
  1599 +
  1600 + var columns = header.length;
  1601 + var rows = columns > 0 ? cells.length / columns : 0;
  1602 + var body = new Array( rows );
  1603 + var cellCounter = 0;
  1604 +
  1605 + for ( var i=0, ien=rows ; i<ien ; i++ ) {
  1606 + var row = new Array( columns );
  1607 +
  1608 + for ( var j=0 ; j<columns ; j++ ) {
  1609 + row[j] = config.format.body( cells[ cellCounter ], i, j, cellNodes[ cellCounter ] );
  1610 + cellCounter++;
  1611 + }
  1612 +
  1613 + body[i] = row;
  1614 + }
  1615 +
  1616 + return {
  1617 + header: header,
  1618 + footer: footer,
  1619 + body: body
  1620 + };
  1621 +};
  1622 +
  1623 +
  1624 +/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1625 + * DataTables interface
  1626 + */
  1627 +
  1628 +// Attach to DataTables objects for global access
  1629 +$.fn.dataTable.Buttons = Buttons;
  1630 +$.fn.DataTable.Buttons = Buttons;
  1631 +
  1632 +
  1633 +
  1634 +// DataTables creation - check if the buttons have been defined for this table,
  1635 +// they will have been if the `B` option was used in `dom`, otherwise we should
  1636 +// create the buttons instance here so they can be inserted into the document
  1637 +// using the API. Listen for `init` for compatibility with pre 1.10.10, but to
  1638 +// be removed in future.
  1639 +$(document).on( 'init.dt plugin-init.dt', function (e, settings) {
  1640 + if ( e.namespace !== 'dt' ) {
  1641 + return;
  1642 + }
  1643 +
  1644 + var opts = settings.oInit.buttons || DataTable.defaults.buttons;
  1645 +
  1646 + if ( opts && ! settings._buttons ) {
  1647 + new Buttons( settings, opts ).container();
  1648 + }
  1649 +} );
  1650 +
  1651 +// DataTables `dom` feature option
  1652 +DataTable.ext.feature.push( {
  1653 + fnInit: function( settings ) {
  1654 + var api = new DataTable.Api( settings );
  1655 + var opts = api.init().buttons || DataTable.defaults.buttons;
  1656 +
  1657 + return new Buttons( api, opts ).container();
  1658 + },
  1659 + cFeature: "B"
  1660 +} );
  1661 +
  1662 +
  1663 +return Buttons;
  1664 +}));
... ...
amadeus/static/js/goals_reports.js
... ... @@ -23,8 +23,26 @@ function getAnswered() {
23 23 // return false;
24 24 // });
25 25 $('#answered_table').DataTable({
26   - "dom": "frtip",
27   - "language": dataTablei18n
  26 + "dom": "Bfrtip",
  27 + "language": dataTablei18n,
  28 + buttons: {
  29 + dom: {
  30 + container: {
  31 + className: 'col-md-3'
  32 + },
  33 + buttonContainer: {
  34 + tag: 'h4',
  35 + className: 'history-header'
  36 + },
  37 + },
  38 + buttons: [
  39 + {
  40 + extend: 'csv',
  41 + text: csvBtnLabeli18n,
  42 + filename: 'report-answered'
  43 + }
  44 + ]
  45 + }
28 46 });
29 47 // var items = $("#answered_table").children(":visible").length;
30 48  
... ...
goals/templates/goals/_answered.html
... ... @@ -2,9 +2,6 @@
2 2  
3 3 <div class="row">
4 4 <div class="col-md-12">
5   - <div class="col-md-3">
6   - <h4 class="history-header"><a href="#"><i class="fa fa-download"></i> {% trans 'Download .csv file' %}</a></h4>
7   - </div>
8 5 <table id="answered_table" class="table table-striped table-bordered">
9 6 <thead>
10 7 <th>
... ...
goals/templates/goals/reports.html
... ... @@ -6,12 +6,15 @@
6 6 {% block style %}
7 7 {{block.super}}
8 8 <link rel="stylesheet" type="text/css" href="{% static 'css/dataTables.bootstrap.css' %}">
  9 + <link rel="stylesheet" type="text/css" href="{% static 'css/dataTables.buttons.bootstrap.css' %}">
9 10 {% endblock %}
10 11  
11 12 {% block javascript%}
12 13 {{ block.super }}
13 14 <script type="text/javascript" src="{% static 'js/jquery.dataTables.js' %} "></script>
  15 + <script type="text/javascript" src="{% static 'js/dataTables.buttons.js' %} "></script>
14 16 <script type="text/javascript" src="{% static 'js/dataTables.bootstrap.js' %} "></script>
  17 + <script type="text/javascript" src="{% static 'js/dataTables.buttons.html5.js' %} "></script>
15 18 {% endblock%}
16 19  
17 20 {% block breadcrumbs %}
... ... @@ -90,7 +93,9 @@
90 93 "sortAscending": "{% trans ': activate to sort column ascending' %}",
91 94 "sortDescending": "{% trans ': activate to sort column descending' %}"
92 95 }
93   - }
  96 + };
  97 +
  98 + var csvBtnLabeli18n = "<i class='fa fa-download'></i> {% trans 'Download .csv file' %}";
94 99 </script>
95 100 <script type="text/javascript" src="{% static 'js/goals_reports.js' %}"></script>
96 101 {% endblock %}
97 102 \ No newline at end of file
... ...