dataTables.buttons.js 40 KB

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