dataTables.buttons.js 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638
  1. /*! Buttons for DataTables 1.1.2
  2. * ©2015 SpryMedia Ltd - datatables.net/license
  3. */
  4. (function( factory ){
  5. if ( typeof define === 'function' && define.amd ) {
  6. // AMD
  7. define( ['jquery', 'datatables.net'], function ( $ ) {
  8. return factory( $, window, document );
  9. } );
  10. }
  11. else if ( typeof exports === 'object' ) {
  12. // CommonJS
  13. module.exports = function (root, $) {
  14. if ( ! root ) {
  15. root = window;
  16. }
  17. if ( ! $ || ! $.fn.dataTable ) {
  18. $ = require('datatables.net')(root, $).$;
  19. }
  20. return factory( $, root, root.document );
  21. };
  22. }
  23. else {
  24. // Browser
  25. factory( jQuery, window, document );
  26. }
  27. }(function( $, window, document, undefined ) {
  28. 'use strict';
  29. var DataTable = $.fn.dataTable;
  30. // Used for namespacing events added to the document by each instance, so they
  31. // can be removed on destroy
  32. var _instCounter = 0;
  33. // Button namespacing counter for namespacing events on individual buttons
  34. var _buttonCounter = 0;
  35. var _dtButtons = DataTable.ext.buttons;
  36. /**
  37. * [Buttons description]
  38. * @param {[type]}
  39. * @param {[type]}
  40. */
  41. var Buttons = function( dt, config )
  42. {
  43. // Allow a boolean true for defaults
  44. if ( config === true ) {
  45. config = {};
  46. }
  47. // For easy configuration of buttons an array can be given
  48. if ( $.isArray( config ) ) {
  49. config = { buttons: config };
  50. }
  51. this.c = $.extend( true, {}, Buttons.defaults, config );
  52. // Don't want a deep copy for the buttons
  53. if ( config.buttons ) {
  54. this.c.buttons = config.buttons;
  55. }
  56. this.s = {
  57. dt: new DataTable.Api( dt ),
  58. buttons: [],
  59. subButtons: [],
  60. listenKeys: '',
  61. namespace: 'dtb'+(_instCounter++)
  62. };
  63. this.dom = {
  64. container: $('<'+this.c.dom.container.tag+'/>')
  65. .addClass( this.c.dom.container.className )
  66. };
  67. this._constructor();
  68. };
  69. $.extend( Buttons.prototype, {
  70. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  71. * Public methods
  72. */
  73. /**
  74. * Get the action of a button
  75. * @param {int|string} Button index
  76. * @return {function}
  77. *//**
  78. * Set the action of a button
  79. * @param {int|string} Button index
  80. * @param {function} Function to set
  81. * @return {Buttons} Self for chaining
  82. */
  83. action: function ( idx, action )
  84. {
  85. var button = this._indexToButton( idx ).conf;
  86. var dt = this.s.dt;
  87. if ( action === undefined ) {
  88. return button.action;
  89. }
  90. button.action = action;
  91. return this;
  92. },
  93. /**
  94. * Add an active class to the button to make to look active or get current
  95. * active state.
  96. * @param {int|string} Button index
  97. * @param {boolean} [flag] Enable / disable flag
  98. * @return {Buttons} Self for chaining or boolean for getter
  99. */
  100. active: function ( idx, flag ) {
  101. var button = this._indexToButton( idx );
  102. var klass = this.c.dom.button.active;
  103. if ( flag === undefined ) {
  104. return button.node.hasClass( klass );
  105. }
  106. button.node.toggleClass( klass, flag === undefined ? true : flag );
  107. return this;
  108. },
  109. /**
  110. * Add a new button
  111. * @param {int|string} Button index for where to insert the button
  112. * @param {object} Button configuration object, base string name or function
  113. * @return {Buttons} Self for chaining
  114. */
  115. add: function ( idx, config )
  116. {
  117. if ( typeof idx === 'string' && idx.indexOf('-') !== -1 ) {
  118. var idxs = idx.split('-');
  119. this.c.buttons[idxs[0]*1].buttons.splice( idxs[1]*1, 0, config );
  120. }
  121. else {
  122. this.c.buttons.splice( idx*1, 0, config );
  123. }
  124. this.dom.container.empty();
  125. this._buildButtons( this.c.buttons );
  126. return this;
  127. },
  128. /**
  129. * Get the container node for the buttons
  130. * @return {jQuery} Buttons node
  131. */
  132. container: function ()
  133. {
  134. return this.dom.container;
  135. },
  136. /**
  137. * Disable a button
  138. * @param {int|string} Button index
  139. * @return {Buttons} Self for chaining
  140. */
  141. disable: function ( idx ) {
  142. var button = this._indexToButton( idx );
  143. button.node.addClass( this.c.dom.button.disabled );
  144. return this;
  145. },
  146. /**
  147. * Destroy the instance, cleaning up event handlers and removing DOM
  148. * elements
  149. * @return {Buttons} Self for chaining
  150. */
  151. destroy: function ()
  152. {
  153. // Key event listener
  154. $('body').off( 'keyup.'+this.s.namespace );
  155. // Individual button destroy (so they can remove their own events if
  156. // needed
  157. var buttons = this.s.buttons;
  158. var subButtons = this.s.subButtons;
  159. var i, ien, j, jen;
  160. for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
  161. this.removePrep( i );
  162. for ( j=0, jen=subButtons[i].length ; j<jen ; j++ ) {
  163. this.removePrep( i+'-'+j );
  164. }
  165. }
  166. this.removeCommit();
  167. // Container
  168. this.dom.container.remove();
  169. // Remove from the settings object collection
  170. var buttonInsts = this.s.dt.settings()[0];
  171. for ( i=0, ien=buttonInsts.length ; i<ien ; i++ ) {
  172. if ( buttonInsts.inst === this ) {
  173. buttonInsts.splice( i, 1 );
  174. break;
  175. }
  176. }
  177. return this;
  178. },
  179. /**
  180. * Enable / disable a button
  181. * @param {int|string} Button index
  182. * @param {boolean} [flag=true] Enable / disable flag
  183. * @return {Buttons} Self for chaining
  184. */
  185. enable: function ( idx, flag )
  186. {
  187. if ( flag === false ) {
  188. return this.disable( idx );
  189. }
  190. var button = this._indexToButton( idx );
  191. button.node.removeClass( this.c.dom.button.disabled );
  192. return this;
  193. },
  194. /**
  195. * Get the instance name for the button set selector
  196. * @return {string} Instance name
  197. */
  198. name: function ()
  199. {
  200. return this.c.name;
  201. },
  202. /**
  203. * Get a button's node
  204. * @param {int|string} Button index
  205. * @return {jQuery} Button element
  206. */
  207. node: function ( idx )
  208. {
  209. var button = this._indexToButton( idx );
  210. return button.node;
  211. },
  212. /**
  213. * Tidy up any buttons that have been scheduled for removal. This is
  214. * required so multiple buttons can be removed without upsetting the button
  215. * indexes while removing them.
  216. * @param {int|string} Button index
  217. * @return {Buttons} Self for chaining
  218. */
  219. removeCommit: function ()
  220. {
  221. var buttons = this.s.buttons;
  222. var subButtons = this.s.subButtons;
  223. var i, ien, j;
  224. for ( i=buttons.length-1 ; i>=0 ; i-- ) {
  225. if ( buttons[i] === null ) {
  226. buttons.splice( i, 1 );
  227. subButtons.splice( i, 1 );
  228. this.c.buttons.splice( i, 1 );
  229. }
  230. }
  231. for ( i=0, ien=subButtons.length ; i<ien ; i++ ) {
  232. for ( j=subButtons[i].length-1 ; j>=0 ; j-- ) {
  233. if ( subButtons[i][j] === null ) {
  234. subButtons[i].splice( j, 1 );
  235. this.c.buttons[i].buttons.splice( j, 1 );
  236. }
  237. }
  238. }
  239. return this;
  240. },
  241. /**
  242. * Scheduled a button for removal. This is required so multiple buttons can
  243. * be removed without upsetting the button indexes while removing them.
  244. * @return {Buttons} Self for chaining
  245. */
  246. removePrep: function ( idx )
  247. {
  248. var button;
  249. var dt = this.s.dt;
  250. if ( typeof idx === 'number' || idx.indexOf('-') === -1 ) {
  251. // Top level button
  252. button = this.s.buttons[ idx*1 ];
  253. if ( button.conf.destroy ) {
  254. button.conf.destroy.call( dt.button(idx), dt, button, button.conf );
  255. }
  256. button.node.remove();
  257. this._removeKey( button.conf );
  258. this.s.buttons[ idx*1 ] = null;
  259. }
  260. else {
  261. // Collection button
  262. var idxs = idx.split('-');
  263. button = this.s.subButtons[ idxs[0]*1 ][ idxs[1]*1 ];
  264. if ( button.conf.destroy ) {
  265. button.conf.destroy.call( dt.button(idx), dt, button, button.conf );
  266. }
  267. button.node.remove();
  268. this._removeKey( button.conf );
  269. this.s.subButtons[ idxs[0]*1 ][ idxs[1]*1 ] = null;
  270. }
  271. return this;
  272. },
  273. /**
  274. * Get the text for a button
  275. * @param {int|string} Button index
  276. * @return {string} Button text
  277. *//**
  278. * Set the text for a button
  279. * @param {int|string|function} Button index
  280. * @param {string} Text
  281. * @return {Buttons} Self for chaining
  282. */
  283. text: function ( idx, label )
  284. {
  285. var button = this._indexToButton( idx );
  286. var buttonLiner = this.c.dom.collection.buttonLiner;
  287. var linerTag = typeof idx === 'string' && idx.indexOf( '-' ) !== -1 && buttonLiner && buttonLiner.tag ?
  288. buttonLiner.tag :
  289. this.c.dom.buttonLiner.tag;
  290. var dt = this.s.dt;
  291. var text = function ( opt ) {
  292. return typeof opt === 'function' ?
  293. opt( dt, button.node, button.conf ) :
  294. opt;
  295. };
  296. if ( label === undefined ) {
  297. return text( button.conf.text );
  298. }
  299. button.conf.text = label;
  300. if ( linerTag ) {
  301. button.node.children( linerTag ).html( text(label) );
  302. }
  303. else {
  304. button.node.html( text(label) );
  305. }
  306. return this;
  307. },
  308. /**
  309. * Calculate button index from a node
  310. * @param {node} Button node (_not_ a jQuery object)
  311. * @return {string} Index. Undefined if not found
  312. */
  313. toIndex: function ( node )
  314. {
  315. var i, ien, j, jen;
  316. var buttons = this.s.buttons;
  317. var subButtons = this.s.subButtons;
  318. // Loop the main buttons first
  319. for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
  320. if ( buttons[i].node[0] === node ) {
  321. return i+'';
  322. }
  323. }
  324. // Then the sub-buttons
  325. for ( i=0, ien=subButtons.length ; i<ien ; i++ ) {
  326. for ( j=0, jen=subButtons[i].length ; j<jen ; j++ ) {
  327. if ( subButtons[i][j].node[0] === node ) {
  328. return i+'-'+j;
  329. }
  330. }
  331. }
  332. },
  333. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  334. * Constructor
  335. */
  336. /**
  337. * Buttons constructor
  338. * @private
  339. */
  340. _constructor: function ()
  341. {
  342. var that = this;
  343. var dt = this.s.dt;
  344. var dtSettings = dt.settings()[0];
  345. if ( ! dtSettings._buttons ) {
  346. dtSettings._buttons = [];
  347. }
  348. dtSettings._buttons.push( {
  349. inst: this,
  350. name: this.c.name
  351. } );
  352. this._buildButtons( this.c.buttons );
  353. dt.on( 'destroy', function () {
  354. that.destroy();
  355. } );
  356. // Global key event binding to listen for button keys
  357. $('body').on( 'keyup.'+this.s.namespace, function ( e ) {
  358. if ( ! document.activeElement || document.activeElement === document.body ) {
  359. // SUse a string of characters for fast lookup of if we need to
  360. // handle this
  361. var character = String.fromCharCode(e.keyCode).toLowerCase();
  362. if ( that.s.listenKeys.toLowerCase().indexOf( character ) !== -1 ) {
  363. that._keypress( character, e );
  364. }
  365. }
  366. } );
  367. },
  368. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  369. * Private methods
  370. */
  371. /**
  372. * Add a new button to the key press listener
  373. * @param {object} Resolved button configuration object
  374. * @private
  375. */
  376. _addKey: function ( conf )
  377. {
  378. if ( conf.key ) {
  379. this.s.listenKeys += $.isPlainObject( conf.key ) ?
  380. conf.key.key :
  381. conf.key;
  382. }
  383. },
  384. /**
  385. * Create buttons from an array of buttons
  386. * @param {array} Buttons to create
  387. * @param {jQuery} Container node into which the created button should be
  388. * inserted.
  389. * @param {int} Counter for sub-buttons to be stored in a collection
  390. * @private
  391. */
  392. _buildButtons: function ( buttons, container, collectionCounter )
  393. {
  394. var dt = this.s.dt;
  395. var buttonCounter = 0;
  396. if ( ! container ) {
  397. container = this.dom.container;
  398. this.s.buttons = [];
  399. this.s.subButtons = [];
  400. }
  401. for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
  402. var conf = this._resolveExtends( buttons[i] );
  403. if ( ! conf ) {
  404. continue;
  405. }
  406. // If the configuration is an array, then expand the buttons at this
  407. // point
  408. if ( $.isArray( conf ) ) {
  409. this._buildButtons( conf, container, collectionCounter );
  410. continue;
  411. }
  412. var button = this._buildButton(
  413. conf,
  414. collectionCounter!==undefined ? true : false
  415. );
  416. if ( ! button ) {
  417. continue;
  418. }
  419. var buttonNode = button.node;
  420. container.append( button.inserter );
  421. if ( collectionCounter === undefined ) {
  422. this.s.buttons.push( {
  423. node: buttonNode,
  424. conf: conf,
  425. inserter: button.inserter
  426. } );
  427. this.s.subButtons.push( [] );
  428. }
  429. else {
  430. this.s.subButtons[ collectionCounter ].push( {
  431. node: buttonNode,
  432. conf: conf,
  433. inserter: button.inserter
  434. } );
  435. }
  436. if ( conf.buttons ) {
  437. var collectionDom = this.c.dom.collection;
  438. conf._collection = $('<'+collectionDom.tag+'/>')
  439. .addClass( collectionDom.className );
  440. this._buildButtons( conf.buttons, conf._collection, buttonCounter );
  441. }
  442. // init call is made here, rather than buildButton as it needs to
  443. // have been added to the buttons / subButtons array first
  444. if ( conf.init ) {
  445. conf.init.call( dt.button( buttonNode ), dt, buttonNode, conf );
  446. }
  447. buttonCounter++;
  448. }
  449. },
  450. /**
  451. * Create an individual button
  452. * @param {object} config Resolved button configuration
  453. * @param {boolean} collectionButton `true` if a collection button
  454. * @return {jQuery} Created button node (jQuery)
  455. * @private
  456. */
  457. _buildButton: function ( config, collectionButton )
  458. {
  459. var that = this;
  460. var buttonDom = this.c.dom.button;
  461. var linerDom = this.c.dom.buttonLiner;
  462. var collectionDom = this.c.dom.collection;
  463. var dt = this.s.dt;
  464. var text = function ( opt ) {
  465. return typeof opt === 'function' ?
  466. opt( dt, button, config ) :
  467. opt;
  468. };
  469. if ( collectionButton && collectionDom.button ) {
  470. buttonDom = collectionDom.button;
  471. }
  472. if ( collectionButton && collectionDom.buttonLiner ) {
  473. linerDom = collectionDom.buttonLiner;
  474. }
  475. // Make sure that the button is available based on whatever requirements
  476. // it has. For example, Flash buttons require Flash
  477. if ( config.available && ! config.available( dt, config ) ) {
  478. return false;
  479. }
  480. var action = function ( e, dt, button, config ) {
  481. config.action.call( dt.button( button ), e, dt, button, config );
  482. $(dt.table().node()).triggerHandler( 'buttons-action.dt', [
  483. dt.button( button ), dt, button, config
  484. ] );
  485. };
  486. var button = $('<'+buttonDom.tag+'/>')
  487. .addClass( buttonDom.className )
  488. .attr( 'tabindex', this.s.dt.settings()[0].iTabIndex )
  489. .attr( 'aria-controls', this.s.dt.table().node().id )
  490. .on( 'click.dtb', function (e) {
  491. e.preventDefault();
  492. if ( ! button.hasClass( buttonDom.disabled ) && config.action ) {
  493. action( e, dt, button, config );
  494. }
  495. button.blur();
  496. } )
  497. .on( 'keyup.dtb', function (e) {
  498. if ( e.keyCode === 13 ) {
  499. if ( ! button.hasClass( buttonDom.disabled ) && config.action ) {
  500. action( e, dt, button, config );
  501. }
  502. }
  503. } );
  504. if ( linerDom.tag ) {
  505. button.append(
  506. $('<'+linerDom.tag+'/>')
  507. .html( text( config.text ) )
  508. .addClass( linerDom.className )
  509. );
  510. }
  511. else {
  512. button.html( text( config.text ) );
  513. }
  514. if ( config.enabled === false ) {
  515. button.addClass( buttonDom.disabled );
  516. }
  517. if ( config.className ) {
  518. button.addClass( config.className );
  519. }
  520. if ( config.titleAttr ) {
  521. button.attr( 'title', config.titleAttr );
  522. }
  523. if ( ! config.namespace ) {
  524. config.namespace = '.dt-button-'+(_buttonCounter++);
  525. }
  526. var buttonContainer = this.c.dom.buttonContainer;
  527. var inserter;
  528. if ( buttonContainer && buttonContainer.tag ) {
  529. inserter = $('<'+buttonContainer.tag+'/>')
  530. .addClass( buttonContainer.className )
  531. .append( button );
  532. }
  533. else {
  534. inserter = button;
  535. }
  536. this._addKey( config );
  537. return {
  538. node: button,
  539. inserter: inserter
  540. };
  541. },
  542. /**
  543. * Get a button's host information from a button index
  544. * @param {int|string} Button index
  545. * @return {object} Button information - object contains `node` and `conf`
  546. * properties
  547. * @private
  548. */
  549. _indexToButton: function ( idx )
  550. {
  551. if ( typeof idx === 'number' || idx.indexOf('-') === -1 ) {
  552. return this.s.buttons[ idx*1 ];
  553. }
  554. var idxs = idx.split('-');
  555. return this.s.subButtons[ idxs[0]*1 ][ idxs[1]*1 ];
  556. },
  557. /**
  558. * Handle a key press - determine if any button's key configured matches
  559. * what was typed and trigger the action if so.
  560. * @param {string} The character pressed
  561. * @param {object} Key event that triggered this call
  562. * @private
  563. */
  564. _keypress: function ( character, e )
  565. {
  566. var i, ien, j, jen;
  567. var buttons = this.s.buttons;
  568. var subButtons = this.s.subButtons;
  569. var run = function ( conf, node ) {
  570. if ( ! conf.key ) {
  571. return;
  572. }
  573. if ( conf.key === character ) {
  574. node.click();
  575. }
  576. else if ( $.isPlainObject( conf.key ) ) {
  577. if ( conf.key.key !== character ) {
  578. return;
  579. }
  580. if ( conf.key.shiftKey && ! e.shiftKey ) {
  581. return;
  582. }
  583. if ( conf.key.altKey && ! e.altKey ) {
  584. return;
  585. }
  586. if ( conf.key.ctrlKey && ! e.ctrlKey ) {
  587. return;
  588. }
  589. if ( conf.key.metaKey && ! e.metaKey ) {
  590. return;
  591. }
  592. // Made it this far - it is good
  593. node.click();
  594. }
  595. };
  596. // Loop the main buttons first
  597. for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
  598. run( buttons[i].conf, buttons[i].node );
  599. }
  600. // Then the sub-buttons
  601. for ( i=0, ien=subButtons.length ; i<ien ; i++ ) {
  602. for ( j=0, jen=subButtons[i].length ; j<jen ; j++ ) {
  603. run( subButtons[i][j].conf, subButtons[i][j].node );
  604. }
  605. }
  606. },
  607. /**
  608. * Remove a key from the key listener for this instance (to be used when a
  609. * button is removed)
  610. * @param {object} Button configuration
  611. */
  612. _removeKey: function ( conf )
  613. {
  614. if ( conf.key ) {
  615. var character = $.isPlainObject( conf.key ) ?
  616. conf.key.key :
  617. conf.key;
  618. // Remove only one character, as multiple buttons could have the
  619. // same listening key
  620. var a = this.s.listenKeys.split('');
  621. var idx = $.inArray( character, a );
  622. a.splice( idx, 1 );
  623. this.s.listenKeys = a.join('');
  624. }
  625. },
  626. /**
  627. * Resolve a button configuration
  628. * @param {string|function|object} Button config to resolve
  629. * @return {object} Button configuration
  630. */
  631. _resolveExtends: function ( conf )
  632. {
  633. var dt = this.s.dt;
  634. var i, ien;
  635. var toConfObject = function ( base ) {
  636. var loop = 0;
  637. // Loop until we have resolved to a button configuration, or an
  638. // array of button configurations (which will be iterated
  639. // separately)
  640. while ( ! $.isPlainObject(base) && ! $.isArray(base) ) {
  641. if ( base === undefined ) {
  642. return;
  643. }
  644. if ( typeof base === 'function' ) {
  645. base = base( dt, conf );
  646. if ( ! base ) {
  647. return false;
  648. }
  649. }
  650. else if ( typeof base === 'string' ) {
  651. if ( ! _dtButtons[ base ] ) {
  652. throw 'Unknown button type: '+base;
  653. }
  654. base = _dtButtons[ base ];
  655. }
  656. loop++;
  657. if ( loop > 30 ) {
  658. // Protect against misconfiguration killing the browser
  659. throw 'Buttons: Too many iterations';
  660. }
  661. }
  662. return $.isArray( base ) ?
  663. base :
  664. $.extend( {}, base );
  665. };
  666. conf = toConfObject( conf );
  667. while ( conf && conf.extend ) {
  668. // Use `toConfObject` in case the button definition being extended
  669. // is itself a string or a function
  670. if ( ! _dtButtons[ conf.extend ] ) {
  671. throw 'Cannot extend unknown button type: '+conf.extend;
  672. }
  673. var objArray = toConfObject( _dtButtons[ conf.extend ] );
  674. if ( $.isArray( objArray ) ) {
  675. return objArray;
  676. }
  677. else if ( ! objArray ) {
  678. // This is a little brutal as it might be possible to have a
  679. // valid button without the extend, but if there is no extend
  680. // then the host button would be acting in an undefined state
  681. return false;
  682. }
  683. // Stash the current class name
  684. var originalClassName = objArray.className;
  685. conf = $.extend( {}, objArray, conf );
  686. // The extend will have overwritten the original class name if the
  687. // `conf` object also assigned a class, but we want to concatenate
  688. // them so they are list that is combined from all extended buttons
  689. if ( originalClassName && conf.className !== originalClassName ) {
  690. conf.className = originalClassName+' '+conf.className;
  691. }
  692. // Buttons to be added to a collection -gives the ability to define
  693. // if buttons should be added to the start or end of a collection
  694. var postfixButtons = conf.postfixButtons;
  695. if ( postfixButtons ) {
  696. if ( ! conf.buttons ) {
  697. conf.buttons = [];
  698. }
  699. for ( i=0, ien=postfixButtons.length ; i<ien ; i++ ) {
  700. conf.buttons.push( postfixButtons[i] );
  701. }
  702. conf.postfixButtons = null;
  703. }
  704. var prefixButtons = conf.prefixButtons;
  705. if ( prefixButtons ) {
  706. if ( ! conf.buttons ) {
  707. conf.buttons = [];
  708. }
  709. for ( i=0, ien=prefixButtons.length ; i<ien ; i++ ) {
  710. conf.buttons.splice( i, 0, prefixButtons[i] );
  711. }
  712. conf.prefixButtons = null;
  713. }
  714. // Although we want the `conf` object to overwrite almost all of
  715. // the properties of the object being extended, the `extend`
  716. // property should come from the object being extended
  717. conf.extend = objArray.extend;
  718. }
  719. return conf;
  720. }
  721. } );
  722. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  723. * Statics
  724. */
  725. /**
  726. * Show / hide a background layer behind a collection
  727. * @param {boolean} Flag to indicate if the background should be shown or
  728. * hidden
  729. * @param {string} Class to assign to the background
  730. * @static
  731. */
  732. Buttons.background = function ( show, className, fade ) {
  733. if ( fade === undefined ) {
  734. fade = 400;
  735. }
  736. if ( show ) {
  737. $('<div/>')
  738. .addClass( className )
  739. .css( 'display', 'none' )
  740. .appendTo( 'body' )
  741. .fadeIn( fade );
  742. }
  743. else {
  744. $('body > div.'+className)
  745. .fadeOut( fade, function () {
  746. $(this).remove();
  747. } );
  748. }
  749. };
  750. /**
  751. * Instance selector - select Buttons instances based on an instance selector
  752. * value from the buttons assigned to a DataTable. This is only useful if
  753. * multiple instances are attached to a DataTable.
  754. * @param {string|int|array} Instance selector - see `instance-selector`
  755. * documentation on the DataTables site
  756. * @param {array} Button instance array that was attached to the DataTables
  757. * settings object
  758. * @return {array} Buttons instances
  759. * @static
  760. */
  761. Buttons.instanceSelector = function ( group, buttons )
  762. {
  763. if ( ! group ) {
  764. return $.map( buttons, function ( v ) {
  765. return v.inst;
  766. } );
  767. }
  768. var ret = [];
  769. var names = $.map( buttons, function ( v ) {
  770. return v.name;
  771. } );
  772. // Flatten the group selector into an array of single options
  773. var process = function ( input ) {
  774. if ( $.isArray( input ) ) {
  775. for ( var i=0, ien=input.length ; i<ien ; i++ ) {
  776. process( input[i] );
  777. }
  778. return;
  779. }
  780. if ( typeof input === 'string' ) {
  781. if ( input.indexOf( ',' ) !== -1 ) {
  782. // String selector, list of names
  783. process( input.split(',') );
  784. }
  785. else {
  786. // String selector individual name
  787. var idx = $.inArray( $.trim(input), names );
  788. if ( idx !== -1 ) {
  789. ret.push( buttons[ idx ].inst );
  790. }
  791. }
  792. }
  793. else if ( typeof input === 'number' ) {
  794. // Index selector
  795. ret.push( buttons[ input ].inst );
  796. }
  797. };
  798. process( group );
  799. return ret;
  800. };
  801. /**
  802. * Button selector - select one or more buttons from a selector input so some
  803. * operation can be performed on them.
  804. * @param {array} Button instances array that the selector should operate on
  805. * @param {string|int|node|jQuery|array} Button selector - see
  806. * `button-selector` documentation on the DataTables site
  807. * @return {array} Array of objects containing `inst` and `idx` properties of
  808. * the selected buttons so you know which instance each button belongs to.
  809. * @static
  810. */
  811. Buttons.buttonSelector = function ( insts, selector )
  812. {
  813. var ret = [];
  814. var run = function ( selector, inst ) {
  815. var i, ien, j, jen;
  816. var buttons = [];
  817. $.each( inst.s.buttons, function (i, v) {
  818. if ( v !== null ) {
  819. buttons.push( {
  820. node: v.node[0],
  821. name: v.conf.name
  822. } );
  823. }
  824. } );
  825. $.each( inst.s.subButtons, function (i, v) {
  826. $.each( v, function (j, w) {
  827. if ( w !== null ) {
  828. buttons.push( {
  829. node: w.node[0],
  830. name: w.conf.name
  831. } );
  832. }
  833. } );
  834. } );
  835. var nodes = $.map( buttons, function (v) {
  836. return v.node;
  837. } );
  838. if ( $.isArray( selector ) || selector instanceof $ ) {
  839. for ( i=0, ien=selector.length ; i<ien ; i++ ) {
  840. run( selector[i], inst );
  841. }
  842. return;
  843. }
  844. if ( selector === null || selector === undefined || selector === '*' ) {
  845. // Select all
  846. for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
  847. ret.push( {
  848. inst: inst,
  849. idx: inst.toIndex( buttons[i].node )
  850. } );
  851. }
  852. }
  853. else if ( typeof selector === 'number' ) {
  854. // Main button index selector
  855. ret.push( {
  856. inst: inst,
  857. idx: selector
  858. } );
  859. }
  860. else if ( typeof selector === 'string' ) {
  861. if ( selector.indexOf( ',' ) !== -1 ) {
  862. // Split
  863. var a = selector.split(',');
  864. for ( i=0, ien=a.length ; i<ien ; i++ ) {
  865. run( $.trim(a[i]), inst );
  866. }
  867. }
  868. else if ( selector.match( /^\d+(\-\d+)?$/ ) ) {
  869. // Sub-button index selector
  870. ret.push( {
  871. inst: inst,
  872. idx: selector
  873. } );
  874. }
  875. else if ( selector.indexOf( ':name' ) !== -1 ) {
  876. // Button name selector
  877. var name = selector.replace( ':name', '' );
  878. for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
  879. if ( buttons[i].name === name ) {
  880. ret.push( {
  881. inst: inst,
  882. idx: inst.toIndex( buttons[i].node )
  883. } );
  884. }
  885. }
  886. }
  887. else {
  888. // jQuery selector on the nodes
  889. $( nodes ).filter( selector ).each( function () {
  890. ret.push( {
  891. inst: inst,
  892. idx: inst.toIndex( this )
  893. } );
  894. } );
  895. }
  896. }
  897. else if ( typeof selector === 'object' && selector.nodeName ) {
  898. // Node selector
  899. var idx = $.inArray( selector, nodes );
  900. if ( idx !== -1 ) {
  901. ret.push( {
  902. inst: inst,
  903. idx: inst.toIndex( nodes[ idx ] )
  904. } );
  905. }
  906. }
  907. };
  908. for ( var i=0, ien=insts.length ; i<ien ; i++ ) {
  909. var inst = insts[i];
  910. run( selector, inst );
  911. }
  912. return ret;
  913. };
  914. /**
  915. * Buttons defaults. For full documentation, please refer to the docs/option
  916. * directory or the DataTables site.
  917. * @type {Object}
  918. * @static
  919. */
  920. Buttons.defaults = {
  921. buttons: [ 'copy', 'excel', 'csv', 'pdf', 'print' ],
  922. name: 'main',
  923. tabIndex: 0,
  924. dom: {
  925. container: {
  926. tag: 'div',
  927. className: 'dt-buttons'
  928. },
  929. collection: {
  930. tag: 'div',
  931. className: 'dt-button-collection'
  932. },
  933. button: {
  934. tag: 'a',
  935. className: 'dt-button',
  936. active: 'active',
  937. disabled: 'disabled'
  938. },
  939. buttonLiner: {
  940. tag: 'span',
  941. className: ''
  942. }
  943. }
  944. };
  945. /**
  946. * Version information
  947. * @type {string}
  948. * @static
  949. */
  950. Buttons.version = '1.1.2';
  951. $.extend( _dtButtons, {
  952. collection: {
  953. text: function ( dt, button, config ) {
  954. return dt.i18n( 'buttons.collection', 'Collection' );
  955. },
  956. className: 'buttons-collection',
  957. action: function ( e, dt, button, config ) {
  958. var background;
  959. var host = button;
  960. var hostOffset = host.offset();
  961. var tableContainer = $( dt.table().container() );
  962. var multiLevel = false;
  963. // Remove any old collection
  964. if ( $('div.dt-button-background').length ) {
  965. multiLevel = $('div.dt-button-collection').offset();
  966. $(document).trigger( 'click.dtb-collection' );
  967. }
  968. config._collection
  969. .addClass( config.collectionLayout )
  970. .css( 'display', 'none' )
  971. .appendTo( 'body' )
  972. .fadeIn( config.fade );
  973. var position = config._collection.css( 'position' );
  974. if ( multiLevel && position === 'absolute' ) {
  975. config._collection.css( {
  976. top: multiLevel.top + 5, // magic numbers for a little offset
  977. left: multiLevel.left + 5
  978. } );
  979. }
  980. else if ( position === 'absolute' ) {
  981. config._collection.css( {
  982. top: hostOffset.top + host.outerHeight(),
  983. left: hostOffset.left
  984. } );
  985. var listRight = hostOffset.left + config._collection.outerWidth();
  986. var tableRight = tableContainer.offset().left + tableContainer.width();
  987. if ( listRight > tableRight ) {
  988. config._collection.css( 'left', hostOffset.left - ( listRight - tableRight ) );
  989. }
  990. }
  991. else {
  992. // Fix position - centre on screen
  993. var top = config._collection.height() / 2;
  994. if ( top > $(window).height() / 2 ) {
  995. top = $(window).height() / 2;
  996. }
  997. config._collection.css( 'marginTop', top*-1 );
  998. }
  999. if ( config.background ) {
  1000. Buttons.background( true, config.backgroundClassName, config.fade );
  1001. }
  1002. // Need to break the 'thread' for the collection button being
  1003. // activated by a click - it would also trigger this event
  1004. setTimeout( function () {
  1005. // This is bonkers, but if we don't have a click listener on the
  1006. // background element, iOS Safari will ignore the body click
  1007. // listener below. An empty function here is all that is
  1008. // required to make it work...
  1009. $('div.dt-button-background').on( 'click.dtb-collection', function () {} );
  1010. $('body').on( 'click.dtb-collection', function (e) {
  1011. if ( ! $(e.target).parents().andSelf().filter( config._collection ).length ) {
  1012. config._collection
  1013. .fadeOut( config.fade, function () {
  1014. config._collection.detach();
  1015. } );
  1016. $('div.dt-button-background').off( 'click.dtb-collection' );
  1017. Buttons.background( false, config.backgroundClassName, config.fade );
  1018. $('body').off( 'click.dtb-collection' );
  1019. dt.off( 'buttons-action.b-internal' );
  1020. }
  1021. } );
  1022. }, 10 );
  1023. if ( config.autoClose ) {
  1024. dt.on( 'buttons-action.b-internal', function () {
  1025. $('div.dt-button-background').click();
  1026. } );
  1027. }
  1028. },
  1029. background: true,
  1030. collectionLayout: '',
  1031. backgroundClassName: 'dt-button-background',
  1032. autoClose: false,
  1033. fade: 400
  1034. },
  1035. copy: function ( dt, conf ) {
  1036. if ( _dtButtons.copyHtml5 ) {
  1037. return 'copyHtml5';
  1038. }
  1039. if ( _dtButtons.copyFlash && _dtButtons.copyFlash.available( dt, conf ) ) {
  1040. return 'copyFlash';
  1041. }
  1042. },
  1043. csv: function ( dt, conf ) {
  1044. // Common option that will use the HTML5 or Flash export buttons
  1045. if ( _dtButtons.csvHtml5 && _dtButtons.csvHtml5.available( dt, conf ) ) {
  1046. return 'csvHtml5';
  1047. }
  1048. if ( _dtButtons.csvFlash && _dtButtons.csvFlash.available( dt, conf ) ) {
  1049. return 'csvFlash';
  1050. }
  1051. },
  1052. excel: function ( dt, conf ) {
  1053. // Common option that will use the HTML5 or Flash export buttons
  1054. if ( _dtButtons.excelHtml5 && _dtButtons.excelHtml5.available( dt, conf ) ) {
  1055. return 'excelHtml5';
  1056. }
  1057. if ( _dtButtons.excelFlash && _dtButtons.excelFlash.available( dt, conf ) ) {
  1058. return 'excelFlash';
  1059. }
  1060. },
  1061. pdf: function ( dt, conf ) {
  1062. // Common option that will use the HTML5 or Flash export buttons
  1063. if ( _dtButtons.pdfHtml5 && _dtButtons.pdfHtml5.available( dt, conf ) ) {
  1064. return 'pdfHtml5';
  1065. }
  1066. if ( _dtButtons.pdfFlash && _dtButtons.pdfFlash.available( dt, conf ) ) {
  1067. return 'pdfFlash';
  1068. }
  1069. },
  1070. pageLength: function ( dt, conf ) {
  1071. var lengthMenu = dt.settings()[0].aLengthMenu;
  1072. var vals = $.isArray( lengthMenu[0] ) ? lengthMenu[0] : lengthMenu;
  1073. var lang = $.isArray( lengthMenu[0] ) ? lengthMenu[1] : lengthMenu;
  1074. var text = function ( dt ) {
  1075. return dt.i18n( 'buttons.pageLength', {
  1076. "-1": 'Show all rows',
  1077. _: 'Show %d rows'
  1078. }, dt.page.len() );
  1079. };
  1080. return {
  1081. extend: 'collection',
  1082. text: text,
  1083. className: 'buttons-page-length',
  1084. autoClose: true,
  1085. buttons: $.map( vals, function ( val, i ) {
  1086. return {
  1087. text: lang[i],
  1088. action: function ( e, dt, button, conf ) {
  1089. dt.page.len( val ).draw();
  1090. },
  1091. init: function ( dt, node, conf ) {
  1092. var that = this;
  1093. var fn = function () {
  1094. that.active( dt.page.len() === val );
  1095. };
  1096. dt.on( 'length.dt'+conf.namespace, fn );
  1097. fn();
  1098. },
  1099. destroy: function ( dt, node, conf ) {
  1100. dt.off( 'length.dt'+conf.namespace );
  1101. }
  1102. };
  1103. } ),
  1104. init: function ( dt, node, conf ) {
  1105. var that = this;
  1106. dt.on( 'length.dt'+conf.namespace, function () {
  1107. that.text( text( dt ) );
  1108. } );
  1109. },
  1110. destroy: function ( dt, node, conf ) {
  1111. dt.off( 'length.dt'+conf.namespace );
  1112. }
  1113. };
  1114. }
  1115. } );
  1116. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1117. * DataTables API
  1118. *
  1119. * For complete documentation, please refer to the docs/api directory or the
  1120. * DataTables site
  1121. */
  1122. // Buttons group and individual button selector
  1123. DataTable.Api.register( 'buttons()', function ( group, selector ) {
  1124. // Argument shifting
  1125. if ( selector === undefined ) {
  1126. selector = group;
  1127. group = undefined;
  1128. }
  1129. return this.iterator( true, 'table', function ( ctx ) {
  1130. if ( ctx._buttons ) {
  1131. return Buttons.buttonSelector(
  1132. Buttons.instanceSelector( group, ctx._buttons ),
  1133. selector
  1134. );
  1135. }
  1136. }, true );
  1137. } );
  1138. // Individual button selector
  1139. DataTable.Api.register( 'button()', function ( group, selector ) {
  1140. // just run buttons() and truncate
  1141. var buttons = this.buttons( group, selector );
  1142. if ( buttons.length > 1 ) {
  1143. buttons.splice( 1, buttons.length );
  1144. }
  1145. return buttons;
  1146. } );
  1147. // Active buttons
  1148. DataTable.Api.registerPlural( 'buttons().active()', 'button().active()', function ( flag ) {
  1149. if ( flag === undefined ) {
  1150. return this.map( function ( set ) {
  1151. return set.inst.active( set.idx );
  1152. } );
  1153. }
  1154. return this.each( function ( set ) {
  1155. set.inst.active( set.idx, flag );
  1156. } );
  1157. } );
  1158. // Get / set button action
  1159. DataTable.Api.registerPlural( 'buttons().action()', 'button().action()', function ( action ) {
  1160. if ( action === undefined ) {
  1161. return this.map( function ( set ) {
  1162. return set.inst.action( set.idx );
  1163. } );
  1164. }
  1165. return this.each( function ( set ) {
  1166. set.inst.action( set.idx, action );
  1167. } );
  1168. } );
  1169. // Enable / disable buttons
  1170. DataTable.Api.register( ['buttons().enable()', 'button().enable()'], function ( flag ) {
  1171. return this.each( function ( set ) {
  1172. set.inst.enable( set.idx, flag );
  1173. } );
  1174. } );
  1175. // Disable buttons
  1176. DataTable.Api.register( ['buttons().disable()', 'button().disable()'], function () {
  1177. return this.each( function ( set ) {
  1178. set.inst.disable( set.idx );
  1179. } );
  1180. } );
  1181. // Get button nodes
  1182. DataTable.Api.registerPlural( 'buttons().nodes()', 'button().node()', function () {
  1183. var jq = $();
  1184. // jQuery will automatically reduce duplicates to a single entry
  1185. $( this.each( function ( set ) {
  1186. jq = jq.add( set.inst.node( set.idx ) );
  1187. } ) );
  1188. return jq;
  1189. } );
  1190. // Get / set button text (i.e. the button labels)
  1191. DataTable.Api.registerPlural( 'buttons().text()', 'button().text()', function ( label ) {
  1192. if ( label === undefined ) {
  1193. return this.map( function ( set ) {
  1194. return set.inst.text( set.idx );
  1195. } );
  1196. }
  1197. return this.each( function ( set ) {
  1198. set.inst.text( set.idx, label );
  1199. } );
  1200. } );
  1201. // Trigger a button's action
  1202. DataTable.Api.registerPlural( 'buttons().trigger()', 'button().trigger()', function () {
  1203. return this.each( function ( set ) {
  1204. set.inst.node( set.idx ).trigger( 'click' );
  1205. } );
  1206. } );
  1207. // Get the container elements for the button sets selected
  1208. DataTable.Api.registerPlural( 'buttons().containers()', 'buttons().container()', function () {
  1209. var jq = $();
  1210. // jQuery will automatically reduce duplicates to a single entry
  1211. $( this.each( function ( set ) {
  1212. jq = jq.add( set.inst.container() );
  1213. } ) );
  1214. return jq;
  1215. } );
  1216. // Add a new button
  1217. DataTable.Api.register( 'button().add()', function ( idx, conf ) {
  1218. if ( this.length === 1 ) {
  1219. this[0].inst.add( idx, conf );
  1220. }
  1221. return this.button( idx );
  1222. } );
  1223. // Destroy the button sets selected
  1224. DataTable.Api.register( 'buttons().destroy()', function ( idx ) {
  1225. this.pluck( 'inst' ).unique().each( function ( inst ) {
  1226. inst.destroy();
  1227. } );
  1228. return this;
  1229. } );
  1230. // Remove a button
  1231. DataTable.Api.registerPlural( 'buttons().remove()', 'buttons().remove()', function () {
  1232. // Need to split into prep and commit so the indexes remain constant during the remove
  1233. this.each( function ( set ) {
  1234. set.inst.removePrep( set.idx );
  1235. } );
  1236. this.pluck( 'inst' ).unique().each( function ( inst ) {
  1237. inst.removeCommit();
  1238. } );
  1239. return this;
  1240. } );
  1241. // Information box that can be used by buttons
  1242. var _infoTimer;
  1243. DataTable.Api.register( 'buttons.info()', function ( title, message, time ) {
  1244. var that = this;
  1245. if ( title === false ) {
  1246. $('#datatables_buttons_info').fadeOut( function () {
  1247. $(this).remove();
  1248. } );
  1249. clearTimeout( _infoTimer );
  1250. _infoTimer = null;
  1251. return this;
  1252. }
  1253. if ( _infoTimer ) {
  1254. clearTimeout( _infoTimer );
  1255. }
  1256. if ( $('#datatables_buttons_info').length ) {
  1257. $('#datatables_buttons_info').remove();
  1258. }
  1259. title = title ? '<h2>'+title+'</h2>' : '';
  1260. $('<div id="datatables_buttons_info" class="dt-button-info"/>')
  1261. .html( title )
  1262. .append( $('<div/>')[ typeof message === 'string' ? 'html' : 'append' ]( message ) )
  1263. .css( 'display', 'none' )
  1264. .appendTo( 'body' )
  1265. .fadeIn();
  1266. if ( time !== undefined && time !== 0 ) {
  1267. _infoTimer = setTimeout( function () {
  1268. that.buttons.info( false );
  1269. }, time );
  1270. }
  1271. return this;
  1272. } );
  1273. // Get data from the table for export - this is common to a number of plug-in
  1274. // buttons so it is included in the Buttons core library
  1275. DataTable.Api.register( 'buttons.exportData()', function ( options ) {
  1276. if ( this.context.length ) {
  1277. return _exportData( new DataTable.Api( this.context[0] ), options );
  1278. }
  1279. } );
  1280. var _exportTextarea = $('<textarea/>')[0];
  1281. var _exportData = function ( dt, inOpts )
  1282. {
  1283. var config = $.extend( true, {}, {
  1284. rows: null,
  1285. columns: '',
  1286. modifier: {
  1287. search: 'applied',
  1288. order: 'applied'
  1289. },
  1290. orthogonal: 'display',
  1291. stripHtml: true,
  1292. stripNewlines: true,
  1293. decodeEntities: true,
  1294. trim: true,
  1295. format: {
  1296. header: function ( d ) {
  1297. return strip( d );
  1298. },
  1299. footer: function ( d ) {
  1300. return strip( d );
  1301. },
  1302. body: function ( d ) {
  1303. return strip( d );
  1304. }
  1305. }
  1306. }, inOpts );
  1307. var strip = function ( str ) {
  1308. if ( typeof str !== 'string' ) {
  1309. return str;
  1310. }
  1311. if ( config.stripHtml ) {
  1312. str = str.replace( /<.*?>/g, '' );
  1313. }
  1314. if ( config.trim ) {
  1315. str = str.replace( /^\s+|\s+$/g, '' );
  1316. }
  1317. if ( config.stripNewlines ) {
  1318. str = str.replace( /\n/g, ' ' );
  1319. }
  1320. if ( config.decodeEntities ) {
  1321. _exportTextarea.innerHTML = str;
  1322. str = _exportTextarea.value;
  1323. }
  1324. return str;
  1325. };
  1326. var header = dt.columns( config.columns ).indexes().map( function (idx, i) {
  1327. return config.format.header( dt.column( idx ).header().innerHTML, idx );
  1328. } ).toArray();
  1329. var footer = dt.table().footer() ?
  1330. dt.columns( config.columns ).indexes().map( function (idx, i) {
  1331. var el = dt.column( idx ).footer();
  1332. return config.format.footer( el ? el.innerHTML : '', idx );
  1333. } ).toArray() :
  1334. null;
  1335. var rowIndexes = dt.rows( config.rows, config.modifier ).indexes().toArray();
  1336. var cells = dt
  1337. .cells( rowIndexes, config.columns )
  1338. .render( config.orthogonal )
  1339. .toArray();
  1340. var columns = header.length;
  1341. var rows = columns > 0 ? cells.length / columns : 0;
  1342. var body = new Array( rows );
  1343. var cellCounter = 0;
  1344. for ( var i=0, ien=rows ; i<ien ; i++ ) {
  1345. var row = new Array( columns );
  1346. for ( var j=0 ; j<columns ; j++ ) {
  1347. row[j] = config.format.body( cells[ cellCounter ], j, i );
  1348. cellCounter++;
  1349. }
  1350. body[i] = row;
  1351. }
  1352. return {
  1353. header: header,
  1354. footer: footer,
  1355. body: body
  1356. };
  1357. };
  1358. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1359. * DataTables interface
  1360. */
  1361. // Attach to DataTables objects for global access
  1362. $.fn.dataTable.Buttons = Buttons;
  1363. $.fn.DataTable.Buttons = Buttons;
  1364. // DataTables creation - check if the buttons have been defined for this table,
  1365. // they will have been if the `B` option was used in `dom`, otherwise we should
  1366. // create the buttons instance here so they can be inserted into the document
  1367. // using the API. Listen for `init` for compatibility with pre 1.10.10, but to
  1368. // be removed in future.
  1369. $(document).on( 'init.dt plugin-init.dt', function (e, settings, json) {
  1370. if ( e.namespace !== 'dt' ) {
  1371. return;
  1372. }
  1373. var opts = settings.oInit.buttons || DataTable.defaults.buttons;
  1374. if ( opts && ! settings._buttons ) {
  1375. new Buttons( settings, opts ).container();
  1376. }
  1377. } );
  1378. // DataTables `dom` feature option
  1379. DataTable.ext.feature.push( {
  1380. fnInit: function( settings ) {
  1381. var api = new DataTable.Api( settings );
  1382. var opts = api.init().buttons || DataTable.defaults.buttons;
  1383. return new Buttons( api, opts ).container();
  1384. },
  1385. cFeature: "B"
  1386. } );
  1387. return Buttons;
  1388. }));