From 68778a937443c0030656d3fda5c0614b1762d1bd Mon Sep 17 00:00:00 2001 From: Jose Jimenez Date: Fri, 18 Nov 2016 14:21:58 -0600 Subject: [PATCH 1/6] Bump datatables to 1.10.12, bump extras --- .../dataTables/extras/dataTables.autoFill.js | 49 +- .../dataTables/extras/dataTables.buttons.js | 650 +- .../extras/dataTables.colReorder.js | 56 +- .../extras/dataTables.fixedColumns.js | 169 +- .../extras/dataTables.fixedHeader.js | 87 +- .../dataTables/extras/dataTables.keyTable.js | 207 +- .../extras/dataTables.responsive.js | 215 +- .../extras/dataTables.rowReorder.js | 0 .../dataTables/extras/dataTables.scroller.js | 82 +- .../dataTables/extras/dataTables.select.js | 243 +- .../dataTables/jquery.dataTables.js | 11860 ++++++++-------- .../dataTables/extras/autoFill.dataTables.css | 92 + .../extras/colReorder.dataTables.css | 11 + .../extras/dataTables.autoFill.scss | 24 - .../extras/dataTables.colReorder.scss | 14 - .../extras/dataTables.fixedColumns.scss | 25 - .../extras/dataTables.fixedHeader.scss | 7 - .../extras/dataTables.keyTable.scss | 7 - .../extras/dataTables.responsive.scss | 149 - .../extras/dataTables.scroller.scss | 44 - .../extras/fixedColumns.dataTables.css | 18 + .../extras/fixedHeader.dataTables.css | 19 + .../dataTables/extras/keyTable.dataTables.css | 5 + .../extras/responsive.dataTables.css | 178 + .../extras/rowReorder.dataTables.css | 22 + .../dataTables/extras/scroller.dataTables.css | 20 + .../dataTables/extras/select.dataTables.css | 100 + .../jquery/datatables/install_generator.rb | 2 +- 28 files changed, 7577 insertions(+), 6778 deletions(-) mode change 100755 => 100644 app/assets/javascripts/dataTables/extras/dataTables.rowReorder.js mode change 100644 => 100755 app/assets/javascripts/dataTables/jquery.dataTables.js create mode 100755 app/assets/stylesheets/dataTables/extras/autoFill.dataTables.css create mode 100755 app/assets/stylesheets/dataTables/extras/colReorder.dataTables.css delete mode 100644 app/assets/stylesheets/dataTables/extras/dataTables.autoFill.scss delete mode 100644 app/assets/stylesheets/dataTables/extras/dataTables.colReorder.scss delete mode 100644 app/assets/stylesheets/dataTables/extras/dataTables.fixedColumns.scss delete mode 100644 app/assets/stylesheets/dataTables/extras/dataTables.fixedHeader.scss delete mode 100644 app/assets/stylesheets/dataTables/extras/dataTables.keyTable.scss delete mode 100644 app/assets/stylesheets/dataTables/extras/dataTables.responsive.scss delete mode 100644 app/assets/stylesheets/dataTables/extras/dataTables.scroller.scss create mode 100755 app/assets/stylesheets/dataTables/extras/fixedColumns.dataTables.css create mode 100755 app/assets/stylesheets/dataTables/extras/fixedHeader.dataTables.css create mode 100755 app/assets/stylesheets/dataTables/extras/keyTable.dataTables.css create mode 100755 app/assets/stylesheets/dataTables/extras/responsive.dataTables.css create mode 100755 app/assets/stylesheets/dataTables/extras/rowReorder.dataTables.css create mode 100755 app/assets/stylesheets/dataTables/extras/scroller.dataTables.css create mode 100755 app/assets/stylesheets/dataTables/extras/select.dataTables.css diff --git a/app/assets/javascripts/dataTables/extras/dataTables.autoFill.js b/app/assets/javascripts/dataTables/extras/dataTables.autoFill.js index b6c524a..b322b2e 100755 --- a/app/assets/javascripts/dataTables/extras/dataTables.autoFill.js +++ b/app/assets/javascripts/dataTables/extras/dataTables.autoFill.js @@ -1,11 +1,11 @@ -/*! AutoFill 2.1.0 +/*! AutoFill 2.1.2 * ©2008-2015 SpryMedia Ltd - datatables.net/license */ /** * @summary AutoFill * @description Add Excel like click and drag auto-fill options to DataTables - * @version 2.1.0 + * @version 2.1.2 * @file dataTables.autoFill.js * @author SpryMedia Ltd (www.sprymedia.co.uk) * @contact www.sprymedia.co.uk/contact @@ -192,6 +192,8 @@ $.extend( AutoFill.prototype, { var idx = dt.cell( node ).index(); var handle = this.dom.handle; var handleDim = this.s.handle; + var dtScroll = $('div.dataTables_scrollBody', this.s.dt.table().container() ); + var scrollOffsetX=0, scrollOffsetY=0; if ( ! idx || dt.columns( this.c.columns ).indexes().indexOf( idx.column ) === -1 ) { this._detach(); @@ -212,11 +214,18 @@ $.extend( AutoFill.prototype, { var offset = $(node).position(); + // If scrolling, and the table is not itself the offset parent, need to + // offset for the scrolling position + if ( dtScroll.length && this.dom.offsetParent[0] !== dt.table().node() ) { + scrollOffsetY = dtScroll.scrollTop(); + scrollOffsetX = dtScroll.scrollLeft(); + } + this.dom.attachedTo = node; handle .css( { - top: offset.top + node.offsetHeight - handleDim.height, - left: offset.left + node.offsetWidth - handleDim.width + top: offset.top + node.offsetHeight - handleDim.height + scrollOffsetY, + left: offset.left + node.offsetWidth - handleDim.width + scrollOffsetX } ) .appendTo( this.dom.offsetParent ); }, @@ -344,7 +353,7 @@ $.extend( AutoFill.prototype, { width = right.position().left + right.outerWidth() - left; var dtScroll = this.dom.dtScroll; - if ( dtScroll ) { + if ( dtScroll && this.dom.offsetParent[0] !== dt.table().node() ) { top += dtScroll.scrollTop(); left += dtScroll.scrollLeft(); } @@ -620,9 +629,11 @@ $.extend( AutoFill.prototype, { } // Build a matrix representation of the selected rows - var rows = this._range( start.row, end.row ); - var columns = this._range( start.column, end.column ); - var selected = []; + var rows = this._range( start.row, end.row ); + var columns = this._range( start.column, end.column ); + var selected = []; + var dtSettings = dt.settings()[0]; + var dtColumns = dtSettings.aoColumns; // Can't use Array.prototype.map as IE8 doesn't support it // Can't use $.map as jQuery flattens 2D arrays @@ -631,17 +642,29 @@ $.extend( AutoFill.prototype, { selected.push( $.map( columns, function (column) { var cell = dt.cell( ':eq('+rows[rowIdx]+')', column+':visible', {page:'current'} ); + var data = cell.data(); + var cellIndex = cell.index(); + var editField = dtColumns[ cellIndex.column ].editField; + + if ( editField !== undefined ) { + data = dtSettings.oApi._fnGetObjectDataFn( editField )( dt.row( cellIndex.row ).data() ); + } return { cell: cell, - data: cell.data(), - index: cell.index() + data: data, + label: cell.data(), + index: cellIndex }; } ) ); } this._actionSelector( selected ); + + // Stop shiftScroll + clearInterval( this.s.scrollInterval ); + this.s.scrollInterval = null; }, @@ -842,7 +865,7 @@ $.extend( AutoFill.prototype, { AutoFill.actions = { increment: { available: function ( dt, cells ) { - return $.isNumeric( cells[0][0].data ); + return $.isNumeric( cells[0][0].label ); }, option: function ( dt, cells ) { @@ -872,7 +895,7 @@ AutoFill.actions = { }, option: function ( dt, cells ) { - return dt.i18n('autoFill.fill', 'Fill all cells with '+cells[0][0].data+'' ); + return dt.i18n('autoFill.fill', 'Fill all cells with '+cells[0][0].label+'' ); }, execute: function ( dt, cells, node ) { @@ -947,7 +970,7 @@ AutoFill.actions = { * @static * @type String */ -AutoFill.version = '2.1.0'; +AutoFill.version = '2.1.2'; /** diff --git a/app/assets/javascripts/dataTables/extras/dataTables.buttons.js b/app/assets/javascripts/dataTables/extras/dataTables.buttons.js index 36610f6..470fc00 100755 --- a/app/assets/javascripts/dataTables/extras/dataTables.buttons.js +++ b/app/assets/javascripts/dataTables/extras/dataTables.buttons.js @@ -1,5 +1,5 @@ -/*! Buttons for DataTables 1.1.0 - * ©2015 SpryMedia Ltd - datatables.net/license +/*! Buttons for DataTables 1.2.2 + * ©2016 SpryMedia Ltd - datatables.net/license */ (function( factory ){ @@ -68,7 +68,6 @@ var Buttons = function( dt, config ) this.s = { dt: new DataTable.Api( dt ), buttons: [], - subButtons: [], listenKeys: '', namespace: 'dtb'+(_instCounter++) }; @@ -93,57 +92,68 @@ $.extend( Buttons.prototype, { * @return {function} *//** * Set the action of a button - * @param {int|string} Button index - * @param {function} Function to set + * @param {node} node Button element + * @param {function} action Function to set * @return {Buttons} Self for chaining */ - action: function ( idx, action ) + action: function ( node, action ) { - var button = this._indexToButton( idx ).conf; + var button = this._nodeToButton( node ); if ( action === undefined ) { - return button.action; + return button.conf.action; } - button.action = action; + button.conf.action = action; return this; }, /** - * Add an active class to the button to make to look active - * @param {int|string} Button index - * @param {boolean} [flag=true] Enable / disable flag - * @return {Buttons} Self for chaining + * Add an active class to the button to make to look active or get current + * active state. + * @param {node} node Button element + * @param {boolean} [flag] Enable / disable flag + * @return {Buttons} Self for chaining or boolean for getter */ - active: function ( idx, flag ) { - var button = this._indexToButton( idx ); - button.node.toggleClass( - this.c.dom.button.active, - flag === undefined ? true : flag - ); + active: function ( node, flag ) { + var button = this._nodeToButton( node ); + var klass = this.c.dom.button.active; + var jqNode = $(button.node); + + if ( flag === undefined ) { + return jqNode.hasClass( klass ); + } + + jqNode.toggleClass( klass, flag === undefined ? true : flag ); return this; }, /** * Add a new button - * @param {int|string} Button index for where to insert the button - * @param {object} Button configuration object, base string name or function + * @param {object} config Button configuration object, base string name or function + * @param {int|string} [idx] Button index for where to insert the button * @return {Buttons} Self for chaining */ - add: function ( idx, config ) + add: function ( config, idx ) { - if ( typeof idx === 'string' && idx.indexOf('-') !== -1 ) { - var idxs = idx.split('-'); - this.c.buttons[idxs[0]*1].buttons.splice( idxs[1]*1, 0, config ); - } - else { - this.c.buttons.splice( idx*1, 0, config ); + var buttons = this.s.buttons; + + if ( typeof idx === 'string' ) { + var split = idx.split('-'); + var base = this.s; + + for ( var i=0, ien=split.length-1 ; i=0 ; i-- ) { - if ( buttons[i] === null ) { - buttons.splice( i, 1 ); - subButtons.splice( i, 1 ); - this.c.buttons.splice( i, 1 ); - } - } - - for ( i=0, ien=subButtons.length ; i=0 ; j-- ) { - if ( subButtons[i][j] === null ) { - subButtons[i].splice( j, 1 ); - this.c.buttons[i].buttons.splice( j, 1 ); - } - } - } - - return this; + var button = this._nodeToButton( node ); + return $(button.node); }, /** - * Scheduled a button for removal. This is required so multiple buttons can - * be removed without upsetting the button indexes while removing them. + * Remove a button. + * @param {node} node Button node * @return {Buttons} Self for chaining */ - removePrep: function ( idx ) + remove: function ( node ) { - var button; + var button = this._nodeToButton( node ); + var host = this._nodeToHost( node ); var dt = this.s.dt; - if ( typeof idx === 'number' || idx.indexOf('-') === -1 ) { - // Top level button - button = this.s.buttons[ idx*1 ]; - - if ( button.conf.destroy ) { - button.conf.destroy.call( dt.button(idx), dt, button, button.conf ); + // Remove any child buttons first + if ( button.buttons.length ) { + for ( var i=button.buttons.length-1 ; i>=0 ; i-- ) { + this.remove( button.buttons[i].node ); } + } - button.node.remove(); - this._removeKey( button.conf ); - this.s.buttons[ idx*1 ] = null; + // Allow the button to remove event handlers, etc + if ( button.conf.destroy ) { + button.conf.destroy.call( dt.button(node), dt, $(node), button.conf ); } - else { - // Collection button - var idxs = idx.split('-'); - button = this.s.subButtons[ idxs[0]*1 ][ idxs[1]*1 ]; - if ( button.conf.destroy ) { - button.conf.destroy.call( dt.button(idx), dt, button, button.conf ); - } + this._removeKey( button.conf ); - button.node.remove(); - this._removeKey( button.conf ); - this.s.subButtons[ idxs[0]*1 ][ idxs[1]*1 ] = null; - } + $(button.node).remove(); + + var idx = $.inArray( button, host ); + host.splice( idx, 1 ); return this; }, /** * Get the text for a button - * @param {int|string} Button index + * @param {int|string} node Button index * @return {string} Button text *//** * Set the text for a button - * @param {int|string|function} Button index - * @param {string} Text + * @param {int|string|function} node Button index + * @param {string} label Text * @return {Buttons} Self for chaining */ - text: function ( idx, label ) + text: function ( node, label ) { - var button = this._indexToButton( idx ); + var button = this._nodeToButton( node ); var buttonLiner = this.c.dom.collection.buttonLiner; - var linerTag = typeof idx === 'string' && idx.indexOf( '-' ) !== -1 && buttonLiner && buttonLiner.tag ? + var linerTag = button.inCollection && buttonLiner && buttonLiner.tag ? buttonLiner.tag : this.c.dom.buttonLiner.tag; var dt = this.s.dt; + var jqNode = $(button.node); var text = function ( opt ) { return typeof opt === 'function' ? - opt( dt, button.node, button.conf ) : + opt( dt, jqNode, button.conf ) : opt; }; @@ -352,43 +318,15 @@ $.extend( Buttons.prototype, { button.conf.text = label; if ( linerTag ) { - button.node.children( linerTag ).html( text(label) ); + jqNode.children( linerTag ).html( text(label) ); } else { - button.node.html( text(label) ); + jqNode.html( text(label) ); } return this; }, - /** - * Calculate button index from a node - * @param {node} Button node (_not_ a jQuery object) - * @return {string} Index. Undefined if not found - */ - toIndex: function ( node ) - { - var i, ien, j, jen; - var buttons = this.s.buttons; - var subButtons = this.s.subButtons; - - // Loop the main buttons first - for ( i=0, ien=buttons.length ; i') + built.collection = $('<'+collectionDom.tag+'/>') .addClass( collectionDom.className ); + built.conf._collection = built.collection; - this._buildButtons( conf.buttons, conf._collection, i ); + this._expandButton( built.buttons, built.conf.buttons, true, attachPoint ); } // init call is made here, rather than buildButton as it needs to - // have been added to the buttons / subButtons array first + // be selectable, and for that it needs to be in the buttons array if ( conf.init ) { - conf.init.call( dt.button( buttonNode ), dt, buttonNode, conf ); + conf.init.call( dt.button( built.node ), dt, $(built.node), conf ); } + + buttonCounter++; } }, /** * Create an individual button * @param {object} config Resolved button configuration - * @param {boolean} collectionButton `true` if a collection button + * @param {boolean} inCollection `true` if a collection button * @return {jQuery} Created button node (jQuery) * @private */ - _buildButton: function ( config, collectionButton ) + _buildButton: function ( config, inCollection ) { - var that = this; var buttonDom = this.c.dom.button; var linerDom = this.c.dom.buttonLiner; var collectionDom = this.c.dom.collection; @@ -548,11 +497,11 @@ $.extend( Buttons.prototype, { opt; }; - if ( collectionButton && collectionDom.button ) { + if ( inCollection && collectionDom.button ) { buttonDom = collectionDom.button; } - if ( collectionButton && collectionDom.buttonLiner ) { + if ( inCollection && collectionDom.buttonLiner ) { linerDom = collectionDom.buttonLiner; } @@ -562,6 +511,14 @@ $.extend( Buttons.prototype, { return false; } + var action = function ( e, dt, button, config ) { + config.action.call( dt.button( button ), e, dt, button, config ); + + $(dt.table().node()).triggerHandler( 'buttons-action.dt', [ + dt.button( button ), dt, button, config + ] ); + }; + var button = $('<'+buttonDom.tag+'/>') .addClass( buttonDom.className ) .attr( 'tabindex', this.s.dt.settings()[0].iTabIndex ) @@ -570,7 +527,7 @@ $.extend( Buttons.prototype, { e.preventDefault(); if ( ! button.hasClass( buttonDom.disabled ) && config.action ) { - config.action.call( dt.button( button ), e, dt, button, config ); + action( e, dt, button, config ); } button.blur(); @@ -578,17 +535,26 @@ $.extend( Buttons.prototype, { .on( 'keyup.dtb', function (e) { if ( e.keyCode === 13 ) { if ( ! button.hasClass( buttonDom.disabled ) && config.action ) { - config.action.call( dt.button( button ), e, dt, button, config ); + action( e, dt, button, config ); } } } ); + // Make `a` tags act like a link + if ( buttonDom.tag.toLowerCase() === 'a' ) { + button.attr( 'href', '#' ); + } + if ( linerDom.tag ) { - button.append( - $('<'+linerDom.tag+'/>') - .html( text( config.text ) ) - .addClass( linerDom.className ) - ); + var liner = $('<'+linerDom.tag+'/>') + .html( text( config.text ) ) + .addClass( linerDom.className ); + + if ( linerDom.tag.toLowerCase() === 'a' ) { + liner.attr( 'href', '#' ); + } + + button.append( liner ); } else { button.html( text( config.text ) ); @@ -612,7 +578,7 @@ $.extend( Buttons.prototype, { var buttonContainer = this.c.dom.buttonContainer; var inserter; - if ( buttonContainer ) { + if ( buttonContainer && buttonContainer.tag ) { inserter = $('<'+buttonContainer.tag+'/>') .addClass( buttonContainer.className ) .append( button ); @@ -624,47 +590,87 @@ $.extend( Buttons.prototype, { this._addKey( config ); return { - node: button, - inserter: inserter + conf: config, + node: button.get(0), + inserter: inserter, + buttons: [], + inCollection: inCollection, + collection: null }; }, /** - * Get a button's host information from a button index - * @param {int|string} Button index - * @return {object} Button information - object contains `node` and `conf` - * properties + * Get the button object from a node (recursive) + * @param {node} node Button node + * @param {array} [buttons] Button array, uses base if not defined + * @return {object} Button object + * @private + */ + _nodeToButton: function ( node, buttons ) + { + if ( ! buttons ) { + buttons = this.s.buttons; + } + + for ( var i=0, ien=buttons.length ; i div.'+className) .fadeOut( fade, function () { - $(this).remove(); + $(this) + .removeClass( className ) + .remove(); } ); } }; @@ -947,29 +957,35 @@ Buttons.instanceSelector = function ( group, buttons ) Buttons.buttonSelector = function ( insts, selector ) { var ret = []; - var run = function ( selector, inst ) { - var i, ien, j, jen; - var buttons = []; + var nodeBuilder = function ( a, buttons, baseIdx ) { + var button; + var idx; - $.each( inst.s.buttons, function (i, v) { - if ( v !== null ) { - buttons.push( { - node: v.node[0], - name: v.name + for ( var i=0, ien=buttons.length ; i/g, '' ); + str = str.replace( /<[^>]*>/g, '' ); } if ( config.trim ) { @@ -1524,14 +1576,15 @@ var _exportData = function ( dt, inOpts ) }; - var header = dt.columns( config.columns ).indexes().map( function (idx, i) { - return config.format.header( dt.column( idx ).header().innerHTML, idx ); + var header = dt.columns( config.columns ).indexes().map( function (idx) { + var el = dt.column( idx ).header(); + return config.format.header( el.innerHTML, idx, el ); } ).toArray(); var footer = dt.table().footer() ? - dt.columns( config.columns ).indexes().map( function (idx, i) { + dt.columns( config.columns ).indexes().map( function (idx) { var el = dt.column( idx ).footer(); - return config.format.footer( el ? el.innerHTML : '', idx ); + return config.format.footer( el ? el.innerHTML : '', idx, el ); } ).toArray() : null; @@ -1540,6 +1593,11 @@ var _exportData = function ( dt, inOpts ) .cells( rowIndexes, config.columns ) .render( config.orthogonal ) .toArray(); + var cellNodes = dt + .cells( rowIndexes, config.columns ) + .nodes() + .toArray(); + var columns = header.length; var rows = columns > 0 ? cells.length / columns : 0; var body = new Array( rows ); @@ -1549,7 +1607,7 @@ var _exportData = function ( dt, inOpts ) var row = new Array( columns ); for ( var j=0 ; j * @default [] */ - "aiInnerWidths": [] + "aiInnerWidths": [], + + + /** + * Is the document layout right-to-left + * @type boolean + */ + rtl: $(dtSettings.nTable).css('direction') === 'rtl' }; @@ -490,11 +497,24 @@ $.extend( FixedColumns.prototype , { /* Event handlers */ var mouseController; + var mouseDown = false; + + // When the mouse is down (drag scroll) the mouse controller cannot + // change, as the browser keeps the original element as the scrolling one + $(this.s.dt.nTableWrapper).on( 'mousedown.DTFC', function () { + mouseDown = true; + + $(document).one( 'mouseup', function () { + mouseDown = false; + } ); + } ); // When the body is scrolled - scroll the left and right columns $(this.dom.scroller) .on( 'mouseover.DTFC touchstart.DTFC', function () { - mouseController = 'main'; + if ( ! mouseDown ) { + mouseController = 'main'; + } } ) .on( 'scroll.DTFC', function (e) { if ( ! mouseController && e.originalEvent ) { @@ -519,7 +539,9 @@ $.extend( FixedColumns.prototype , { // When scrolling the left column, scroll the body and right column $(that.dom.grid.left.liner) .on( 'mouseover.DTFC touchstart.DTFC', function () { - mouseController = 'left'; + if ( ! mouseDown ) { + mouseController = 'left'; + } } ) .on( 'scroll.DTFC', function ( e ) { if ( ! mouseController && e.originalEvent ) { @@ -546,7 +568,9 @@ $.extend( FixedColumns.prototype , { // When scrolling the right column, scroll the body and the left column $(that.dom.grid.right.liner) .on( 'mouseover.DTFC touchstart.DTFC', function () { - mouseController = 'right'; + if ( ! mouseDown ) { + mouseController = 'right'; + } } ) .on( 'scroll.DTFC', function ( e ) { if ( ! mouseController && e.originalEvent ) { @@ -578,6 +602,7 @@ $.extend( FixedColumns.prototype , { jqTable .on( 'draw.dt.DTFC', function () { + that._fnColCalc(); that._fnDraw.call( that, bFirstDraw ); bFirstDraw = false; } ) @@ -592,16 +617,22 @@ $.extend( FixedColumns.prototype , { that._fnDraw( true ); } } ) + .on( 'select.dt.DTFC deselect.dt.DTFC', function ( e, dt, type, indexes ) { + if ( e.namespace === 'dt' ) { + that._fnDraw( false ); + } + } ) .on( 'destroy.dt.DTFC', function () { - jqTable.off( 'column-sizing.dt.DTFC column-visibility.dt.DTFC destroy.dt.DTFC draw.dt.DTFC' ); + jqTable.off( '.DTFC' ); - $(that.dom.scroller).off( 'mouseover.DTFC touchstart.DTFC scroll.DTFC' ); - $(window).off( 'resize.DTFC' ); + $(that.dom.scroller).off( '.DTFC' ); + $(window).off( '.DTFC' ); + $(that.s.dt.nTableWrapper).off( '.DTFC' ); - $(that.dom.grid.left.liner).off( 'mouseover.DTFC touchstart.DTFC scroll.DTFC '+wheelType ); + $(that.dom.grid.left.liner).off( '.DTFC '+wheelType ); $(that.dom.grid.left.wrapper).remove(); - $(that.dom.grid.right.liner).off( 'mouseover.DTFC touchstart.DTFC scroll.DTFC '+wheelType ); + $(that.dom.grid.right.liner).off( '.DTFC '+wheelType ); $(that.dom.grid.right.wrapper).remove(); } ); @@ -703,7 +734,7 @@ $.extend( FixedColumns.prototype , { ''+ '
'+ ''+ - '
'+ + '
'+ '
'+ '
'+ '
'+ @@ -740,6 +771,8 @@ $.extend( FixedColumns.prototype , { this.dom.grid.right.body = nRight.childNodes[1]; this.dom.grid.right.liner = $('div.DTFC_RightBodyLiner', nSWrapper)[0]; + nRight.style.right = oOverflow.bar+"px"; + block = $('div.DTFC_RightHeadBlocker', nSWrapper)[0]; block.style.width = oOverflow.bar+"px"; block.style.right = -oOverflow.bar+"px"; @@ -765,6 +798,14 @@ $.extend( FixedColumns.prototype , { this.dom.grid.right.foot = nRight.childNodes[2]; } } + + // RTL support - swap the position of the left and right columns (#48) + if ( this.s.rtl ) { + $('div.DTFC_RightHeadBlocker', nSWrapper).css( { + left: -oOverflow.bar+'px', + right: '' + } ); + } }, @@ -775,15 +816,16 @@ $.extend( FixedColumns.prototype , { */ "_fnGridLayout": function () { + var that = this; var oGrid = this.dom.grid; var iWidth = $(oGrid.wrapper).width(); var iBodyHeight = $(this.s.dt.nTable.parentNode).outerHeight(); var iFullHeight = $(this.s.dt.nTable.parentNode.parentNode).outerHeight(); var oOverflow = this._fnDTOverflow(); - var - iLeftWidth = this.s.iLeftWidth, - iRightWidth = this.s.iRightWidth, - iRight; + var iLeftWidth = this.s.iLeftWidth; + var iRightWidth = this.s.iRightWidth; + var rtl = $(this.dom.body).css('direction') === 'rtl'; + var wrapper; var scrollbarAdjust = function ( node, width ) { if ( ! oOverflow.bar ) { // If there is no scrollbar (Macs) we need to hide the auto scrollbar @@ -791,6 +833,12 @@ $.extend( FixedColumns.prototype , { node.style.paddingRight = "20px"; node.style.boxSizing = "border-box"; } + else if ( that._firefoxScrollError() ) { + // See the above function for why this is required + if ( $(node).height() > 34 ) { + node.style.width = (width+oOverflow.bar)+"px"; + } + } else { // Otherwise just overflow by the scrollbar node.style.width = (width+oOverflow.bar)+"px"; @@ -807,8 +855,21 @@ $.extend( FixedColumns.prototype , { if ( this.s.iLeftColumns > 0 ) { - oGrid.left.wrapper.style.width = iLeftWidth+"px"; - oGrid.left.wrapper.style.height = "1px"; + wrapper = oGrid.left.wrapper; + wrapper.style.width = iLeftWidth+'px'; + wrapper.style.height = '1px'; + + // Swap the position of the left and right columns for rtl (#48) + // This is always up against the edge, scrollbar on the far side + if ( rtl ) { + wrapper.style.left = ''; + wrapper.style.right = 0; + } + else { + wrapper.style.left = 0; + wrapper.style.right = ''; + } + oGrid.left.body.style.height = iBodyHeight+"px"; if ( oGrid.left.foot ) { oGrid.left.foot.style.top = (oOverflow.x ? oOverflow.bar : 0)+"px"; // shift footer for scrollbar @@ -820,15 +881,20 @@ $.extend( FixedColumns.prototype , { if ( this.s.iRightColumns > 0 ) { - iRight = iWidth - iRightWidth; - if ( oOverflow.y ) - { - iRight -= oOverflow.bar; + wrapper = oGrid.right.wrapper; + wrapper.style.width = iRightWidth+'px'; + wrapper.style.height = '1px'; + + // Need to take account of the vertical scrollbar + if ( this.s.rtl ) { + wrapper.style.left = oOverflow.y ? oOverflow.bar+'px' : 0; + wrapper.style.right = ''; + } + else { + wrapper.style.left = ''; + wrapper.style.right = oOverflow.y ? oOverflow.bar+'px' : 0; } - oGrid.right.wrapper.style.width = iRightWidth+"px"; - oGrid.right.wrapper.style.left = iRight+"px"; - oGrid.right.wrapper.style.height = "1px"; oGrid.right.body.style.height = iBodyHeight+"px"; if ( oGrid.right.foot ) { oGrid.right.foot.style.top = (oOverflow.x ? oOverflow.bar : 0)+"px"; @@ -1135,12 +1201,14 @@ $.extend( FixedColumns.prototype , { /* Add in the tbody elements, cloning form the master table */ $('>tbody>tr', that.dom.body).each( function (z) { - var n = this.cloneNode(false); - n.removeAttribute('id'); var i = that.s.dt.oFeatures.bServerSide===false ? that.s.dt.aiDisplay[ that.s.dt._iDisplayStart+z ] : z; var aTds = that.s.dt.aoData[ i ].anCells || $(this).children('td, th'); + var n = this.cloneNode(false); + n.removeAttribute('id'); + n.setAttribute( 'data-dt-row', i ); + for ( iIndex=0 ; iIndex 0 ) { nClone = $( aTds[iColumn] ).clone(true, true)[0]; + nClone.setAttribute( 'data-dt-row', i ); + nClone.setAttribute( 'data-dt-column', iIndex ); n.appendChild( nClone ); } } @@ -1314,6 +1384,41 @@ $.extend( FixedColumns.prototype , { anClone[i].style.height = heights[i]+"px"; anOriginal[i].style.height = heights[i]+"px"; } + }, + + /** + * Determine if the UA suffers from Firefox's overflow:scroll scrollbars + * not being shown bug. + * + * Firefox doesn't draw scrollbars, even if it is told to using + * overflow:scroll, if the div is less than 34px height. See bugs 292284 and + * 781885. Using UA detection here since this is particularly hard to detect + * using objects - its a straight up rendering error in Firefox. + * + * @return {boolean} True if Firefox error is present, false otherwise + */ + _firefoxScrollError: function () { + if ( _firefoxScroll === undefined ) { + var test = $('
') + .css( { + position: 'absolute', + top: 0, + left: 0, + height: 10, + width: 50, + overflow: 'scroll' + } ) + .appendTo( 'body' ); + + // Make sure this doesn't apply on Macs with 0 width scrollbars + _firefoxScroll = ( + test[0].clientWidth === test[0].offsetWidth && this._fnDTOverflow().bar !== 0 + ); + + test.remove(); + } + + return _firefoxScroll; } } ); @@ -1411,7 +1516,7 @@ FixedColumns.defaults = /** @lends FixedColumns.defaults */{ * @default See code * @static */ -FixedColumns.version = "3.2.0"; +FixedColumns.version = "3.2.2"; @@ -1515,4 +1620,4 @@ $.fn.dataTable.FixedColumns = FixedColumns; $.fn.DataTable.FixedColumns = FixedColumns; return FixedColumns; -})); \ No newline at end of file +})); diff --git a/app/assets/javascripts/dataTables/extras/dataTables.fixedHeader.js b/app/assets/javascripts/dataTables/extras/dataTables.fixedHeader.js index 636697f..7c236da 100755 --- a/app/assets/javascripts/dataTables/extras/dataTables.fixedHeader.js +++ b/app/assets/javascripts/dataTables/extras/dataTables.fixedHeader.js @@ -1,16 +1,16 @@ -/*! FixedHeader 3.1.0 - * ©2009-2015 SpryMedia Ltd - datatables.net/license +/*! FixedHeader 3.1.2 + * ©2009-2016 SpryMedia Ltd - datatables.net/license */ /** * @summary FixedHeader * @description Fix a table's header or footer, so it is always visible while * scrolling - * @version 3.1.0 + * @version 3.1.2 * @file dataTables.fixedHeader.js * @author SpryMedia Ltd (www.sprymedia.co.uk) * @contact www.sprymedia.co.uk/contact - * @copyright Copyright 2009-2015 SpryMedia Ltd. + * @copyright Copyright 2009-2016 SpryMedia Ltd. * * This source file is free software, available under the following license: * MIT license - http://datatables.net/license/mit @@ -220,7 +220,17 @@ $.extend( FixedHeader.prototype, { that.update(); } ); - dt.on( 'column-reorder.dt.dtfc column-visibility.dt.dtfc draw.dt.dtfc', function () { + var autoHeader = $('.fh-fixedHeader'); + if ( ! this.c.headerOffset && autoHeader.length ) { + this.c.headerOffset = autoHeader.outerHeight(); + } + + var autoFooter = $('.fh-fixedFooter'); + if ( ! this.c.footerOffset && autoFooter.length ) { + this.c.footerOffset = autoFooter.outerHeight(); + } + + dt.on( 'column-reorder.dt.dtfc column-visibility.dt.dtfc draw.dt.dtfc column-sizing.dt.dtfc', function () { that.update(); } ); @@ -263,18 +273,20 @@ $.extend( FixedHeader.prototype, { else { if ( itemDom.floating ) { itemDom.placeholder.remove(); + this._unsize( item ); itemDom.floating.children().detach(); itemDom.floating.remove(); } itemDom.floating = $( dt.table().node().cloneNode( false ) ) + .css( 'table-layout', 'fixed' ) .removeAttr( 'id' ) .append( itemElement ) .appendTo( 'body' ); // Insert a fake thead/tfoot into the DataTable to stop it jumping around itemDom.placeholder = itemElement.clone( false ); - itemDom.host.append( itemDom.placeholder ); + itemDom.host.prepend( itemDom.placeholder ); // Clone widths this._matchWidths( itemDom.placeholder, itemDom.floating ); @@ -293,19 +305,27 @@ $.extend( FixedHeader.prototype, { * @private */ _matchWidths: function ( from, to ) { - var type = function ( name ) { - var toWidths = $(name, from) + var get = function ( name ) { + return $(name, from) .map( function () { return $(this).width(); } ).toArray(); + }; + var set = function ( name, toWidths ) { $(name, to).each( function ( i ) { - $(this).width( toWidths[i] ).css("min-width", toWidths[i] ); + $(this).css( { + width: toWidths[i], + minWidth: toWidths[i] + } ); } ); }; - type( 'th' ); - type( 'td' ); + var thWidths = get( 'th' ); + var tdWidths = get( 'td' ); + + set( 'th', thWidths ); + set( 'td', tdWidths ); }, /** @@ -321,7 +341,13 @@ $.extend( FixedHeader.prototype, { var el = this.dom[ item ].floating; if ( el && (item === 'footer' || (item === 'header' && ! this.s.autoWidth)) ) { - $('th, td', el).css( 'width', '' ); + $('th, td', el).css( { + width: '', + minWidth: '' + } ); + } + else if ( el && item === 'header' ) { + $('th, td', el).css( 'min-width', '' ); } }, @@ -367,6 +393,13 @@ $.extend( FixedHeader.prototype, { var itemDom = this.dom[ item ]; var position = this.s.position; + // Record focus. Browser's will cause input elements to loose focus if + // they are inserted else where in the doc + var tablePart = this.dom[ item==='footer' ? 'tfoot' : 'thead' ]; + var focus = $.contains( tablePart[0], document.activeElement ) ? + document.activeElement : + null; + if ( mode === 'in-place' ) { // Insert the header back into the table's real header if ( itemDom.placeholder ) { @@ -376,10 +409,12 @@ $.extend( FixedHeader.prototype, { this._unsize( item ); - itemDom.host.append( item === 'header' ? - this.dom.thead : - this.dom.tfoot - ); + if ( item === 'header' ) { + itemDom.host.prepend( this.dom.thead ); + } + else { + itemDom.host.append( this.dom.tfoot ); + } if ( itemDom.floating ) { itemDom.floating.remove(); @@ -422,6 +457,11 @@ $.extend( FixedHeader.prototype, { .css( 'width', position.width+'px' ); } + // Restore focus if it was lost + if ( focus && focus !== document.activeElement ) { + focus.focus(); + } + this.s.scrollLeft.header = -1; this.s.scrollLeft.footer = -1; this.s[item+'Mode'] = mode; @@ -531,7 +571,7 @@ $.extend( FixedHeader.prototype, { * @type {String} * @static */ -FixedHeader.version = "3.1.0"; +FixedHeader.version = "3.1.2"; /** * Defaults @@ -557,15 +597,20 @@ $.fn.DataTable.FixedHeader = FixedHeader; // DataTables creation - check if the FixedHeader option has been defined on the // table and if so, initialise -$(document).on( 'init.dt.dtb', function (e, settings, json) { +$(document).on( 'init.dt.dtfh', function (e, settings, json) { if ( e.namespace !== 'dt' ) { return; } - var opts = settings.oInit.fixedHeader || DataTable.defaults.fixedHeader; + var init = settings.oInit.fixedHeader; + var defaults = DataTable.defaults.fixedHeader; - if ( opts && ! settings._fixedHeader ) { - new FixedHeader( settings, opts ); + if ( (init || defaults) && ! settings._fixedHeader ) { + var opts = $.extend( {}, defaults, init ); + + if ( init !== false ) { + new FixedHeader( settings, opts ); + } } } ); diff --git a/app/assets/javascripts/dataTables/extras/dataTables.keyTable.js b/app/assets/javascripts/dataTables/extras/dataTables.keyTable.js index d746de2..4920731 100755 --- a/app/assets/javascripts/dataTables/extras/dataTables.keyTable.js +++ b/app/assets/javascripts/dataTables/extras/dataTables.keyTable.js @@ -1,15 +1,15 @@ -/*! KeyTable 2.1.0 - * ©2009-2015 SpryMedia Ltd - datatables.net/license +/*! KeyTable 2.1.3 + * ©2009-2016 SpryMedia Ltd - datatables.net/license */ /** * @summary KeyTable * @description Spreadsheet like keyboard navigation for DataTables - * @version 2.1.0 + * @version 2.1.3 * @file dataTables.keyTable.js * @author SpryMedia Ltd (www.sprymedia.co.uk) * @contact www.sprymedia.co.uk/contact - * @copyright Copyright 2009-2015 SpryMedia Ltd. + * @copyright Copyright 2009-2016 SpryMedia Ltd. * * This source file is free software, available under the following license: * MIT license - http://datatables.net/license/mit @@ -69,7 +69,10 @@ var KeyTable = function ( dt, opts ) { /** @type {DataTable.Api} DataTables' API instance */ dt: new DataTable.Api( dt ), - enable: true + enable: true, + + /** @type {bool} Flag for if a draw is triggered by focus */ + focusDraw: false }; // DOM items @@ -93,7 +96,7 @@ $.extend( KeyTable.prototype, { /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * API methods for DataTables API interface */ - + /** * Blur the table's cell focus */ @@ -163,7 +166,7 @@ $.extend( KeyTable.prototype, { } // Click to focus - $( dt.table().body() ).on( 'click.keyTable', 'th, td', function () { + $( dt.table().body() ).on( 'click.keyTable', 'th, td', function (e) { if ( that.s.enable === false ) { return; } @@ -174,17 +177,17 @@ $.extend( KeyTable.prototype, { return; } - that._focus( cell ); + that._focus( cell, null, false, e ); } ); // Key events - $( document.body ).on( 'keydown.keyTable', function (e) { + $( document ).on( 'keydown.keyTable', function (e) { that._key( e ); } ); // Click blur if ( this.c.blurable ) { - $( document.body ).on( 'click.keyTable', function ( e ) { + $( document ).on( 'click.keyTable', function ( e ) { // Click on the search input will blur focus if ( $(e.target).parents( '.dataTables_filter' ).length ) { that._blur(); @@ -200,12 +203,17 @@ $.extend( KeyTable.prototype, { return; } + //If the click was inside the fixed columns container, don't blur + if ( $(e.target).parents().filter('.DTFC_Cloned').length ) { + return; + } + that._blur(); } ); } if ( this.c.editor ) { - dt.on( 'key.kt', function ( e, dt, key, cell, orig ) { + dt.on( 'key.keyTable', function ( e, dt, key, cell, orig ) { that._editor( key, orig ); } ); } @@ -219,6 +227,27 @@ $.extend( KeyTable.prototype, { } ); } + // Reload - re-focus on the currently selected item. In SSP mode this + // has the effect of keeping the focus in position when changing page as + // well (which is different from how client-side processing works). + dt.on( 'xhr.keyTable', function ( e ) { + if ( that.s.focusDraw ) { + // Triggered by server-side processing, and thus `_focus` will + // do the refocus on the next draw event + return; + } + + var lastFocus = that.s.lastFocus; + + if ( lastFocus ) { + that.s.lastFocus = null; + + dt.one( 'draw', function () { + that._focus( lastFocus ); + } ); + } + } ); + dt.on( 'destroy.keyTable', function () { dt.off( '.keyTable' ); $( dt.table().body() ).off( 'click.keyTable', 'th, td' ); @@ -231,7 +260,15 @@ $.extend( KeyTable.prototype, { var state = dt.state.loaded(); if ( state && state.keyTable ) { - dt.cell( state.keyTable ).focus(); + // Wait until init is done + dt.one( 'init', function () { + var cell = dt.cell( state.keyTable ); + + // Ensure that the saved cell still exists + if ( cell.any() ) { + cell.focus(); + } + } ); } else if ( this.c.focus ) { dt.cell( this.c.focus ).focus(); @@ -261,6 +298,8 @@ $.extend( KeyTable.prototype, { $( cell.node() ).removeClass( this.c.className ); this.s.lastFocus = null; + this._updateFixedColumns(cell.index().column); + this._emitEvent( 'key-blur', [ this.s.dt, cell ] ); }, @@ -302,16 +341,19 @@ $.extend( KeyTable.prototype, { orig.stopPropagation(); + // Return key should do nothing - for textareas's it would empty the + // contents + if ( key === 13 ) { + orig.preventDefault(); + } + editor.inline( this.s.lastFocus.index() ); // Excel style - select all text - var input = $('div.DTE input'); - if ( input.length ) { - input[0].select(); - } + $('div.DTE input, div.DTE textarea').select(); // Reduce the keys the Keys listens for - dt.keys.enable( 'navigation-only' ); + dt.keys.enable( this.c.editorKeys ); // On blur of the navigation submit dt.one( 'key-blur.editor', function () { @@ -352,14 +394,17 @@ $.extend( KeyTable.prototype, { * @param {integer} [column] Not required if a cell is given as the first * parameter. Otherwise this is the column data index for the cell to * focus on + * @param {boolean} [shift=true] Should the viewport be moved to show cell * @private */ - _focus: function ( row, column ) + _focus: function ( row, column, shift, originalEvent ) { var that = this; var dt = this.s.dt; var pageInfo = dt.page.info(); var lastFocus = this.s.lastFocus; + if( ! originalEvent) + originalEvent = null; if ( ! this.s.enable ) { return; @@ -385,8 +430,11 @@ $.extend( KeyTable.prototype, { // Is the row on the current page? If not, we need to redraw to show the // page if ( pageInfo.length !== -1 && (row < pageInfo.start || row >= pageInfo.start+pageInfo.length) ) { + this.s.focusDraw = true; + dt .one( 'draw', function () { + that.s.focusDraw = false; that._focus( row, column ); } ) .page( Math.floor( row / pageInfo.length ) ) @@ -421,20 +469,24 @@ $.extend( KeyTable.prototype, { var node = $( cell.node() ); node.addClass( this.c.className ); + this._updateFixedColumns(column); + // Shift viewpoint and page to make cell visible - this._scroll( $(window), $(document.body), node, 'offset' ); + if ( shift === undefined || shift === true ) { + this._scroll( $(window), $(document.body), node, 'offset' ); - var bodyParent = dt.table().body().parentNode; - if ( bodyParent !== dt.table().header().parentNode ) { - var parent = $(bodyParent.parentNode); + var bodyParent = dt.table().body().parentNode; + if ( bodyParent !== dt.table().header().parentNode ) { + var parent = $(bodyParent.parentNode); - this._scroll( parent, parent, node, 'position' ); + this._scroll( parent, parent, node, 'position' ); + } } // Event and finish this.s.lastFocus = cell; - this._emitEvent( 'key-focus', [ this.s.dt, cell ] ); + this._emitEvent( 'key-focus', [ this.s.dt, cell, originalEvent || null ] ); dt.state.save(); }, @@ -447,7 +499,9 @@ $.extend( KeyTable.prototype, { */ _key: function ( e ) { - if ( ! this.s.enable ) { + var enable = this.s.enable; + var navEnable = enable === true || enable === 'navigation-only'; + if ( ! enable ) { return; } @@ -471,62 +525,75 @@ $.extend( KeyTable.prototype, { switch( e.keyCode ) { case 9: // tab + // `enable` can be tab-only this._shift( e, e.shiftKey ? 'left' : 'right', true ); break; case 27: // esc - if ( this.s.blurable && this.s.enable === true ) { + if ( this.s.blurable && enable === true ) { this._blur(); } break; case 33: // page up (previous page) case 34: // page down (next page) - e.preventDefault(); - var index = dt.cells( {page: 'current'} ).nodes().indexOf( cell.node() ); - - dt - .one( 'draw', function () { - var nodes = dt.cells( {page: 'current'} ).nodes(); - - that._focus( dt.cell( index < nodes.length ? - nodes[ index ] : - nodes[ nodes.length-1 ] - ) ); - } ) - .page( e.keyCode === 33 ? 'previous' : 'next' ) - .draw( false ); + if ( navEnable ) { + e.preventDefault(); + var index = dt.cells( {page: 'current'} ).nodes().indexOf( cell.node() ); + + dt + .one( 'draw', function () { + var nodes = dt.cells( {page: 'current'} ).nodes(); + + that._focus( dt.cell( index < nodes.length ? + nodes[ index ] : + nodes[ nodes.length-1 ] + ) , null, true, e); + } ) + .page( e.keyCode === 33 ? 'previous' : 'next' ) + .draw( false ); + } break; case 35: // end (end of current page) case 36: // home (start of current page) - e.preventDefault(); - var indexes = dt.cells( {page: 'current'} ).indexes(); + if ( navEnable ) { + e.preventDefault(); + var indexes = dt.cells( {page: 'current'} ).indexes(); - this._focus( dt.cell( - indexes[ e.keyCode === 35 ? indexes.length-1 : 0 ] - ) ); + this._focus( dt.cell( + indexes[ e.keyCode === 35 ? indexes.length-1 : 0 ] + ), null, true, e ); + } break; case 37: // left arrow - this._shift( e, 'left' ); + if ( navEnable ) { + this._shift( e, 'left' ); + } break; case 38: // up arrow - this._shift( e, 'up' ); + if ( navEnable ) { + this._shift( e, 'up' ); + } break; case 39: // right arrow - this._shift( e, 'right' ); + if ( navEnable ) { + this._shift( e, 'right' ); + } break; case 40: // down arrow - this._shift( e, 'down' ); + if ( navEnable ) { + this._shift( e, 'down' ); + } break; default: // Everything else - pass through only when fully enabled - if ( this.s.enable === true ) { + if ( enable === true ) { this._emitEvent( 'key', [ dt, e.keyCode, this.s.lastFocus, e ] ); } break; @@ -567,12 +634,12 @@ $.extend( KeyTable.prototype, { } // Bottom correction - if ( offset.top + height > scrollTop + containerHeight ) { + if ( offset.top + height > scrollTop + containerHeight && height < containerHeight ) { scroller.scrollTop( offset.top + height - containerHeight ); } // Right correction - if ( offset.left + width > scrollLeft + containerWidth ) { + if ( offset.left + width > scrollLeft + containerWidth && width < containerWidth ) { scroller.scrollLeft( offset.left + width - containerWidth ); } }, @@ -652,7 +719,7 @@ $.extend( KeyTable.prototype, { ) { e.preventDefault(); - this._focus( row, column ); + this._focus( row, column, true, e ); } else if ( ! keyBlurable || ! this.c.blurable ) { // No new focus, but if the table isn't blurable, then don't loose @@ -692,10 +759,27 @@ $.extend( KeyTable.prototype, { } ) .insertBefore( dt.table().node() ); - div.children().on( 'focus', function () { - that._focus( dt.cell(':eq(0)', {page: 'current'}) ); + div.children().on( 'focus', function (e) { + that._focus( dt.cell(':eq(0)', '0:visible', {page: 'current'}), null, true, e ); } ); - } + }, + /** + * Update fixed columns if they are enabled and if the cell we are focusing is inside a fixed column + * @param {integer} column Index of the column being changed + * + * @private + */ + _updateFixedColumns:function(column){ + var dt = this.s.dt; + var settings = dt.settings()[0]; + + if(settings._oFixedColumns){ + var leftCols = settings._oFixedColumns.s.iLeftColumns; + var rightCols = settings.aoColumns.length - settings._oFixedColumns.s.iRightColumns; + if (column < leftCols || column > rightCols) + dt.fixedColumns().update(); + } + } } ); @@ -732,6 +816,13 @@ KeyTable.defaults = { */ editor: null, + /** + * Option that defines what KeyTable's behaviour will be when used with + * Editor's inline editing. Can be `navigation-only` or `tab-only`. + * @type {String} + */ + editorKeys: 'navigation-only', + /** * Select a cell to automatically select on start up. `null` for no * automatic selection @@ -754,7 +845,7 @@ KeyTable.defaults = { -KeyTable.version = "2.1.0"; +KeyTable.version = "2.1.3"; $.fn.dataTable.KeyTable = KeyTable; @@ -826,7 +917,7 @@ $(document).on( 'preInit.dt.dtk', function (e, settings, json) { var defaults = DataTable.defaults.keys; if ( init || defaults ) { - var opts = $.extend( {}, init, defaults ); + var opts = $.extend( {}, defaults, init ); if ( init !== false ) { new KeyTable( settings, opts ); diff --git a/app/assets/javascripts/dataTables/extras/dataTables.responsive.js b/app/assets/javascripts/dataTables/extras/dataTables.responsive.js index 0437e70..0400668 100755 --- a/app/assets/javascripts/dataTables/extras/dataTables.responsive.js +++ b/app/assets/javascripts/dataTables/extras/dataTables.responsive.js @@ -1,15 +1,15 @@ -/*! Responsive 2.0.0 - * 2014-2015 SpryMedia Ltd - datatables.net/license +/*! Responsive 2.1.0 + * 2014-2016 SpryMedia Ltd - datatables.net/license */ /** * @summary Responsive * @description Responsive tables plug-in for DataTables - * @version 2.0.0 + * @version 2.1.0 * @file dataTables.responsive.js * @author SpryMedia Ltd (www.sprymedia.co.uk) * @contact www.sprymedia.co.uk/contact - * @copyright Copyright 2014-2015 SpryMedia Ltd. + * @copyright Copyright 2014-2016 SpryMedia Ltd. * * This source file is free software, available under the following license: * MIT license - http://datatables.net/license/mit @@ -113,9 +113,16 @@ var Responsive = function ( settings, opts ) { } // details is an object, but for simplicity the user can give it as a string + // or a boolean if ( opts && typeof opts.details === 'string' ) { opts.details = { type: opts.details }; } + else if ( opts && opts.details === false ) { + opts.details = { type: false }; + } + else if ( opts && opts.details === true ) { + opts.details = { type: 'inline' }; + } this.c = $.extend( true, {}, Responsive.defaults, DataTable.defaults.responsive, opts ); settings.responsive = this; @@ -137,13 +144,21 @@ $.extend( Responsive.prototype, { var that = this; var dt = this.s.dt; var dtPrivateSettings = dt.settings()[0]; + var oldWindowWidth = $(window).width(); dt.settings()[0]._responsive = this; // Use DataTables' throttle function to avoid processor thrashing on // resize $(window).on( 'resize.dtr orientationchange.dtr', DataTable.util.throttle( function () { - that._resize(); + // iOS has a bug whereby resize can fire when only scrolling + // See: http://stackoverflow.com/questions/8898412 + var width = $(window).width(); + + if ( width !== oldWindowWidth ) { + that._resize(); + oldWindowWidth = width; + } } ) ); // DataTables doesn't currently trigger an event when a row is added, so @@ -187,6 +202,7 @@ $.extend( Responsive.prototype, { // Details handler var details = this.c.details; + if ( details.type !== false ) { that._detailsInit(); @@ -209,11 +225,25 @@ $.extend( Responsive.prototype, { } dt.on( 'column-reorder.dtr', function (e, settings, details) { - // This requires ColReorder 1.2.1 or newer - if ( details.drop ) { - that._classLogic(); - that._resizeAuto(); - that._resize(); + that._classLogic(); + that._resizeAuto(); + that._resize(); + } ); + + // Change in column sizes means we need to calc + dt.on( 'column-sizing.dtr', function () { + that._resizeAuto(); + that._resize(); + }); + + dt.on( 'init.dtr', function (e, settings, details) { + that._resizeAuto(); + that._resize(); + + // If columns were hidden, then DataTables needs to adjust the + // column sizing + if ( $.inArray( false, that.s.current ) ) { + dt.columns.adjust(); } } ); @@ -261,7 +291,6 @@ $.extend( Responsive.prototype, { return a.columnIdx - b.columnIdx; } ); - // Class logic - determine which columns are in this breakpoint based // on the classes. If no class control (i.e. `auto`) then `-` is used // to indicate this to the rest of the function @@ -375,8 +404,10 @@ $.extend( Responsive.prototype, { var priority = dt.settings()[0].aoColumns[i].responsivePriority; if ( priority === undefined ) { - priority = $(column.header).data('priority') !== undefined ? - $(column.header).data('priority') * 1 : + var dataPriority = $(column.header()).data('priority'); + + priority = dataPriority !== undefined ? + dataPriority * 1 : 10000; } @@ -509,15 +540,18 @@ $.extend( Responsive.prototype, { { var that = this; var dt = this.s.dt; + var details = this.c.details; - var res = this.c.details.display( row, update, function () { - return that.c.details.renderer( - dt, row[0], that._detailsObj(row[0]) - ); - } ); + if ( details && details.type !== false ) { + var res = details.display( row, update, function () { + return details.renderer( + dt, row[0], that._detailsObj(row[0]) + ); + } ); - if ( res === true || res === false ) { - $(dt.table().node()).triggerHandler( 'responsive-display.dt', [dt, row, res, update] ); + if ( res === true || res === false ) { + $(dt.table().node()).triggerHandler( 'responsive-display.dt', [dt, row, res, update] ); + } } }, @@ -535,7 +569,7 @@ $.extend( Responsive.prototype, { // The inline type always uses the first child as the target if ( details.type === 'inline' ) { - details.target = 'td:first-child'; + details.target = 'td:first-child, th:first-child'; } // Keyboard accessibility @@ -544,7 +578,7 @@ $.extend( Responsive.prototype, { } ); that._tabIndexes(); // Initial draw has already happened - $( dt.table().body() ).on( 'keyup.dtr', 'td', function (e) { + $( dt.table().body() ).on( 'keyup.dtr', 'td, th', function (e) { if ( e.keyCode === 13 && $(this).data('dtr-keyboard') ) { $(this).click(); } @@ -552,15 +586,11 @@ $.extend( Responsive.prototype, { // type.target can be a string jQuery selector or a column index var target = details.target; - var selector = typeof target === 'string' ? target : 'td'; + var selector = typeof target === 'string' ? target : 'td, th'; // Click handler to show / hide the details rows when they are available $( dt.table().body() ) - .on( 'mousedown.dtr', selector, function (e) { - // For mouse users, prevent the focus ring from showing - e.preventDefault(); - } ) - .on( 'click.dtr', selector, function () { + .on( 'click.dtr mousedown.dtr mouseup.dtr', selector, function (e) { // If the table is not collapsed (i.e. there is no hidden columns) // then take no action if ( ! $(dt.table().node()).hasClass('collapsed' ) ) { @@ -587,10 +617,21 @@ $.extend( Responsive.prototype, { // $().closest() includes itself in its check var row = dt.row( $(this).closest('tr') ); - // The renderer is given as a function so the caller can execute it - // only when they need (i.e. if hiding there is no point is running - // the renderer) - that._detailsDisplay( row, false ); + // Check event type to do an action + if ( e.type === 'click' ) { + // The renderer is given as a function so the caller can execute it + // only when they need (i.e. if hiding there is no point is running + // the renderer) + that._detailsDisplay( row, false ); + } + else if ( e.type === 'mousedown' ) { + // For mouse users, prevent the focus ring from showing + $(this).css('outline', 'none'); + } + else if ( e.type === 'mouseup' ) { + // And then re-allow at the end of the click + $(this).blur().css('outline', ''); + } } ); }, @@ -606,14 +647,17 @@ $.extend( Responsive.prototype, { var dt = this.s.dt; return $.map( this.s.columns, function( col, i ) { - if ( col.never ) { + // Never and control columns should not be passed to the renderer + if ( col.never || col.control ) { return; } return { - title: dt.settings()[0].aoColumns[ i ].sTitle, - data: dt.cell( rowIdx, i ).render( that.c.orthogonal ), - hidden: dt.column( i ).visible() && !that.s.current[ i ] + title: dt.settings()[0].aoColumns[ i ].sTitle, + data: dt.cell( rowIdx, i ).render( that.c.orthogonal ), + hidden: dt.column( i ).visible() && !that.s.current[ i ], + columnIndex: i, + rowIndex: rowIdx }; } ); }, @@ -692,7 +736,7 @@ $.extend( Responsive.prototype, { // any columns that are not visible but can be shown var collapsedClass = false; for ( i=0, ien=columns.length ; i') .css( { width: 1, @@ -961,12 +1013,6 @@ Responsive.display = { ) .appendTo( 'body' ); - if ( options && options.header ) { - modal.find( 'div.dtr-modal-content' ).prepend( - '

'+options.header( row )+'

' - ); - } - $(document).on( 'keyup.dtr', function (e) { if ( e.keyCode === 27 ) { e.stopPropagation(); @@ -980,11 +1026,65 @@ Responsive.display = { .empty() .append( render() ); } + + if ( options && options.header ) { + $('div.dtr-modal-content').prepend( + '

'+options.header( row )+'

' + ); + } }; } }; +/** + * Display methods - functions which define how the hidden data should be shown + * in the table. + * + * @namespace + * @name Responsive.defaults + * @static + */ +Responsive.renderer = { + listHidden: function () { + return function ( api, rowIdx, columns ) { + var data = $.map( columns, function ( col ) { + return col.hidden ? + '
  • '+ + ''+ + col.title+ + ' '+ + ''+ + col.data+ + ''+ + '
  • ' : + ''; + } ).join(''); + + return data ? + $('
      ').append( data ) : + false; + } + }, + + tableAll: function ( options ) { + options = $.extend( { + tableClass: '' + }, options ); + + return function ( api, rowIdx, columns ) { + var data = $.map( columns, function ( col ) { + return ''+ + ''+col.title+':'+' '+ + ''+col.data+''+ + ''; + } ).join(''); + + return $('').append( data ); + } + } +}; + /** * Responsive default settings for initialisation * @@ -1033,24 +1133,7 @@ Responsive.defaults = { details: { display: Responsive.display.childRow, - renderer: function ( api, rowIdx, columns ) { - var data = $.map( columns, function ( col, i ) { - return col.hidden ? - '
    • '+ - ''+ - col.title+ - ' '+ - ''+ - col.data+ - ''+ - '
    • ' : - ''; - } ).join(''); - - return data ? - $('
        ').append( data ) : - false; - }, + renderer: Responsive.renderer.listHidden(), target: 0, @@ -1118,7 +1201,7 @@ Api.register( 'responsive.hasHidden()', function () { * @name Responsive.version * @static */ -Responsive.version = '2.0.0'; +Responsive.version = '2.1.0'; $.fn.dataTable.Responsive = Responsive; @@ -1126,7 +1209,7 @@ $.fn.DataTable.Responsive = Responsive; // Attach a listener to the document which listens for DataTables initialisation // events so we can automatically initialise -$(document).on( 'init.dt.dtr', function (e, settings, json) { +$(document).on( 'preInit.dt.dtr', function (e, settings, json) { if ( e.namespace !== 'dt' ) { return; } diff --git a/app/assets/javascripts/dataTables/extras/dataTables.rowReorder.js b/app/assets/javascripts/dataTables/extras/dataTables.rowReorder.js old mode 100755 new mode 100644 diff --git a/app/assets/javascripts/dataTables/extras/dataTables.scroller.js b/app/assets/javascripts/dataTables/extras/dataTables.scroller.js index 354c59f..6d9f98f 100755 --- a/app/assets/javascripts/dataTables/extras/dataTables.scroller.js +++ b/app/assets/javascripts/dataTables/extras/dataTables.scroller.js @@ -1,15 +1,15 @@ -/*! Scroller 1.4.0 - * ©2011-2015 SpryMedia Ltd - datatables.net/license +/*! Scroller 1.4.2 + * ©2011-2016 SpryMedia Ltd - datatables.net/license */ /** * @summary Scroller * @description Virtual rendering for DataTables - * @version 1.4.0 + * @version 1.4.2 * @file dataTables.scroller.js * @author SpryMedia Ltd (www.sprymedia.co.uk) * @contact www.sprymedia.co.uk/contact - * @copyright Copyright 2011-2015 SpryMedia Ltd. + * @copyright Copyright 2011-2016 SpryMedia Ltd. * * This source file is free software, available under the following license: * MIT license - http://datatables.net/license/mit @@ -431,9 +431,11 @@ $.extend( Scroller.prototype, { var heights = this.s.heights; - heights.viewport = $(this.dom.scroller).height(); - this.s.viewportRows = parseInt( heights.viewport / heights.row, 10 )+1; - this.s.dt._iDisplayLength = this.s.viewportRows * this.s.displayBuffer; + if ( heights.row ) { + heights.viewport = $(this.dom.scroller).height(); + this.s.viewportRows = parseInt( heights.viewport / heights.row, 10 )+1; + this.s.dt._iDisplayLength = this.s.viewportRows * this.s.displayBuffer; + } if ( bRedraw === undefined || bRedraw ) { @@ -442,6 +444,30 @@ $.extend( Scroller.prototype, { }, + /** + * Get information about current displayed record range. This corresponds to + * the information usually displayed in the "Info" block of the table. + * + * @returns {object} info as an object: + * { + * start: {int}, // the 0-indexed record at the top of the viewport + * end: {int}, // the 0-indexed record at the bottom of the viewport + * } + */ + "fnPageInfo": function() + { + var + dt = this.s.dt, + iScrollTop = this.dom.scroller.scrollTop, + iTotal = dt.fnRecordsDisplay(), + iPossibleEnd = Math.ceil(this.fnPixelsToRow(iScrollTop + this.s.heights.viewport, false, this.s.ani)); + + return { + start: Math.floor(this.fnPixelsToRow(iScrollTop, false, this.s.ani)), + end: iTotal < iPossibleEnd ? iTotal-1 : iPossibleEnd-1 + }; + }, + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Private methods (they are of course public in JS, but recommended as private) @@ -556,7 +582,11 @@ $.extend( Scroller.prototype, { this.s.topRowFloat = this.s.dt.oLoadedState.iScrollerTopRow || 0; } - $(this.s.dt.nTable).on( 'init.dt', function () { + // Measure immediately. Scroller will have been added using preInit, so + // we can reliably do this here. We could potentially also measure on + // init complete, which would be useful for cases where the data is Ajax + // loaded and longer than a single line. + $(this.s.dt.nTable).one( 'init.dt', function () { that.fnMeasure(); } ); @@ -631,7 +661,7 @@ $.extend( Scroller.prototype, { if ( Math.abs( iScrollTop - this.s.lastScrollTop ) > heights.viewport || this.s.ani ) { iTopRow = parseInt(this._domain( 'physicalToVirtual', iScrollTop ) / heights.row, 10) - preRows; - this.s.topRowFloat = (this._domain( 'physicalToVirtual', iScrollTop ) / heights.row); + this.s.topRowFloat = this._domain( 'physicalToVirtual', iScrollTop ) / heights.row; } else { iTopRow = this.fnPixelsToRow( iScrollTop ) - preRows; @@ -687,6 +717,9 @@ $.extend( Scroller.prototype, { } } } + else { + this.s.topRowFloat = this._domain( 'physicalToVirtual', iScrollTop ) / heights.row; + } this.s.lastScrollTop = iScrollTop; this.s.stateSaveThrottle(); @@ -854,10 +887,12 @@ $.extend( Scroller.prototype, { // Because of the order of the DT callbacks, the info update will // take precedence over the one we want here. So a 'thread' break is - // needed - setTimeout( function () { - that._fnInfo.call( that ); - }, 0 ); + // needed. Only add the thread break if bInfo is set + if ( this.s.dt.oFeatures.bInfo ) { + setTimeout( function () { + that._fnInfo.call( that ); + }, 0 ); + } // Hide the loading indicator if ( this.dom.loader && this.s.loaderVisible ) { @@ -930,7 +965,13 @@ $.extend( Scroller.prototype, { $('div.'+dt.oClasses.sScrollBody, container).append( nTable ); // If initialised using `dom`, use the holding element as the insert point - container.appendTo( this.s.dt.nHolding || origTable.parentNode ); + var insertEl = this.s.dt.nHolding || origTable.parentNode; + + if ( ! $(insertEl).is(':visible') ) { + insertEl = 'body'; + } + + container.appendTo( insertEl ); this.s.heights.row = $('tr', tbody).eq(1).outerHeight(); container.remove(); @@ -1019,6 +1060,9 @@ $.extend( Scroller.prototype, { $(n[i]).html( sOut ); } } + + // DT doesn't actually (yet) trigger this event, but it will in future + $(dt.nTable).triggerHandler( 'info.dt' ); } } ); @@ -1173,7 +1217,7 @@ Scroller.oDefaults = Scroller.defaults; * @name Scroller.version * @static */ -Scroller.version = "1.4.0"; +Scroller.version = "1.4.2"; @@ -1292,6 +1336,14 @@ Api.register( 'scroller.measure()', function ( redraw ) { return this; } ); +Api.register( 'scroller.page()', function() { + var ctx = this.context; + + if ( ctx.length && ctx[0].oScroller ) { + return ctx[0].oScroller.fnPageInfo(); + } + // undefined +} ); return Scroller; })); diff --git a/app/assets/javascripts/dataTables/extras/dataTables.select.js b/app/assets/javascripts/dataTables/extras/dataTables.select.js index fe73851..0e04ee2 100755 --- a/app/assets/javascripts/dataTables/extras/dataTables.select.js +++ b/app/assets/javascripts/dataTables/extras/dataTables.select.js @@ -1,16 +1,16 @@ -/*! Select for DataTables 1.1.0 - * 2015 SpryMedia Ltd - datatables.net/license/mit +/*! Select for DataTables 1.2.0 + * 2015-2016 SpryMedia Ltd - datatables.net/license/mit */ /** * @summary Select for DataTables * @description A collection of API methods, events and buttons for DataTables * that provides selection options of the items in a DataTable - * @version 1.1.0 + * @version 1.2.0 * @file dataTables.select.js * @author SpryMedia Ltd (www.sprymedia.co.uk) * @contact datatables.net/forums - * @copyright Copyright 2015 SpryMedia Ltd. + * @copyright Copyright 2015-2016 SpryMedia Ltd. * * This source file is free software, available under the following license: * MIT license - http://datatables.net/license/mit @@ -53,7 +53,86 @@ var DataTable = $.fn.dataTable; // Version information for debugger DataTable.select = {}; -DataTable.select.version = '1.1.0'; + +DataTable.select.version = '1.2.0'; + +DataTable.select.init = function ( dt ) { + var ctx = dt.settings()[0]; + var init = ctx.oInit.select; + var defaults = DataTable.defaults.select; + var opts = init === undefined ? + defaults : + init; + + // Set defaults + var items = 'row'; + var style = 'api'; + var blurable = false; + var info = true; + var selector = 'td, th'; + var className = 'selected'; + + ctx._select = {}; + + // Initialisation customisations + if ( opts === true ) { + style = 'os'; + } + else if ( typeof opts === 'string' ) { + style = opts; + } + else if ( $.isPlainObject( opts ) ) { + if ( opts.blurable !== undefined ) { + blurable = opts.blurable; + } + + if ( opts.info !== undefined ) { + info = opts.info; + } + + if ( opts.items !== undefined ) { + items = opts.items; + } + + if ( opts.style !== undefined ) { + style = opts.style; + } + + if ( opts.selector !== undefined ) { + selector = opts.selector; + } + + if ( opts.className !== undefined ) { + className = opts.className; + } + } + + dt.select.selector( selector ); + dt.select.items( items ); + dt.select.style( style ); + dt.select.blurable( blurable ); + dt.select.info( info ); + ctx._select.className = className; + + + // Sort table based on selected rows. Requires Select Datatables extension + $.fn.dataTable.ext.order['select-checkbox'] = function ( settings, col ) { + return this.api().column( col, {order: 'index'} ).nodes().map( function ( td ) { + if ( settings._select.items === 'row' ) { + return $( td ).parent().hasClass( settings._select.className ); + } else if ( settings._select.items === 'cell' ) { + return $( td ).hasClass( settings._select.className ); + } + return false; + }); + }; + + // If the init options haven't enabled select, but there is a selectable + // class name, then enable + if ( $( dt.table().node() ).hasClass( 'selectable' ) ) { + dt.select.style( 'os' ); + } +}; /* @@ -246,7 +325,7 @@ function enableMouseSelection ( dt ) .on( 'mousedown.dtSelect', selector, function(e) { // Disallow text selection for shift clicking on the table so multi // element selection doesn't look terrible! - if ( e.shiftKey ) { + if ( e.shiftKey || e.metaKey || e.ctrlKey ) { body .css( '-moz-user-select', 'none' ) .one('selectstart.dtSelect', selector, function () { @@ -254,7 +333,7 @@ function enableMouseSelection ( dt ) } ); } } ) - .on( 'mouseup.dtSelect', selector, function(e) { + .on( 'mouseup.dtSelect', selector, function() { // Allow text selection to occur again, Mozilla style (tested in FF // 35.0.1 - still required) body.css( '-moz-user-select', '' ); @@ -263,32 +342,45 @@ function enableMouseSelection ( dt ) var items = dt.select.items(); var idx; + // If text was selected (click and drag), then we shouldn't change + // the row's selected state + if ( window.getSelection && window.getSelection().toString() ) { + return; + } + var ctx = dt.settings()[0]; // Ignore clicks inside a sub-table - if ( $(e.target).closest('tbody')[0] != body[0] ) { + if ( $(e.target).closest('div.dataTables_wrapper')[0] != dt.table().container() ) { return; } - var cell = $(e.target).closest('td, th'); - var cellIndex = dt.cell( cell ).index(); + var cell = dt.cell( $(e.target).closest('td, th') ); - // Check the cell actually belongs to the host DataTable (so child rows, - // etc, are ignored) - if ( ! dt.cell( cell ).any() ) { + // Check the cell actually belongs to the host DataTable (so child + // rows, etc, are ignored) + if ( ! cell.any() ) { return; } + var event = $.Event('user-select.dt'); + eventTrigger( dt, event, [ items, cell, e ] ); + + if ( event.isDefaultPrevented() ) { + return; + } + + var cellIndex = cell.index(); if ( items === 'row' ) { idx = cellIndex.row; typeSelect( e, dt, ctx, 'row', idx ); } else if ( items === 'column' ) { - idx = dt.cell( cell ).index().column; + idx = cell.index().column; typeSelect( e, dt, ctx, 'column', idx ); } else if ( items === 'cell' ) { - idx = dt.cell( cell ).index(); + idx = cell.index(); typeSelect( e, dt, ctx, 'cell', idx ); } @@ -329,9 +421,13 @@ function eventTrigger ( api, type, args, any ) return; } + if ( typeof type === 'string' ) { + type = type +'.dt'; + } + args.unshift( api ); - $(api.table().node()).triggerHandler( type+'.dt', args ); + $(api.table().node()).triggerHandler( type, args ); } /** @@ -450,7 +546,7 @@ function init ( ctx ) { } ); // Update the table information element with selected item summary - api.on( 'draw.dtSelect.dt select.dtSelect.dt deselect.dtSelect.dt', function () { + api.on( 'draw.dtSelect.dt select.dtSelect.dt deselect.dtSelect.dt info.dt', function () { info( api ); } ); @@ -569,6 +665,21 @@ function typeSelect ( e, dt, ctx, type, idx ) dt[type]( idx ).select(); } } + } else if ( style == 'multi+shift' ) { + if ( e.shiftKey ) { + if ( type === 'cell' ) { + cellRange( dt, idx, ctx._select_lastCell || null ); + } + else { + rowColumnRange( dt, type, idx, ctx._select_lastCell ? + ctx._select_lastCell[type] : + null + ); + } + } + else { + dt[ type ]( idx ).select( ! isSelected ); + } } else { dt[ type ]( idx ).select( ! isSelected ); @@ -601,7 +712,7 @@ $.each( [ data = settings[ o.prop ][ indexes[i] ]; if ( (selected === true && data._select_selected === true) || - (selected === false && ! data._select_selected ) + (selected === false && ! data._select_selected ) ) { out.push( indexes[i] ); } @@ -624,7 +735,7 @@ DataTable.ext.selector.cell.push( function ( settings, opts, cells ) { rowData = settings.aoData[ cells[i].row ]; if ( (selected === true && rowData._selected_cells && rowData._selected_cells[ cells[i].column ] === true) || - (selected === false && ( ! rowData._selected_cells || ! rowData._selected_cells[ cells[i].column ] ) ) + (selected === false && ( ! rowData._selected_cells || ! rowData._selected_cells[ cells[i].column ] ) ) ) { out.push( cells[i] ); } @@ -646,7 +757,11 @@ DataTable.ext.selector.cell.push( function ( settings, opts, cells ) { var apiRegister = DataTable.Api.register; var apiRegisterPlural = DataTable.Api.registerPlural; -apiRegister( 'select()', function () {} ); +apiRegister( 'select()', function () { + return this.iterator( 'table', function ( ctx ) { + DataTable.select.init( new DataTable.Api( ctx ) ); + } ); +} ); apiRegister( 'select.blurable()', function ( flag ) { if ( flag === undefined ) { @@ -889,7 +1004,7 @@ $.extend( DataTable.ext.buttons, { selected: { text: i18n( 'selected', 'Selected' ), className: 'buttons-selected', - init: function ( dt, button, config ) { + init: function ( dt ) { var that = this; // .DT namespace listeners are removed by DataTables automatically @@ -908,7 +1023,7 @@ $.extend( DataTable.ext.buttons, { selectedSingle: { text: i18n( 'selectedSingle', 'Selected single' ), className: 'buttons-selected-single', - init: function ( dt, button, config ) { + init: function ( dt ) { var that = this; dt.on( 'draw.dt.DT select.dt.DT deselect.dt.DT', function () { @@ -935,6 +1050,19 @@ $.extend( DataTable.ext.buttons, { className: 'buttons-select-none', action: function () { clear( this.settings()[0], true ); + }, + init: function ( dt ) { + var that = this; + + dt.on( 'draw.dt.DT select.dt.DT deselect.dt.DT', function () { + var count = dt.rows( { selected: true } ).flatten().length + + dt.columns( { selected: true } ).flatten().length + + dt.cells( { selected: true } ).flatten().length; + + that.enable( count > 0 ); + } ); + + this.disable(); } } } ); @@ -948,7 +1076,7 @@ $.each( [ 'Row', 'Column', 'Cell' ], function ( i, item ) { action: function () { this.select.items( lc ); }, - init: function ( dt, button, config ) { + init: function ( dt ) { var that = this; dt.on( 'selectItems.dt.DT', function ( e, ctx, items ) { @@ -964,73 +1092,16 @@ $.each( [ 'Row', 'Column', 'Cell' ], function ( i, item ) { * Initialisation */ -// DataTables creation - check if the buttons have been defined for this table, -// they will have been if the `B` option was used in `dom`, otherwise we should -// create the buttons instance here so they can be inserted into the document -// using the API -$(document).on( 'preInit.dt.dtSelect', function (e, ctx, json) { +// DataTables creation - check if select has been defined in the options. Note +// this required that the table be in the document! If it isn't then something +// needs to trigger this method unfortunately. The next major release of +// DataTables will rework the events and address this. +$(document).on( 'preInit.dt.dtSelect', function (e, ctx) { if ( e.namespace !== 'dt' ) { return; } - var opts = ctx.oInit.select || DataTable.defaults.select; - var dt = new DataTable.Api( ctx ); - - // Set defaults - var items = 'row'; - var style = 'api'; - var blurable = false; - var info = true; - var selector = 'td, th'; - var className = 'selected'; - - ctx._select = {}; - - // Initialisation customisations - if ( opts === true ) { - style = 'os'; - } - else if ( typeof opts === 'string' ) { - style = opts; - } - else if ( $.isPlainObject( opts ) ) { - if ( opts.blurable !== undefined ) { - blurable = opts.blurable; - } - - if ( opts.info !== undefined ) { - info = opts.info; - } - - if ( opts.items !== undefined ) { - items = opts.items; - } - - if ( opts.style !== undefined ) { - style = opts.style; - } - - if ( opts.selector !== undefined ) { - selector = opts.selector; - } - - if ( opts.className !== undefined ) { - className = opts.className; - } - } - - dt.select.selector( selector ); - dt.select.items( items ); - dt.select.style( style ); - dt.select.blurable( blurable ); - dt.select.info( info ); - ctx._select.className = className; - - // If the init options haven't enabled select, but there is a selectable - // class name, then enable - if ( $( dt.table().node() ).hasClass( 'selectable' ) ) { - dt.select.style( 'os' ); - } + DataTable.select.init( new DataTable.Api( ctx ) ); } ); diff --git a/app/assets/javascripts/dataTables/jquery.dataTables.js b/app/assets/javascripts/dataTables/jquery.dataTables.js old mode 100644 new mode 100755 index f0aece9..5b032ae --- a/app/assets/javascripts/dataTables/jquery.dataTables.js +++ b/app/assets/javascripts/dataTables/jquery.dataTables.js @@ -1,11 +1,11 @@ -/*! DataTables 1.10.10 +/*! DataTables 1.10.12 * ©2008-2015 SpryMedia Ltd - datatables.net/license */ /** * @summary DataTables * @description Paginate, search and order HTML tables - * @version 1.10.10 + * @version 1.10.12 * @file jquery.dataTables.js * @author SpryMedia Ltd (www.sprymedia.co.uk) * @contact www.sprymedia.co.uk/contact @@ -91,6528 +91,6580 @@ * } ); * } ); */ - var DataTable; - - - /* - * It is useful to have variables which are scoped locally so only the - * DataTables functions can access them and they don't leak into global space. - * At the same time these functions are often useful over multiple files in the - * core and API, so we list, or at least document, all variables which are used - * by DataTables as private variables here. This also ensures that there is no - * clashing of variable names and that they can easily referenced for reuse. - */ - - - // Defined else where - // _selector_run - // _selector_opts - // _selector_first - // _selector_row_indexes - - var _ext; // DataTable.ext - var _Api; // DataTable.Api - var _api_register; // DataTable.Api.register - var _api_registerPlural; // DataTable.Api.registerPlural - - var _re_dic = {}; - var _re_new_lines = /[\r\n]/g; - var _re_html = /<.*?>/g; - var _re_date_start = /^[\w\+\-]/; - var _re_date_end = /[\w\+\-]$/; - - // Escape regular expression special characters - var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' ); - - // http://en.wikipedia.org/wiki/Foreign_exchange_market - // - \u20BD - Russian ruble. - // - \u20a9 - South Korean Won - // - \u20BA - Turkish Lira - // - \u20B9 - Indian Rupee - // - R - Brazil (R$) and South Africa - // - fr - Swiss Franc - // - kr - Swedish krona, Norwegian krone and Danish krone - // - \u2009 is thin space and \u202F is narrow no-break space, both used in many - // standards as thousands separators. - var _re_formatted_numeric = /[',$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi; - - - var _empty = function ( d ) { - return !d || d === true || d === '-' ? true : false; - }; - - - var _intVal = function ( s ) { - var integer = parseInt( s, 10 ); - return !isNaN(integer) && isFinite(s) ? integer : null; - }; - - // Convert from a formatted number with characters other than `.` as the - // decimal place, to a Javascript number - var _numToDecimal = function ( num, decimalPoint ) { - // Cache created regular expressions for speed as this function is called often - if ( ! _re_dic[ decimalPoint ] ) { - _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' ); - } - return typeof num === 'string' && decimalPoint !== '.' ? - num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) : - num; - }; - - - var _isNumber = function ( d, decimalPoint, formatted ) { - var strType = typeof d === 'string'; - - // If empty return immediately so there must be a number if it is a - // formatted string (this stops the string "k", or "kr", etc being detected - // as a formatted number for currency - if ( _empty( d ) ) { - return true; - } - - if ( decimalPoint && strType ) { - d = _numToDecimal( d, decimalPoint ); - } - - if ( formatted && strType ) { - d = d.replace( _re_formatted_numeric, '' ); - } - - return !isNaN( parseFloat(d) ) && isFinite( d ); - }; - - - // A string without HTML in it can be considered to be HTML still - var _isHtml = function ( d ) { - return _empty( d ) || typeof d === 'string'; - }; - - - var _htmlNumeric = function ( d, decimalPoint, formatted ) { - if ( _empty( d ) ) { - return true; - } - - var html = _isHtml( d ); - return ! html ? - null : - _isNumber( _stripHtml( d ), decimalPoint, formatted ) ? - true : - null; - }; - - - var _pluck = function ( a, prop, prop2 ) { - var out = []; - var i=0, ien=a.length; - - // Could have the test in the loop for slightly smaller code, but speed - // is essential here - if ( prop2 !== undefined ) { - for ( ; i') - .css( { - position: 'fixed', - top: 0, - left: 0, - height: 1, - width: 1, - overflow: 'hidden' - } ) - .append( - $('
        ') - .css( { - position: 'absolute', - top: 1, - left: 1, - width: 100, - overflow: 'scroll' - } ) - .append( - $('
        ') - .css( { - width: '100%', - height: 10 - } ) - ) - ) - .appendTo( 'body' ); - - var outer = n.children(); - var inner = outer.children(); - - // Numbers below, in order, are: - // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth - // - // IE6 XP: 100 100 100 83 - // IE7 Vista: 100 100 100 83 - // IE 8+ Windows: 83 83 100 83 - // Evergreen Windows: 83 83 100 83 - // Evergreen Mac with scrollbars: 85 85 100 85 - // Evergreen Mac without scrollbars: 100 100 100 100 - - // Get scrollbar width - browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth; - - // IE6/7 will oversize a width 100% element inside a scrolling element, to - // include the width of the scrollbar, while other browsers ensure the inner - // element is contained without forcing scrolling - browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100; - - // In rtl text layout, some browsers (most, but not all) will place the - // scrollbar on the left, rather than the right. - browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1; - - // IE8- don't provide height and width for getBoundingClientRect - browser.bBounding = n[0].getBoundingClientRect().width ? true : false; - - n.remove(); - } - - $.extend( settings.oBrowser, DataTable.__browser ); - settings.oScroll.iBarWidth = DataTable.__browser.barWidth; - } - - - /** - * Array.prototype reduce[Right] method, used for browsers which don't support - * JS 1.6. Done this way to reduce code size, since we iterate either way - * @param {object} settings dataTables settings object - * @memberof DataTable#oApi - */ - function _fnReduce ( that, fn, init, start, end, inc ) - { - var - i = start, - value, - isSet = false; - - if ( init !== undefined ) { - value = init; - isSet = true; - } - - while ( i !== end ) { - if ( ! that.hasOwnProperty(i) ) { - continue; - } - - value = isSet ? - fn( value, that[i], i, that ) : - that[i]; - - isSet = true; - i += inc; - } - - return value; - } - - /** - * Add a column to the list used for the table with default values - * @param {object} oSettings dataTables settings object - * @param {node} nTh The th element for this column - * @memberof DataTable#oApi - */ - function _fnAddColumn( oSettings, nTh ) - { - // Add column to aoColumns array - var oDefaults = DataTable.defaults.column; - var iCol = oSettings.aoColumns.length; - var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { - "nTh": nTh ? nTh : document.createElement('th'), - "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '', - "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], - "mData": oDefaults.mData ? oDefaults.mData : iCol, - idx: iCol - } ); - oSettings.aoColumns.push( oCol ); - - // Add search object for column specific search. Note that the `searchCols[ iCol ]` - // passed into extend can be undefined. This allows the user to give a default - // with only some of the parameters defined, and also not give a default - var searchCols = oSettings.aoPreSearchCols; - searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] ); - - // Use the default column options function to initialise classes etc - _fnColumnOptions( oSettings, iCol, $(nTh).data() ); - } - - - /** - * Apply options for a column - * @param {object} oSettings dataTables settings object - * @param {int} iCol column index to consider - * @param {object} oOptions object with sType, bVisible and bSearchable etc - * @memberof DataTable#oApi - */ - function _fnColumnOptions( oSettings, iCol, oOptions ) - { - var oCol = oSettings.aoColumns[ iCol ]; - var oClasses = oSettings.oClasses; - var th = $(oCol.nTh); - - // Try to get width information from the DOM. We can't get it from CSS - // as we'd need to parse the CSS stylesheet. `width` option can override - if ( ! oCol.sWidthOrig ) { - // Width attribute - oCol.sWidthOrig = th.attr('width') || null; - - // Style attribute - var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/); - if ( t ) { - oCol.sWidthOrig = t[1]; - } - } - - /* User specified column options */ - if ( oOptions !== undefined && oOptions !== null ) - { - // Backwards compatibility - _fnCompatCols( oOptions ); - - // Map camel case parameters to their Hungarian counterparts - _fnCamelToHungarian( DataTable.defaults.column, oOptions ); - - /* Backwards compatibility for mDataProp */ - if ( oOptions.mDataProp !== undefined && !oOptions.mData ) - { - oOptions.mData = oOptions.mDataProp; - } - - if ( oOptions.sType ) - { - oCol._sManualType = oOptions.sType; - } - - // `class` is a reserved word in Javascript, so we need to provide - // the ability to use a valid name for the camel case input - if ( oOptions.className && ! oOptions.sClass ) - { - oOptions.sClass = oOptions.className; - } - - $.extend( oCol, oOptions ); - _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" ); - - /* iDataSort to be applied (backwards compatibility), but aDataSort will take - * priority if defined - */ - if ( oOptions.iDataSort !== undefined ) - { - oCol.aDataSort = [ oOptions.iDataSort ]; - } - _fnMap( oCol, oOptions, "aDataSort" ); - } - - /* Cache the data get and set functions for speed */ - var mDataSrc = oCol.mData; - var mData = _fnGetObjectDataFn( mDataSrc ); - var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null; - - var attrTest = function( src ) { - return typeof src === 'string' && src.indexOf('@') !== -1; - }; - oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && ( - attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter) - ); - - oCol.fnGetData = function (rowData, type, meta) { - var innerData = mData( rowData, type, undefined, meta ); - - return mRender && type ? - mRender( innerData, type, rowData, meta ) : - innerData; - }; - oCol.fnSetData = function ( rowData, val, meta ) { - return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta ); - }; - - // Indicate if DataTables should read DOM data as an object or array - // Used in _fnGetRowElements - if ( typeof mDataSrc !== 'number' ) { - oSettings._rowReadObject = true; - } - - /* Feature sorting overrides column specific when off */ - if ( !oSettings.oFeatures.bSort ) - { - oCol.bSortable = false; - th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called - } - - /* Check that the class assignment is correct for sorting */ - var bAsc = $.inArray('asc', oCol.asSorting) !== -1; - var bDesc = $.inArray('desc', oCol.asSorting) !== -1; - if ( !oCol.bSortable || (!bAsc && !bDesc) ) - { - oCol.sSortingClass = oClasses.sSortableNone; - oCol.sSortingClassJUI = ""; - } - else if ( bAsc && !bDesc ) - { - oCol.sSortingClass = oClasses.sSortableAsc; - oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed; - } - else if ( !bAsc && bDesc ) - { - oCol.sSortingClass = oClasses.sSortableDesc; - oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed; - } - else - { - oCol.sSortingClass = oClasses.sSortable; - oCol.sSortingClassJUI = oClasses.sSortJUI; - } - } - - - /** - * Adjust the table column widths for new data. Note: you would probably want to - * do a redraw after calling this function! - * @param {object} settings dataTables settings object - * @memberof DataTable#oApi - */ - function _fnAdjustColumnSizing ( settings ) - { - /* Not interested in doing column width calculation if auto-width is disabled */ - if ( settings.oFeatures.bAutoWidth !== false ) - { - var columns = settings.aoColumns; - - _fnCalculateColumnWidths( settings ); - for ( var i=0 , iLen=columns.length ; i
      ').appendTo(this); + } + oSettings.nTBody = tbody[0]; + + var tfoot = $this.children('tfoot'); + if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) + { + // If we are a scrolling table, and no footer has been given, then we need to create + // a tfoot element for the caption element to be appended to + tfoot = $('').appendTo(this); + } + + if ( tfoot.length === 0 || tfoot.children().length === 0 ) { + $this.addClass( oClasses.sNoFooter ); + } + else if ( tfoot.length > 0 ) { + oSettings.nTFoot = tfoot[0]; + _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot ); + } + + /* Check if there is data passing into the constructor */ + if ( oInit.aaData ) + { + for ( i=0 ; i/g; + var _re_date_start = /^[\w\+\-]/; + var _re_date_end = /[\w\+\-]$/; - /* Tell the draw function we have been filtering */ - oSettings.bFiltered = true; - _fnCallbackFire( oSettings, null, 'search', [oSettings] ); - } + // Escape regular expression special characters + var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' ); + // http://en.wikipedia.org/wiki/Foreign_exchange_market + // - \u20BD - Russian ruble. + // - \u20a9 - South Korean Won + // - \u20BA - Turkish Lira + // - \u20B9 - Indian Rupee + // - R - Brazil (R$) and South Africa + // - fr - Swiss Franc + // - kr - Swedish krona, Norwegian krone and Danish krone + // - \u2009 is thin space and \u202F is narrow no-break space, both used in many + // standards as thousands separators. + var _re_formatted_numeric = /[',$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi; - /** - * Apply custom filtering functions - * @param {object} oSettings dataTables settings object - * @memberof DataTable#oApi - */ - function _fnFilterCustom( settings ) - { - var filters = DataTable.ext.search; - var displayRows = settings.aiDisplay; - var row, rowIdx; - for ( var i=0, ien=filters.length ; i=0 ; i-- ) { - data = settings.aoData[ display[i] ]._aFilterData[ colIdx ]; + if ( decimalPoint && strType ) { + d = _numToDecimal( d, decimalPoint ); + } - if ( ! rpSearch.test( data ) ) { - display.splice( i, 1 ); - } + if ( formatted && strType ) { + d = d.replace( _re_formatted_numeric, '' ); } - } + return !isNaN( parseFloat(d) ) && isFinite( d ); + }; - /** - * Filter the data table based on user input and draw the table - * @param {object} settings dataTables settings object - * @param {string} input string to filter on - * @param {int} force optional - force a research of the master array (1) or not (undefined or 0) - * @param {bool} regex treat as a regular expression or not - * @param {bool} smart perform smart filtering or not - * @param {bool} caseInsensitive Do case insenstive matching or not - * @memberof DataTable#oApi - */ - function _fnFilter( settings, input, force, regex, smart, caseInsensitive ) - { - var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive ); - var prevSearch = settings.oPreviousSearch.sSearch; - var displayMaster = settings.aiDisplayMaster; - var display, invalidated, i; - // Need to take account of custom filtering functions - always filter - if ( DataTable.ext.search.length !== 0 ) { - force = true; + // A string without HTML in it can be considered to be HTML still + var _isHtml = function ( d ) { + return _empty( d ) || typeof d === 'string'; + }; + + + var _htmlNumeric = function ( d, decimalPoint, formatted ) { + if ( _empty( d ) ) { + return true; } - // Check if any of the rows were invalidated - invalidated = _fnFilterData( settings ); + var html = _isHtml( d ); + return ! html ? + null : + _isNumber( _stripHtml( d ), decimalPoint, formatted ) ? + true : + null; + }; - // If the input is blank - we just want the full data set - if ( input.length <= 0 ) { - settings.aiDisplay = displayMaster.slice(); + + var _pluck = function ( a, prop, prop2 ) { + var out = []; + var i=0, ien=a.length; + + // Could have the test in the loop for slightly smaller code, but speed + // is essential here + if ( prop2 !== undefined ) { + for ( ; i input.length || - input.indexOf(prevSearch) !== 0 || - settings.bSorted // On resort, the display master needs to be - // re-filtered since indexes will have changed - ) { - settings.aiDisplay = displayMaster.slice(); + for ( ; i=0 ; i-- ) { - if ( ! rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) { - display.splice( i, 1 ); + + // Basically the same as _pluck, but rather than looping over `a` we use `order` + // as the indexes to pick from `a` + var _pluck_order = function ( a, order, prop, prop2 ) + { + var out = []; + var i=0, ien=order.length; + + // Could have the test in the loop for slightly smaller code, but speed + // is essential here + if ( prop2 !== undefined ) { + for ( ; i')[0]; - var __filter_div_textContent = __filter_div.textContent !== undefined; + for ( var i=0, ien=a.length ; i', { - 'class': settings.oClasses.sInfo, - 'id': ! nodes ? tid+'_info' : null - } ); + function _fnLanguageCompat( lang ) + { + var defaults = DataTable.defaults.oLanguage; + var zeroRecords = lang.sZeroRecords; - if ( ! nodes ) { - // Update display on each draw - settings.aoDrawCallback.push( { - "fn": _fnUpdateInfo, - "sName": "information" - } ); + /* Backwards compatibility - if there is no sEmptyTable given, then use the same as + * sZeroRecords - assuming that is given. + */ + if ( ! lang.sEmptyTable && zeroRecords && + defaults.sEmptyTable === "No data available in table" ) + { + _fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' ); + } - n - .attr( 'role', 'status' ) - .attr( 'aria-live', 'polite' ); + /* Likewise with loading records */ + if ( ! lang.sLoadingRecords && zeroRecords && + defaults.sLoadingRecords === "Loading..." ) + { + _fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' ); + } - // Table is described by our info div - $(settings.nTable).attr( 'aria-describedby', tid+'_info' ); + // Old parameter name of the thousands separator mapped onto the new + if ( lang.sInfoThousands ) { + lang.sThousands = lang.sInfoThousands; } - return n[0]; + var decimal = lang.sDecimal; + if ( decimal ) { + _addNumericSort( decimal ); + } } /** - * Update the information elements in the display - * @param {object} settings dataTables settings object - * @memberof DataTable#oApi + * Map one parameter onto another + * @param {object} o Object to map + * @param {*} knew The new parameter name + * @param {*} old The old parameter name */ - function _fnUpdateInfo ( settings ) - { - /* Show information about the table */ - var nodes = settings.aanFeatures.i; - if ( nodes.length === 0 ) { - return; + var _fnCompatMap = function ( o, knew, old ) { + if ( o[ knew ] !== undefined ) { + o[ old ] = o[ knew ]; } + }; - var - lang = settings.oLanguage, - start = settings._iDisplayStart+1, - end = settings.fnDisplayEnd(), - max = settings.fnRecordsTotal(), - total = settings.fnRecordsDisplay(), - out = total ? - lang.sInfo : - lang.sInfoEmpty; - if ( total !== max ) { - /* Record set after filtering */ - out += ' ' + lang.sInfoFiltered; + /** + * Provide backwards compatibility for the main DT options. Note that the new + * options are mapped onto the old parameters, so this is an external interface + * change only. + * @param {object} init Object to map + */ + function _fnCompatOpts ( init ) + { + _fnCompatMap( init, 'ordering', 'bSort' ); + _fnCompatMap( init, 'orderMulti', 'bSortMulti' ); + _fnCompatMap( init, 'orderClasses', 'bSortClasses' ); + _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' ); + _fnCompatMap( init, 'order', 'aaSorting' ); + _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' ); + _fnCompatMap( init, 'paging', 'bPaginate' ); + _fnCompatMap( init, 'pagingType', 'sPaginationType' ); + _fnCompatMap( init, 'pageLength', 'iDisplayLength' ); + _fnCompatMap( init, 'searching', 'bFilter' ); + + // Boolean initialisation of x-scrolling + if ( typeof init.sScrollX === 'boolean' ) { + init.sScrollX = init.sScrollX ? '100%' : ''; + } + if ( typeof init.scrollX === 'boolean' ) { + init.scrollX = init.scrollX ? '100%' : ''; } - // Convert the macros - out += lang.sInfoPostFix; - out = _fnInfoMacros( settings, out ); + // Column search objects are in an array, so it needs to be converted + // element by element + var searchCols = init.aoSearchCols; - var callback = lang.fnInfoCallback; - if ( callback !== null ) { - out = callback.call( settings.oInstance, - settings, start, end, max, total, out - ); + if ( searchCols ) { + for ( var i=0, ien=searchCols.length ; i') + .css( { + position: 'fixed', + top: 0, + left: 0, + height: 1, + width: 1, + overflow: 'hidden' + } ) + .append( + $('
      ') + .css( { + position: 'absolute', + top: 1, + left: 1, + width: 100, + overflow: 'scroll' + } ) + .append( + $('
      ') + .css( { + width: '100%', + height: 10 + } ) + ) + ) + .appendTo( 'body' ); - _fnCallbackFire( settings, null, 'preInit', [settings] ); + var outer = n.children(); + var inner = outer.children(); - // If there is default sorting required - let's do it. The sort function - // will do the drawing for us. Otherwise we draw the table regardless of the - // Ajax source - this allows the table to look initialised for Ajax sourcing - // data (show 'loading' message possibly) - _fnReDraw( settings ); + // Numbers below, in order, are: + // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth + // + // IE6 XP: 100 100 100 83 + // IE7 Vista: 100 100 100 83 + // IE 8+ Windows: 83 83 100 83 + // Evergreen Windows: 83 83 100 83 + // Evergreen Mac with scrollbars: 85 85 100 85 + // Evergreen Mac without scrollbars: 100 100 100 100 - // Server-side processing init complete is done by _fnAjaxUpdateDraw - var dataSrc = _fnDataSource( settings ); - if ( dataSrc != 'ssp' || deferLoading ) { - // if there is an ajax source load the data - if ( dataSrc == 'ajax' ) { - _fnBuildAjax( settings, [], function(json) { - var aData = _fnAjaxDataSrc( settings, json ); + // Get scrollbar width + browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth; - // Got the data - add it to the table - for ( i=0 ; i', { - 'name': tableId+'_length', - 'aria-controls': tableId, - 'class': classes.sLengthSelect + // Add column to aoColumns array + var oDefaults = DataTable.defaults.column; + var iCol = oSettings.aoColumns.length; + var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, { + "nTh": nTh ? nTh : document.createElement('th'), + "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '', + "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol], + "mData": oDefaults.mData ? oDefaults.mData : iCol, + idx: iCol } ); + oSettings.aoColumns.push( oCol ); - for ( var i=0, ien=lengths.length ; i
      ').addClass( classes.sLength ); - if ( ! settings.aanFeatures.l ) { - div[0].id = tableId+'_length'; - } - - div.children().append( - settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML ) - ); - - // Can't use `select` variable as user might provide their own and the - // reference is broken by the use of outerHTML - $('select', div) - .val( settings._iDisplayLength ) - .bind( 'change.DT', function(e) { - _fnLengthChange( settings, $(this).val() ); - _fnDraw( settings ); - } ); - - // Update node value whenever anything changes the table's length - $(settings.nTable).bind( 'length.dt.DT', function (e, s, len) { - if ( settings === s ) { - $('select', div).val( len ); - } - } ); + // Add search object for column specific search. Note that the `searchCols[ iCol ]` + // passed into extend can be undefined. This allows the user to give a default + // with only some of the parameters defined, and also not give a default + var searchCols = oSettings.aoPreSearchCols; + searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] ); - return div[0]; + // Use the default column options function to initialise classes etc + _fnColumnOptions( oSettings, iCol, $(nTh).data() ); } - - /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * Note that most of the paging logic is done in - * DataTable.ext.pager - */ - /** - * Generate the node required for default pagination + * Apply options for a column * @param {object} oSettings dataTables settings object - * @returns {node} Pagination feature node + * @param {int} iCol column index to consider + * @param {object} oOptions object with sType, bVisible and bSearchable etc * @memberof DataTable#oApi */ - function _fnFeatureHtmlPaginate ( settings ) + function _fnColumnOptions( oSettings, iCol, oOptions ) { - var - type = settings.sPaginationType, - plugin = DataTable.ext.pager[ type ], - modern = typeof plugin === 'function', - redraw = function( settings ) { - _fnDraw( settings ); - }, - node = $('
      ').addClass( settings.oClasses.sPaging + type )[0], - features = settings.aanFeatures; + var oCol = oSettings.aoColumns[ iCol ]; + var oClasses = oSettings.oClasses; + var th = $(oCol.nTh); - if ( ! modern ) { - plugin.fnInit( settings, node, redraw ); + // Try to get width information from the DOM. We can't get it from CSS + // as we'd need to parse the CSS stylesheet. `width` option can override + if ( ! oCol.sWidthOrig ) { + // Width attribute + oCol.sWidthOrig = th.attr('width') || null; + + // Style attribute + var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/); + if ( t ) { + oCol.sWidthOrig = t[1]; + } } - /* Add a draw callback for the pagination on first instance, to update the paging display */ - if ( ! features.p ) + /* User specified column options */ + if ( oOptions !== undefined && oOptions !== null ) { - node.id = settings.sTableId+'_paginate'; + // Backwards compatibility + _fnCompatCols( oOptions ); - settings.aoDrawCallback.push( { - "fn": function( settings ) { - if ( modern ) { - var - start = settings._iDisplayStart, - len = settings._iDisplayLength, - visRecords = settings.fnRecordsDisplay(), - all = len === -1, - page = all ? 0 : Math.ceil( start / len ), - pages = all ? 1 : Math.ceil( visRecords / len ), - buttons = plugin(page, pages), - i, ien; + // Map camel case parameters to their Hungarian counterparts + _fnCamelToHungarian( DataTable.defaults.column, oOptions ); - for ( i=0, ien=features.p.length ; i records ) - { - start = 0; - } - } - else if ( action == "first" ) + /* Feature sorting overrides column specific when off */ + if ( !oSettings.oFeatures.bSort ) { - start = 0; + oCol.bSortable = false; + th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called } - else if ( action == "previous" ) - { - start = len >= 0 ? - start - len : - 0; - if ( start < 0 ) - { - start = 0; - } + /* Check that the class assignment is correct for sorting */ + var bAsc = $.inArray('asc', oCol.asSorting) !== -1; + var bDesc = $.inArray('desc', oCol.asSorting) !== -1; + if ( !oCol.bSortable || (!bAsc && !bDesc) ) + { + oCol.sSortingClass = oClasses.sSortableNone; + oCol.sSortingClassJUI = ""; } - else if ( action == "next" ) + else if ( bAsc && !bDesc ) { - if ( start + len < records ) - { - start += len; - } + oCol.sSortingClass = oClasses.sSortableAsc; + oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed; } - else if ( action == "last" ) + else if ( !bAsc && bDesc ) { - start = Math.floor( (records-1) / len) * len; + oCol.sSortingClass = oClasses.sSortableDesc; + oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed; } else { - _fnLog( settings, 0, "Unknown paging action: "+action, 5 ); + oCol.sSortingClass = oClasses.sSortable; + oCol.sSortingClassJUI = oClasses.sSortJUI; } + } - var changed = settings._iDisplayStart !== start; - settings._iDisplayStart = start; - if ( changed ) { - _fnCallbackFire( settings, null, 'page', [settings] ); + /** + * Adjust the table column widths for new data. Note: you would probably want to + * do a redraw after calling this function! + * @param {object} settings dataTables settings object + * @memberof DataTable#oApi + */ + function _fnAdjustColumnSizing ( settings ) + { + /* Not interested in doing column width calculation if auto-width is disabled */ + if ( settings.oFeatures.bAutoWidth !== false ) + { + var columns = settings.aoColumns; - if ( redraw ) { - _fnDraw( settings ); + _fnCalculateColumnWidths( settings ); + for ( var i=0 , iLen=columns.length ; i
      ').appendTo( tmpTable.find('tbody') ); - // Clone the table header and footer - we can't use the header / footer - // from the cloned table, since if scrolling is active, the table's - // real header and footer are contained in different table tags - tmpTable.find('thead, tfoot').remove(); - tmpTable - .append( $(oSettings.nTHead).clone() ) - .append( $(oSettings.nTFoot).clone() ); + if ( iTargetIndex != -1 && splice === undefined ) + { + a.splice( iTargetIndex, 1 ); + } + } - // Remove any assigned widths from the footer (from scrolling) - tmpTable.find('tfoot th, tfoot td').css('width', ''); - // Apply custom sizing to the cloned header - headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] ); + /** + * Mark cached data as invalid such that a re-read of the data will occur when + * the cached data is next requested. Also update from the data source object. + * + * @param {object} settings DataTables settings object + * @param {int} rowIdx Row index to invalidate + * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom' + * or 'data' + * @param {int} [colIdx] Column index to invalidate. If undefined the whole + * row will be invalidated + * @memberof DataTable#oApi + * + * @todo For the modularisation of v1.11 this will need to become a callback, so + * the sort and filter methods can subscribe to it. That will required + * initialisation options for sorting, which is why it is not already baked in + */ + function _fnInvalidate( settings, rowIdx, src, colIdx ) + { + var row = settings.aoData[ rowIdx ]; + var i, ien; + var cellWrite = function ( cell, col ) { + // This is very frustrating, but in IE if you just write directly + // to innerHTML, and elements that are overwritten are GC'ed, + // even if there is a reference to them elsewhere + while ( cell.childNodes.length ) { + cell.removeChild( cell.firstChild ); + } - for ( i=0 ; i').css( { - width: column.sWidthOrig, - margin: 0, - padding: 0, - border: 0, - height: 1 - } ) ); + if ( cells ) { + if ( colIdx !== undefined ) { + cellWrite( cells[colIdx], colIdx ); + } + else { + for ( i=0, ien=cells.length ; i').css( scrollX || scrollY ? - { - position: 'absolute', - top: 0, - left: 0, - height: 1, - right: 0, - overflow: 'hidden' - } : - {} - ) - .append( tmpTable ) - .appendTo( tableContainer ); + // Update DataTables special `DT_*` attributes for the row + _fnRowAttributes( settings, row ); + } + } + + + /** + * Build a data source object from an HTML row, reading the contents of the + * cells that are in the row. + * + * @param {object} settings DataTables settings object + * @param {node|object} TR element from which to read data or existing row + * object from which to re-read the data from the cells + * @param {int} [colIdx] Optional column index + * @param {array|object} [d] Data source object. If `colIdx` is given then this + * parameter should also be given and will be used to write the data into. + * Only the column in question will be written + * @returns {object} Object with two parameters: `data` the data read, in + * document order, and `cells` and array of nodes (they can be useful to the + * caller, so rather than needing a second traversal to get them, just return + * them from here). + * @memberof DataTable#oApi + */ + function _fnGetRowElements( settings, row, colIdx, d ) + { + var + tds = [], + td = row.firstChild, + name, col, o, i=0, contents, + columns = settings.aoColumns, + objectRead = settings._rowReadObject; + + // Allow the data object to be passed in, or construct + d = d !== undefined ? + d : + objectRead ? + {} : + []; + + var attr = function ( str, td ) { + if ( typeof str === 'string' ) { + var idx = str.indexOf('@'); - // When scrolling (X or Y) we want to set the width of the table as - // appropriate. However, when not scrolling leave the table width as it - // is. This results in slightly different, but I think correct behaviour - if ( scrollX && scrollXInner ) { - tmpTable.width( scrollXInner ); + if ( idx !== -1 ) { + var attr = str.substring( idx+1 ); + var setter = _fnSetObjectDataFn( str ); + setter( d, td.getAttribute( attr ) ); + } } - else if ( scrollX ) { - tmpTable.css( 'width', 'auto' ); - tmpTable.removeAttr('width'); + }; - // If there is no width attribute or style, then allow the table to - // collapse - if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) { - tmpTable.width( tableContainer.clientWidth ); + // Read data from a cell and store into the data object + var cellProcess = function ( cell ) { + if ( colIdx === undefined || colIdx === i ) { + col = columns[i]; + contents = $.trim(cell.innerHTML); + + if ( col && col._bAttrSrc ) { + var setter = _fnSetObjectDataFn( col.mData._ ); + setter( d, contents ); + + attr( col.mData.sort, cell ); + attr( col.mData.type, cell ); + attr( col.mData.filter, cell ); + } + else { + // Depending on the `data` option for the columns the data can + // be read to either an object or an array. + if ( objectRead ) { + if ( ! col._setter ) { + // Cache the setter function + col._setter = _fnSetObjectDataFn( col.mData ); + } + col._setter( d, contents ); + } + else { + d[i] = contents; + } } - } - else if ( scrollY ) { - tmpTable.width( tableContainer.clientWidth ); - } - else if ( tableWidthAttr ) { - tmpTable.width( tableWidthAttr ); } - // Get the width of each column in the constructed table - we need to - // know the inner width (so it can be assigned to the other table's - // cells) and the outer width so we can calculate the full width of the - // table. This is safe since DataTables requires a unique cell for each - // column, but if ever a header can span multiple columns, this will - // need to be modified. - var total = 0; - for ( i=0 ; i') - .css( 'width', _fnStringToCss( width ) ) - .appendTo( parent || document.body ); + nTd = nTrIn ? anTds[i] : document.createElement( oCol.sCellType ); + nTd._DT_CellIndex = { + row: iRow, + column: i + }; + + cells.push( nTd ); - var val = n[0].offsetWidth; - n.remove(); + // Need to create the HTML if new, or if a rendering function is defined + if ( (!nTrIn || oCol.mRender || oCol.mData !== i) && + (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display') + ) { + nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' ); + } - return val; + /* Add user defined class */ + if ( oCol.sClass ) + { + nTd.className += ' '+oCol.sClass; + } + + // Visibility - add or remove as required + if ( oCol.bVisible && ! nTrIn ) + { + nTr.appendChild( nTd ); + } + else if ( ! oCol.bVisible && nTrIn ) + { + nTd.parentNode.removeChild( nTd ); + } + + if ( oCol.fnCreatedCell ) + { + oCol.fnCreatedCell.call( oSettings.oInstance, + nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i + ); + } + } + + _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow] ); + } + + // Remove once webkit bug 131819 and Chromium bug 365619 have been resolved + // and deployed + row.nTr.setAttribute( 'role', 'row' ); } /** - * Get the widest node - * @param {object} settings dataTables settings object - * @param {int} colIdx column of interest - * @returns {node} widest table node + * Add attributes to a row based on the special `DT_*` parameters in a data + * source object. + * @param {object} settings DataTables settings object + * @param {object} DataTables row object for the row to be modified * @memberof DataTable#oApi */ - function _fnGetWidestNode( settings, colIdx ) + function _fnRowAttributes( settings, row ) { - var idx = _fnGetMaxLenString( settings, colIdx ); - if ( idx < 0 ) { - return null; - } + var tr = row.nTr; + var data = row._aData; - var data = settings.aoData[ idx ]; - return ! data.nTr ? // Might not have been created when deferred rendering - $('').appendTo( thead ); } - // Check it has a unit character already - return s.match(/\d$/) ? - s+'px' : - s; - } + for ( i=0, ien=columns.length ; itr').attr('role', 'row'); - for ( i=0 ; itr>th, >tr>td').addClass( classes.sHeaderTH ); + $(tfoot).find('>tr>th, >tr>td').addClass( classes.sFooterTH ); - for ( k=0, kLen=aDataSort.length ; k').appendTo( tmpTable.find('tbody') ); + + // Clone the table header and footer - we can't use the header / footer + // from the cloned table, since if scrolling is active, the table's + // real header and footer are contained in different table tags + tmpTable.find('thead, tfoot').remove(); + tmpTable + .append( $(oSettings.nTHead).clone() ) + .append( $(oSettings.nTFoot).clone() ); + + // Remove any assigned widths from the footer (from scrolling) + tmpTable.find('tfoot th, tfoot td').css('width', ''); + + // Apply custom sizing to the cloned header + headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] ); + + for ( i=0 ; i').css( { + width: column.sWidthOrig, + margin: 0, + padding: 0, + border: 0, + height: 1 + } ) ); + } + } + + // Find the widest cell for each column and put it into the table + if ( oSettings.aoData.length ) { + for ( i=0 ; i').css( scrollX || scrollY ? { - _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 ); - return; - } - } - - /* If the element we are initialising has the same ID as a table which was previously - * initialised, but the table nodes don't match (from before) then we destroy the old - * instance by simply deleting it. This is under the assumption that the table has been - * destroyed by other methods. Anyone using non-id selectors will need to do this manually - */ - if ( s.sTableId == this.id ) - { - allSettings.splice( i, 1 ); - break; + position: 'absolute', + top: 0, + left: 0, + height: 1, + right: 0, + overflow: 'hidden' + } : + {} + ) + .append( tmpTable ) + .appendTo( tableContainer ); + + // When scrolling (X or Y) we want to set the width of the table as + // appropriate. However, when not scrolling leave the table width as it + // is. This results in slightly different, but I think correct behaviour + if ( scrollX && scrollXInner ) { + tmpTable.width( scrollXInner ); + } + else if ( scrollX ) { + tmpTable.css( 'width', 'auto' ); + tmpTable.removeAttr('width'); + + // If there is no width attribute or style, then allow the table to + // collapse + if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) { + tmpTable.width( tableContainer.clientWidth ); } } - - /* Ensure the table has an ID - required for accessibility */ - if ( sId === null || sId === "" ) - { - sId = "DataTables_Table_"+(DataTable.ext._unique++); - this.id = sId; + else if ( scrollY ) { + tmpTable.width( tableContainer.clientWidth ); } - - /* Create the settings object for this table and set some of the default parameters */ - var oSettings = $.extend( true, {}, DataTable.models.oSettings, { - "sDestroyWidth": $this[0].style.width, - "sInstance": sId, - "sTableId": sId - } ); - oSettings.nTable = this; - oSettings.oApi = _that.internal; - oSettings.oInit = oInit; - - allSettings.push( oSettings ); - - // Need to add the instance after the instance after the settings object has been added - // to the settings array, so we can self reference the table instance if more than one - oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable(); - - // Backwards compatibility, before we apply all the defaults - _fnCompatOpts( oInit ); - - if ( oInit.oLanguage ) - { - _fnLanguageCompat( oInit.oLanguage ); + else if ( tableWidthAttr ) { + tmpTable.width( tableWidthAttr ); } - - // If the length menu is given, but the init display length is not, use the length menu - if ( oInit.aLengthMenu && ! oInit.iDisplayLength ) - { - oInit.iDisplayLength = $.isArray( oInit.aLengthMenu[0] ) ? - oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0]; + + // Get the width of each column in the constructed table - we need to + // know the inner width (so it can be assigned to the other table's + // cells) and the outer width so we can calculate the full width of the + // table. This is safe since DataTables requires a unique cell for each + // column, but if ever a header can span multiple columns, this will + // need to be modified. + var total = 0; + for ( i=0 ; i') + .css( 'width', _fnStringToCss( width ) ) + .appendTo( parent || document.body ); + + var val = n[0].offsetWidth; + n.remove(); + + return val; + } + + + /** + * Get the widest node + * @param {object} settings dataTables settings object + * @param {int} colIdx column of interest + * @returns {node} widest table node + * @memberof DataTable#oApi + */ + function _fnGetWidestNode( settings, colIdx ) + { + var idx = _fnGetMaxLenString( settings, colIdx ); + if ( idx < 0 ) { + return null; + } + + var data = settings.aoData[ idx ]; + return ! data.nTr ? // Might not have been created when deferred rendering + $('').appendTo(this); - } - oSettings.nTHead = thead[0]; - - var tbody = $this.children('tbody'); - if ( tbody.length === 0 ) - { - tbody = $('').appendTo(this); + } + + // Search + if ( state.search !== undefined ) { + $.extend( settings.oPreviousSearch, _fnSearchToHung( state.search ) ); + } + + // Columns + for ( i=0, ien=state.columns.length ; i 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) - { - // If we are a scrolling table, and no footer has been given, then we need to create - // a tfoot element for the caption element to be appended to - tfoot = $('').appendTo(this); + + // Search + if ( col.search !== undefined ) { + $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) ); } - - if ( tfoot.length === 0 || tfoot.children().length === 0 ) { - $this.addClass( oClasses.sNoFooter ); + } + + _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, state] ); + } + + + /** + * Return the settings object for a particular table + * @param {node} table table we are using as a dataTable + * @returns {object} Settings object - or null if not found + * @memberof DataTable#oApi + */ + function _fnSettingsFromNode ( table ) + { + var settings = DataTable.settings; + var idx = $.inArray( table, _pluck( settings, 'nTable' ) ); + + return idx !== -1 ? + settings[ idx ] : + null; + } + + + /** + * Log an error message + * @param {object} settings dataTables settings object + * @param {int} level log error messages, or display them to the user + * @param {string} msg error message + * @param {int} tn Technical note id to get more information about the error. + * @memberof DataTable#oApi + */ + function _fnLog( settings, level, msg, tn ) + { + msg = 'DataTables warning: '+ + (settings ? 'table id='+settings.sTableId+' - ' : '')+msg; + + if ( tn ) { + msg += '. For more information about this error, please see '+ + 'http://datatables.net/tn/'+tn; + } + + if ( ! level ) { + // Backwards compatibility pre 1.10 + var ext = DataTable.ext; + var type = ext.sErrMode || ext.errMode; + + if ( settings ) { + _fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] ); } - else if ( tfoot.length > 0 ) { - oSettings.nTFoot = tfoot[0]; - _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot ); + + if ( type == 'alert' ) { + alert( msg ); } - - /* Check if there is data passing into the constructor */ - if ( oInit.aaData ) - { - for ( i=0 ; i= end ) + { + start = end - len; + } + + // Keep the start record on the current page + start -= (start % len); + + if ( len === -1 || start < 0 ) + { + start = 0; + } + + settings._iDisplayStart = start; + } + + + function _fnRenderer( settings, type ) + { + var renderer = settings.renderer; + var host = DataTable.ext.renderer[type]; + + if ( $.isPlainObject( renderer ) && renderer[type] ) { + // Specific renderer for this type. If available use it, otherwise use + // the default. + return host[renderer[type]] || host._; + } + else if ( typeof renderer === 'string' ) { + // Common renderer - if there is one available for this type use it, + // otherwise use the default + return host[renderer] || host._; + } + + // Use the default + return host._; + } + + + /** + * Detect the data source being used for the table. Used to simplify the code + * a little (ajax) and to make it compress a little smaller. + * + * @param {object} settings dataTables settings object + * @returns {string} Data source + * @memberof DataTable#oApi + */ + function _fnDataSource ( settings ) + { + if ( settings.oFeatures.bServerSide ) { + return 'ssp'; + } + else if ( settings.ajax || settings.sAjaxSource ) { + return 'ajax'; + } + return 'dom'; + } + @@ -7789,9 +7841,17 @@ // Selector - node if ( sel.nodeName ) { - if ( $.inArray( sel, nodes ) !== -1 ) { - return [ sel._DT_RowIndex ]; // sel is a TR node that is in the table - // and DataTables adds a prop for fast lookup + if ( sel._DT_RowIndex !== undefined ) { + return [ sel._DT_RowIndex ]; // Property added by DT for fast lookup + } + else if ( sel._DT_CellIndex ) { + return [ sel._DT_CellIndex.row ]; + } + else { + var host = $(sel).closest('*[data-dt-row]'); + return host.length ? + [ host.data('dt-row') ] : + []; } } @@ -8303,7 +8363,7 @@ if ( s === '' ) { return _range( columns.length ); } - + // Selector - index if ( selInt !== null ) { return [ selInt >= 0 ? @@ -8311,7 +8371,7 @@ columns.length + selInt // Count from right (+ because its a negative value) ]; } - + // Selector = function if ( typeof s === 'function' ) { var rows = _selector_row_indexes( settings, opts ); @@ -8351,24 +8411,42 @@ return $.map( names, function (name, i) { return name === match[1] ? i : null; } ); + + default: + return []; } } - else { - // jQuery selector on the TH elements for the columns - return $( nodes ) - .filter( s ) - .map( function () { - return $.inArray( this, nodes ); // `nodes` is column index complete and in order - } ) - .toArray(); + + // Cell in the table body + if ( s.nodeName && s._DT_CellIndex ) { + return [ s._DT_CellIndex.column ]; } + + // jQuery selector on the TH elements for the columns + var jqResult = $( nodes ) + .filter( s ) + .map( function () { + return $.inArray( this, nodes ); // `nodes` is column index complete and in order + } ) + .toArray(); + + if ( jqResult.length || ! s.nodeName ) { + return jqResult; + } + + // Otherwise a node which might have a `dt-column` data attribute, or be + // a child or such an element + var host = $(s).closest('*[data-dt-column]'); + return host.length ? + [ host.data('dt-column') ] : + []; }; return _selector_run( 'column', selector, run, settings, opts ); }; - var __setColumnVis = function ( settings, column, vis, recalc ) { + var __setColumnVis = function ( settings, column, vis ) { var cols = settings.aoColumns, col = cols[ column ], @@ -8411,18 +8489,6 @@ _fnDrawHead( settings, settings.aoHeader ); _fnDrawHead( settings, settings.aoFooter ); - if ( recalc === undefined || recalc ) { - // Automatically adjust column sizing - _fnAdjustColumnSizing( settings ); - - // Realign columns for scrolling - if ( settings.oScroll.sX || settings.oScroll.sY ) { - _fnScrollDraw( settings ); - } - } - - _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, recalc] ); - _fnSaveState( settings ); }; @@ -8487,12 +8553,26 @@ } ); _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) { - return this.iterator( 'column', function ( settings, column ) { + var ret = this.iterator( 'column', function ( settings, column ) { if ( vis === undefined ) { return settings.aoColumns[ column ].bVisible; } // else - __setColumnVis( settings, column, vis, calc ); + __setColumnVis( settings, column, vis ); } ); + + // Group the column visibility changes + if ( vis !== undefined ) { + // Second loop once the first is done for events + this.iterator( 'column', function ( settings, column ) { + _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] ); + } ); + + if ( calc === undefined || calc ) { + this.columns.adjust(); + } + } + + return ret; } ); _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) { @@ -8528,7 +8608,6 @@ - var __cell_selector = function ( settings, selector, opts ) { var data = settings.aoData; @@ -8579,7 +8658,7 @@ } // Selector - jQuery filtered cells - return allCells + var jqResult = allCells .filter( s ) .map( function (i, el) { return { // use a new object, in case someone changes the values @@ -8588,6 +8667,21 @@ }; } ) .toArray(); + + if ( jqResult.length || ! s.nodeName ) { + return jqResult; + } + + // Otherwise the selector is a node, and there is one last option - the + // element might be a child of an element which has dt-row and dt-column + // data attributes + host = $(s).closest('*[data-dt-row]'); + return host.length ? + [ { + row: host.data('dt-row'), + column: host.data('dt-column') + } ] : + []; }; return _selector_run( 'cell', selector, run, settings, opts ); @@ -8655,9 +8749,10 @@ _api_registerPlural( 'cells().nodes()', 'cell().node()', function () { return this.iterator( 'cell', function ( settings, row, column ) { - var cells = settings.aoData[ row ].anCells; - return cells ? - cells[ column ] : + var data = settings.aoData[ row ]; + + return data && data.anCells ? + data.anCells[ column ] : undefined; }, 1 ); } ); @@ -8773,7 +8868,7 @@ // Simple column / direction passed in order = [ [ order, dir ] ]; } - else if ( ! $.isArray( order[0] ) ) { + else if ( order.length && ! $.isArray( order[0] ) ) { // Arguments passed in (list of 1D arrays) order = Array.prototype.slice.call( arguments ); } @@ -9037,38 +9132,6 @@ }; - /** - * DataTables utility methods - * - * This namespace provides helper methods that DataTables uses internally to - * create a DataTable, but which are not exclusively used only for DataTables. - * These methods can be used by extension authors to save the duplication of - * code. - * - * @namespace - */ - DataTable.util = { - /** - * Throttle the calls to a function. Arguments and context are maintained - * for the throttled function. - * - * @param {function} fn Function to be called - * @param {integer} freq Call frequency in mS - * @return {function} Wrapped function - */ - throttle: _fnThrottle, - - - /** - * Escape a string such that it can be used in a regular expression - * - * @param {string} sVal string to escape - * @returns {string} escaped string - */ - escapeRegex: _fnEscapeRegex - }; - - /** * Convert from camel case parameters to Hungarian notation. This is made public * for the extensions to provide the same ability as DataTables core to accept @@ -9305,7 +9368,7 @@ * @type string * @default Version number */ - DataTable.version = "1.10.10"; + DataTable.version = "1.10.12"; /** * Private data store, containing all of the settings objects that are @@ -14826,6 +14889,12 @@ * to make working with DataTables a little bit easier. */ + var __htmlEscapeEntities = function ( d ) { + return typeof d === 'string' ? + d.replace(//g, '>').replace(/"/g, '"') : + d; + }; + /** * Helpers for `columns.render`. * @@ -14864,9 +14933,10 @@ var flo = parseFloat( d ); // If NaN then there isn't much formatting that we can do - just - // return immediately + // return immediately, escaping any HTML (this was supposed to + // be a number after all) if ( isNaN( flo ) ) { - return d; + return __htmlEscapeEntities( d ); } d = Math.abs( flo ); @@ -14888,11 +14958,7 @@ text: function () { return { - display: function ( d ) { - return typeof d === 'string' ? - d.replace(//g, '>').replace(/"/g, '"') : - d; - } + display: __htmlEscapeEntities }; } }; diff --git a/app/assets/stylesheets/dataTables/extras/autoFill.dataTables.css b/app/assets/stylesheets/dataTables/extras/autoFill.dataTables.css new file mode 100755 index 0000000..e6a27b5 --- /dev/null +++ b/app/assets/stylesheets/dataTables/extras/autoFill.dataTables.css @@ -0,0 +1,92 @@ +div.dt-autofill-handle { + position: absolute; + height: 8px; + width: 8px; + z-index: 102; + box-sizing: border-box; + border: 1px solid #316ad1; + background: linear-gradient(to bottom, #abcffb 0%, #4989de 100%); +} + +div.dt-autofill-select { + position: absolute; + z-index: 1001; + background-color: #4989de; + background-image: repeating-linear-gradient(45deg, transparent, transparent 5px, rgba(255, 255, 255, 0.5) 5px, rgba(255, 255, 255, 0.5) 10px); +} +div.dt-autofill-select.top, div.dt-autofill-select.bottom { + height: 3px; + margin-top: -1px; +} +div.dt-autofill-select.left, div.dt-autofill-select.right { + width: 3px; + margin-left: -1px; +} + +div.dt-autofill-list { + position: fixed; + top: 50%; + left: 50%; + width: 500px; + margin-left: -250px; + background-color: white; + border-radius: 6px; + box-shadow: 0 0 5px #555; + border: 2px solid #444; + z-index: 11; + box-sizing: border-box; + padding: 1.5em 2em; +} +div.dt-autofill-list ul { + display: table; + margin: 0; + padding: 0; + list-style: none; + width: 100%; +} +div.dt-autofill-list ul li { + display: table-row; +} +div.dt-autofill-list ul li:last-child div.dt-autofill-question, div.dt-autofill-list ul li:last-child div.dt-autofill-button { + border-bottom: none; +} +div.dt-autofill-list ul li:hover { + background-color: #f6f6f6; +} +div.dt-autofill-list div.dt-autofill-question { + display: table-cell; + padding: 0.5em 0; + border-bottom: 1px solid #ccc; +} +div.dt-autofill-list div.dt-autofill-question input[type=number] { + padding: 6px; + width: 30px; + margin: -2px 0; +} +div.dt-autofill-list div.dt-autofill-button { + display: table-cell; + padding: 0.5em 0; + border-bottom: 1px solid #ccc; +} +div.dt-autofill-list div.dt-autofill-button button { + color: white; + margin: 0; + padding: 6px 12px; + text-align: center; + border: 1px solid #2e6da4; + background-color: #337ab7; + border-radius: 4px; + cursor: pointer; + vertical-align: middle; +} + +div.dt-autofill-background { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.7); + background: radial-gradient(ellipse farthest-corner at center, rgba(0, 0, 0, 0.3) 0%, rgba(0, 0, 0, 0.7) 100%); + z-index: 10; +} diff --git a/app/assets/stylesheets/dataTables/extras/colReorder.dataTables.css b/app/assets/stylesheets/dataTables/extras/colReorder.dataTables.css new file mode 100755 index 0000000..a2854c0 --- /dev/null +++ b/app/assets/stylesheets/dataTables/extras/colReorder.dataTables.css @@ -0,0 +1,11 @@ +table.DTCR_clonedTable.dataTable { + position: absolute !important; + background-color: rgba(255, 255, 255, 0.7); + z-index: 202; +} + +div.DTCR_pointer { + width: 1px; + background-color: #0259C4; + z-index: 201; +} diff --git a/app/assets/stylesheets/dataTables/extras/dataTables.autoFill.scss b/app/assets/stylesheets/dataTables/extras/dataTables.autoFill.scss deleted file mode 100644 index 4c85792..0000000 --- a/app/assets/stylesheets/dataTables/extras/dataTables.autoFill.scss +++ /dev/null @@ -1,24 +0,0 @@ -/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * - * AutoFill styles - */ - -div.AutoFill_filler { - display: none; - position: absolute; - height: 14px; - width: 14px; - background: image-url('dataTables/extras/filler.png') no-repeat center center; - z-index: 1002; -} - -div.AutoFill_border { - display: none; - position: absolute; - background-color: #0063dc; - z-index: 1001; - - box-shadow: 0px 0px 5px #76b4ff; - -moz-box-shadow: 0px 0px 5px #76b4ff; - -webkit-box-shadow: 0px 0px 5px #76b4ff; -} - diff --git a/app/assets/stylesheets/dataTables/extras/dataTables.colReorder.scss b/app/assets/stylesheets/dataTables/extras/dataTables.colReorder.scss deleted file mode 100644 index bdd6aa0..0000000 --- a/app/assets/stylesheets/dataTables/extras/dataTables.colReorder.scss +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Namespace DTCR - "DataTables ColReorder" plug-in - */ - -table.DTCR_clonedTable { - background-color: rgba(255, 255, 255, 0.7); - z-index: 202; -} - -div.DTCR_pointer { - width: 1px; - background-color: #0259C4; - z-index: 201; -} \ No newline at end of file diff --git a/app/assets/stylesheets/dataTables/extras/dataTables.fixedColumns.scss b/app/assets/stylesheets/dataTables/extras/dataTables.fixedColumns.scss deleted file mode 100644 index 0d6e18a..0000000 --- a/app/assets/stylesheets/dataTables/extras/dataTables.fixedColumns.scss +++ /dev/null @@ -1,25 +0,0 @@ - - -/* Block out what is behind the fixed column's header and footer */ -table.DTFC_Cloned thead, -table.DTFC_Cloned tfoot { - background-color: white; -} - -/* Block out the gap above the scrollbar on the right, when there is a fixed - * right column - */ -div.DTFC_Blocker { - background-color: white; -} - -div.DTFC_LeftWrapper table.dataTable, -div.DTFC_RightWrapper table.dataTable { - margin-bottom: 0; - z-index: 2; -} - -div.DTFC_LeftWrapper table.dataTable.no-footer, -div.DTFC_RightWrapper table.dataTable.no-footer { - border-bottom: none; -} diff --git a/app/assets/stylesheets/dataTables/extras/dataTables.fixedHeader.scss b/app/assets/stylesheets/dataTables/extras/dataTables.fixedHeader.scss deleted file mode 100644 index 724317a..0000000 --- a/app/assets/stylesheets/dataTables/extras/dataTables.fixedHeader.scss +++ /dev/null @@ -1,7 +0,0 @@ - - -div.FixedHeader_Cloned th, -div.FixedHeader_Cloned td { - background-color: white !important; -} - diff --git a/app/assets/stylesheets/dataTables/extras/dataTables.keyTable.scss b/app/assets/stylesheets/dataTables/extras/dataTables.keyTable.scss deleted file mode 100644 index 2759df1..0000000 --- a/app/assets/stylesheets/dataTables/extras/dataTables.keyTable.scss +++ /dev/null @@ -1,7 +0,0 @@ - - -table.KeyTable th.focus, -table.KeyTable td.focus { - outline: 3px solid #3366FF; - outline-offset: -3px; -} diff --git a/app/assets/stylesheets/dataTables/extras/dataTables.responsive.scss b/app/assets/stylesheets/dataTables/extras/dataTables.responsive.scss deleted file mode 100644 index ed657ff..0000000 --- a/app/assets/stylesheets/dataTables/extras/dataTables.responsive.scss +++ /dev/null @@ -1,149 +0,0 @@ - -// -// Mixins -// -@mixin control() { - display: block; - position: absolute; - color: white; - border: 2px solid white; - border-radius: 16px; - text-align: center; - line-height: 14px; - box-shadow: 0 0 3px #444; - box-sizing: content-box; -} - -@mixin control-open() { - content: '+'; - background-color: #31b131; -} - -@mixin control-close() { - content: '-'; - background-color: #d33333; -} - - -// -// Table styles -// -table.dataTable { - // Styling for the `inline` type - &.dtr-inline.collapsed > tbody { - > tr > td:first-child, - > tr > th:first-child { - position: relative; - padding-left: 30px; - cursor: pointer; - - &:before { - top: 8px; - left: 4px; - height: 16px; - width: 16px; - @include control; - @include control-open; - } - - &.dataTables_empty:before { - display: none; - } - } - - > tr.parent { - > td:first-child:before, - > th:first-child:before { - @include control-close; - } - } - - > tr.child td:before { - display: none; - } - } - - // DataTables' `compact` styling - &.dtr-inline.collapsed.compact > tbody { - > tr > td:first-child, - > tr > th:first-child { - padding-left: 27px; - - &:before { - top: 5px; - left: 4px; - height: 14px; - width: 14px; - border-radius: 14px; - line-height: 12px; - } - } - } - - - // Styling for the `column` type - &.dtr-column > tbody { - > tr > td.control, - > tr > th.control { - position: relative; - cursor: pointer; - - &:before { - top: 50%; - left: 50%; - height: 16px; - width: 16px; - margin-top: -10px; - margin-left: -10px; - @include control; - @include control-open; - } - } - - > tr.parent { - td.control:before, - th.control:before { - @include control-close; - } - } - } - - - // Child row styling - > tbody > tr.child { - padding: 0.5em 1em; - - &:hover { - background: transparent !important; - } - - ul { - display: inline-block; - list-style-type: none; - margin: 0; - padding: 0; - - li { - border-bottom: 1px solid #efefef; - padding: 0.5em 0; - - &:first-child { - padding-top: 0; - } - - &:last-child { - border-bottom: none; - } - } - } - - span.dtr-title { - display: inline-block; - min-width: 75px; - font-weight: bold; - } - - span.dtr-data {} - } -} - diff --git a/app/assets/stylesheets/dataTables/extras/dataTables.scroller.scss b/app/assets/stylesheets/dataTables/extras/dataTables.scroller.scss deleted file mode 100644 index 1e92238..0000000 --- a/app/assets/stylesheets/dataTables/extras/dataTables.scroller.scss +++ /dev/null @@ -1,44 +0,0 @@ - -/* - * Namespace: DTS (DataTables Scroller) - */ - -div.DTS tbody th, -div.DTS tbody td { - white-space: nowrap; -} - -div.DTS tbody tr.even { - background-color: white; -} - -div.DTS div.DTS_Loading { - position: absolute; - top: 50%; - left: 50%; - width: 200px; - height: 20px; - margin-top: -20px; - margin-left: -100px; - z-index: 1; - - border: 1px solid #999; - padding: 20px 0; - text-align: center; - background-color: white; - background-color: rgba(255, 255, 255, 0.5); -} - -div.DTS div.dataTables_scrollHead, -div.DTS div.dataTables_scrollFoot { - background-color: white; -} - -div.DTS div.dataTables_scrollBody { - z-index: 2; -} - -div.DTS div.dataTables_scroll { - background: image-url('dataTables/extras/loading-background.png') repeat 0 0; -} - diff --git a/app/assets/stylesheets/dataTables/extras/fixedColumns.dataTables.css b/app/assets/stylesheets/dataTables/extras/fixedColumns.dataTables.css new file mode 100755 index 0000000..9b94ffa --- /dev/null +++ b/app/assets/stylesheets/dataTables/extras/fixedColumns.dataTables.css @@ -0,0 +1,18 @@ +table.DTFC_Cloned thead, +table.DTFC_Cloned tfoot { + background-color: white; +} + +div.DTFC_Blocker { + background-color: white; +} + +div.DTFC_LeftWrapper table.dataTable, +div.DTFC_RightWrapper table.dataTable { + margin-bottom: 0; + z-index: 2; +} +div.DTFC_LeftWrapper table.dataTable.no-footer, +div.DTFC_RightWrapper table.dataTable.no-footer { + border-bottom: none; +} diff --git a/app/assets/stylesheets/dataTables/extras/fixedHeader.dataTables.css b/app/assets/stylesheets/dataTables/extras/fixedHeader.dataTables.css new file mode 100755 index 0000000..7750971 --- /dev/null +++ b/app/assets/stylesheets/dataTables/extras/fixedHeader.dataTables.css @@ -0,0 +1,19 @@ +table.fixedHeader-floating { + position: fixed !important; + background-color: white; +} + +table.fixedHeader-floating.no-footer { + border-bottom-width: 0; +} + +table.fixedHeader-locked { + position: absolute !important; + background-color: white; +} + +@media print { + table.fixedHeader-floating { + display: none; + } +} diff --git a/app/assets/stylesheets/dataTables/extras/keyTable.dataTables.css b/app/assets/stylesheets/dataTables/extras/keyTable.dataTables.css new file mode 100755 index 0000000..65e3a00 --- /dev/null +++ b/app/assets/stylesheets/dataTables/extras/keyTable.dataTables.css @@ -0,0 +1,5 @@ +table.dataTable th.focus, +table.dataTable td.focus { + outline: 3px solid #3366FF; + outline-offset: -1px; +} diff --git a/app/assets/stylesheets/dataTables/extras/responsive.dataTables.css b/app/assets/stylesheets/dataTables/extras/responsive.dataTables.css new file mode 100755 index 0000000..838f769 --- /dev/null +++ b/app/assets/stylesheets/dataTables/extras/responsive.dataTables.css @@ -0,0 +1,178 @@ +table.dataTable.dtr-inline.collapsed > tbody > tr > td.child, +table.dataTable.dtr-inline.collapsed > tbody > tr > th.child, +table.dataTable.dtr-inline.collapsed > tbody > tr > td.dataTables_empty { + cursor: default !important; +} +table.dataTable.dtr-inline.collapsed > tbody > tr > td.child:before, +table.dataTable.dtr-inline.collapsed > tbody > tr > th.child:before, +table.dataTable.dtr-inline.collapsed > tbody > tr > td.dataTables_empty:before { + display: none !important; +} +table.dataTable.dtr-inline.collapsed > tbody > tr > td:first-child, +table.dataTable.dtr-inline.collapsed > tbody > tr > th:first-child { + position: relative; + padding-left: 30px; + cursor: pointer; +} +table.dataTable.dtr-inline.collapsed > tbody > tr > td:first-child:before, +table.dataTable.dtr-inline.collapsed > tbody > tr > th:first-child:before { + top: 9px; + left: 4px; + height: 14px; + width: 14px; + display: block; + position: absolute; + color: white; + border: 2px solid white; + border-radius: 14px; + box-shadow: 0 0 3px #444; + box-sizing: content-box; + text-align: center; + font-family: 'Courier New', Courier, monospace; + line-height: 14px; + content: '+'; + background-color: #31b131; +} +table.dataTable.dtr-inline.collapsed > tbody > tr.parent > td:first-child:before, +table.dataTable.dtr-inline.collapsed > tbody > tr.parent > th:first-child:before { + content: '-'; + background-color: #d33333; +} +table.dataTable.dtr-inline.collapsed > tbody > tr.child td:before { + display: none; +} +table.dataTable.dtr-inline.collapsed.compact > tbody > tr > td:first-child, +table.dataTable.dtr-inline.collapsed.compact > tbody > tr > th:first-child { + padding-left: 27px; +} +table.dataTable.dtr-inline.collapsed.compact > tbody > tr > td:first-child:before, +table.dataTable.dtr-inline.collapsed.compact > tbody > tr > th:first-child:before { + top: 5px; + left: 4px; + height: 14px; + width: 14px; + border-radius: 14px; + line-height: 14px; + text-indent: 3px; +} +table.dataTable.dtr-column > tbody > tr > td.control, +table.dataTable.dtr-column > tbody > tr > th.control { + position: relative; + cursor: pointer; +} +table.dataTable.dtr-column > tbody > tr > td.control:before, +table.dataTable.dtr-column > tbody > tr > th.control:before { + top: 50%; + left: 50%; + height: 16px; + width: 16px; + margin-top: -10px; + margin-left: -10px; + display: block; + position: absolute; + color: white; + border: 2px solid white; + border-radius: 14px; + box-shadow: 0 0 3px #444; + box-sizing: content-box; + text-align: center; + font-family: 'Courier New', Courier, monospace; + line-height: 14px; + content: '+'; + background-color: #31b131; +} +table.dataTable.dtr-column > tbody > tr.parent td.control:before, +table.dataTable.dtr-column > tbody > tr.parent th.control:before { + content: '-'; + background-color: #d33333; +} +table.dataTable > tbody > tr.child { + padding: 0.5em 1em; +} +table.dataTable > tbody > tr.child:hover { + background: transparent !important; +} +table.dataTable > tbody > tr.child ul { + display: inline-block; + list-style-type: none; + margin: 0; + padding: 0; +} +table.dataTable > tbody > tr.child ul li { + border-bottom: 1px solid #efefef; + padding: 0.5em 0; +} +table.dataTable > tbody > tr.child ul li:first-child { + padding-top: 0; +} +table.dataTable > tbody > tr.child ul li:last-child { + border-bottom: none; +} +table.dataTable > tbody > tr.child span.dtr-title { + display: inline-block; + min-width: 75px; + font-weight: bold; +} + +div.dtr-modal { + position: fixed; + box-sizing: border-box; + top: 0; + left: 0; + height: 100%; + width: 100%; + z-index: 100; + padding: 10em 1em; +} +div.dtr-modal div.dtr-modal-display { + position: absolute; + top: 0; + left: 0; + bottom: 0; + right: 0; + width: 50%; + height: 50%; + overflow: auto; + margin: auto; + z-index: 102; + overflow: auto; + background-color: #f5f5f7; + border: 1px solid black; + border-radius: 0.5em; + box-shadow: 0 12px 30px rgba(0, 0, 0, 0.6); +} +div.dtr-modal div.dtr-modal-content { + position: relative; + padding: 1em; +} +div.dtr-modal div.dtr-modal-close { + position: absolute; + top: 6px; + right: 6px; + width: 22px; + height: 22px; + border: 1px solid #eaeaea; + background-color: #f9f9f9; + text-align: center; + border-radius: 3px; + cursor: pointer; + z-index: 12; +} +div.dtr-modal div.dtr-modal-close:hover { + background-color: #eaeaea; +} +div.dtr-modal div.dtr-modal-background { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 101; + background: rgba(0, 0, 0, 0.6); +} + +@media screen and (max-width: 767px) { + div.dtr-modal div.dtr-modal-display { + width: 95%; + } +} diff --git a/app/assets/stylesheets/dataTables/extras/rowReorder.dataTables.css b/app/assets/stylesheets/dataTables/extras/rowReorder.dataTables.css new file mode 100755 index 0000000..7c2548f --- /dev/null +++ b/app/assets/stylesheets/dataTables/extras/rowReorder.dataTables.css @@ -0,0 +1,22 @@ +table.dt-rowReorder-float { + position: absolute !important; + opacity: 0.8; + table-layout: fixed; + outline: 2px solid #888; + outline-offset: -2px; + z-index: 2001; +} + +tr.dt-rowReorder-moving { + outline: 2px solid #555; + outline-offset: -2px; +} + +body.dt-rowReorder-noOverflow { + overflow-x: hidden; +} + +table.dataTable td.reorder { + text-align: center; + cursor: move; +} diff --git a/app/assets/stylesheets/dataTables/extras/scroller.dataTables.css b/app/assets/stylesheets/dataTables/extras/scroller.dataTables.css new file mode 100755 index 0000000..528760d --- /dev/null +++ b/app/assets/stylesheets/dataTables/extras/scroller.dataTables.css @@ -0,0 +1,20 @@ +div.DTS { + display: block !important; +} +div.DTS tbody th, +div.DTS tbody td { + white-space: nowrap; +} +div.DTS div.DTS_Loading { + z-index: 1; +} +div.DTS div.dataTables_scrollBody { + background: repeating-linear-gradient(45deg, #edeeff, #edeeff 10px, white 10px, white 20px); +} +div.DTS div.dataTables_scrollBody table { + z-index: 2; +} +div.DTS div.dataTables_paginate, +div.DTS div.dataTables_length { + display: none; +} diff --git a/app/assets/stylesheets/dataTables/extras/select.dataTables.css b/app/assets/stylesheets/dataTables/extras/select.dataTables.css new file mode 100755 index 0000000..36f4878 --- /dev/null +++ b/app/assets/stylesheets/dataTables/extras/select.dataTables.css @@ -0,0 +1,100 @@ +table.dataTable tbody > tr.selected, +table.dataTable tbody > tr > .selected { + background-color: #B0BED9; +} +table.dataTable.stripe tbody > tr.odd.selected, +table.dataTable.stripe tbody > tr.odd > .selected, table.dataTable.display tbody > tr.odd.selected, +table.dataTable.display tbody > tr.odd > .selected { + background-color: #acbad4; +} +table.dataTable.hover tbody > tr.selected:hover, +table.dataTable.hover tbody > tr > .selected:hover, table.dataTable.display tbody > tr.selected:hover, +table.dataTable.display tbody > tr > .selected:hover { + background-color: #aab7d1; +} +table.dataTable.order-column tbody > tr.selected > .sorting_1, +table.dataTable.order-column tbody > tr.selected > .sorting_2, +table.dataTable.order-column tbody > tr.selected > .sorting_3, +table.dataTable.order-column tbody > tr > .selected, table.dataTable.display tbody > tr.selected > .sorting_1, +table.dataTable.display tbody > tr.selected > .sorting_2, +table.dataTable.display tbody > tr.selected > .sorting_3, +table.dataTable.display tbody > tr > .selected { + background-color: #acbad5; +} +table.dataTable.display tbody > tr.odd.selected > .sorting_1, table.dataTable.order-column.stripe tbody > tr.odd.selected > .sorting_1 { + background-color: #a6b4cd; +} +table.dataTable.display tbody > tr.odd.selected > .sorting_2, table.dataTable.order-column.stripe tbody > tr.odd.selected > .sorting_2 { + background-color: #a8b5cf; +} +table.dataTable.display tbody > tr.odd.selected > .sorting_3, table.dataTable.order-column.stripe tbody > tr.odd.selected > .sorting_3 { + background-color: #a9b7d1; +} +table.dataTable.display tbody > tr.even.selected > .sorting_1, table.dataTable.order-column.stripe tbody > tr.even.selected > .sorting_1 { + background-color: #acbad5; +} +table.dataTable.display tbody > tr.even.selected > .sorting_2, table.dataTable.order-column.stripe tbody > tr.even.selected > .sorting_2 { + background-color: #aebcd6; +} +table.dataTable.display tbody > tr.even.selected > .sorting_3, table.dataTable.order-column.stripe tbody > tr.even.selected > .sorting_3 { + background-color: #afbdd8; +} +table.dataTable.display tbody > tr.odd > .selected, table.dataTable.order-column.stripe tbody > tr.odd > .selected { + background-color: #a6b4cd; +} +table.dataTable.display tbody > tr.even > .selected, table.dataTable.order-column.stripe tbody > tr.even > .selected { + background-color: #acbad5; +} +table.dataTable.display tbody > tr.selected:hover > .sorting_1, table.dataTable.order-column.hover tbody > tr.selected:hover > .sorting_1 { + background-color: #a2aec7; +} +table.dataTable.display tbody > tr.selected:hover > .sorting_2, table.dataTable.order-column.hover tbody > tr.selected:hover > .sorting_2 { + background-color: #a3b0c9; +} +table.dataTable.display tbody > tr.selected:hover > .sorting_3, table.dataTable.order-column.hover tbody > tr.selected:hover > .sorting_3 { + background-color: #a5b2cb; +} +table.dataTable.display tbody > tr:hover > .selected, +table.dataTable.display tbody > tr > .selected:hover, table.dataTable.order-column.hover tbody > tr:hover > .selected, +table.dataTable.order-column.hover tbody > tr > .selected:hover { + background-color: #a2aec7; +} +table.dataTable td.select-checkbox { + position: relative; +} +table.dataTable td.select-checkbox:before, table.dataTable td.select-checkbox:after { + display: block; + position: absolute; + top: 1.2em; + left: 50%; + width: 12px; + height: 12px; + box-sizing: border-box; +} +table.dataTable td.select-checkbox:before { + content: ' '; + margin-top: -6px; + margin-left: -6px; + border: 1px solid black; + border-radius: 3px; +} +table.dataTable tr.selected td.select-checkbox:after { + content: '\2714'; + margin-top: -11px; + margin-left: -4px; + text-align: center; + text-shadow: 1px 1px #B0BED9, -1px -1px #B0BED9, 1px -1px #B0BED9, -1px 1px #B0BED9; +} + +div.dataTables_wrapper span.select-info, +div.dataTables_wrapper span.select-item { + margin-left: 0.5em; +} + +@media screen and (max-width: 640px) { + div.dataTables_wrapper span.select-info, + div.dataTables_wrapper span.select-item { + margin-left: 0; + display: block; + } +} diff --git a/lib/generators/jquery/datatables/install_generator.rb b/lib/generators/jquery/datatables/install_generator.rb index 9cc0504..a4f5a3d 100644 --- a/lib/generators/jquery/datatables/install_generator.rb +++ b/lib/generators/jquery/datatables/install_generator.rb @@ -66,7 +66,7 @@ def css_assets_map :bootstrap2 => [" *= require dataTables/bootstrap/2/jquery.dataTables.bootstrap\n"], :bootstrap3 => [" *= require dataTables/bootstrap/3/jquery.dataTables.bootstrap\n"], :foundation => [" *= require dataTables/jquery.dataTables.foundation\n"], - :responsive => [" *= require dataTables/extras/dataTables.responsive\n"] + :responsive => [" *= require dataTables/extras/responsive.dataTables\n"] } end end From 6b4a31b015f70564b9cd689f61dae5881288c8ca Mon Sep 17 00:00:00 2001 From: Jose Jimenez Date: Wed, 14 Dec 2016 15:26:08 -0600 Subject: [PATCH 2/6] Bump datatables to 1.10.12, bump extras --- app/assets/images/dataTables/sort_asc.png | Bin 1118 -> 160 bytes .../images/dataTables/sort_asc_disabled.png | Bin 2916 -> 148 bytes app/assets/images/dataTables/sort_both.png | Bin 1136 -> 201 bytes app/assets/images/dataTables/sort_desc.png | Bin 1127 -> 158 bytes .../images/dataTables/sort_desc_disabled.png | Bin 1045 -> 146 bytes ...s.bootstrap.js => dataTables.bootstrap.js} | 0 .../extras/bootstrap/autoFill.bootstrap.js | 43 + .../extras/bootstrap/buttons.bootstrap.js | 68 + .../extras/bootstrap/responsive.bootstrap.js | 81 + .../dataTables/extras/buttons.colVis.js | 198 + .../dataTables/extras/buttons.flash.js | 1339 ++ .../dataTables/extras/buttons.html5.js | 1345 ++ .../dataTables/extras/buttons.print.js | 176 + .../dataTables/extras/dataTables.autoFill.js | 1036 -- .../extras/dataTables.rowReorder.js | 156 +- .../extras/foundation/autoFill.foundation.js | 43 + .../extras/foundation/buttons.foundation.js | 85 + .../foundation/responsive.foundation.js | 62 + .../dataTables/extras/jquery.dataTables.js | 15278 ++++++++++++++++ .../extras/jqueryui/autoFill.jqueryui.js | 43 + .../extras/jqueryui/buttons.jqueryui.js | 62 + .../extras/jqueryui/responsive.jqueryui.js | 63 + .../dataTables.foundation.js} | 36 +- .../jqueryui/dataTables.jqueryui.js | 164 + app/assets/media/flashExport.swf | Bin 0 -> 64573 bytes .../bootstrap/3/dataTables.bootstrap.css | 185 + .../3/jquery.dataTables.bootstrap.scss | 371 - app/assets/stylesheets/dataTables/common.scss | 27 + .../extras/bootstrap/autoFill.bootstrap.css | 81 + .../extras/bootstrap/buttons.bootstrap.css | 102 + .../extras/bootstrap/colReorder.bootstrap.css | 11 + .../bootstrap/fixedColumns.bootstrap.css | 44 + .../bootstrap/fixedHeader.bootstrap.css | 20 + .../extras/bootstrap/keyTable.bootstrap.css | 5 + .../extras/bootstrap/responsive.bootstrap.css | 181 + .../extras/bootstrap/rowReorder.bootstrap.css | 22 + .../extras/bootstrap/scroller.bootstrap.css | 24 + .../extras/bootstrap/select.bootstrap.css | 110 + .../{ => dataTables}/autoFill.dataTables.css | 0 .../extras/dataTables/buttons.dataTables.css | 298 + .../colReorder.dataTables.css | 0 .../{ => dataTables}/dataTables.colVis.scss | 0 .../dataTables.colvis.jqueryui.scss | 0 .../dataTables.tableTools.scss | 0 .../fixedColumns.dataTables.css | 0 .../fixedHeader.dataTables.css | 0 .../{ => dataTables}/keyTable.dataTables.css | 0 .../responsive.dataTables.css | 0 .../rowReorder.dataTables.css | 0 .../{ => dataTables}/scroller.dataTables.css | 0 .../{ => dataTables}/select.dataTables.css | 0 .../extras/foundation/autoFill.foundation.css | 85 + .../extras/foundation/buttons.foundation.css | 129 + .../foundation/colReorder.foundation.css | 11 + .../foundation/fixedColumns.foundation.css | 27 + .../foundation/fixedHeader.foundation.css | 20 + .../extras/foundation/keyTable.foundation.css | 5 + .../foundation/responsive.foundation.css | 181 + .../foundation/rowReorder.foundation.css | 22 + .../extras/foundation/scroller.foundation.css | 17 + .../extras/foundation/select.foundation.css | 112 + .../extras/jqueryui/autoFill.jqueryui.css | 85 + .../extras/jqueryui/buttons.jqueryui.css | 162 + .../extras/jqueryui/colReorder.jqueryui.css | 11 + .../extras/jqueryui/fixedColumns.jqueryui.css | 8 + .../extras/jqueryui/fixedHeader.jqueryui.css | 15 + .../extras/jqueryui/keyTable.jqueryui.css | 5 + .../extras/jqueryui/responsive.jqueryui.css | 178 + .../extras/jqueryui/rowReorder.jqueryui.css | 22 + .../extras/jqueryui/scroller.jqueryui.css | 20 + .../extras/jqueryui/select.jqueryui.css | 100 + .../foundation/dataTables.foundation.css | 116 + ....dataTables.scss => jquery.dataTables.css} | 59 +- .../jquery.dataTables.foundation.scss | 222 - .../jquery.dataTables_themeroller.css | 416 + .../jqueryui/dataTables.jqueryui.css | 481 + app/assets/stylesheets/dataTables/mixins.scss | 89 + 77 files changed, 22655 insertions(+), 1702 deletions(-) mode change 100644 => 100755 app/assets/images/dataTables/sort_asc.png mode change 100644 => 100755 app/assets/images/dataTables/sort_asc_disabled.png mode change 100644 => 100755 app/assets/images/dataTables/sort_both.png mode change 100644 => 100755 app/assets/images/dataTables/sort_desc.png mode change 100644 => 100755 app/assets/images/dataTables/sort_desc_disabled.png rename app/assets/javascripts/dataTables/bootstrap/3/{jquery.dataTables.bootstrap.js => dataTables.bootstrap.js} (100%) create mode 100755 app/assets/javascripts/dataTables/extras/bootstrap/autoFill.bootstrap.js create mode 100755 app/assets/javascripts/dataTables/extras/bootstrap/buttons.bootstrap.js create mode 100755 app/assets/javascripts/dataTables/extras/bootstrap/responsive.bootstrap.js create mode 100755 app/assets/javascripts/dataTables/extras/buttons.colVis.js create mode 100755 app/assets/javascripts/dataTables/extras/buttons.flash.js create mode 100755 app/assets/javascripts/dataTables/extras/buttons.html5.js create mode 100755 app/assets/javascripts/dataTables/extras/buttons.print.js delete mode 100755 app/assets/javascripts/dataTables/extras/dataTables.autoFill.js mode change 100644 => 100755 app/assets/javascripts/dataTables/extras/dataTables.rowReorder.js create mode 100755 app/assets/javascripts/dataTables/extras/foundation/autoFill.foundation.js create mode 100755 app/assets/javascripts/dataTables/extras/foundation/buttons.foundation.js create mode 100755 app/assets/javascripts/dataTables/extras/foundation/responsive.foundation.js create mode 100755 app/assets/javascripts/dataTables/extras/jquery.dataTables.js create mode 100755 app/assets/javascripts/dataTables/extras/jqueryui/autoFill.jqueryui.js create mode 100755 app/assets/javascripts/dataTables/extras/jqueryui/buttons.jqueryui.js create mode 100755 app/assets/javascripts/dataTables/extras/jqueryui/responsive.jqueryui.js rename app/assets/javascripts/dataTables/{jquery.dataTables.foundation.js => foundation/dataTables.foundation.js} (79%) create mode 100755 app/assets/javascripts/dataTables/jqueryui/dataTables.jqueryui.js create mode 100755 app/assets/media/flashExport.swf create mode 100755 app/assets/stylesheets/dataTables/bootstrap/3/dataTables.bootstrap.css delete mode 100644 app/assets/stylesheets/dataTables/bootstrap/3/jquery.dataTables.bootstrap.scss create mode 100755 app/assets/stylesheets/dataTables/common.scss create mode 100755 app/assets/stylesheets/dataTables/extras/bootstrap/autoFill.bootstrap.css create mode 100755 app/assets/stylesheets/dataTables/extras/bootstrap/buttons.bootstrap.css create mode 100755 app/assets/stylesheets/dataTables/extras/bootstrap/colReorder.bootstrap.css create mode 100755 app/assets/stylesheets/dataTables/extras/bootstrap/fixedColumns.bootstrap.css create mode 100755 app/assets/stylesheets/dataTables/extras/bootstrap/fixedHeader.bootstrap.css create mode 100755 app/assets/stylesheets/dataTables/extras/bootstrap/keyTable.bootstrap.css create mode 100755 app/assets/stylesheets/dataTables/extras/bootstrap/responsive.bootstrap.css create mode 100755 app/assets/stylesheets/dataTables/extras/bootstrap/rowReorder.bootstrap.css create mode 100755 app/assets/stylesheets/dataTables/extras/bootstrap/scroller.bootstrap.css create mode 100755 app/assets/stylesheets/dataTables/extras/bootstrap/select.bootstrap.css rename app/assets/stylesheets/dataTables/extras/{ => dataTables}/autoFill.dataTables.css (100%) create mode 100755 app/assets/stylesheets/dataTables/extras/dataTables/buttons.dataTables.css rename app/assets/stylesheets/dataTables/extras/{ => dataTables}/colReorder.dataTables.css (100%) rename app/assets/stylesheets/dataTables/extras/{ => dataTables}/dataTables.colVis.scss (100%) rename app/assets/stylesheets/dataTables/extras/{ => dataTables}/dataTables.colvis.jqueryui.scss (100%) rename app/assets/stylesheets/dataTables/extras/{ => dataTables}/dataTables.tableTools.scss (100%) rename app/assets/stylesheets/dataTables/extras/{ => dataTables}/fixedColumns.dataTables.css (100%) rename app/assets/stylesheets/dataTables/extras/{ => dataTables}/fixedHeader.dataTables.css (100%) rename app/assets/stylesheets/dataTables/extras/{ => dataTables}/keyTable.dataTables.css (100%) rename app/assets/stylesheets/dataTables/extras/{ => dataTables}/responsive.dataTables.css (100%) rename app/assets/stylesheets/dataTables/extras/{ => dataTables}/rowReorder.dataTables.css (100%) rename app/assets/stylesheets/dataTables/extras/{ => dataTables}/scroller.dataTables.css (100%) rename app/assets/stylesheets/dataTables/extras/{ => dataTables}/select.dataTables.css (100%) create mode 100755 app/assets/stylesheets/dataTables/extras/foundation/autoFill.foundation.css create mode 100755 app/assets/stylesheets/dataTables/extras/foundation/buttons.foundation.css create mode 100755 app/assets/stylesheets/dataTables/extras/foundation/colReorder.foundation.css create mode 100755 app/assets/stylesheets/dataTables/extras/foundation/fixedColumns.foundation.css create mode 100755 app/assets/stylesheets/dataTables/extras/foundation/fixedHeader.foundation.css create mode 100755 app/assets/stylesheets/dataTables/extras/foundation/keyTable.foundation.css create mode 100755 app/assets/stylesheets/dataTables/extras/foundation/responsive.foundation.css create mode 100755 app/assets/stylesheets/dataTables/extras/foundation/rowReorder.foundation.css create mode 100755 app/assets/stylesheets/dataTables/extras/foundation/scroller.foundation.css create mode 100755 app/assets/stylesheets/dataTables/extras/foundation/select.foundation.css create mode 100755 app/assets/stylesheets/dataTables/extras/jqueryui/autoFill.jqueryui.css create mode 100755 app/assets/stylesheets/dataTables/extras/jqueryui/buttons.jqueryui.css create mode 100755 app/assets/stylesheets/dataTables/extras/jqueryui/colReorder.jqueryui.css create mode 100755 app/assets/stylesheets/dataTables/extras/jqueryui/fixedColumns.jqueryui.css create mode 100755 app/assets/stylesheets/dataTables/extras/jqueryui/fixedHeader.jqueryui.css create mode 100755 app/assets/stylesheets/dataTables/extras/jqueryui/keyTable.jqueryui.css create mode 100755 app/assets/stylesheets/dataTables/extras/jqueryui/responsive.jqueryui.css create mode 100755 app/assets/stylesheets/dataTables/extras/jqueryui/rowReorder.jqueryui.css create mode 100755 app/assets/stylesheets/dataTables/extras/jqueryui/scroller.jqueryui.css create mode 100755 app/assets/stylesheets/dataTables/extras/jqueryui/select.jqueryui.css create mode 100755 app/assets/stylesheets/dataTables/foundation/dataTables.foundation.css rename app/assets/stylesheets/dataTables/{jquery.dataTables.scss => jquery.dataTables.css} (93%) mode change 100644 => 100755 delete mode 100644 app/assets/stylesheets/dataTables/jquery.dataTables.foundation.scss create mode 100755 app/assets/stylesheets/dataTables/jquery.dataTables_themeroller.css create mode 100755 app/assets/stylesheets/dataTables/jqueryui/dataTables.jqueryui.css create mode 100755 app/assets/stylesheets/dataTables/mixins.scss diff --git a/app/assets/images/dataTables/sort_asc.png b/app/assets/images/dataTables/sort_asc.png old mode 100644 new mode 100755 index a88d7975fe9017e4e5f2289a94bd1ed66a5f59dc..e1ba61a8055fcb18273f2468d335572204667b1f GIT binary patch delta 131 zcmV-}0DS-62%rIwBz$K{L_t(I%VS^|vY@KrE*B6_0Ae<37*-0zzkoQKQiHUB_%RUw z2jbg6tW2&!j6ggWY7h)h1c{Slm?IFsLo@6X5ZjPy5HAq#Lo*15cK|UvDTXBi@lPQB ljs=6nBN;})C>RB3007>4Y%M*;NZSAa002ovPDHLkV1nAlFQWhe literal 1118 zcmbVLO=#0l98awuV{uMt6P_}u4rcI3idKFP2SpU%ZJIE?RL`X zK?Oyo=*5GG2SxDYK=7akJqV&hrl{aWJa`y*5xh+1%i2y4V}gO?edPc9{r;a9vjc}) zn|Cxb4AYwFmvVG%_ui)U^y_4!ujsO!qzYuv8YUIR!Aw%KiWp=JrG#@>(I!s4#N7H->?w+cxsH2#GA};A>g8lyFDGPKh!5)vuP_{)}*83+N zJUBU!S0_i+E{*Lu1iGsNB``2iK-CyCU7?y_mv{xb_pUh>ESZqe1Y2{eAZLMSIT%EO zFrdOH1W^=3p>Qk~I{J+k#s5zQ@j{%aIA!l^GQjJ zqA1Uc2%!{8qBKfMNh#9DCnKS_*uZ8?mnf!+8@f8xtz#prVg=E`3bCBLWsNmDAX~PG z<(4fQh=UOzE2?gKXRkc9XeI3Er?HlHECVd%SI}3`hy1_du3@$R$r(qT;k@Sft63UX zv;)2Ea_iH>^6+4jPK-lGM{Zw37Tz>~~zlHzO61x51(V4jcaKrcIVDG$-d>)z}S|7f!xxYhfUE}Kj zug_h&HZN}go22$5Ym1}P8~vYNx7-~$TWFJ;_nh!wFYSAQJF{CCo=xpK8^7?iY1^!H haOA^1D_`VC7fU=jcT diff --git a/app/assets/images/dataTables/sort_asc_disabled.png b/app/assets/images/dataTables/sort_asc_disabled.png old mode 100644 new mode 100755 index dcd7b7b8cab2304b374e6e4b9dc8c05faa2e1130..fb11dfe24a6c564cb7ddf8bc96703ebb121df1e7 GIT binary patch delta 130 zcmV-|0Db@D7L);y8G8f(007uo{zm`+09#2!K~yNuV_={uxWjd4!W}lk%1ZD2x|2;v zk=C8ZcmChGeMcFeBE~y&fr>zI;vGiZ${g>!gDU%U#|D=o-aGrCia>D39d_)>67T%H k^BoR<-ic%w1%nCz0Jq9Q1i6F>QUCw|07*qoM6N<$g4oPCPyhe` literal 2916 zcmV-q3!C(bP)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z0001wNklMmy=Vj0U5L|&Qa zAci?`!#UyS+{-Phs?wA?8dNtGmSy>F2e{#k$14mWWHsAkhwZm(PH{jFM}%BhffL5j zPa?R;fz7e}$TpbOg$;4VD3M>#uLE1h2KU4)uu9(LZ=be>wXk2no&x|foEHH5)G);W O0000Xe8plwo z)$YLo+(YFe{!XvZ2+e3VB4T$6G1Go|nR&+iI>fYuI%_znHIy+UOr$;(&?5*!N8n}! zi)Aah3nh#e3Bo}1&MJ1cYj?^|jg^nI>YYUlx7$V%S6#eh__w;5ADHFm> mrae+~(CGh$w8UQ;)8_;zs)PrLK4~fd0000O=#0l98WD1Hz^GK+C=e@fhgE~b#2$Ux^~T`1v5)mw1NlIe}zC z+ge9alrMQeN|SYi`>tC{zIG}!O_oO7k;UC8kBf>8sknx65F`zy2d1H-4fel=trX>@ z^-LCL<%6P%3`TJ=Ov$hao1$9VN|vJbLJV@SM>nJN{L>dS(6uOiBq(#Tm4F5Pz>p2Q zhq^NAP_G)%=(c^JwImV&17Zb~j6Ty5OHq1RS0sD)n5Dro1ouYi-$7;N6i6T&f*`~B zRW8JV5YO;|=5RQ?2M8R`v7Es2f}anI0YT(Au=3Evo2})=wA8uci&#;*fUzaAY_V8m ziU9`MJuDxIL|hF)@DqgJ88op{@|#XmML~j&YU>u(kqKNyC5HxZlqQk>PQkENWld+L zOr&6JNwHX-;oOueKw17j)G$`j4o<^A@%~fT$qZVMO+yC_*eYpUzR7iEi3uAj7}*(w z`YKgS6%a;F0a+l?9R#wX>ZWTi<7HV)nhsV>6(*%9O%xbi*F?TK!383rh#(|*p6}q} zd?z25;!?0(hzA2Li3(Rj>VN@FT;Xbexbdo7cN7eZc$T28pMYAYjSR4yvZz;&C0tc+ zg{xJMrKKvDCBd+6WB+P&<%mp=yImbyVyq56G|9BvWUP^I>ms=lb4e+lDSgg;Us`JO zKB6{wH+j~F#-A4FY3K3qm~Z6m@V6}oQ%8?p-E$dw`#0C$PJfmCV8)v}3>Ydha%`fZ zJk~G*M^A3LGk$Td;R`icF67R~`sBOHv)Hlqlc%$jy~9_oZJcNyWxkbb_O9u#|7hLF z-<-NMLzh3S0YA@8gd1Pt(Df|3@16Y-n=aSvsF@AkI`ioeFg>&H3bXU&vBnE6gIChkL+(Ey+0iB4Z$Eze7t_CX>Hq)$ diff --git a/app/assets/images/dataTables/sort_desc.png b/app/assets/images/dataTables/sort_desc.png old mode 100644 new mode 100755 index def071ed5afd264a036f6d9e75856366fd6ad153..0e156deb5f61d18f9e2ec5da4f6a8c94a5b4fb41 GIT binary patch delta 129 zcmV-{0Dk}H2%Z6uBzk2@L_t(I%VS^|1*2dTpn|)+00000NkvXXu0mjf)F(A( literal 1127 zcmbVMOK8+U7*1U&zKRu5sR)h{1;yRWWV^4}ShvZpU2*HWU2!iy(qy)cZ89;Lb+`3m zMbruv!GjkO!3qksP*5)lD)k}=Dp*ht-n@8G5m8XoN!zU+ih_Y;=AZe$?|)|~*Ri8v z(dtDU$2DZy)jV65`|pB!_H}d7Cv0h=sUqzpC0fy3%q0!dg+a#Bx^W(BM*oq=xP{{a zC9_bZ#q2IgCss)FbwX9kVQ7wPX{|b%-is;d!ri7V^Y8E8=YeU+{JuyQW*r6hnC$~D z?i}bS=mWia!r)uCftISo2rNuBP__DOPpZoN6tBeg{;|M=DHYl)^V3chvpJv;7lTL$ z26Y&PAc{gL+#HL=wg3?#C_qs_Vi3iouqZ(YW*(kdbB&UeSJN}Lm?ZN(lsb|iR4SEF zB^)Adw}29fgwG+0L8cM(`faLJgSNN6#-L(PcTI+l@K3y+Xf(g*^61+0|J+O6zN2mb?UNGh6GU@A{1+eF%d@N2(^XdVmhis(y25|iAr;gV=io5OsYy0 zB}Gv|2&GUGrBPB%s*yG^841Ug8a88lRI_zlvuiTDGuXsmv6A9qjS{y&NMEf3ay^6+ zuZK85>5PD^rkl1e`{kLAR>iJ)6dP%mSYRr@k~xQcDE=$%X{_--ITM&Og5Ml}G)wJ> zb)dhUZG9%p4iC23#JFrUCcmwHz{cugMoku~ue-kg{Mj0~%`FeCcz9jAdg}QET-kSG za`+2B_+lRTaeAVz>E`F1pN7h>B=BbGqcz13d%ywZR&4OjkNNrF_U}#EcXDGa@V52B z>JnIW7#s%CHi diff --git a/app/assets/images/dataTables/sort_desc_disabled.png b/app/assets/images/dataTables/sort_desc_disabled.png old mode 100644 new mode 100755 index 7824973cc60fc1841b16f2cb39323cefcdc3f942..c9fdd8a1502fda301682e907afde86bc450da10f GIT binary patch delta 128 zcmV-`0Du3L2$BJi8F>T%007uo{zm`+09i>yK~yNuV_+Bsg9_YrN?Q i|FYegaEFVwVG00qwn7AP_>L$50000KN??ODrK{#&I!}_Kh{rzS=%m2N%F- zAW={L0VZBJnRrkSCK{q1NKA||(ZmA>6Hgw9o;Z-;>)3_|u*vIt-(X0AeGY5Bm`Mgoq{>2>Xkbiu%Ds= zw2?31f^tL9kQr8eOxQDR!ltPHq-U$zG{j&MP8pU+Z@qp?149?-TQP-IYzdZ(;duv+ z&5z`@`Drbo)5+_g-xG*{39$-1bH;K7Po%550y+EF3=OIfJT20DK^2ryARz~WSeOlI zY%dFXxiA-r#^dp8fM+?DVR?q*LtI>l@B+(%+D8*_j$RaUa;D~sSR!4**cKS3TrP*p zkuY+m7%q`W_!>MPB8ZS%v9RieEVsL^AVXJk3>zEB0=}X;iDt1#lSubcFztq{<<`nX z3dVS<&2VAXPpJ-6l>b9bvw?PT4(`W$ps<^-*pSIV7tJ~vX67YQ8ELa7v~ZoP?{i~^a{W;-ZQ@ymjxh)IjDt*2O<6Dwh=q$vY$VY; zc&J{Ds~-?cjVm3>Wk@iL-`IZ|UB4pJ;~yJiON_?gLyJtiL&kbxZhV_OiPfx}%6s1@ zcXoG^ffrPJ;LQ4(`t<(ickJ1j|E0&fC8lSh8sUh5lwUg=l~QoqsK t`nTanN|e2@a&yVMdhy'); +}; + + +return DataTable.Buttons; +})); diff --git a/app/assets/javascripts/dataTables/extras/bootstrap/responsive.bootstrap.js b/app/assets/javascripts/dataTables/extras/bootstrap/responsive.bootstrap.js new file mode 100755 index 0000000..9eed7f5 --- /dev/null +++ b/app/assets/javascripts/dataTables/extras/bootstrap/responsive.bootstrap.js @@ -0,0 +1,81 @@ +/*! Bootstrap integration for DataTables' Responsive + * ©2015-2016 SpryMedia Ltd - datatables.net/license + */ + +(function( factory ){ + if ( typeof define === 'function' && define.amd ) { + // AMD + define( ['jquery', 'datatables.net-bs', 'datatables.net-responsive'], function ( $ ) { + return factory( $, window, document ); + } ); + } + else if ( typeof exports === 'object' ) { + // CommonJS + module.exports = function (root, $) { + if ( ! root ) { + root = window; + } + + if ( ! $ || ! $.fn.dataTable ) { + $ = require('datatables.net-bs')(root, $).$; + } + + if ( ! $.fn.dataTable.Responsive ) { + require('datatables.net-responsive')(root, $); + } + + return factory( $, root, root.document ); + }; + } + else { + // Browser + factory( jQuery, window, document ); + } +}(function( $, window, document, undefined ) { +'use strict'; +var DataTable = $.fn.dataTable; + + +var _display = DataTable.Responsive.display; +var _original = _display.modal; +var _modal = $( + '
      ', { - 'valign': 'top', - 'colSpan': _fnVisbleColumns( oSettings ), - 'class': oSettings.oClasses.sRowEmpty - } ).html( sZero ) )[0]; - } - - /* Header and footer callbacks */ - _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0], - _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); - - _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0], - _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); - - var body = $(oSettings.nTBody); - - body.children().detach(); - body.append( $(anRows) ); - - /* Call all required callback functions for the end of a draw */ - _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] ); - - /* Draw is complete, sorting and filtering must be as well */ - oSettings.bSorted = false; - oSettings.bFiltered = false; - oSettings.bDrawing = false; - } - - - /** - * Redraw the table - taking account of the various features which are enabled - * @param {object} oSettings dataTables settings object - * @param {boolean} [holdPosition] Keep the current paging position. By default - * the paging is reset to the first page - * @memberof DataTable#oApi - */ - function _fnReDraw( settings, holdPosition ) - { - var - features = settings.oFeatures, - sort = features.bSort, - filter = features.bFilter; - - if ( sort ) { - _fnSort( settings ); - } - - if ( filter ) { - _fnFilterComplete( settings, settings.oPreviousSearch ); - } - else { - // No filtering, so we want to just use the display master - settings.aiDisplay = settings.aiDisplayMaster.slice(); - } - - if ( holdPosition !== true ) { - settings._iDisplayStart = 0; - } - - // Let any modules know about the draw hold position state (used by - // scrolling internally) - settings._drawHold = holdPosition; - - _fnDraw( settings ); - - settings._drawHold = false; - } - - - /** - * Add the options to the page HTML for the table - * @param {object} oSettings dataTables settings object - * @memberof DataTable#oApi - */ - function _fnAddOptionsHtml ( oSettings ) - { - var classes = oSettings.oClasses; - var table = $(oSettings.nTable); - var holding = $('
      ').insertBefore( table ); // Holding element for speed - var features = oSettings.oFeatures; - - // All DataTables are wrapped in a div - var insert = $('
      ', { - id: oSettings.sTableId+'_wrapper', - 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter) - } ); - - oSettings.nHolding = holding[0]; - oSettings.nTableWrapper = insert[0]; - oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling; - - /* Loop over the user set positioning and place the elements as needed */ - var aDom = oSettings.sDom.split(''); - var featureNode, cOption, nNewNode, cNext, sAttr, j; - for ( var i=0 ; i')[0]; - - /* Check to see if we should append an id and/or a class name to the container */ - cNext = aDom[i+1]; - if ( cNext == "'" || cNext == '"' ) - { - sAttr = ""; - j = 2; - while ( aDom[i+j] != cNext ) - { - sAttr += aDom[i+j]; - j++; - } - - /* Replace jQuery UI constants @todo depreciated */ - if ( sAttr == "H" ) - { - sAttr = classes.sJUIHeader; - } - else if ( sAttr == "F" ) - { - sAttr = classes.sJUIFooter; - } - - /* The attribute can be in the format of "#id.class", "#id" or "class" This logic - * breaks the string into parts and applies them as needed - */ - if ( sAttr.indexOf('.') != -1 ) - { - var aSplit = sAttr.split('.'); - nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1); - nNewNode.className = aSplit[1]; - } - else if ( sAttr.charAt(0) == "#" ) - { - nNewNode.id = sAttr.substr(1, sAttr.length-1); - } - else - { - nNewNode.className = sAttr; - } - - i += j; /* Move along the position array */ - } - - insert.append( nNewNode ); - insert = $(nNewNode); - } - else if ( cOption == '>' ) - { - /* End container div */ - insert = insert.parent(); + var api = this.api( true ); + + if ( iColumn === null || iColumn === undefined ) { + api.search( sInput, bRegex, bSmart, bCaseInsensitive ); } - // @todo Move options into their own plugins? - else if ( cOption == 'l' && features.bPaginate && features.bLengthChange ) - { - /* Length */ - featureNode = _fnFeatureHtmlLength( oSettings ); + else { + api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive ); } - else if ( cOption == 'f' && features.bFilter ) - { - /* Filter */ - featureNode = _fnFeatureHtmlFilter( oSettings ); + + api.draw(); + }; + + + /** + * Get the data for the whole table, an individual row or an individual cell based on the + * provided parameters. + * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as + * a TR node then the data source for the whole row will be returned. If given as a + * TD/TH cell node then iCol will be automatically calculated and the data for the + * cell returned. If given as an integer, then this is treated as the aoData internal + * data index for the row (see fnGetPosition) and the data for that row used. + * @param {int} [col] Optional column index that you want the data of. + * @returns {array|object|string} If mRow is undefined, then the data for all rows is + * returned. If mRow is defined, just data for that row, and is iCol is + * defined, only data for the designated cell is returned. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * // Row data + * $(document).ready(function() { + * oTable = $('#example').dataTable(); + * + * oTable.$('tr').click( function () { + * var data = oTable.fnGetData( this ); + * // ... do something with the array / object of data for the row + * } ); + * } ); + * + * @example + * // Individual cell data + * $(document).ready(function() { + * oTable = $('#example').dataTable(); + * + * oTable.$('td').click( function () { + * var sData = oTable.fnGetData( this ); + * alert( 'The cell clicked on had the value of '+sData ); + * } ); + * } ); + */ + this.fnGetData = function( src, col ) + { + var api = this.api( true ); + + if ( src !== undefined ) { + var type = src.nodeName ? src.nodeName.toLowerCase() : ''; + + return col !== undefined || type == 'td' || type == 'th' ? + api.cell( src, col ).data() : + api.row( src ).data() || null; } - else if ( cOption == 'r' && features.bProcessing ) - { - /* pRocessing */ - featureNode = _fnFeatureHtmlProcessing( oSettings ); + + return api.data().toArray(); + }; + + + /** + * Get an array of the TR nodes that are used in the table's body. Note that you will + * typically want to use the '$' API method in preference to this as it is more + * flexible. + * @param {int} [iRow] Optional row index for the TR element you want + * @returns {array|node} If iRow is undefined, returns an array of all TR elements + * in the table's body, or iRow is defined, just the TR element requested. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Get the nodes from the table + * var nNodes = oTable.fnGetNodes( ); + * } ); + */ + this.fnGetNodes = function( iRow ) + { + var api = this.api( true ); + + return iRow !== undefined ? + api.row( iRow ).node() : + api.rows().nodes().flatten().toArray(); + }; + + + /** + * Get the array indexes of a particular cell from it's DOM element + * and column index including hidden columns + * @param {node} node this can either be a TR, TD or TH in the table's body + * @returns {int} If nNode is given as a TR, then a single index is returned, or + * if given as a cell, an array of [row index, column index (visible), + * column index (all)] is given. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * $('#example tbody td').click( function () { + * // Get the position of the current data from the node + * var aPos = oTable.fnGetPosition( this ); + * + * // Get the data array for this row + * var aData = oTable.fnGetData( aPos[0] ); + * + * // Update the data array and return the value + * aData[ aPos[1] ] = 'clicked'; + * this.innerHTML = 'clicked'; + * } ); + * + * // Init DataTables + * oTable = $('#example').dataTable(); + * } ); + */ + this.fnGetPosition = function( node ) + { + var api = this.api( true ); + var nodeName = node.nodeName.toUpperCase(); + + if ( nodeName == 'TR' ) { + return api.row( node ).index(); } - else if ( cOption == 't' ) - { - /* Table */ - featureNode = _fnFeatureHtmlTable( oSettings ); + else if ( nodeName == 'TD' || nodeName == 'TH' ) { + var cell = api.cell( node ).index(); + + return [ + cell.row, + cell.columnVisible, + cell.column + ]; } - else if ( cOption == 'i' && features.bInfo ) - { - /* Info */ - featureNode = _fnFeatureHtmlInfo( oSettings ); + return null; + }; + + + /** + * Check to see if a row is 'open' or not. + * @param {node} nTr the table row to check + * @returns {boolean} true if the row is currently open, false otherwise + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable; + * + * // 'open' an information row when a row is clicked on + * $('#example tbody tr').click( function () { + * if ( oTable.fnIsOpen(this) ) { + * oTable.fnClose( this ); + * } else { + * oTable.fnOpen( this, "Temporary row opened", "info_row" ); + * } + * } ); + * + * oTable = $('#example').dataTable(); + * } ); + */ + this.fnIsOpen = function( nTr ) + { + return this.api( true ).row( nTr ).child.isShown(); + }; + + + /** + * This function will place a new row directly after a row which is currently + * on display on the page, with the HTML contents that is passed into the + * function. This can be used, for example, to ask for confirmation that a + * particular record should be deleted. + * @param {node} nTr The table row to 'open' + * @param {string|node|jQuery} mHtml The HTML to put into the row + * @param {string} sClass Class to give the new TD cell + * @returns {node} The row opened. Note that if the table row passed in as the + * first parameter, is not found in the table, this method will silently + * return. + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable; + * + * // 'open' an information row when a row is clicked on + * $('#example tbody tr').click( function () { + * if ( oTable.fnIsOpen(this) ) { + * oTable.fnClose( this ); + * } else { + * oTable.fnOpen( this, "Temporary row opened", "info_row" ); + * } + * } ); + * + * oTable = $('#example').dataTable(); + * } ); + */ + this.fnOpen = function( nTr, mHtml, sClass ) + { + return this.api( true ) + .row( nTr ) + .child( mHtml, sClass ) + .show() + .child()[0]; + }; + + + /** + * Change the pagination - provides the internal logic for pagination in a simple API + * function. With this function you can have a DataTables table go to the next, + * previous, first or last pages. + * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last" + * or page number to jump to (integer), note that page 0 is the first page. + * @param {bool} [bRedraw=true] Redraw the table or not + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * oTable.fnPageChange( 'next' ); + * } ); + */ + this.fnPageChange = function ( mAction, bRedraw ) + { + var api = this.api( true ).page( mAction ); + + if ( bRedraw === undefined || bRedraw ) { + api.draw(false); } - else if ( cOption == 'p' && features.bPaginate ) - { - /* Pagination */ - featureNode = _fnFeatureHtmlPaginate( oSettings ); + }; + + + /** + * Show a particular column + * @param {int} iCol The column whose display should be changed + * @param {bool} bShow Show (true) or hide (false) the column + * @param {bool} [bRedraw=true] Redraw the table or not + * @dtopt API + * @deprecated Since v1.10 + * + * @example + * $(document).ready(function() { + * var oTable = $('#example').dataTable(); + * + * // Hide the second column after initialisation + * oTable.fnSetColumnVis( 1, false ); + * } ); + */ + this.fnSetColumnVis = function ( iCol, bShow, bRedraw ) + { + var api = this.api( true ).column( iCol ).visible( bShow ); + + if ( bRedraw === undefined || bRedraw ) { + api.columns.adjust().draw(); } - else if ( DataTable.ext.feature.length !== 0 ) - { - /* Plug-in features */ - var aoFeatures = DataTable.ext.feature; - for ( var k=0, kLen=aoFeatures.length ; k
      ').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] : - data.anCells[ colIdx ]; - } + if ( tr ) { + var id = settings.rowIdFn( data ); + + if ( id ) { + tr.id = id; + } + if ( data.DT_RowClass ) { + // Remove any classes added by DT_RowClass before + var a = data.DT_RowClass.split(' '); + row.__rowc = row.__rowc ? + _unique( row.__rowc.concat( a ) ) : + a; - /** - * Get the maximum strlen for each data column - * @param {object} settings dataTables settings object - * @param {int} colIdx column of interest - * @returns {string} max string length for each column - * @memberof DataTable#oApi - */ - function _fnGetMaxLenString( settings, colIdx ) - { - var s, max=-1, maxIdx = -1; + $(tr) + .removeClass( row.__rowc.join(' ') ) + .addClass( data.DT_RowClass ); + } - for ( var i=0, ien=settings.aoData.length ; i max ) { - max = s.length; - maxIdx = i; + if ( data.DT_RowData ) { + $(tr).data( data.DT_RowData ); } } - - return maxIdx; } /** - * Append a CSS unit (only if required) to a string - * @param {string} value to css-ify - * @returns {string} value with css unit + * Create the HTML header for the table + * @param {object} oSettings dataTables settings object * @memberof DataTable#oApi */ - function _fnStringToCss( s ) + function _fnBuildHead( oSettings ) { - if ( s === null ) { - return '0px'; - } + var i, ien, cell, row, column; + var thead = oSettings.nTHead; + var tfoot = oSettings.nTFoot; + var createHeader = $('th, td', thead).length === 0; + var classes = oSettings.oClasses; + var columns = oSettings.aoColumns; - if ( typeof s == 'number' ) { - return s < 0 ? - '0px' : - s+'px'; + if ( createHeader ) { + row = $('
      ', { + 'valign': 'top', + 'colSpan': _fnVisbleColumns( oSettings ), + 'class': oSettings.oClasses.sRowEmpty + } ).html( sZero ) )[0]; } - } + /* Header and footer callbacks */ + _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0], + _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); + + _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0], + _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] ); - /** - * Function to run on user sort request - * @param {object} settings dataTables settings object - * @param {node} attachTo node to attach the handler to - * @param {int} colIdx column sorting index - * @param {boolean} [append=false] Append the requested sort to the existing - * sort if true (i.e. multi-column sort) - * @param {function} [callback] callback function - * @memberof DataTable#oApi - */ - function _fnSortListener ( settings, colIdx, append, callback ) - { - var col = settings.aoColumns[ colIdx ]; - var sorting = settings.aaSorting; - var asSorting = col.asSorting; - var nextSortIdx; - var next = function ( a, overflow ) { - var idx = a._idx; - if ( idx === undefined ) { - idx = $.inArray( a[1], asSorting ); - } + var body = $(oSettings.nTBody); - return idx+1 < asSorting.length ? - idx+1 : - overflow ? - null : - 0; - }; + body.children().detach(); + body.append( $(anRows) ); - // Convert to 2D array if needed - if ( typeof sorting[0] === 'number' ) { - sorting = settings.aaSorting = [ sorting ]; - } + /* Call all required callback functions for the end of a draw */ + _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] ); - // If appending the sort then we are multi-column sorting - if ( append && settings.oFeatures.bSortMulti ) { - // Are we already doing some kind of sort on this column? - var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') ); + /* Draw is complete, sorting and filtering must be as well */ + oSettings.bSorted = false; + oSettings.bFiltered = false; + oSettings.bDrawing = false; + } - if ( sortIdx !== -1 ) { - // Yes, modify the sort - nextSortIdx = next( sorting[sortIdx], true ); - if ( nextSortIdx === null && sorting.length === 1 ) { - nextSortIdx = 0; // can't remove sorting completely - } + /** + * Redraw the table - taking account of the various features which are enabled + * @param {object} oSettings dataTables settings object + * @param {boolean} [holdPosition] Keep the current paging position. By default + * the paging is reset to the first page + * @memberof DataTable#oApi + */ + function _fnReDraw( settings, holdPosition ) + { + var + features = settings.oFeatures, + sort = features.bSort, + filter = features.bFilter; - if ( nextSortIdx === null ) { - sorting.splice( sortIdx, 1 ); - } - else { - sorting[sortIdx][1] = asSorting[ nextSortIdx ]; - sorting[sortIdx]._idx = nextSortIdx; - } - } - else { - // No sort on this column yet - sorting.push( [ colIdx, asSorting[0], 0 ] ); - sorting[sorting.length-1]._idx = 0; - } + if ( sort ) { + _fnSort( settings ); } - else if ( sorting.length && sorting[0][0] == colIdx ) { - // Single column - already sorting on this column, modify the sort - nextSortIdx = next( sorting[0] ); - sorting.length = 1; - sorting[0][1] = asSorting[ nextSortIdx ]; - sorting[0]._idx = nextSortIdx; + if ( filter ) { + _fnFilterComplete( settings, settings.oPreviousSearch ); } else { - // Single column - sort only on this column - sorting.length = 0; - sorting.push( [ colIdx, asSorting[0] ] ); - sorting[0]._idx = 0; + // No filtering, so we want to just use the display master + settings.aiDisplay = settings.aiDisplayMaster.slice(); } - // Run the sort by calling a full redraw - _fnReDraw( settings ); - - // callback used for async user interaction - if ( typeof callback == 'function' ) { - callback( settings ); + if ( holdPosition !== true ) { + settings._iDisplayStart = 0; } + + // Let any modules know about the draw hold position state (used by + // scrolling internally) + settings._drawHold = holdPosition; + + _fnDraw( settings ); + + settings._drawHold = false; } /** - * Attach a sort handler (click) to a node - * @param {object} settings dataTables settings object - * @param {node} attachTo node to attach the handler to - * @param {int} colIdx column sorting index - * @param {function} [callback] callback function + * Add the options to the page HTML for the table + * @param {object} oSettings dataTables settings object * @memberof DataTable#oApi */ - function _fnSortAttachListener ( settings, attachTo, colIdx, callback ) + function _fnAddOptionsHtml ( oSettings ) { - var col = settings.aoColumns[ colIdx ]; + var classes = oSettings.oClasses; + var table = $(oSettings.nTable); + var holding = $('
      ').insertBefore( table ); // Holding element for speed + var features = oSettings.oFeatures; - _fnBindAction( attachTo, {}, function (e) { - /* If the column is not sortable - don't to anything */ - if ( col.bSortable === false ) { - return; - } + // All DataTables are wrapped in a div + var insert = $('
      ', { + id: oSettings.sTableId+'_wrapper', + 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter) + } ); - // If processing is enabled use a timeout to allow the processing - // display to be shown - otherwise to it synchronously - if ( settings.oFeatures.bProcessing ) { - _fnProcessingDisplay( settings, true ); + oSettings.nHolding = holding[0]; + oSettings.nTableWrapper = insert[0]; + oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling; - setTimeout( function() { - _fnSortListener( settings, colIdx, e.shiftKey, callback ); + /* Loop over the user set positioning and place the elements as needed */ + var aDom = oSettings.sDom.split(''); + var featureNode, cOption, nNewNode, cNext, sAttr, j; + for ( var i=0 ; i')[0]; + + /* Check to see if we should append an id and/or a class name to the container */ + cNext = aDom[i+1]; + if ( cNext == "'" || cNext == '"' ) + { + sAttr = ""; + j = 2; + while ( aDom[i+j] != cNext ) + { + sAttr += aDom[i+j]; + j++; } - }, 0 ); - } - else { - _fnSortListener( settings, colIdx, e.shiftKey, callback ); - } - } ); - } + /* Replace jQuery UI constants @todo depreciated */ + if ( sAttr == "H" ) + { + sAttr = classes.sJUIHeader; + } + else if ( sAttr == "F" ) + { + sAttr = classes.sJUIFooter; + } - /** - * Set the sorting classes on table's body, Note: it is safe to call this function - * when bSort and bSortClasses are false - * @param {object} oSettings dataTables settings object - * @memberof DataTable#oApi - */ - function _fnSortingClasses( settings ) - { - var oldSort = settings.aLastSort; - var sortClass = settings.oClasses.sSortColumn; - var sort = _fnSortFlatten( settings ); - var features = settings.oFeatures; - var i, ien, colIdx; + /* The attribute can be in the format of "#id.class", "#id" or "class" This logic + * breaks the string into parts and applies them as needed + */ + if ( sAttr.indexOf('.') != -1 ) + { + var aSplit = sAttr.split('.'); + nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1); + nNewNode.className = aSplit[1]; + } + else if ( sAttr.charAt(0) == "#" ) + { + nNewNode.id = sAttr.substr(1, sAttr.length-1); + } + else + { + nNewNode.className = sAttr; + } - if ( features.bSort && features.bSortClasses ) { - // Remove old sorting classes - for ( i=0, ien=oldSort.length ; i' ) + { + /* End container div */ + insert = insert.parent(); + } + // @todo Move options into their own plugins? + else if ( cOption == 'l' && features.bPaginate && features.bLengthChange ) + { + /* Length */ + featureNode = _fnFeatureHtmlLength( oSettings ); + } + else if ( cOption == 'f' && features.bFilter ) + { + /* Filter */ + featureNode = _fnFeatureHtmlFilter( oSettings ); + } + else if ( cOption == 'r' && features.bProcessing ) + { + /* pRocessing */ + featureNode = _fnFeatureHtmlProcessing( oSettings ); + } + else if ( cOption == 't' ) + { + /* Table */ + featureNode = _fnFeatureHtmlTable( oSettings ); + } + else if ( cOption == 'i' && features.bInfo ) + { + /* Info */ + featureNode = _fnFeatureHtmlInfo( oSettings ); + } + else if ( cOption == 'p' && features.bPaginate ) + { + /* Pagination */ + featureNode = _fnFeatureHtmlPaginate( oSettings ); + } + else if ( DataTable.ext.feature.length !== 0 ) + { + /* Plug-in features */ + var aoFeatures = DataTable.ext.feature; + for ( var k=0, kLen=aoFeatures.length ; k 0 && state.time < +new Date() - (duration*1000) ) { - return; + var ajaxData; + var ajax = oSettings.ajax; + var instance = oSettings.oInstance; + var callback = function ( json ) { + _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] ); + fn( json ); + }; + + if ( $.isPlainObject( ajax ) && ajax.data ) + { + ajaxData = ajax.data; + + var newData = $.isFunction( ajaxData ) ? + ajaxData( data, oSettings ) : // fn can manipulate data or return + ajaxData; // an object object or array to merge + + // If the function returned something, use that alone + data = $.isFunction( ajaxData ) && newData ? + newData : + $.extend( true, data, newData ); + + // Remove the data property as we've resolved it already and don't want + // jQuery to do it again (it is restored at the end of the function) + delete ajax.data; } - // Number of columns have changed - all bets are off, no restore of settings - if ( columns.length !== state.columns.length ) { - return; - } + var baseAjax = { + "data": data, + "success": function (json) { + var error = json.error || json.sError; + if ( error ) { + _fnLog( oSettings, 0, error ); + } + + oSettings.json = json; + callback( json ); + }, + "dataType": "json", + "cache": false, + "type": oSettings.sServerMethod, + "error": function (xhr, error, thrown) { + var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] ); + + if ( $.inArray( true, ret ) === -1 ) { + if ( error == "parsererror" ) { + _fnLog( oSettings, 0, 'Invalid JSON response', 1 ); + } + else if ( xhr.readyState === 4 ) { + _fnLog( oSettings, 0, 'Ajax error', 7 ); + } + } + + _fnProcessingDisplay( oSettings, false ); + } + }; + + // Store the data submitted for the API + oSettings.oAjaxData = data; - // Store the saved state so it might be accessed at any time - settings.oLoadedState = $.extend( true, {}, state ); + // Allow plug-ins and external processes to modify the data + _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] ); - // Restore key features - todo - for 1.11 this needs to be done by - // subscribed events - if ( state.start !== undefined ) { - settings._iDisplayStart = state.start; - settings.iInitDisplayStart = state.start; - } - if ( state.length !== undefined ) { - settings._iDisplayLength = state.length; + if ( oSettings.fnServerData ) + { + // DataTables 1.9- compatibility + oSettings.fnServerData.call( instance, + oSettings.sAjaxSource, + $.map( data, function (val, key) { // Need to convert back to 1.9 trad format + return { name: key, value: val }; + } ), + callback, + oSettings + ); } - - // Order - if ( state.order !== undefined ) { - settings.aaSorting = []; - $.each( state.order, function ( i, col ) { - settings.aaSorting.push( col[0] >= columns.length ? - [ 0, col[1] ] : - col - ); - } ); + else if ( oSettings.sAjaxSource || typeof ajax === 'string' ) + { + // DataTables 1.9- compatibility + oSettings.jqXHR = $.ajax( $.extend( baseAjax, { + url: ajax || oSettings.sAjaxSource + } ) ); } - - // Search - if ( state.search !== undefined ) { - $.extend( settings.oPreviousSearch, _fnSearchToHung( state.search ) ); + else if ( $.isFunction( ajax ) ) + { + // Is a function - let the caller define what needs to be done + oSettings.jqXHR = ajax.call( instance, data, callback, oSettings ); } + else + { + // Object to extend the base settings + oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) ); - // Columns - for ( i=0, ien=state.columns.length ; i'; - if ( callbackArr ) { - ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) { - return val.fn.apply( settings.oInstance, args ); - } ); - } + var str = language.sSearch; + str = str.match(/_INPUT_/) ? + str.replace('_INPUT_', input) : + str+input; - if ( eventName !== null ) { - var e = $.Event( eventName+'.dt' ); + var filter = $('
      ', { + 'id': ! features.f ? tableId+'_filter' : null, + 'class': classes.sFilter + } ) + .append( $('
      ').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] : + data.anCells[ colIdx ]; + } + + + /** + * Get the maximum strlen for each data column + * @param {object} settings dataTables settings object + * @param {int} colIdx column of interest + * @returns {string} max string length for each column + * @memberof DataTable#oApi + */ + function _fnGetMaxLenString( settings, colIdx ) + { + var s, max=-1, maxIdx = -1; + + for ( var i=0, ien=settings.aoData.length ; i max ) { + max = s.length; + maxIdx = i; } - - // Apply the defaults and init options to make a single init object will all - // options defined from defaults and instance options. - oInit = _fnExtend( $.extend( true, {}, defaults ), oInit ); - - - // Map the initialisation options onto the settings object - _fnMap( oSettings.oFeatures, oInit, [ - "bPaginate", - "bLengthChange", - "bFilter", - "bSort", - "bSortMulti", - "bInfo", - "bProcessing", - "bAutoWidth", - "bSortClasses", - "bServerSide", - "bDeferRender" - ] ); - _fnMap( oSettings, oInit, [ - "asStripeClasses", - "ajax", - "fnServerData", - "fnFormatNumber", - "sServerMethod", - "aaSorting", - "aaSortingFixed", - "aLengthMenu", - "sPaginationType", - "sAjaxSource", - "sAjaxDataProp", - "iStateDuration", - "sDom", - "bSortCellsTop", - "iTabIndex", - "fnStateLoadCallback", - "fnStateSaveCallback", - "renderer", - "searchDelay", - "rowId", - [ "iCookieDuration", "iStateDuration" ], // backwards compat - [ "oSearch", "oPreviousSearch" ], - [ "aoSearchCols", "aoPreSearchCols" ], - [ "iDisplayLength", "_iDisplayLength" ], - [ "bJQueryUI", "bJUI" ] - ] ); - _fnMap( oSettings.oScroll, oInit, [ - [ "sScrollX", "sX" ], - [ "sScrollXInner", "sXInner" ], - [ "sScrollY", "sY" ], - [ "bScrollCollapse", "bCollapse" ] - ] ); - _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" ); - - /* Callback functions which are array driven */ - _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' ); - _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' ); - _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user' ); - _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user' ); - _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user' ); - _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user' ); - _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user' ); - _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user' ); - _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' ); - _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' ); - _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' ); - - oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId ); - - /* Browser support detection */ - _fnBrowserDetect( oSettings ); - - var oClasses = oSettings.oClasses; - - // @todo Remove in 1.11 - if ( oInit.bJQueryUI ) - { - /* Use the JUI classes object for display. You could clone the oStdClasses object if - * you want to have multiple tables with multiple independent classes - */ - $.extend( oClasses, DataTable.ext.oJUIClasses, oInit.oClasses ); - - if ( oInit.sDom === defaults.sDom && defaults.sDom === "lfrtip" ) - { - /* Set the DOM to use a layout suitable for jQuery UI's theming */ - oSettings.sDom = '<"H"lfr>t<"F"ip>'; + } + + return maxIdx; + } + + + /** + * Append a CSS unit (only if required) to a string + * @param {string} value to css-ify + * @returns {string} value with css unit + * @memberof DataTable#oApi + */ + function _fnStringToCss( s ) + { + if ( s === null ) { + return '0px'; + } + + if ( typeof s == 'number' ) { + return s < 0 ? + '0px' : + s+'px'; + } + + // Check it has a unit character already + return s.match(/\d$/) ? + s+'px' : + s; + } + + + + function _fnSortFlatten ( settings ) + { + var + i, iLen, k, kLen, + aSort = [], + aiOrig = [], + aoColumns = settings.aoColumns, + aDataSort, iCol, sType, srcCol, + fixed = settings.aaSortingFixed, + fixedObj = $.isPlainObject( fixed ), + nestedSort = [], + add = function ( a ) { + if ( a.length && ! $.isArray( a[0] ) ) { + // 1D array + nestedSort.push( a ); } - - if ( ! oSettings.renderer ) { - oSettings.renderer = 'jqueryui'; + else { + // 2D array + $.merge( nestedSort, a ); } - else if ( $.isPlainObject( oSettings.renderer ) && ! oSettings.renderer.header ) { - oSettings.renderer.header = 'jqueryui'; + }; + + // Build the sort array, with pre-fix and post-fix options if they have been + // specified + if ( $.isArray( fixed ) ) { + add( fixed ); + } + + if ( fixedObj && fixed.pre ) { + add( fixed.pre ); + } + + add( settings.aaSorting ); + + if (fixedObj && fixed.post ) { + add( fixed.post ); + } + + for ( i=0 ; iy ? 1 : 0; + if ( test !== 0 ) { + return sort.dir === 'asc' ? test : -test; + } + } + + x = aiOrig[a]; + y = aiOrig[b]; + return xy ? 1 : 0; + } ); } - - /* Language definitions */ - var oLanguage = oSettings.oLanguage; - $.extend( true, oLanguage, oInit.oLanguage ); - - if ( oLanguage.sUrl !== "" ) - { - /* Get the language definitions from a file - because this Ajax call makes the language - * get async to the remainder of this function we use bInitHandedOff to indicate that - * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor - */ - $.ajax( { - dataType: 'json', - url: oLanguage.sUrl, - success: function ( json ) { - _fnLanguageCompat( json ); - _fnCamelToHungarian( defaults.oLanguage, json ); - $.extend( true, oLanguage, json ); - _fnInitialise( oSettings ); - }, - error: function () { - // Error occurred loading language file, continue on as best we can - _fnInitialise( oSettings ); + else { + // Depreciated - remove in 1.11 (providing a plug-in option) + // Not all sort types have formatting methods, so we have to call their sorting + // methods. + displayMaster.sort( function ( a, b ) { + var + x, y, k, l, test, sort, fn, + len=aSort.length, + dataA = aoData[a]._aSortData, + dataB = aoData[b]._aSortData; + + for ( k=0 ; ky ? 1 : 0; } ); - bInitHandedOff = true; } - - /* - * Stripes - */ - if ( oInit.asStripeClasses === null ) - { - oSettings.asStripeClasses =[ - oClasses.sStripeOdd, - oClasses.sStripeEven - ]; + } + + /* Tell the draw function that we have sorted the data */ + oSettings.bSorted = true; + } + + + function _fnSortAria ( settings ) + { + var label; + var nextSort; + var columns = settings.aoColumns; + var aSort = _fnSortFlatten( settings ); + var oAria = settings.oLanguage.oAria; + + // ARIA attributes - need to loop all columns, to update all (removing old + // attributes as needed) + for ( var i=0, iLen=columns.length ; i/g, "" ); + var th = col.nTh; + + // IE7 is throwing an error when setting these properties with jQuery's + // attr() and removeAttr() methods... + th.removeAttribute('aria-sort'); + + /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */ + if ( col.bSortable ) { + if ( aSort.length > 0 && aSort[0].col == i ) { + th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" ); + nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0]; + } + else { + nextSort = asSorting[0]; + } + + label = sTitle + ( nextSort === "asc" ? + oAria.sSortAscending : + oAria.sSortDescending + ); } - - /* Remove row stripe classes if they are already on the table row */ - var stripeClasses = oSettings.asStripeClasses; - var rowOne = $this.children('tbody').find('tr').eq(0); - if ( $.inArray( true, $.map( stripeClasses, function(el, i) { - return rowOne.hasClass(el); - } ) ) !== -1 ) { - $('tbody tr', this).removeClass( stripeClasses.join(' ') ); - oSettings.asDestroyStripes = stripeClasses.slice(); + else { + label = sTitle; } - - /* - * Columns - * See if we should load columns automatically or use defined ones - */ - var anThs = []; - var aoColumnsInit; - var nThead = this.getElementsByTagName('thead'); - if ( nThead.length !== 0 ) - { - _fnDetectHeader( oSettings.aoHeader, nThead[0] ); - anThs = _fnGetUniqueThs( oSettings ); + + th.setAttribute('aria-label', label); + } + } + + + /** + * Function to run on user sort request + * @param {object} settings dataTables settings object + * @param {node} attachTo node to attach the handler to + * @param {int} colIdx column sorting index + * @param {boolean} [append=false] Append the requested sort to the existing + * sort if true (i.e. multi-column sort) + * @param {function} [callback] callback function + * @memberof DataTable#oApi + */ + function _fnSortListener ( settings, colIdx, append, callback ) + { + var col = settings.aoColumns[ colIdx ]; + var sorting = settings.aaSorting; + var asSorting = col.asSorting; + var nextSortIdx; + var next = function ( a, overflow ) { + var idx = a._idx; + if ( idx === undefined ) { + idx = $.inArray( a[1], asSorting ); } - - /* If not given a column array, generate one with nulls */ - if ( oInit.aoColumns === null ) - { - aoColumnsInit = []; - for ( i=0, iLen=anThs.length ; i 0 && state.time < +new Date() - (duration*1000) ) { + return; + } + + // Number of columns have changed - all bets are off, no restore of settings + if ( columns.length !== state.columns.length ) { + return; + } + + // Store the saved state so it might be accessed at any time + settings.oLoadedState = $.extend( true, {}, state ); + + // Restore key features - todo - for 1.11 this needs to be done by + // subscribed events + if ( state.start !== undefined ) { + settings._iDisplayStart = state.start; + settings.iInitDisplayStart = state.start; + } + if ( state.length !== undefined ) { + settings._iDisplayLength = state.length; + } + + // Order + if ( state.order !== undefined ) { + settings.aaSorting = []; + $.each( state.order, function ( i, col ) { + settings.aaSorting.push( col[0] >= columns.length ? + [ 0, col[1] ] : + col + ); } ); - - var thead = $this.children('thead'); - if ( thead.length === 0 ) - { - thead = $('