Subversion-Projekte lars-tiefland.ci

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
875 lars 1
/*! AutoFill 2.0.0
2
 * ©2008-2015 SpryMedia Ltd - datatables.net/license
3
 */
4
 
5
/**
6
 * @summary     AutoFill
7
 * @description Add Excel like click and drag auto-fill options to DataTables
8
 * @version     2.0.0
9
 * @file        dataTables.autoFill.js
10
 * @author      SpryMedia Ltd (www.sprymedia.co.uk)
11
 * @contact     www.sprymedia.co.uk/contact
12
 * @copyright   Copyright 2010-2015 SpryMedia Ltd.
13
 *
14
 * This source file is free software, available under the following license:
15
 *   MIT license - http://datatables.net/license/mit
16
 *
17
 * This source file is distributed in the hope that it will be useful, but
18
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
19
 * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
20
 *
21
 * For details please refer to: http://www.datatables.net
22
 */
23
 
24
(function( window, document, undefined ) {
25
 
26
var factory = function( $, DataTable ) {
27
"use strict";
28
 
29
var _instance = 0;
30
 
31
/**
32
 * AutoFill provides Excel like auto-fill features for a DataTable
33
 *
34
 * @class AutoFill
35
 * @constructor
36
 * @param {object} oTD DataTables settings object
37
 * @param {object} oConfig Configuration object for AutoFill
38
 */
39
var AutoFill = function( dt, opts )
40
{
41
	if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) {
42
		throw( "Warning: AutoFill requires DataTables 1.10.8 or greater");
43
	}
44
 
45
	// User and defaults configuration object
46
	this.c = $.extend( true, {},
47
		DataTable.defaults.autoFill,
48
		AutoFill.defaults,
49
		opts
50
	);
51
 
52
	/**
53
	 * @namespace Settings object which contains customisable information for AutoFill instance
54
	 */
55
	this.s = {
56
		/** @type {DataTable.Api} DataTables' API instance */
57
		dt: new DataTable.Api( dt ),
58
 
59
		/** @type {String} Unique namespace for events attached to the document */
60
		namespace: '.autoFill'+(_instance++),
61
 
62
		/** @type {Object} Cached dimension information for use in the mouse move event handler */
63
		scroll: {},
64
 
65
		/** @type {integer} Interval object used for smooth scrolling */
66
		scrollInterval: null
67
	};
68
 
69
 
70
	/**
71
	 * @namespace Common and useful DOM elements for the class instance
72
	 */
73
	this.dom = {
74
		/** @type {jQuery} AutoFill handle */
75
		handle: $('<div class="dt-autofill-handle"/>'),
76
 
77
		/**
78
		 * @type {Object} Selected cells outline - Need to use 4 elements,
79
		 *   otherwise the mouse over if you back into the selected rectangle
80
		 *   will be over that element, rather than the cells!
81
		 */
82
		select: {
83
			top:    $('<div class="dt-autofill-select top"/>'),
84
			right:  $('<div class="dt-autofill-select right"/>'),
85
			bottom: $('<div class="dt-autofill-select bottom"/>'),
86
			left:   $('<div class="dt-autofill-select left"/>')
87
		},
88
 
89
		/** @type {jQuery} Fill type chooser background */
90
		background: $('<div class="dt-autofill-background"/>'),
91
 
92
		/** @type {jQuery} Fill type chooser */
93
		list: $('<div class="dt-autofill-list">'+this.s.dt.i18n('autoFill.info', '')+'<ul/></div>'),
94
 
95
		/** @type {jQuery} DataTables scrolling container */
96
		dtScroll: null
97
	};
98
 
99
 
100
	/* Constructor logic */
101
	this._constructor();
102
};
103
 
104
 
105
 
106
AutoFill.prototype = {
107
	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
108
	 * Constructor
109
	 */
110
 
111
	/**
112
	 * Initialise the RowReorder instance
113
	 *
114
	 * @private
115
	 */
116
	_constructor: function ()
117
	{
118
		var that = this;
119
		var dt = this.s.dt;
120
		var dtScroll = $('div.dataTables_scrollBody', this.s.dt.table().container());
121
 
122
		if ( dtScroll.length ) {
123
			this.dom.dtScroll = dtScroll;
124
 
125
			// Need to scroll container to be the offset parent
126
			if ( dtScroll.css('position') === 'static' ) {
127
				dtScroll.css( 'position', 'relative' );
128
			}
129
		}
130
 
131
		this._focusListener();
132
 
133
		this.dom.handle.on( 'mousedown', function (e) {
134
			that._mousedown( e );
135
			return false;
136
		} );
137
 
138
		dt.on( 'destroy.autoFill', function () {
139
			dt.off( '.autoFill' );
140
			$(dt.table().body()).off( that.s.namespace );
141
			$(document.body).off( that.s.namespace );
142
		} );
143
	},
144
 
145
 
146
	/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
147
	 * Private methods
148
	 */
149
 
150
	/**
151
	 * Display the AutoFill drag handle by appending it to a table cell. This
152
	 * is the opposite of the _detach method.
153
	 *
154
	 * @param  {node} node TD/TH cell to insert the handle into
155
	 * @private
156
	 */
157
	_attach: function ( node )
158
	{
159
		var dt = this.s.dt;
160
		var idx = dt.cell( node ).index();
161
 
162
		if ( ! idx || dt.columns( this.c.columns ).indexes().indexOf( idx.column ) === -1 ) {
163
			this._detach();
164
			return;
165
		}
166
 
167
		this.dom.attachedTo = node;
168
		this.dom.handle.appendTo( node );
169
	},
170
 
171
 
172
	/**
173
	 * Determine can the fill type should be. This can be automatic, or ask the
174
	 * end user.
175
	 *
176
	 * @param {array} cells Information about the selected cells from the key
177
	 *     up function
178
	 * @private
179
	 */
180
	_actionSelector: function ( cells )
181
	{
182
		var that = this;
183
		var dt = this.s.dt;
184
		var actions = AutoFill.actions;
185
		var available = [];
186
 
187
		// "Ask" each plug-in if it wants to handle this data
188
		$.each( actions, function ( key, action ) {
189
			if ( action.available( dt, cells ) ) {
190
				available.push( key );
191
			}
192
		} );
193
 
194
		if ( available.length === 1 && this.c.alwaysAsk === false ) {
195
			// Only one action available - enact it immediately
196
			var result = actions[ available[0] ].execute( dt, cells );
197
			this._update( result, cells );
198
		}
199
		else {
200
			// Multiple actions available - ask the end user what they want to do
201
			var list = this.dom.list.children('ul').empty();
202
 
203
			// Add a cancel option
204
			available.push( 'cancel' );
205
 
206
			$.each( available, function ( i, name ) {
207
				list.append( $('<li/>')
208
					.append(
209
						'<div class="dt-autofill-question">'+
210
							actions[ name ].option( dt, cells )+
211
						'<div>'
212
					)
213
					.append( $('<div class="dt-autofill-button">' )
214
						.append( $('<button class="'+AutoFill.classes.btn+'">'+dt.i18n('autoFill.button', '&gt;')+'</button>')
215
							.on( 'click', function () {
216
								var result = actions[ name ].execute(
217
									dt, cells, $(this).closest('li')
218
								);
219
								that._update( result, cells );
220
 
221
								that.dom.background.remove();
222
								that.dom.list.remove();
223
							} )
224
						)
225
					)
226
				);
227
			} );
228
 
229
			this.dom.background.appendTo( 'body' );
230
			this.dom.list.appendTo( 'body' );
231
 
232
			this.dom.list.css( 'margin-top', this.dom.list.outerHeight()/2 * -1 );
233
		}
234
	},
235
 
236
 
237
	/**
238
	 * Remove the AutoFill handle from the document
239
	 *
240
	 * @private
241
	 */
242
	_detach: function ()
243
	{
244
		this.dom.attachedTo = null;
245
		this.dom.handle.detach();
246
	},
247
 
248
 
249
	/**
250
	 * Draw the selection outline by calculating the range between the start
251
	 * and end cells, then placing the highlighting elements to draw a rectangle
252
	 *
253
	 * @param  {node}   target End cell
254
	 * @param  {object} e      Originating event
255
	 * @private
256
	 */
257
	_drawSelection: function ( target, e )
258
	{
259
		// Calculate boundary for start cell to this one
260
		var dt = this.s.dt;
261
		var start = this.s.start;
262
		var startCell = $(this.dom.start);
263
		var endCell = $(target);
264
		var end = {
265
			row: endCell.parent().index(),
266
			column: endCell.index()
267
		};
268
 
269
		// Be sure that is a DataTables controlled cell
270
		if ( ! dt.cell( endCell ).any() ) {
271
			return;
272
		}
273
 
274
		// if target is not in the columns available - do nothing
275
		if ( dt.columns( this.c.columns ).indexes().indexOf( end.column ) === -1 ) {
276
			return;
277
		}
278
 
279
		this.s.end = end;
280
 
281
		var top, bottom, left, right, height, width;
282
 
283
		top    = start.row    < end.row    ? startCell : endCell;
284
		bottom = start.row    < end.row    ? endCell   : startCell;
285
		left   = start.column < end.column ? startCell : endCell;
286
		right  = start.column < end.column ? endCell   : startCell;
287
 
288
		top    = top.position().top;
289
		left   = left.position().left;
290
		height = bottom.position().top + bottom.outerHeight() - top;
291
		width  = right.position().left + right.outerWidth() - left;
292
 
293
		var dtScroll = this.dom.dtScroll;
294
		if ( dtScroll ) {
295
			top += dtScroll.scrollTop();
296
			left += dtScroll.scrollLeft();
297
		}
298
 
299
		var select = this.dom.select;
300
		select.top.css( {
301
			top: top,
302
			left: left,
303
			width: width
304
		} );
305
 
306
		select.left.css( {
307
			top: top,
308
			left: left,
309
			height: height
310
		} );
311
 
312
		select.bottom.css( {
313
			top: top + height,
314
			left: left,
315
			width: width
316
		} );
317
 
318
		select.right.css( {
319
			top: top,
320
			left: left + width,
321
			height: height
322
		} );
323
	},
324
 
325
 
326
	/**
327
	 * Use the Editor API to perform an update based on the new data for the
328
	 * cells
329
	 *
330
	 * @param {array} cells Information about the selected cells from the key
331
	 *     up function
332
	 * @private
333
	 */
334
	_editor: function ( cells )
335
	{
336
		var dt = this.s.dt;
337
		var editor = this.c.editor;
338
 
339
		if ( ! editor ) {
340
			return;
341
		}
342
 
343
		// Build the object structure for Editor's multi-row editing
344
		var idValues = {};
345
		var nodes = [];
346
 
347
		for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
348
			for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
349
				var cell = cells[i][j];
350
 
351
				// Determine the field name for the cell being edited
352
				var col = dt.settings()[0].aoColumns[ cell.index.column ];
353
				var dataSrc = col.editField !== undefined ?
354
					col.editField :
355
					col.mData;
356
 
357
				if ( ! dataSrc ) {
358
					throw 'Could not automatically determine field name. '+
359
						'Please see https://datatables.net/tn/11';
360
				}
361
 
362
				if ( ! idValues[ dataSrc ] ) {
363
					idValues[ dataSrc ] = {};
364
				}
365
 
366
				var id = dt.row( cell.index.row ).id();
367
				idValues[ dataSrc ][ id ] = cell.set;
368
 
369
				// Keep a list of cells so we can activate the bubble editing
370
				// with them
371
				nodes.push( cell.index );
372
			}
373
		}
374
 
375
		// Perform the edit using bubble editing as it allows us to specify
376
		// the cells to be edited, rather than using full rows
377
		editor
378
			.bubble( nodes, false )
379
			.multiSet( idValues )
380
			.submit();
381
	},
382
 
383
 
384
	/**
385
	 * Emit an event on the DataTable for listeners
386
	 *
387
	 * @param  {string} name Event name
388
	 * @param  {array} args Event arguments
389
	 * @private
390
	 */
391
	_emitEvent: function ( name, args )
392
	{
393
		this.s.dt.iterator( 'table', function ( ctx, i ) {
394
			$(ctx.nTable).triggerHandler( name+'.dt', args );
395
		} );
396
	},
397
 
398
 
399
	/**
400
	 * Attach suitable listeners (based on the configuration) that will attach
401
	 * and detach the AutoFill handle in the document.
402
	 *
403
	 * @private
404
	 */
405
	_focusListener: function ()
406
	{
407
		var that = this;
408
		var dt = this.s.dt;
409
		var namespace = this.s.namespace;
410
		var focus = this.c.focus !== null ?
411
			this.c.focus :
412
			dt.settings()[0].keytable ?
413
				'focus' :
414
				'hover';
415
 
416
		// All event listeners attached here are removed in the `destroy`
417
		// callback in the constructor
418
		if ( focus === 'focus' ) {
419
			dt
420
				.on( 'key-focus.autoFill', function ( e, dt, cell ) {
421
					that._attach( cell.node() );
422
				} )
423
				.on( 'key-blur.autoFill', function ( e, dt, cell ) {
424
					that._detach();
425
				} );
426
		}
427
		else if ( focus === 'click' ) {
428
			$(dt.table().body()).on( 'click'+namespace, 'td, th', function (e) {
429
				that._attach( this );
430
			} );
431
 
432
			$(document.body).on( 'click'+namespace, function (e) {
433
				if ( ! $(e.target).parents().filter( dt.table().body() ).length ) {
434
					that._detach();
435
				}
436
			} );
437
		}
438
		else {
439
			$(dt.table().body())
440
				.on( 'mouseenter'+namespace, 'td, th', function (e) {
441
					that._attach( this );
442
				} )
443
				.on( 'mouseleave'+namespace, function (e) {
444
					that._detach();
445
				} );
446
		}
447
	},
448
 
449
 
450
	/**
451
	 * Start mouse drag - selects the start cell
452
	 *
453
	 * @param  {object} e Mouse down event
454
	 * @private
455
	 */
456
	_mousedown: function ( e )
457
	{
458
		var that = this;
459
		var dt = this.s.dt;
460
 
461
		this.dom.start = this.dom.attachedTo;
462
		this.s.start = {
463
			row: $(this.dom.start).parent().index(),
464
			column: $(this.dom.start).index()
465
		};
466
 
467
		$(document.body)
468
			.on( 'mousemove.autoFill', function (e) {
469
				that._mousemove( e );
470
			} )
471
			.on( 'mouseup.autoFill', function (e) {
472
				that._mouseup( e );
473
			} );
474
 
475
		var select = this.dom.select;
476
		var offsetParent = $(this.s.dt.table().body()).offsetParent();
477
		select.top.appendTo( offsetParent );
478
		select.left.appendTo( offsetParent );
479
		select.right.appendTo( offsetParent );
480
		select.bottom.appendTo( offsetParent );
481
 
482
		this._drawSelection( this.dom.start, e );
483
 
484
		this.dom.handle.css( 'display', 'none' );
485
 
486
		// Cache scrolling information so mouse move doesn't need to read.
487
		// This assumes that the window and DT scroller will not change size
488
		// during an AutoFill drag, which I think is a fair assumption
489
		var scrollWrapper = this.dom.dtScroll;
490
		this.s.scroll = {
491
			windowHeight: $(window).height(),
492
			windowWidth:  $(window).width(),
493
			dtTop:        scrollWrapper ? scrollWrapper.offset().top : null,
494
			dtLeft:       scrollWrapper ? scrollWrapper.offset().left : null,
495
			dtHeight:     scrollWrapper ? scrollWrapper.outerHeight() : null,
496
			dtWidth:      scrollWrapper ? scrollWrapper.outerWidth() : null
497
		};
498
	},
499
 
500
 
501
	/**
502
	 * Mouse drag - selects the end cell and update the selection display for
503
	 * the end user
504
	 *
505
	 * @param  {object} e Mouse move event
506
	 * @private
507
	 */
508
	_mousemove: function ( e )
509
	{
510
		var that = this;
511
		var dt = this.s.dt;
512
		var name = e.target.nodeName.toLowerCase();
513
		if ( name !== 'td' && name !== 'th' ) {
514
			return;
515
		}
516
 
517
		this._drawSelection( e.target, e );
518
		this._shiftScroll( e );
519
	},
520
 
521
 
522
	/**
523
	 * End mouse drag - perform the update actions
524
	 *
525
	 * @param  {object} e Mouse up event
526
	 * @private
527
	 */
528
	_mouseup: function ( e )
529
	{
530
		$(document.body).off( '.autoFill' );
531
 
532
		var dt = this.s.dt;
533
		var select = this.dom.select;
534
		select.top.remove();
535
		select.left.remove();
536
		select.right.remove();
537
		select.bottom.remove();
538
 
539
		this.dom.handle.css( 'display', 'block' );
540
 
541
		// Display complete - now do something useful with the selection!
542
		var start = this.s.start;
543
		var end = this.s.end;
544
 
545
		// Haven't selected multiple cells, so nothing to do
546
		if ( start.row === end.row && start.column === end.column ) {
547
			return;
548
		}
549
 
550
		// Build a matrix representation of the selected rows
551
		var rows     = this._range( start.row, end.row );
552
		var columns  = this._range( start.column, end.column );
553
		var selected = [];
554
 
555
		// Can't use Array.prototype.map as IE8 doesn't support it
556
		// Can't use $.map as jQuery flattens 2D arrays
557
		// Need to use a good old fashioned for loop
558
		for ( var rowIdx=0 ; rowIdx<rows.length ; rowIdx++ ) {
559
			selected.push(
560
				$.map( columns, function (column) {
561
					var cell = dt.cell( ':eq('+rows[rowIdx]+')', column+':visible', {page:'current'} );
562
 
563
					return {
564
						cell:  cell,
565
						data:  cell.data(),
566
						index: cell.index()
567
					};
568
				} )
569
			);
570
		}
571
 
572
		this._actionSelector( selected );
573
	},
574
 
575
 
576
	/**
577
	 * Create an array with a range of numbers defined by the start and end
578
	 * parameters passed in (inclusive!).
579
	 *
580
	 * @param  {integer} start Start
581
	 * @param  {integer} end   End
582
	 * @private
583
	 */
584
	_range: function ( start, end )
585
	{
586
		var out = [];
587
		var i;
588
 
589
		if ( start <= end ) {
590
			for ( i=start ; i<=end ; i++ ) {
591
				out.push( i );
592
			}
593
		}
594
		else {
595
			for ( i=start ; i>=end ; i-- ) {
596
				out.push( i );
597
			}
598
		}
599
 
600
		return out;
601
	},
602
 
603
 
604
	/**
605
	 * Move the window and DataTables scrolling during a drag to scroll new
606
	 * content into view. This is done by proximity to the edge of the scrolling
607
	 * container of the mouse - for example near the top edge of the window
608
	 * should scroll up. This is a little complicated as there are two elements
609
	 * that can be scrolled - the window and the DataTables scrolling view port
610
	 * (if scrollX and / or scrollY is enabled).
611
	 *
612
	 * @param  {object} e Mouse move event object
613
	 * @private
614
	 */
615
	_shiftScroll: function ( e )
616
	{
617
		var that = this;
618
		var dt = this.s.dt;
619
		var scroll = this.s.scroll;
620
		var runInterval = false;
621
		var scrollSpeed = 5;
622
		var buffer = 65;
623
		var
624
			windowY = e.pageY - document.body.scrollTop,
625
			windowX = e.pageX - document.body.scrollLeft,
626
			windowVert, windowHoriz,
627
			dtVert, dtHoriz;
628
 
629
		// Window calculations - based on the mouse position in the window,
630
		// regardless of scrolling
631
		if ( windowY < buffer ) {
632
			windowVert = scrollSpeed * -1;
633
		}
634
		else if ( windowY > scroll.windowHeight - buffer ) {
635
			windowVert = scrollSpeed;
636
		}
637
 
638
		if ( windowX < buffer ) {
639
			windowHoriz = scrollSpeed * -1;
640
		}
641
		else if ( windowX > scroll.windowWidth - buffer ) {
642
			windowHoriz = scrollSpeed;
643
		}
644
 
645
		// DataTables scrolling calculations - based on the table's position in
646
		// the document and the mouse position on the page
647
		if ( scroll.dtTop !== null && e.pageY < scroll.dtTop + buffer ) {
648
			dtVert = scrollSpeed * -1;
649
		}
650
		else if ( scroll.dtTop !== null && e.pageY > scroll.dtTop + scroll.dtHeight - buffer ) {
651
			dtVert = scrollSpeed;
652
		}
653
 
654
		if ( scroll.dtLeft !== null && e.pageX < scroll.dtLeft + buffer ) {
655
			dtHoriz = scrollSpeed * -1;
656
		}
657
		else if ( scroll.dtLeft !== null && e.pageX > scroll.dtLeft + scroll.dtWidth - buffer ) {
658
			dtHoriz = scrollSpeed;
659
		}
660
 
661
		// This is where it gets interesting. We want to continue scrolling
662
		// without requiring a mouse move, so we need an interval to be
663
		// triggered. The interval should continue until it is no longer needed,
664
		// but it must also use the latest scroll commands (for example consider
665
		// that the mouse might move from scrolling up to scrolling left, all
666
		// with the same interval running. We use the `scroll` object to "pass"
667
		// this information to the interval. Can't use local variables as they
668
		// wouldn't be the ones that are used by an already existing interval!
669
		if ( windowVert || windowHoriz || dtVert || dtHoriz ) {
670
			scroll.windowVert = windowVert;
671
			scroll.windowHoriz = windowHoriz;
672
			scroll.dtVert = dtVert;
673
			scroll.dtHoriz = dtHoriz;
674
			runInterval = true;
675
		}
676
		else if ( this.s.scrollInterval ) {
677
			// Don't need to scroll - remove any existing timer
678
			clearInterval( this.s.scrollInterval );
679
			this.s.scrollInterval = null;
680
		}
681
 
682
		// If we need to run the interval to scroll and there is no existing
683
		// interval (if there is an existing one, it will continue to run)
684
		if ( ! this.s.scrollInterval && runInterval ) {
685
			this.s.scrollInterval = setInterval( function () {
686
				// Don't need to worry about setting scroll <0 or beyond the
687
				// scroll bound as the browser will just reject that.
688
				if ( scroll.windowVert ) {
689
					document.body.scrollTop += scroll.windowVert;
690
				}
691
				if ( scroll.windowHoriz ) {
692
					document.body.scrollLeft += scroll.windowHoriz;
693
				}
694
 
695
				// DataTables scrolling
696
				if ( scroll.dtVert || scroll.dtHoriz ) {
697
					var scroller = that.dom.dtScroll[0];
698
 
699
					if ( scroll.dtVert ) {
700
						scroller.scrollTop += scroll.dtVert;
701
					}
702
					if ( scroll.dtHoriz ) {
703
						scroller.scrollLeft += scroll.dtHoriz;
704
					}
705
				}
706
			}, 20 );
707
		}
708
	},
709
 
710
 
711
	/**
712
	 * Update the DataTable after the user has selected what they want to do
713
	 *
714
	 * @param  {false|undefined} result Return from the `execute` method - can
715
	 *   be false internally to do nothing. This is not documented for plug-ins
716
	 *   and is used only by the cancel option.
717
	 * @param {array} cells Information about the selected cells from the key
718
	 *     up function, argumented with the set values
719
	 * @private
720
	 */
721
	_update: function ( result, cells )
722
	{
723
		// Do nothing on `false` return from an execute function
724
		if ( result === false ) {
725
			return;
726
		}
727
 
728
		var dt = this.s.dt;
729
		var cell;
730
 
731
		// Potentially allow modifications to the cells matrix
732
		this._emitEvent( 'preAutoFill', [ dt, cells ] );
733
 
734
		this._editor( cells );
735
 
736
		// Automatic updates are not performed if `update` is null and the
737
		// `editor` parameter is passed in - the reason being that Editor will
738
		// update the data once submitted
739
		var update = this.c.update !== null ?
740
			this.c.update :
741
			this.c.editor ?
742
				false :
743
				true;
744
 
745
		if ( update ) {
746
			for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
747
				for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
748
					cell = cells[i][j];
749
 
750
					cell.cell.data( cell.set );
751
				}
752
			}
753
 
754
			dt.draw(false);
755
		}
756
 
757
		this._emitEvent( 'autoFill', [ dt, cells ] );
758
	}
759
};
760
 
761
 
762
/**
763
 * AutoFill actions. The options here determine how AutoFill will fill the data
764
 * in the table when the user has selected a range of cells. Please see the
765
 * documentation on the DataTables site for full details on how to create plug-
766
 * ins.
767
 *
768
 * @type {Object}
769
 */
770
AutoFill.actions = {
771
	increment: {
772
		available: function ( dt, cells ) {
773
			return $.isNumeric( cells[0][0].data );
774
		},
775
 
776
		option: function ( dt, cells ) {
777
			return dt.i18n(
778
				'autoFill.increment',
779
				'Increment / decrement each cell by: <input type="number" value="1">'
780
			);
781
		},
782
 
783
		execute: function ( dt, cells, node ) {
784
			var value = cells[0][0].data * 1;
785
			var increment = $('input', node).val() * 1;
786
 
787
			for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
788
				for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
789
					cells[i][j].set = value;
790
 
791
					value += increment;
792
				}
793
			}
794
		}
795
	},
796
 
797
	fill: {
798
		available: function ( dt, cells ) {
799
			return true;
800
		},
801
 
802
		option: function ( dt, cells ) {
803
			return dt.i18n('autoFill.fill', 'Fill all cells with <i>'+cells[0][0].data+'</i>' );
804
		},
805
 
806
		execute: function ( dt, cells, node ) {
807
			var value = cells[0][0].data;
808
 
809
			for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
810
				for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
811
					cells[i][j].set = value;
812
				}
813
			}
814
		}
815
	},
816
 
817
	fillHorizontal: {
818
		available: function ( dt, cells ) {
819
			return cells.length > 1 && cells[0].length > 1;
820
		},
821
 
822
		option: function ( dt, cells ) {
823
			return dt.i18n('autoFill.fillHorizontal', 'Fill cells horizontally' );
824
		},
825
 
826
		execute: function ( dt, cells, node ) {
827
			for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
828
				for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
829
					cells[i][j].set = cells[i][0].data;
830
				}
831
			}
832
		}
833
	},
834
 
835
	fillVertical: {
836
		available: function ( dt, cells ) {
837
			return cells.length > 1 && cells[0].length > 1;
838
		},
839
 
840
		option: function ( dt, cells ) {
841
			return dt.i18n('autoFill.fillVertical', 'Fill cells vertically' );
842
		},
843
 
844
		execute: function ( dt, cells, node ) {
845
			for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
846
				for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
847
					cells[i][j].set = cells[0][j].data;
848
				}
849
			}
850
		}
851
	},
852
 
853
	// Special type that does not make itself available, but is added
854
	// automatically by AutoFill if a multi-choice list is shown. This allows
855
	// sensible code reuse
856
	cancel: {
857
		available: function () {
858
			return false;
859
		},
860
 
861
		option: function ( dt ) {
862
			return dt.i18n('autoFill.cancel', 'Cancel' );
863
		},
864
 
865
		execute: function () {
866
			return false;
867
		}
868
	}
869
};
870
 
871
 
872
/**
873
 * AutoFill version
874
 *
875
 * @static
876
 * @type      String
877
 */
878
AutoFill.version = '2.0.0';
879
 
880
 
881
/**
882
 * AutoFill defaults
883
 *
884
 * @namespace
885
 */
886
AutoFill.defaults = {
887
	/** @type {Boolean} Ask user what they want to do, even for a single option */
888
	alwaysAsk: false,
889
 
890
	/** @type {string|null} What will trigger a focus */
891
	focus: null, // focus, click, hover
892
 
893
	/** @type {column-selector} Columns to provide auto fill for */
894
	columns: '', // all
895
 
896
	/** @type {boolean|null} Update the cells after a drag */
897
	update: null, // false is editor given, true otherwise
898
 
899
	/** @type {DataTable.Editor} Editor instance for automatic submission */
900
	editor: null
901
};
902
 
903
 
904
/**
905
 * Classes used by AutoFill that are configurable
906
 *
907
 * @namespace
908
 */
909
AutoFill.classes = {
910
	/** @type {String} Class used by the selection button */
911
	btn: 'btn'
912
};
913
 
914
 
915
// Attach a listener to the document which listens for DataTables initialisation
916
// events so we can automatically initialise
917
$(document).on( 'init.dt.autofill', function (e, settings, json) {
918
	if ( e.namespace !== 'dt' ) {
919
		return;
920
	}
921
 
922
	var init = settings.oInit.autoFill;
923
	var defaults = DataTable.defaults.autoFill;
924
 
925
	if ( init || defaults ) {
926
		var opts = $.extend( {}, init, defaults );
927
 
928
		if ( init !== false ) {
929
			new AutoFill( settings, opts  );
930
		}
931
	}
932
} );
933
 
934
 
935
// Alias for access
936
DataTable.AutoFill = AutoFill;
937
DataTable.AutoFill = AutoFill;
938
 
939
 
940
return AutoFill;
941
};
942
 
943
 
944
// Define as an AMD module if possible
945
if ( typeof define === 'function' && define.amd ) {
946
	define( ['jquery', 'datatables'], factory );
947
}
948
else if ( typeof exports === 'object' ) {
949
    // Node/CommonJS
950
    factory( require('jquery'), require('datatables') );
951
}
952
else if ( jQuery && !jQuery.fn.dataTable.AutoFill ) {
953
	// Otherwise simply initialise as normal, stopping multiple evaluation
954
	factory( jQuery, jQuery.fn.dataTable );
955
}
956
 
957
 
958
}(window, document));
959