marbu 0.1.0 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/lib/marbu/builder.rb CHANGED
@@ -31,8 +31,9 @@ module Marbu
31
31
  @formatter_clasz.perform(@builder_clasz.finalize(@map_reduce_model.finalize, format))
32
32
  end
33
33
 
34
- def query
35
- @map_reduce_model.query.condition
34
+ def query(format = :text)
35
+ #@builder_clasz.query(@map_reduce_model.query, format)
36
+ @map_reduce_model.query.static
36
37
  end
37
38
 
38
39
  def log
@@ -23,6 +23,13 @@ module Marbu
23
23
  end
24
24
  end
25
25
 
26
+ def self.query(query, format)
27
+ case format
28
+ when :text then self.query_int(query)
29
+ when :mongodb then cleanup(self.query_int(query))
30
+ end
31
+ end
32
+
26
33
  private
27
34
  def self.map_int(map)
28
35
  <<-JS
@@ -51,6 +58,10 @@ function(key, value){
51
58
  JS
52
59
  end
53
60
 
61
+ def self.query_int(query)
62
+ query.static.to_s
63
+ end
64
+
54
65
  def self.cleanup(code)
55
66
  code.gsub!(/[\n|\r]/,'')
56
67
  end
@@ -3,46 +3,32 @@ module Marbu
3
3
  module Db
4
4
  class MongoDb
5
5
  class Exception < StandardError
6
- REFERENCE_ERROR = 'ReferenceError'
7
- NS_ERROR = 'NS_ERROR'
8
- REGEX_DB_COMMAND = { :regex => /(ns doesn't exist)/,
9
- :mapping => { :function => {:type => :string, :value => 'Buu'},
10
- :error_type => {:type => :string, :value => NS_ERROR},
11
- :variable => {:type => :string, :value => 'Buu'},
12
- :assertion_code => {:type => :string, :value => 'Buu'},
13
- :error_message => {:type => :string, :value => 'Buu'} }}
14
- #REGEX_REF_ERROR = { :regex => /(.*?)'(.*?)'(.*?): \((assertion): '(.*?) (.*?): (.*?): (ReferenceError): (.*?)(is.*?)(nofile_b)?:\d'; (assertionCode): '(.*)';.*(errmsg): '(.*?)'/,
15
- # mapping => { :function => 5, :error_type => 8, :variable => 9, :assertion_code => 13, :error_message => 15 }}}
16
- REGEXS = [ REGEX_DB_COMMAND ]
6
+ QUERY_ERROR = 136081
7
+ QUERY_ERROR_ID = 'QUERY_ERROR'
8
+ MAP_COMPILE_ERROR = 13598
9
+ MAP_COMPILE_ERROR_ID = 'MAP_COMPILE_ERROR'
17
10
 
18
11
  def self.explain(e, mrf)
19
12
  parsed_exception = parse(e)
20
13
 
21
- case parsed_exception[:error_type]
22
- when REFERENCE_ERROR then explain_reference_error(parsed_exception, mrf)
23
- when NS_ERROR then { :id => NS_ERROR, :message => explain_ns_error(parsed_exception, mrf)}
14
+ case parsed_exception[:assertion_code]
15
+ when QUERY_ERROR then { id: QUERY_ERROR_ID, message: explain_query_error(parsed_exception, mrf)}
16
+ when MAP_COMPILE_ERROR then { id: MAP_COMPILE_ERROR_ID, message: explain_map_compile_error(parsed_exception, mrf)}
24
17
 
25
- else raise Exception.new("The Error #{e.to_s} is unknown to me. Parsed: #{parsed_exception.inspect}")
18
+ else raise Exception.new("The Error '#{e.to_s}' is unknown to me. Parsed: #{parsed_exception.inspect}")
26
19
  end
27
20
  end
28
21
 
29
22
  def self.parse(e)
30
- {}.tap do |parsed_exception|
31
- # http://rubular.com/r/MYM9ADhz4Z
32
- REGEXS.each do |regex|
33
- e.result["errmsg"] =~ regex[:regex]
34
- if( $1.present? )
35
- parsed_exception[:orig_error] = e
36
- [:function, :error_type, :variable, :assertion_code, :error_message].each do |key|
37
- case regex[:mapping][key][:type]
38
- when :string then parsed_exception[key] = regex[:mapping][key][:value]
39
- else "Unknown type: #{regex[:mapping][key][:type]}"
40
- end
41
- end
42
- break
43
- end
44
- end
45
- end
23
+ parsed_exception = {}
24
+
25
+ parsed_exception[:orig_error] = e.to_s
26
+ parsed_exception[:assertion] = e.result['assertion']
27
+ parsed_exception[:assertion_code] = e.result['assertionCode']
28
+ parsed_exception[:error_code] = e.error_code
29
+ parsed_exception[:errmsg] = e.result['errmsg']
30
+
31
+ parsed_exception
46
32
  end
47
33
 
48
34
  def self.explain_ns_error( parsed_exception, mrf )
@@ -52,6 +38,14 @@ module Marbu
52
38
  def self.explain_reference_error( parsed_exception, mrf )
53
39
  "The variable #{parsed_exception[:variable]} defined in function #{parsed_exception[:function]} is unknown. This either means you are trying to access a datafield that doesn't exist in the collection, or you misspelled a variable name."
54
40
  end
41
+
42
+ def self.explain_query_error( parsed_exception, mrf )
43
+ "The Query you supplied doesn't work as you excpected. Query: #{mrf.query.static}."
44
+ end
45
+
46
+ def self.explain_map_compile_error( parsed_exception, mrf )
47
+ "Your provided Map-Code couldn't be compiled by MongoDB. Please have a look at your javascript and the input/output variables."
48
+ end
55
49
  end
56
50
  end
57
51
  end
@@ -3,7 +3,9 @@ module Marbu
3
3
  class ExceptionLink
4
4
  def self.get_exception_fix_link(error, uuid)
5
5
  case error
6
- when 'NS_ERROR' then "#misc"
6
+ when 'NS_ERROR' then '#misc'
7
+ when 'QUERY_ERROR' then '#query'
8
+ when 'MAP_COMPILE_ERROR' then '#map'
7
9
  end
8
10
  end
9
11
  end
@@ -96,6 +96,10 @@ module Marbu
96
96
  def blank?
97
97
  !present?
98
98
  end
99
+
100
+ def to_s
101
+ name
102
+ end
99
103
  end
100
104
 
101
105
  class Key < KeyValueBase
@@ -5,23 +5,23 @@ module Marbu
5
5
  class Query
6
6
  DEFAULT_DATE_FIELDS = [:created_at]
7
7
 
8
- attr_accessor :condition, :datetime_fields
9
-
8
+ attr_accessor :condition, :datetime_fields, :static
10
9
 
11
10
  def initialize( ext_params = {} )
12
11
  params = default_params.merge( ext_params.keep_if{|k,v|v} )
13
12
  @condition = params[:condition]
14
13
  @datetime_fields = params[:datetime_fields]
14
+ @static = params[:static]
15
15
  end
16
16
 
17
17
  def default_params
18
18
  {
19
- :datetime_fields => DEFAULT_DATE_FIELDS
19
+ datetime_fields: DEFAULT_DATE_FIELDS
20
20
  }
21
21
  end
22
22
 
23
23
  def present?
24
- condition.present? || !datetime_fields.eql?(DEFAULT_DATE_FIELDS)
24
+ condition.present? || !datetime_fields.eql?(DEFAULT_DATE_FIELDS) || static.present?
25
25
  end
26
26
 
27
27
  def blank?
@@ -30,8 +30,9 @@ module Marbu
30
30
 
31
31
  def serializable_hash
32
32
  {
33
- :condition => condition,
34
- :datetime_fields => datetime_fields
33
+ condition: condition,
34
+ datetime_fields: datetime_fields,
35
+ static: static
35
36
  }.delete_if{|k,v|v.blank?}
36
37
  end
37
38
  end
data/lib/marbu/server.rb CHANGED
@@ -42,49 +42,58 @@ module Marbu
42
42
  end
43
43
  end
44
44
 
45
- def sample_data_tree(document, input_id)
45
+ def sample_data_tree(document, method, type)
46
46
  document.inject('') do |html, (key, value)|
47
47
  if (value.is_a?(Hash))
48
48
  html += '<div data-role="collapsible" data-collapsed="true" data-key="' + key + '">'
49
49
  html += '<h3> ' + key + '</h3>'
50
- html += sample_data_tree(value, input_id)
50
+ html += sample_data_tree(value, method, type)
51
51
  html +='</div>'
52
52
  else
53
- html += '<a data-role="button" data-type="sample_data" data-input="' + input_id + '" data-key="' + key + '" data-value="' + value.to_s + '">'
53
+ html += '<a data-role="button" data-type="sample_data" data-input="' + method + '_' + type + '' + '" data-key="' + key.to_s + '" data-value="' + value.to_s + '">'
54
54
  html += key.to_s
55
55
  html += '<span class="example">(example: ' + value.to_s + ')</span>';
56
56
  html += '</a>';
57
57
  end
58
58
  end
59
59
  end
60
-
61
60
  end
62
61
 
63
- get "/" do
62
+ get '/' do
64
63
  @mrms = Marbu::Models::Db::MongoDb.all
65
64
  show 'root'
66
65
  end
67
66
 
68
- get "/builder/new" do
67
+ get '/builder/new' do
69
68
  @mrm = Marbu::Models::Db::MongoDb.new
70
69
  @mrf = @mrm.map_reduce_finalize
71
70
  show 'builder'
72
71
  end
73
72
 
74
- get '/builder/sample_data/:uuid/:input_id' do
73
+ get '/builder/:uuid/sample_data/:method' do
75
74
  @mrm = Marbu::Models::Db::MongoDb.first(conditions: {uuid: params['uuid']})
76
75
  @mrf = @mrm.map_reduce_finalize
77
- @data_samples = Marbu::Models::Db::MongoDb::Structure.get_first_and_last_document(@mrf.misc)
76
+
77
+ if( params[:method].eql?('map'))
78
+ @data_samples = Marbu::Models::Db::MongoDb::Structure.get_first_and_last_document(@mrf.misc)
79
+ elsif( params[:method].eql?('reduce'))
80
+ @data_samples = [@mrf.map.values,@mrf.map.values]
81
+ elsif( params[:method].eql?('finalize'))
82
+ @data_samples = [@mrf.reduce.values,@mrf.reduce.values]
83
+ else
84
+ raise 'Unknown'
85
+ end
86
+
78
87
  show 'sample_data'
79
88
  end
80
89
 
81
- post "/builder" do
90
+ post '/builder' do
82
91
  @mrm = build_mrm_from_params(params.merge({:logger => logger}))
83
92
  @mrm.save!
84
93
  redirect "/builder/#{@mrm.uuid}"
85
94
  end
86
95
 
87
- get "/builder/:uuid" do
96
+ get '/builder/:uuid' do
88
97
  @mrm = Marbu::Models::Db::MongoDb.first(conditions: {uuid: params['uuid']})
89
98
  @mrf = @mrm.map_reduce_finalize
90
99
  @data_samples = Marbu::Models::Db::MongoDb::Structure.get_first_and_last_document(@mrf.misc)
@@ -92,19 +101,19 @@ module Marbu
92
101
  show 'builder'
93
102
  end
94
103
 
95
- put "/builder/:uuid" do
104
+ put '/builder/:uuid' do
96
105
  @mrm = build_mrm_from_params(params.merge({:logger => logger}))
97
106
  @mrm.save!
98
107
  redirect "/builder/#{@mrm.uuid}"
99
108
  end
100
109
 
101
- delete "/builder/:uuid" do
110
+ delete '/builder/:uuid' do
102
111
  @mrm = Marbu::Models::Db::MongoDb.first(conditions: {uuid: params['uuid']})
103
112
  @mrm.destroy if @mrm.present?
104
- redirect "/"
113
+ redirect '/'
105
114
  end
106
115
 
107
- get "/mapreduce/:uuid" do
116
+ get '/mapreduce/:uuid' do
108
117
  @mrm = Marbu::Models::Db::MongoDb.first(conditions: {uuid: params['uuid']})
109
118
  @mrf = @mrm.map_reduce_finalize
110
119
  @builder = Marbu::Builder.new(@mrf)
@@ -116,23 +125,30 @@ module Marbu
116
125
  # TODo: defined in the configuration (security)
117
126
  @res = @mrf.misc.collection.map_reduce( @builder.map(:mongodb), @builder.reduce(:mongodb),
118
127
  {
128
+ #:query => {"_id" => BSON::ObjectId('4f4da5dd83de3b60830000fa')},
129
+ #:query => {"uuid" => "4f4da5dd83de3b60830000fa"},
130
+ #:query => {"value.ad_group_till" => '2012-02-28T23:59:59+00:00'},
119
131
  :query => @builder.query,
120
- :out => {:replace => "tmp."+@mrm.map_reduce_finalize.misc.output_collection},
132
+ :out => {:replace => 'tmp.'+@mrm.map_reduce_finalize.misc.output_collection},
121
133
  :finalize => @builder.finalize(:mongodb)
122
134
  }
123
135
  )
124
136
  rescue Mongo::OperationFailure => e
125
- @parsed_error = Marbu::Models::Db::MongoDb::Exception.explain(e, @mrf)
126
- @error = @parsed_error[:message]
127
- @fix_link = Marbu::Models::ExceptionLink.get_exception_fix_link(@parsed_error[:id], params['uuid'])
137
+ begin
138
+ @parsed_error = Marbu::Models::Db::MongoDb::Exception.explain(e, @mrf)
139
+ @error = @parsed_error[:message]
140
+ @fix_link = Marbu::Models::ExceptionLink.get_exception_fix_link(@parsed_error[:id], params['uuid'])
141
+ rescue Marbu::Models::Db::MongoDb::Exception => e
142
+ @error = e.to_s
143
+ end
128
144
  end
129
-
130
145
  show 'mapreduce'
131
146
  end
132
147
 
133
148
  def build_mrm_from_params(params)
134
149
  name = 'name'
135
150
  function = 'function'
151
+ puts params.inspect
136
152
 
137
153
  if (uuid = params['uuid'])
138
154
  mrm = Marbu::Models::Db::MongoDb.find_or_create_by(uuid: uuid)
@@ -143,23 +159,24 @@ module Marbu
143
159
  mrf = mrm.map_reduce_finalize
144
160
 
145
161
  mrf.map = Marbu::Models::Map.new(
146
- :code => {:text => params['map_code']}
147
- )
162
+ :code => {:text => params['map_code']}
163
+ )
148
164
  mrf.reduce = Marbu::Models::Reduce.new(
149
- :code => {:text => params['reduce_code']}
150
- )
165
+ :code => {:text => params['reduce_code']}
166
+ )
151
167
  mrf.finalize = Marbu::Models::Finalize.new(
152
- :code => {:text => params['finalize_code']}
153
- )
168
+ :code => {:text => params['finalize_code']}
169
+ )
154
170
  mrf.query = Marbu::Models::Query.new(
155
- :condition => params['query_condition'],
156
- :force_query => params['query_force_query']
157
- )
171
+ condition: params['query_condition'],
172
+ force_query: params['query_force_query'],
173
+ static: params['query_static']
174
+ )
158
175
  mrf.misc = Marbu::Models::Misc.new(
159
- :database => params['database'],
160
- :input_collection => params['input_collection'],
161
- :output_collection => params['output_collection']
162
- )
176
+ :database => params['database'],
177
+ :input_collection => params['input_collection'],
178
+ :output_collection => params['output_collection']
179
+ )
163
180
 
164
181
  # add params to map_new, reduce_new, finalize_new
165
182
  ['map', 'reduce', 'finalize'].each do |stage|
@@ -58,11 +58,17 @@ html[xmlns] .clearfix {
58
58
  color: #333;
59
59
  }
60
60
 
61
+ .builder input.ui-input-text,
62
+ .builder textarea.ui-input-text {
63
+ display: inline-block;
64
+ width: 70%;
65
+ }
66
+
61
67
  .builder .col .pair {
62
68
  /* float: left;*/
63
69
  /* margin-right: 2%;*/
64
70
  /* width: 45%;*/
65
- margin: 0 auto 15px auto;
71
+ margin: 0 auto 0 auto;
66
72
  padding: 5px 10px 5px 5px;
67
73
  float: left;
68
74
  width: 40%;
@@ -0,0 +1,2569 @@
1
+ /*
2
+ * File: TableTools.js
3
+ * Version: 2.0.3.dev
4
+ * Description: Tools and buttons for DataTables
5
+ * Author: Allan Jardine (www.sprymedia.co.uk)
6
+ * Language: Javascript
7
+ * License: GPL v2 or BSD 3 point style
8
+ * Project: DataTables
9
+ *
10
+ * Copyright 2009-2012 Allan Jardine, all rights reserved.
11
+ *
12
+ * This source file is free software, under either the GPL v2 license or a
13
+ * BSD style license, available at:
14
+ * http://datatables.net/license_gpl2
15
+ * http://datatables.net/license_bsd
16
+ */
17
+
18
+ /* Global scope for TableTools */
19
+ var TableTools;
20
+
21
+ (function($, window, document) {
22
+
23
+ /**
24
+ * TableTools provides flexible buttons and other tools for a DataTables enhanced table
25
+ * @class TableTools
26
+ * @constructor
27
+ * @param {Object} oDT DataTables instance
28
+ * @param {Object} oOpts TableTools options
29
+ * @param {String} oOpts.sSwfPath ZeroClipboard SWF path
30
+ * @param {String} oOpts.sRowSelect Row selection options - 'none', 'single' or 'multi'
31
+ * @param {Function} oOpts.fnPreRowSelect Callback function just prior to row selection
32
+ * @param {Function} oOpts.fnRowSelected Callback function just after row selection
33
+ * @param {Function} oOpts.fnRowDeselected Callback function when row is deselected
34
+ * @param {Array} oOpts.aButtons List of buttons to be used
35
+ */
36
+ TableTools = function( oDT, oOpts )
37
+ {
38
+ /* Santiy check that we are a new instance */
39
+ if ( !this.CLASS || this.CLASS != "TableTools" )
40
+ {
41
+ alert( "Warning: TableTools must be initialised with the keyword 'new'" );
42
+ }
43
+
44
+
45
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
46
+ * Public class variables
47
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
48
+
49
+ /**
50
+ * @namespace Settings object which contains customisable information for TableTools instance
51
+ */
52
+ this.s = {
53
+ /**
54
+ * Store 'this' so the instance can be retreieved from the settings object
55
+ * @property that
56
+ * @type object
57
+ * @default this
58
+ */
59
+ "that": this,
60
+
61
+ /**
62
+ * DataTables settings objects
63
+ * @property dt
64
+ * @type object
65
+ * @default null
66
+ */
67
+ "dt": null,
68
+
69
+ /**
70
+ * @namespace Print specific information
71
+ */
72
+ "print": {
73
+ /**
74
+ * DataTables draw 'start' point before the printing display was shown
75
+ * @property saveStart
76
+ * @type int
77
+ * @default -1
78
+ */
79
+ "saveStart": -1,
80
+
81
+ /**
82
+ * DataTables draw 'length' point before the printing display was shown
83
+ * @property saveLength
84
+ * @type int
85
+ * @default -1
86
+ */
87
+ "saveLength": -1,
88
+
89
+ /**
90
+ * Page scrolling point before the printing display was shown so it can be restored
91
+ * @property saveScroll
92
+ * @type int
93
+ * @default -1
94
+ */
95
+ "saveScroll": -1,
96
+
97
+ /**
98
+ * Wrapped function to end the print display (to maintain scope)
99
+ * @property funcEnd
100
+ * @type Function
101
+ * @default function () {}
102
+ */
103
+ "funcEnd": function () {}
104
+ },
105
+
106
+ /**
107
+ * A unique ID is assigned to each button in each instance
108
+ * @property buttonCounter
109
+ * @type int
110
+ * @default 0
111
+ */
112
+ "buttonCounter": 0,
113
+
114
+ /**
115
+ * @namespace Select rows specific information
116
+ */
117
+ "select": {
118
+ /**
119
+ * Select type - can be 'none', 'single' or 'multi'
120
+ * @property type
121
+ * @type string
122
+ * @default ""
123
+ */
124
+ "type": "",
125
+
126
+ /**
127
+ * Array of nodes which are currently selected
128
+ * @property selected
129
+ * @type array
130
+ * @default []
131
+ */
132
+ "selected": [],
133
+
134
+ /**
135
+ * Function to run before the selection can take place. Will cancel the select if the
136
+ * function returns false
137
+ * @property preRowSelect
138
+ * @type Function
139
+ * @default null
140
+ */
141
+ "preRowSelect": null,
142
+
143
+ /**
144
+ * Function to run when a row is selected
145
+ * @property postSelected
146
+ * @type Function
147
+ * @default null
148
+ */
149
+ "postSelected": null,
150
+
151
+ /**
152
+ * Function to run when a row is deselected
153
+ * @property postDeselected
154
+ * @type Function
155
+ * @default null
156
+ */
157
+ "postDeselected": null,
158
+
159
+ /**
160
+ * Indicate if all rows are selected (needed for server-side processing)
161
+ * @property all
162
+ * @type boolean
163
+ * @default false
164
+ */
165
+ "all": false,
166
+
167
+ /**
168
+ * Class name to add to selected TR nodes
169
+ * @property selectedClass
170
+ * @type String
171
+ * @default ""
172
+ */
173
+ "selectedClass": ""
174
+ },
175
+
176
+ /**
177
+ * Store of the user input customisation object
178
+ * @property custom
179
+ * @type object
180
+ * @default {}
181
+ */
182
+ "custom": {},
183
+
184
+ /**
185
+ * SWF movie path
186
+ * @property swfPath
187
+ * @type string
188
+ * @default ""
189
+ */
190
+ "swfPath": "",
191
+
192
+ /**
193
+ * Default button set
194
+ * @property buttonSet
195
+ * @type array
196
+ * @default []
197
+ */
198
+ "buttonSet": [],
199
+
200
+ /**
201
+ * When there is more than one TableTools instance for a DataTable, there must be a
202
+ * master which controls events (row selection etc)
203
+ * @property master
204
+ * @type boolean
205
+ * @default false
206
+ */
207
+ "master": false
208
+ };
209
+
210
+
211
+ /**
212
+ * @namespace Common and useful DOM elements for the class instance
213
+ */
214
+ this.dom = {
215
+ /**
216
+ * DIV element that is create and all TableTools buttons (and their children) put into
217
+ * @property container
218
+ * @type node
219
+ * @default null
220
+ */
221
+ "container": null,
222
+
223
+ /**
224
+ * The table node to which TableTools will be applied
225
+ * @property table
226
+ * @type node
227
+ * @default null
228
+ */
229
+ "table": null,
230
+
231
+ /**
232
+ * @namespace Nodes used for the print display
233
+ */
234
+ "print": {
235
+ /**
236
+ * Nodes which have been removed from the display by setting them to display none
237
+ * @property hidden
238
+ * @type array
239
+ * @default []
240
+ */
241
+ "hidden": [],
242
+
243
+ /**
244
+ * The information display saying tellng the user about the print display
245
+ * @property message
246
+ * @type node
247
+ * @default null
248
+ */
249
+ "message": null
250
+ },
251
+
252
+ /**
253
+ * @namespace Nodes used for a collection display. This contains the currently used collection
254
+ */
255
+ "collection": {
256
+ /**
257
+ * The div wrapper containing the buttons in the collection (i.e. the menu)
258
+ * @property collection
259
+ * @type node
260
+ * @default null
261
+ */
262
+ "collection": null,
263
+
264
+ /**
265
+ * Background display to provide focus and capture events
266
+ * @property background
267
+ * @type node
268
+ * @default null
269
+ */
270
+ "background": null
271
+ }
272
+ };
273
+
274
+
275
+
276
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
277
+ * Public class methods
278
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
279
+
280
+ /**
281
+ * Retreieve the settings object from an instance
282
+ * @method fnSettings
283
+ * @returns {object} TableTools settings object
284
+ */
285
+ this.fnSettings = function () {
286
+ return this.s;
287
+ };
288
+
289
+
290
+ /* Constructor logic */
291
+ if ( typeof oOpts == 'undefined' )
292
+ {
293
+ oOpts = {};
294
+ }
295
+
296
+ this.s.dt = oDT.fnSettings();
297
+ this._fnConstruct( oOpts );
298
+
299
+ return this;
300
+ };
301
+
302
+
303
+
304
+ TableTools.prototype = {
305
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
306
+ * Public methods
307
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
308
+
309
+ /**
310
+ * Retreieve the settings object from an instance
311
+ * @method fnGetSelected
312
+ * @returns {array} List of TR nodes which are currently selected
313
+ */
314
+ "fnGetSelected": function ()
315
+ {
316
+ var masterS = this._fnGetMasterSettings();
317
+ return masterS.select.selected;
318
+ },
319
+
320
+
321
+ /**
322
+ * Get the data source objects/arrays from DataTables for the selected rows (same as
323
+ * fnGetSelected followed by fnGetData on each row from the table)
324
+ * @method fnGetSelectedData
325
+ * @returns {array} Data from the TR nodes which are currently selected
326
+ */
327
+ "fnGetSelectedData": function ()
328
+ {
329
+ var masterS = this._fnGetMasterSettings();
330
+ var selected = masterS.select.selected;
331
+ var out = [];
332
+
333
+ for ( var i=0, iLen=selected.length ; i<iLen ; i++ )
334
+ {
335
+ out.push( this.s.dt.oInstance.fnGetData( selected[i] ) );
336
+ }
337
+
338
+ return out;
339
+ },
340
+
341
+
342
+ /**
343
+ * Check to see if a current row is selected or not
344
+ * @method fnGetSelected
345
+ * @param {Node} n TR node to check if it is currently selected or not
346
+ * @returns {Boolean} true if select, false otherwise
347
+ */
348
+ "fnIsSelected": function ( n )
349
+ {
350
+ var selected = this.fnGetSelected();
351
+ for ( var i=0, iLen=selected.length ; i<iLen ; i++ )
352
+ {
353
+ if ( n == selected[i] )
354
+ {
355
+ return true;
356
+ }
357
+ }
358
+ return false;
359
+ },
360
+
361
+
362
+ /**
363
+ * Select all rows in the table
364
+ * @method fnSelectAll
365
+ * @returns void
366
+ */
367
+ "fnSelectAll": function ()
368
+ {
369
+ var masterS = this._fnGetMasterSettings();
370
+ masterS.that._fnRowSelectAll();
371
+ },
372
+
373
+
374
+ /**
375
+ * Deselect all rows in the table
376
+ * @method fnSelectNone
377
+ * @returns void
378
+ */
379
+ "fnSelectNone": function ()
380
+ {
381
+ var masterS = this._fnGetMasterSettings();
382
+ masterS.that._fnRowDeselectAll();
383
+ },
384
+
385
+
386
+ /**
387
+ * Select an individual row
388
+ * @method fnSelect
389
+ * @returns void
390
+ */
391
+ "fnSelect": function ( n )
392
+ {
393
+ /* Check if the row is already selected */
394
+ if ( !this.fnIsSelected( n ) )
395
+ {
396
+ if ( this.s.select.type == "single" )
397
+ {
398
+ this._fnRowSelectSingle( n );
399
+ }
400
+ else if ( this.s.select.type == "multi" )
401
+ {
402
+ this._fnRowSelectMulti( n );
403
+ }
404
+ }
405
+ },
406
+
407
+
408
+ /**
409
+ * Deselect an individual row
410
+ * @method fnDeselect
411
+ * @returns void
412
+ */
413
+ "fnDeselect": function ( n )
414
+ {
415
+ /* Check if the row is already deselected */
416
+ if ( this.fnIsSelected( n ) )
417
+ {
418
+ if ( this.s.select.type == "single" )
419
+ {
420
+ this._fnRowSelectSingle( n );
421
+ }
422
+ else if ( this.s.select.type == "multi" )
423
+ {
424
+ this._fnRowSelectMulti( n );
425
+ }
426
+ }
427
+ },
428
+
429
+
430
+ /**
431
+ * Get the title of the document - useful for file names. The title is retrieved from either
432
+ * the configuration object's 'title' parameter, or the HTML document title
433
+ * @method fnGetTitle
434
+ * @param {Object} oConfig Button configuration object
435
+ * @returns {String} Button title
436
+ */
437
+ "fnGetTitle": function( oConfig )
438
+ {
439
+ var sTitle = "";
440
+ if ( typeof oConfig.sTitle != 'undefined' && oConfig.sTitle !== "" ) {
441
+ sTitle = oConfig.sTitle;
442
+ } else {
443
+ var anTitle = document.getElementsByTagName('title');
444
+ if ( anTitle.length > 0 )
445
+ {
446
+ sTitle = anTitle[0].innerHTML;
447
+ }
448
+ }
449
+
450
+ /* Strip characters which the OS will object to - checking for UTF8 support in the scripting
451
+ * engine
452
+ */
453
+ if ( "\u00A1".toString().length < 4 ) {
454
+ return sTitle.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, "");
455
+ } else {
456
+ return sTitle.replace(/[^a-zA-Z0-9_\.,\-_ !\(\)]/g, "");
457
+ }
458
+ },
459
+
460
+
461
+ /**
462
+ * Calculate a unity array with the column width by proportion for a set of columns to be
463
+ * included for a button. This is particularly useful for PDF creation, where we can use the
464
+ * column widths calculated by the browser to size the columns in the PDF.
465
+ * @method fnCalcColRations
466
+ * @param {Object} oConfig Button configuration object
467
+ * @returns {Array} Unity array of column ratios
468
+ */
469
+ "fnCalcColRatios": function ( oConfig )
470
+ {
471
+ var
472
+ aoCols = this.s.dt.aoColumns,
473
+ aColumnsInc = this._fnColumnTargets( oConfig.mColumns ),
474
+ aColWidths = [],
475
+ iWidth = 0, iTotal = 0, i, iLen;
476
+
477
+ for ( i=0, iLen=aColumnsInc.length ; i<iLen ; i++ )
478
+ {
479
+ if ( aColumnsInc[i] )
480
+ {
481
+ iWidth = aoCols[i].nTh.offsetWidth;
482
+ iTotal += iWidth;
483
+ aColWidths.push( iWidth );
484
+ }
485
+ }
486
+
487
+ for ( i=0, iLen=aColWidths.length ; i<iLen ; i++ )
488
+ {
489
+ aColWidths[i] = aColWidths[i] / iTotal;
490
+ }
491
+
492
+ return aColWidths.join('\t');
493
+ },
494
+
495
+
496
+ /**
497
+ * Get the information contained in a table as a string
498
+ * @method fnGetTableData
499
+ * @param {Object} oConfig Button configuration object
500
+ * @returns {String} Table data as a string
501
+ */
502
+ "fnGetTableData": function ( oConfig )
503
+ {
504
+ /* In future this could be used to get data from a plain HTML source as well as DataTables */
505
+ if ( this.s.dt )
506
+ {
507
+ return this._fnGetDataTablesData( oConfig );
508
+ }
509
+ },
510
+
511
+
512
+ /**
513
+ * Pass text to a flash button instance, which will be used on the button's click handler
514
+ * @method fnSetText
515
+ * @param {Object} clip Flash button object
516
+ * @param {String} text Text to set
517
+ * @returns void
518
+ */
519
+ "fnSetText": function ( clip, text )
520
+ {
521
+ this._fnFlashSetText( clip, text );
522
+ },
523
+
524
+
525
+ /**
526
+ * Resize the flash elements of the buttons attached to this TableTools instance - this is
527
+ * useful for when initialising TableTools when it is hidden (display:none) since sizes can't
528
+ * be calculated at that time.
529
+ * @method fnResizeButtons
530
+ * @returns void
531
+ */
532
+ "fnResizeButtons": function ()
533
+ {
534
+ for ( var cli in ZeroClipboard.clients )
535
+ {
536
+ if ( cli )
537
+ {
538
+ var client = ZeroClipboard.clients[cli];
539
+ if ( typeof client.domElement != 'undefined' &&
540
+ client.domElement.parentNode == this.dom.container )
541
+ {
542
+ client.positionElement();
543
+ }
544
+ }
545
+ }
546
+ },
547
+
548
+
549
+ /**
550
+ * Check to see if any of the ZeroClipboard client's attached need to be resized
551
+ * @method fnResizeRequired
552
+ * @returns void
553
+ */
554
+ "fnResizeRequired": function ()
555
+ {
556
+ for ( var cli in ZeroClipboard.clients )
557
+ {
558
+ if ( cli )
559
+ {
560
+ var client = ZeroClipboard.clients[cli];
561
+ if ( typeof client.domElement != 'undefined' &&
562
+ client.domElement.parentNode == this.dom.container &&
563
+ client.sized === false )
564
+ {
565
+ return true;
566
+ }
567
+ }
568
+ }
569
+ return false;
570
+ },
571
+
572
+
573
+
574
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
575
+ * Private methods (they are of course public in JS, but recommended as private)
576
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
577
+
578
+ /**
579
+ * Constructor logic
580
+ * @method _fnConstruct
581
+ * @param {Object} oOpts Same as TableTools constructor
582
+ * @returns void
583
+ * @private
584
+ */
585
+ "_fnConstruct": function ( oOpts )
586
+ {
587
+ var that = this;
588
+
589
+ this._fnCustomiseSettings( oOpts );
590
+
591
+ /* Container element */
592
+ this.dom.container = document.createElement('div');
593
+ this.dom.container.className = !this.s.dt.bJUI ? "DTTT_container" :
594
+ "DTTT_container ui-buttonset ui-buttonset-multi";
595
+
596
+ /* Row selection config */
597
+ if ( this.s.select.type != 'none' )
598
+ {
599
+ this._fnRowSelectConfig();
600
+ }
601
+
602
+ /* Buttons */
603
+ this._fnButtonDefinations( this.s.buttonSet, this.dom.container );
604
+
605
+ /* Destructor - need to wipe the DOM for IE's garbage collector */
606
+ this.s.dt.aoDestroyCallback.push( {
607
+ "sName": "TableTools",
608
+ "fn": function () {
609
+ that.dom.container.innerHTML = "";
610
+ }
611
+ } );
612
+ },
613
+
614
+
615
+ /**
616
+ * Take the user defined settings and the default settings and combine them.
617
+ * @method _fnCustomiseSettings
618
+ * @param {Object} oOpts Same as TableTools constructor
619
+ * @returns void
620
+ * @private
621
+ */
622
+ "_fnCustomiseSettings": function ( oOpts )
623
+ {
624
+ /* Is this the master control instance or not? */
625
+ if ( typeof this.s.dt._TableToolsInit == 'undefined' )
626
+ {
627
+ this.s.master = true;
628
+ this.s.dt._TableToolsInit = true;
629
+ }
630
+
631
+ /* We can use the table node from comparisons to group controls */
632
+ this.dom.table = this.s.dt.nTable;
633
+
634
+ /* Clone the defaults and then the user options */
635
+ this.s.custom = $.extend( {}, TableTools.DEFAULTS, oOpts );
636
+
637
+ /* Flash file location */
638
+ this.s.swfPath = this.s.custom.sSwfPath;
639
+ if ( typeof ZeroClipboard != 'undefined' )
640
+ {
641
+ ZeroClipboard.moviePath = this.s.swfPath;
642
+ }
643
+
644
+ /* Table row selecting */
645
+ this.s.select.type = this.s.custom.sRowSelect;
646
+ this.s.select.preRowSelect = this.s.custom.fnPreRowSelect;
647
+ this.s.select.postSelected = this.s.custom.fnRowSelected;
648
+ this.s.select.postDeselected = this.s.custom.fnRowDeselected;
649
+ this.s.select.selectedClass = this.s.custom.sSelectedClass;
650
+
651
+ /* Button set */
652
+ this.s.buttonSet = this.s.custom.aButtons;
653
+ },
654
+
655
+
656
+ /**
657
+ * Take the user input arrays and expand them to be fully defined, and then add them to a given
658
+ * DOM element
659
+ * @method _fnButtonDefinations
660
+ * @param {array} buttonSet Set of user defined buttons
661
+ * @param {node} wrapper Node to add the created buttons to
662
+ * @returns void
663
+ * @private
664
+ */
665
+ "_fnButtonDefinations": function ( buttonSet, wrapper )
666
+ {
667
+ var buttonDef;
668
+
669
+ for ( var i=0, iLen=buttonSet.length ; i<iLen ; i++ )
670
+ {
671
+ if ( typeof buttonSet[i] == "string" )
672
+ {
673
+ if ( typeof TableTools.BUTTONS[ buttonSet[i] ] == 'undefined' )
674
+ {
675
+ alert( "TableTools: Warning - unknown button type: "+buttonSet[i] );
676
+ continue;
677
+ }
678
+ buttonDef = $.extend( {}, TableTools.BUTTONS[ buttonSet[i] ], true );
679
+ }
680
+ else
681
+ {
682
+ if ( typeof TableTools.BUTTONS[ buttonSet[i].sExtends ] == 'undefined' )
683
+ {
684
+ alert( "TableTools: Warning - unknown button type: "+buttonSet[i].sExtends );
685
+ continue;
686
+ }
687
+ var o = $.extend( {}, TableTools.BUTTONS[ buttonSet[i].sExtends ], true );
688
+ buttonDef = $.extend( o, buttonSet[i], true );
689
+ }
690
+
691
+ if ( this.s.dt.bJUI )
692
+ {
693
+ buttonDef.sButtonClass += " ui-button ui-state-default";
694
+ buttonDef.sButtonClassHover += " ui-state-hover";
695
+ }
696
+
697
+ wrapper.appendChild( this._fnCreateButton( buttonDef ) );
698
+ }
699
+ },
700
+
701
+
702
+ /**
703
+ * Create and configure a TableTools button
704
+ * @method _fnCreateButton
705
+ * @param {Object} oConfig Button configuration object
706
+ * @returns {Node} Button element
707
+ * @private
708
+ */
709
+ "_fnCreateButton": function ( oConfig )
710
+ {
711
+ var nButton = (oConfig.sAction == 'div') ?
712
+ this._fnDivBase( oConfig ) : this._fnButtonBase( oConfig );
713
+
714
+ if ( oConfig.sAction == "print" )
715
+ {
716
+ this._fnPrintConfig( nButton, oConfig );
717
+ }
718
+ else if ( oConfig.sAction.match(/flash/) )
719
+ {
720
+ this._fnFlashConfig( nButton, oConfig );
721
+ }
722
+ else if ( oConfig.sAction == "text" )
723
+ {
724
+ this._fnTextConfig( nButton, oConfig );
725
+ }
726
+ else if ( oConfig.sAction == "div" )
727
+ {
728
+ this._fnTextConfig( nButton, oConfig );
729
+ }
730
+ else if ( oConfig.sAction == "collection" )
731
+ {
732
+ this._fnTextConfig( nButton, oConfig );
733
+ this._fnCollectionConfig( nButton, oConfig );
734
+ }
735
+
736
+ return nButton;
737
+ },
738
+
739
+
740
+ /**
741
+ * Create the DOM needed for the button and apply some base properties. All buttons start here
742
+ * @method _fnButtonBase
743
+ * @param {o} oConfig Button configuration object
744
+ * @returns {Node} DIV element for the button
745
+ * @private
746
+ */
747
+ "_fnButtonBase": function ( o )
748
+ {
749
+ var
750
+ nButton = document.createElement('button'),
751
+ nSpan = document.createElement('span'),
752
+ masterS = this._fnGetMasterSettings();
753
+
754
+ nButton.className = "DTTT_button "+o.sButtonClass;
755
+ nButton.setAttribute('id', "ToolTables_"+this.s.dt.sInstance+"_"+masterS.buttonCounter );
756
+ nButton.appendChild( nSpan );
757
+ nSpan.innerHTML = o.sButtonText;
758
+
759
+ masterS.buttonCounter++;
760
+
761
+ return nButton;
762
+ },
763
+
764
+
765
+ /**
766
+ * Create a DIV element to use for a non-button
767
+ * @method _fnDivBase
768
+ * @param {o} oConfig Button configuration object
769
+ * @returns {Node} DIV element for the button
770
+ * @private
771
+ */
772
+ "_fnDivBase": function ( o )
773
+ {
774
+ var
775
+ nDiv = document.createElement('div'),
776
+ masterS = this._fnGetMasterSettings();
777
+
778
+ nDiv.className = o.sButtonClass;
779
+ nDiv.setAttribute('id', "ToolTables_"+this.s.dt.sInstance+"_"+masterS.buttonCounter );
780
+ nDiv.innerHTML = o.sButtonText;
781
+
782
+ if ( o.nContent !== null )
783
+ {
784
+ nDiv.appendChild( o.nContent );
785
+ }
786
+
787
+ masterS.buttonCounter++;
788
+
789
+ return nDiv;
790
+ },
791
+
792
+
793
+ /**
794
+ * Get the settings object for the master instance. When more than one TableTools instance is
795
+ * assigned to a DataTable, only one of them can be the 'master' (for the select rows). As such,
796
+ * we will typically want to interact with that master for global properties.
797
+ * @method _fnGetMasterSettings
798
+ * @returns {Object} TableTools settings object
799
+ * @private
800
+ */
801
+ "_fnGetMasterSettings": function ()
802
+ {
803
+ if ( this.s.master )
804
+ {
805
+ return this.s;
806
+ }
807
+ else
808
+ {
809
+ /* Look for the master which has the same DT as this one */
810
+ var instances = TableTools._aInstances;
811
+ for ( var i=0, iLen=instances.length ; i<iLen ; i++ )
812
+ {
813
+ if ( this.dom.table == instances[i].s.dt.nTable )
814
+ {
815
+ return instances[i].s;
816
+ }
817
+ }
818
+ }
819
+ },
820
+
821
+
822
+
823
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
824
+ * Button collection functions
825
+ */
826
+
827
+ /**
828
+ * Create a collection button, when activated will present a drop downlist of other buttons
829
+ * @param {Node} nButton Button to use for the collection activation
830
+ * @param {Object} oConfig Button configuration object
831
+ * @returns void
832
+ * @private
833
+ */
834
+ "_fnCollectionConfig": function ( nButton, oConfig )
835
+ {
836
+ var nHidden = document.createElement('div');
837
+ nHidden.style.display = "none";
838
+ nHidden.className = !this.s.dt.bJUI ? "DTTT_collection" :
839
+ "DTTT_collection ui-buttonset ui-buttonset-multi";
840
+ oConfig._collection = nHidden;
841
+ document.body.appendChild( nHidden );
842
+
843
+ this._fnButtonDefinations( oConfig.aButtons, nHidden );
844
+ },
845
+
846
+
847
+ /**
848
+ * Show a button collection
849
+ * @param {Node} nButton Button to use for the collection
850
+ * @param {Object} oConfig Button configuration object
851
+ * @returns void
852
+ * @private
853
+ */
854
+ "_fnCollectionShow": function ( nButton, oConfig )
855
+ {
856
+ var
857
+ that = this,
858
+ oPos = $(nButton).offset(),
859
+ nHidden = oConfig._collection,
860
+ iDivX = oPos.left,
861
+ iDivY = oPos.top + $(nButton).outerHeight(),
862
+ iWinHeight = $(window).height(), iDocHeight = $(document).height(),
863
+ iWinWidth = $(window).width(), iDocWidth = $(document).width();
864
+
865
+ nHidden.style.position = "absolute";
866
+ nHidden.style.left = iDivX+"px";
867
+ nHidden.style.top = iDivY+"px";
868
+ nHidden.style.display = "block";
869
+ $(nHidden).css('opacity',0);
870
+
871
+ var nBackground = document.createElement('div');
872
+ nBackground.style.position = "absolute";
873
+ nBackground.style.left = "0px";
874
+ nBackground.style.top = "0px";
875
+ nBackground.style.height = ((iWinHeight>iDocHeight)? iWinHeight : iDocHeight) +"px";
876
+ nBackground.style.width = ((iWinWidth>iDocWidth)? iWinWidth : iDocWidth) +"px";
877
+ nBackground.className = "DTTT_collection_background";
878
+ $(nBackground).css('opacity',0);
879
+
880
+ document.body.appendChild( nBackground );
881
+ document.body.appendChild( nHidden );
882
+
883
+ /* Visual corrections to try and keep the collection visible */
884
+ var iDivWidth = $(nHidden).outerWidth();
885
+ var iDivHeight = $(nHidden).outerHeight();
886
+
887
+ if ( iDivX + iDivWidth > iDocWidth )
888
+ {
889
+ nHidden.style.left = (iDocWidth-iDivWidth)+"px";
890
+ }
891
+
892
+ if ( iDivY + iDivHeight > iDocHeight )
893
+ {
894
+ nHidden.style.top = (iDivY-iDivHeight-$(nButton).outerHeight())+"px";
895
+ }
896
+
897
+ this.dom.collection.collection = nHidden;
898
+ this.dom.collection.background = nBackground;
899
+
900
+ /* This results in a very small delay for the end user but it allows the animation to be
901
+ * much smoother. If you don't want the animation, then the setTimeout can be removed
902
+ */
903
+ setTimeout( function () {
904
+ $(nHidden).animate({"opacity": 1}, 500);
905
+ $(nBackground).animate({"opacity": 0.25}, 500);
906
+ }, 10 );
907
+
908
+ /* Event handler to remove the collection display */
909
+ $(nBackground).click( function () {
910
+ that._fnCollectionHide.call( that, null, null );
911
+ } );
912
+ },
913
+
914
+
915
+ /**
916
+ * Hide a button collection
917
+ * @param {Node} nButton Button to use for the collection
918
+ * @param {Object} oConfig Button configuration object
919
+ * @returns void
920
+ * @private
921
+ */
922
+ "_fnCollectionHide": function ( nButton, oConfig )
923
+ {
924
+ if ( oConfig !== null && oConfig.sExtends == 'collection' )
925
+ {
926
+ return;
927
+ }
928
+
929
+ if ( this.dom.collection.collection !== null )
930
+ {
931
+ $(this.dom.collection.collection).animate({"opacity": 0}, 500, function (e) {
932
+ this.style.display = "none";
933
+ } );
934
+
935
+ $(this.dom.collection.background).animate({"opacity": 0}, 500, function (e) {
936
+ this.parentNode.removeChild( this );
937
+ } );
938
+
939
+ this.dom.collection.collection = null;
940
+ this.dom.collection.background = null;
941
+ }
942
+ },
943
+
944
+
945
+
946
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
947
+ * Row selection functions
948
+ */
949
+
950
+ /**
951
+ * Add event handlers to a table to allow for row selection
952
+ * @method _fnRowSelectConfig
953
+ * @returns void
954
+ * @private
955
+ */
956
+ "_fnRowSelectConfig": function ()
957
+ {
958
+ if ( this.s.master )
959
+ {
960
+ var
961
+ that = this,
962
+ i, iLen,
963
+ aoOpenRows = this.s.dt.aoOpenRows;
964
+
965
+ $(that.s.dt.nTable).addClass( 'DTTT_selectable' );
966
+
967
+ $('tr', that.s.dt.nTBody).live( 'click', function(e) {
968
+ /* Sub-table must be ignored (odd that the selector won't do this with >) */
969
+ if ( this.parentNode != that.s.dt.nTBody )
970
+ {
971
+ return;
972
+ }
973
+
974
+ /* Check that we are actually working with a DataTables controlled row */
975
+ var anTableRows = that.s.dt.oInstance.fnGetNodes();
976
+ if ( $.inArray( this, anTableRows ) === -1 ) {
977
+ return;
978
+ }
979
+
980
+ /* User defined selection function */
981
+ if ( that.s.select.preRowSelect !== null && !that.s.select.preRowSelect.call(that, e) )
982
+ {
983
+ return;
984
+ }
985
+
986
+ /* And go */
987
+ if ( that.s.select.type == "single" )
988
+ {
989
+ that._fnRowSelectSingle.call( that, this );
990
+ }
991
+ else
992
+ {
993
+ that._fnRowSelectMulti.call( that, this );
994
+ }
995
+ } );
996
+
997
+ /* Add a draw callback handler for when 'select' all is active and we are using server-side
998
+ * processing, so TableTools will automatically select the new rows for us
999
+ */
1000
+ that.s.dt.aoDrawCallback.push( {
1001
+ "fn": function () {
1002
+ if ( that.s.select.all && that.s.dt.oFeatures.bServerSide )
1003
+ {
1004
+ that.fnSelectAll();
1005
+ }
1006
+ },
1007
+ "sName": "TableTools_select"
1008
+ } );
1009
+ }
1010
+ },
1011
+
1012
+
1013
+ /**
1014
+ * Select or deselect a row based on its current state when only one row is allowed to be
1015
+ * selected at a time (i.e. if there is a row already selected, deselect it). If the selected
1016
+ * row is the one being passed in, just deselect and take no further action.
1017
+ * @method _fnRowSelectSingle
1018
+ * @param {Node} nNode TR element which is being 'activated' in some way
1019
+ * @returns void
1020
+ * @private
1021
+ */
1022
+ "_fnRowSelectSingle": function ( nNode )
1023
+ {
1024
+ if ( this.s.master )
1025
+ {
1026
+ /* Do nothing on the DataTables 'empty' result set row */
1027
+ if ( $('td', nNode).hasClass(this.s.dt.oClasses.sRowEmpty) )
1028
+ {
1029
+ return;
1030
+ }
1031
+
1032
+ if ( $(nNode).hasClass(this.s.select.selectedClass) )
1033
+ {
1034
+ this._fnRowDeselect( nNode );
1035
+ }
1036
+ else
1037
+ {
1038
+ if ( this.s.select.selected.length !== 0 )
1039
+ {
1040
+ this._fnRowDeselectAll();
1041
+ }
1042
+
1043
+ this.s.select.selected.push( nNode );
1044
+ $(nNode).addClass( this.s.select.selectedClass );
1045
+
1046
+ if ( this.s.select.postSelected !== null )
1047
+ {
1048
+ this.s.select.postSelected.call( this, nNode );
1049
+ }
1050
+ }
1051
+
1052
+ TableTools._fnEventDispatch( this, 'select', nNode );
1053
+ }
1054
+ },
1055
+
1056
+
1057
+ /**
1058
+ * Select or deselect a row based on its current state when multiple rows are allowed to be
1059
+ * selected.
1060
+ * @method _fnRowSelectMulti
1061
+ * @param {Node} nNode TR element which is being 'activated' in some way
1062
+ * @returns void
1063
+ * @private
1064
+ */
1065
+ "_fnRowSelectMulti": function ( nNode )
1066
+ {
1067
+ if ( this.s.master )
1068
+ {
1069
+ /* Do nothing on the DataTables 'empty' result set row */
1070
+ if ( $('td', nNode).hasClass(this.s.dt.oClasses.sRowEmpty) )
1071
+ {
1072
+ return;
1073
+ }
1074
+
1075
+ if ( $(nNode).hasClass(this.s.select.selectedClass) )
1076
+ {
1077
+ this._fnRowDeselect( nNode );
1078
+ }
1079
+ else
1080
+ {
1081
+ this.s.select.selected.push( nNode );
1082
+ $(nNode).addClass( this.s.select.selectedClass );
1083
+
1084
+ if ( this.s.select.postSelected !== null )
1085
+ {
1086
+ this.s.select.postSelected.call( this, nNode );
1087
+ }
1088
+ }
1089
+
1090
+ TableTools._fnEventDispatch( this, 'select', nNode );
1091
+ }
1092
+ },
1093
+
1094
+
1095
+ /**
1096
+ * Select all TR elements in the table. Note that this function will still operate in 'single'
1097
+ * select mode, which might not be what you desire (in which case, don't call this function!)
1098
+ * @method _fnRowSelectAll
1099
+ * @returns void
1100
+ * @private
1101
+ */
1102
+ "_fnRowSelectAll": function ( )
1103
+ {
1104
+ if ( this.s.master )
1105
+ {
1106
+ var n;
1107
+ for ( var i=0, iLen=this.s.dt.aiDisplayMaster.length ; i<iLen ; i++ )
1108
+ {
1109
+ n = this.s.dt.aoData[ this.s.dt.aiDisplayMaster[i] ].nTr;
1110
+
1111
+ if ( !$(n).hasClass(this.s.select.selectedClass) )
1112
+ {
1113
+ this.s.select.selected.push( n );
1114
+ $(n).addClass( this.s.select.selectedClass );
1115
+ }
1116
+ }
1117
+
1118
+ if ( this.s.select.postSelected !== null )
1119
+ {
1120
+ this.s.select.postSelected.call( this, null );
1121
+ }
1122
+
1123
+ this.s.select.all = true;
1124
+ TableTools._fnEventDispatch( this, 'select', null );
1125
+ }
1126
+ },
1127
+
1128
+
1129
+ /**
1130
+ * Deselect all TR elements in the table. If nothing is currently selected, then no action is
1131
+ * taken.
1132
+ * @method _fnRowDeselectAll
1133
+ * @returns void
1134
+ * @private
1135
+ */
1136
+ "_fnRowDeselectAll": function ( )
1137
+ {
1138
+ if ( this.s.master )
1139
+ {
1140
+ for ( var i=this.s.select.selected.length-1 ; i>=0 ; i-- )
1141
+ {
1142
+ this._fnRowDeselect( i, false );
1143
+ }
1144
+
1145
+ if ( this.s.select.postDeselected !== null )
1146
+ {
1147
+ this.s.select.postDeselected.call( this, null );
1148
+ }
1149
+
1150
+ this.s.select.all = false;
1151
+ TableTools._fnEventDispatch( this, 'select', null );
1152
+ }
1153
+ },
1154
+
1155
+
1156
+ /**
1157
+ * Deselect a single row, based on its index in the selected array, or a TR node (when the
1158
+ * index is then computed)
1159
+ * @method _fnRowDeselect
1160
+ * @param {int|Node} i Node or index of node in selected array, which is to be deselected
1161
+ * @param {bool} [action=true] Run the post deselected method or not
1162
+ * @returns void
1163
+ * @private
1164
+ */
1165
+ "_fnRowDeselect": function ( i, action )
1166
+ {
1167
+ if ( typeof i.nodeName != 'undefined' )
1168
+ {
1169
+ i = $.inArray( i, this.s.select.selected );
1170
+ }
1171
+
1172
+ var nNode = this.s.select.selected[i];
1173
+ $(nNode).removeClass(this.s.select.selectedClass);
1174
+ this.s.select.selected.splice( i, 1 );
1175
+
1176
+ if ( (typeof action == 'undefined' || action) && this.s.select.postDeselected !== null )
1177
+ {
1178
+ this.s.select.postDeselected.call( this, nNode );
1179
+ }
1180
+
1181
+ this.s.select.all = false;
1182
+ },
1183
+
1184
+
1185
+
1186
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1187
+ * Text button functions
1188
+ */
1189
+
1190
+ /**
1191
+ * Configure a text based button for interaction events
1192
+ * @method _fnTextConfig
1193
+ * @param {Node} nButton Button element which is being considered
1194
+ * @param {Object} oConfig Button configuration object
1195
+ * @returns void
1196
+ * @private
1197
+ */
1198
+ "_fnTextConfig": function ( nButton, oConfig )
1199
+ {
1200
+ var that = this;
1201
+
1202
+ if ( oConfig.fnInit !== null )
1203
+ {
1204
+ oConfig.fnInit.call( this, nButton, oConfig );
1205
+ }
1206
+
1207
+ if ( oConfig.sToolTip !== "" )
1208
+ {
1209
+ nButton.title = oConfig.sToolTip;
1210
+ }
1211
+
1212
+ $(nButton).hover( function () {
1213
+ $(nButton).addClass(oConfig.sButtonClassHover );
1214
+ if ( oConfig.fnMouseover !== null )
1215
+ {
1216
+ oConfig.fnMouseover.call( this, nButton, oConfig, null );
1217
+ }
1218
+ }, function () {
1219
+ $(nButton).removeClass( oConfig.sButtonClassHover );
1220
+ if ( oConfig.fnMouseout !== null )
1221
+ {
1222
+ oConfig.fnMouseout.call( this, nButton, oConfig, null );
1223
+ }
1224
+ } );
1225
+
1226
+ if ( oConfig.fnSelect !== null )
1227
+ {
1228
+ TableTools._fnEventListen( this, 'select', function (n) {
1229
+ oConfig.fnSelect.call( that, nButton, oConfig, n );
1230
+ } );
1231
+ }
1232
+
1233
+ $(nButton).click( function (e) {
1234
+ e.preventDefault();
1235
+
1236
+ if ( oConfig.fnClick !== null )
1237
+ {
1238
+ oConfig.fnClick.call( that, nButton, oConfig, null );
1239
+ }
1240
+
1241
+ /* Provide a complete function to match the behaviour of the flash elements */
1242
+ if ( oConfig.fnComplete !== null )
1243
+ {
1244
+ oConfig.fnComplete.call( that, nButton, oConfig, null, null );
1245
+ }
1246
+
1247
+ that._fnCollectionHide( nButton, oConfig );
1248
+ } );
1249
+ },
1250
+
1251
+
1252
+
1253
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1254
+ * Flash button functions
1255
+ */
1256
+
1257
+ /**
1258
+ * Configure a flash based button for interaction events
1259
+ * @method _fnFlashConfig
1260
+ * @param {Node} nButton Button element which is being considered
1261
+ * @param {o} oConfig Button configuration object
1262
+ * @returns void
1263
+ * @private
1264
+ */
1265
+ "_fnFlashConfig": function ( nButton, oConfig )
1266
+ {
1267
+ var that = this;
1268
+ var flash = new ZeroClipboard.Client();
1269
+
1270
+ if ( oConfig.fnInit !== null )
1271
+ {
1272
+ oConfig.fnInit.call( this, nButton, oConfig );
1273
+ }
1274
+
1275
+ flash.setHandCursor( true );
1276
+
1277
+ if ( oConfig.sAction == "flash_save" )
1278
+ {
1279
+ flash.setAction( 'save' );
1280
+ flash.setCharSet( (oConfig.sCharSet=="utf16le") ? 'UTF16LE' : 'UTF8' );
1281
+ flash.setBomInc( oConfig.bBomInc );
1282
+ flash.setFileName( oConfig.sFileName.replace('*', this.fnGetTitle(oConfig)) );
1283
+ }
1284
+ else if ( oConfig.sAction == "flash_pdf" )
1285
+ {
1286
+ flash.setAction( 'pdf' );
1287
+ flash.setFileName( oConfig.sFileName.replace('*', this.fnGetTitle(oConfig)) );
1288
+ }
1289
+ else
1290
+ {
1291
+ flash.setAction( 'copy' );
1292
+ }
1293
+
1294
+ flash.addEventListener('mouseOver', function(client) {
1295
+ $(nButton).addClass(oConfig.sButtonClassHover );
1296
+
1297
+ if ( oConfig.fnMouseover !== null )
1298
+ {
1299
+ oConfig.fnMouseover.call( that, nButton, oConfig, flash );
1300
+ }
1301
+ } );
1302
+
1303
+ flash.addEventListener('mouseOut', function(client) {
1304
+ $(nButton).removeClass( oConfig.sButtonClassHover );
1305
+
1306
+ if ( oConfig.fnMouseout !== null )
1307
+ {
1308
+ oConfig.fnMouseout.call( that, nButton, oConfig, flash );
1309
+ }
1310
+ } );
1311
+
1312
+ flash.addEventListener('mouseDown', function(client) {
1313
+ if ( oConfig.fnClick !== null )
1314
+ {
1315
+ oConfig.fnClick.call( that, nButton, oConfig, flash );
1316
+ }
1317
+ } );
1318
+
1319
+ flash.addEventListener('complete', function (client, text) {
1320
+ if ( oConfig.fnComplete !== null )
1321
+ {
1322
+ oConfig.fnComplete.call( that, nButton, oConfig, flash, text );
1323
+ }
1324
+ that._fnCollectionHide( nButton, oConfig );
1325
+ } );
1326
+
1327
+ this._fnFlashGlue( flash, nButton, oConfig.sToolTip );
1328
+ },
1329
+
1330
+
1331
+ /**
1332
+ * Wait until the id is in the DOM before we "glue" the swf. Note that this function will call
1333
+ * itself (using setTimeout) until it completes successfully
1334
+ * @method _fnFlashGlue
1335
+ * @param {Object} clip Zero clipboard object
1336
+ * @param {Node} node node to glue swf to
1337
+ * @param {String} text title of the flash movie
1338
+ * @returns void
1339
+ * @private
1340
+ */
1341
+ "_fnFlashGlue": function ( flash, node, text )
1342
+ {
1343
+ var that = this;
1344
+ var id = node.getAttribute('id');
1345
+
1346
+ if ( document.getElementById(id) )
1347
+ {
1348
+ flash.glue( node, text );
1349
+
1350
+ /* Catch those who are using a TableTools 1 version of ZeroClipboard */
1351
+ if ( flash.domElement.parentNode != flash.div.parentNode &&
1352
+ typeof that.__bZCWarning == 'undefined' )
1353
+ {
1354
+ that.s.dt.oApi._fnLog( this.s.dt, 0, "It looks like you are using the version of "+
1355
+ "ZeroClipboard which came with TableTools 1. Please update to use the version that "+
1356
+ "came with TableTools 2." );
1357
+ that.__bZCWarning = true;
1358
+ }
1359
+ }
1360
+ else
1361
+ {
1362
+ setTimeout( function () {
1363
+ that._fnFlashGlue( flash, node, text );
1364
+ }, 100 );
1365
+ }
1366
+ },
1367
+
1368
+
1369
+ /**
1370
+ * Set the text for the flash clip to deal with
1371
+ *
1372
+ * This function is required for large information sets. There is a limit on the
1373
+ * amount of data that can be transfered between Javascript and Flash in a single call, so
1374
+ * we use this method to build up the text in Flash by sending over chunks. It is estimated
1375
+ * that the data limit is around 64k, although it is undocuments, and appears to be different
1376
+ * between different flash versions. We chunk at 8KiB.
1377
+ * @method _fnFlashSetText
1378
+ * @param {Object} clip the ZeroClipboard object
1379
+ * @param {String} sData the data to be set
1380
+ * @returns void
1381
+ * @private
1382
+ */
1383
+ "_fnFlashSetText": function ( clip, sData )
1384
+ {
1385
+ var asData = this._fnChunkData( sData, 8192 );
1386
+
1387
+ clip.clearText();
1388
+ for ( var i=0, iLen=asData.length ; i<iLen ; i++ )
1389
+ {
1390
+ clip.appendText( asData[i] );
1391
+ }
1392
+ },
1393
+
1394
+
1395
+
1396
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1397
+ * Data retrieval functions
1398
+ */
1399
+
1400
+ /**
1401
+ * Convert the mixed columns variable into a boolean array the same size as the columns, which
1402
+ * indicates which columns we want to include
1403
+ * @method _fnColumnTargets
1404
+ * @param {String|Array} mColumns The columns to be included in data retreieval. If a string
1405
+ * then it can take the value of "visible" or "hidden" (to include all visible or
1406
+ * hidden columns respectively). Or an array of column indexes
1407
+ * @returns {Array} A boolean array the length of the columns of the table, which each value
1408
+ * indicating if the column is to be included or not
1409
+ * @private
1410
+ */
1411
+ "_fnColumnTargets": function ( mColumns )
1412
+ {
1413
+ var aColumns = [];
1414
+ var dt = this.s.dt;
1415
+
1416
+ if ( typeof mColumns == "object" )
1417
+ {
1418
+ for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1419
+ {
1420
+ aColumns.push( false );
1421
+ }
1422
+
1423
+ for ( i=0, iLen=mColumns.length ; i<iLen ; i++ )
1424
+ {
1425
+ aColumns[ mColumns[i] ] = true;
1426
+ }
1427
+ }
1428
+ else if ( mColumns == "visible" )
1429
+ {
1430
+ for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1431
+ {
1432
+ aColumns.push( dt.aoColumns[i].bVisible ? true : false );
1433
+ }
1434
+ }
1435
+ else if ( mColumns == "hidden" )
1436
+ {
1437
+ for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1438
+ {
1439
+ aColumns.push( dt.aoColumns[i].bVisible ? false : true );
1440
+ }
1441
+ }
1442
+ else if ( mColumns == "sortable" )
1443
+ {
1444
+ for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1445
+ {
1446
+ aColumns.push( dt.aoColumns[i].bSortable ? true : false );
1447
+ }
1448
+ }
1449
+ else /* all */
1450
+ {
1451
+ for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1452
+ {
1453
+ aColumns.push( true );
1454
+ }
1455
+ }
1456
+
1457
+ return aColumns;
1458
+ },
1459
+
1460
+
1461
+ /**
1462
+ * New line character(s) depend on the platforms
1463
+ * @method method
1464
+ * @param {Object} oConfig Button configuration object - only interested in oConfig.sNewLine
1465
+ * @returns {String} Newline character
1466
+ */
1467
+ "_fnNewline": function ( oConfig )
1468
+ {
1469
+ if ( oConfig.sNewLine == "auto" )
1470
+ {
1471
+ return navigator.userAgent.match(/Windows/) ? "\r\n" : "\n";
1472
+ }
1473
+ else
1474
+ {
1475
+ return oConfig.sNewLine;
1476
+ }
1477
+ },
1478
+
1479
+
1480
+ /**
1481
+ * Get data from DataTables' internals and format it for output
1482
+ * @method _fnGetDataTablesData
1483
+ * @param {Object} oConfig Button configuration object
1484
+ * @param {String} oConfig.sFieldBoundary Field boundary for the data cells in the string
1485
+ * @param {String} oConfig.sFieldSeperator Field seperator for the data cells
1486
+ * @param {String} oConfig.sNewline New line options
1487
+ * @param {Mixed} oConfig.mColumns Which columns should be included in the output
1488
+ * @param {Boolean} oConfig.bHeader Include the header
1489
+ * @param {Boolean} oConfig.bFooter Include the footer
1490
+ * @param {Boolean} oConfig.bSelectedOnly Include only the selected rows in the output
1491
+ * @returns {String} Concatinated string of data
1492
+ * @private
1493
+ */
1494
+ "_fnGetDataTablesData": function ( oConfig )
1495
+ {
1496
+ var i, iLen, j, jLen;
1497
+ var aRow, aData=[], sLoopData='';
1498
+ var dt = this.s.dt;
1499
+ var regex = new RegExp(oConfig.sFieldBoundary, "g"); /* Do it here for speed */
1500
+ var aColumnsInc = this._fnColumnTargets( oConfig.mColumns );
1501
+ var bSelectedOnly = (typeof oConfig.bSelectedOnly != 'undefined') ? oConfig.bSelectedOnly : false;
1502
+
1503
+ /*
1504
+ * Header
1505
+ */
1506
+ if ( oConfig.bHeader )
1507
+ {
1508
+ aRow = [];
1509
+
1510
+ for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1511
+ {
1512
+ if ( aColumnsInc[i] )
1513
+ {
1514
+ sLoopData = dt.aoColumns[i].sTitle.replace(/\n/g," ").replace( /<.*?>/g, "" ).replace(/^\s+|\s+$/g,"");
1515
+ sLoopData = this._fnHtmlDecode( sLoopData );
1516
+
1517
+ aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) );
1518
+ }
1519
+ }
1520
+
1521
+ aData.push( aRow.join(oConfig.sFieldSeperator) );
1522
+ }
1523
+
1524
+ /*
1525
+ * Body
1526
+ */
1527
+ for ( j=0, jLen=dt.aiDisplay.length ; j<jLen ; j++ )
1528
+ {
1529
+ if ( this.s.select.type == "none" || !bSelectedOnly ||
1530
+ (bSelectedOnly && $(dt.aoData[ dt.aiDisplay[j] ].nTr).hasClass( this.s.select.selectedClass )) ||
1531
+ (bSelectedOnly && this.s.select.selected.length == 0) )
1532
+ {
1533
+ aRow = [];
1534
+
1535
+ /* Columns */
1536
+ for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1537
+ {
1538
+ if ( aColumnsInc[i] )
1539
+ {
1540
+ /* Convert to strings (with small optimisation) */
1541
+ var mTypeData = dt.oApi._fnGetCellData( dt, dt.aiDisplay[j], i, 'display' );
1542
+ if ( oConfig.fnCellRender )
1543
+ {
1544
+ sLoopData = oConfig.fnCellRender( mTypeData, i )+"";
1545
+ }
1546
+ else if ( typeof mTypeData == "string" )
1547
+ {
1548
+ /* Strip newlines, replace img tags with alt attr. and finally strip html... */
1549
+ sLoopData = mTypeData.replace(/\n/g," ");
1550
+ sLoopData =
1551
+ sLoopData.replace(/<img.*?\s+alt\s*=\s*(?:"([^"]+)"|'([^']+)'|([^\s>]+)).*?>/gi,
1552
+ '$1$2$3');
1553
+ sLoopData = sLoopData.replace( /<.*?>/g, "" );
1554
+ }
1555
+ else
1556
+ {
1557
+ sLoopData = mTypeData+"";
1558
+ }
1559
+
1560
+ /* Trim and clean the data */
1561
+ sLoopData = sLoopData.replace(/^\s+/, '').replace(/\s+$/, '');
1562
+ sLoopData = this._fnHtmlDecode( sLoopData );
1563
+
1564
+ /* Bound it and add it to the total data */
1565
+ aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) );
1566
+ }
1567
+ }
1568
+
1569
+ aData.push( aRow.join(oConfig.sFieldSeperator) );
1570
+ }
1571
+ }
1572
+
1573
+ /*
1574
+ * Footer
1575
+ */
1576
+ if ( oConfig.bFooter && dt.nTFoot !== null )
1577
+ {
1578
+ aRow = [];
1579
+
1580
+ for ( i=0, iLen=dt.aoColumns.length ; i<iLen ; i++ )
1581
+ {
1582
+ if ( aColumnsInc[i] && dt.aoColumns[i].nTf !== null )
1583
+ {
1584
+ sLoopData = dt.aoColumns[i].nTf.innerHTML.replace(/\n/g," ").replace( /<.*?>/g, "" );
1585
+ sLoopData = this._fnHtmlDecode( sLoopData );
1586
+
1587
+ aRow.push( this._fnBoundData( sLoopData, oConfig.sFieldBoundary, regex ) );
1588
+ }
1589
+ }
1590
+
1591
+ aData.push( aRow.join(oConfig.sFieldSeperator) );
1592
+ }
1593
+
1594
+ _sLastData = aData.join( this._fnNewline(oConfig) );
1595
+ return _sLastData;
1596
+ },
1597
+
1598
+
1599
+ /**
1600
+ * Wrap data up with a boundary string
1601
+ * @method _fnBoundData
1602
+ * @param {String} sData data to bound
1603
+ * @param {String} sBoundary bounding char(s)
1604
+ * @param {RegExp} regex search for the bounding chars - constructed outside for efficincy
1605
+ * in the loop
1606
+ * @returns {String} bound data
1607
+ * @private
1608
+ */
1609
+ "_fnBoundData": function ( sData, sBoundary, regex )
1610
+ {
1611
+ if ( sBoundary === "" )
1612
+ {
1613
+ return sData;
1614
+ }
1615
+ else
1616
+ {
1617
+ return sBoundary + sData.replace(regex, sBoundary+sBoundary) + sBoundary;
1618
+ }
1619
+ },
1620
+
1621
+
1622
+ /**
1623
+ * Break a string up into an array of smaller strings
1624
+ * @method _fnChunkData
1625
+ * @param {String} sData data to be broken up
1626
+ * @param {Int} iSize chunk size
1627
+ * @returns {Array} String array of broken up text
1628
+ * @private
1629
+ */
1630
+ "_fnChunkData": function ( sData, iSize )
1631
+ {
1632
+ var asReturn = [];
1633
+ var iStrlen = sData.length;
1634
+
1635
+ for ( var i=0 ; i<iStrlen ; i+=iSize )
1636
+ {
1637
+ if ( i+iSize < iStrlen )
1638
+ {
1639
+ asReturn.push( sData.substring( i, i+iSize ) );
1640
+ }
1641
+ else
1642
+ {
1643
+ asReturn.push( sData.substring( i, iStrlen ) );
1644
+ }
1645
+ }
1646
+
1647
+ return asReturn;
1648
+ },
1649
+
1650
+
1651
+ /**
1652
+ * Decode HTML entities
1653
+ * @method _fnHtmlDecode
1654
+ * @param {String} sData encoded string
1655
+ * @returns {String} decoded string
1656
+ * @private
1657
+ */
1658
+ "_fnHtmlDecode": function ( sData )
1659
+ {
1660
+ if ( sData.indexOf('&') == -1 )
1661
+ {
1662
+ return sData;
1663
+ }
1664
+
1665
+ var
1666
+ aData = this._fnChunkData( sData, 2048 ),
1667
+ n = document.createElement('div'),
1668
+ i, iLen, iIndex,
1669
+ sReturn = "", sInner;
1670
+
1671
+ /* nodeValue has a limit in browsers - so we chunk the data into smaller segments to build
1672
+ * up the string. Note that the 'trick' here is to remember than we might have split over
1673
+ * an HTML entity, so we backtrack a little to make sure this doesn't happen
1674
+ */
1675
+ for ( i=0, iLen=aData.length ; i<iLen ; i++ )
1676
+ {
1677
+ /* Magic number 8 is because no entity is longer then strlen 8 in ISO 8859-1 */
1678
+ iIndex = aData[i].lastIndexOf( '&' );
1679
+ if ( iIndex != -1 && aData[i].length >= 8 && iIndex > aData[i].length - 8 )
1680
+ {
1681
+ sInner = aData[i].substr( iIndex );
1682
+ aData[i] = aData[i].substr( 0, iIndex );
1683
+ }
1684
+
1685
+ n.innerHTML = aData[i];
1686
+ sReturn += n.childNodes[0].nodeValue;
1687
+ }
1688
+
1689
+ return sReturn;
1690
+ },
1691
+
1692
+
1693
+
1694
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
1695
+ * Printing functions
1696
+ */
1697
+
1698
+ /**
1699
+ * Configure a button for printing
1700
+ * @method _fnPrintConfig
1701
+ * @param {Node} nButton Button element which is being considered
1702
+ * @param {Object} oConfig Button configuration object
1703
+ * @returns void
1704
+ * @private
1705
+ */
1706
+ "_fnPrintConfig": function ( nButton, oConfig )
1707
+ {
1708
+ var that = this;
1709
+
1710
+ if ( oConfig.fnInit !== null )
1711
+ {
1712
+ oConfig.fnInit.call( this, nButton, oConfig );
1713
+ }
1714
+
1715
+ if ( oConfig.sToolTip !== "" )
1716
+ {
1717
+ nButton.title = oConfig.sToolTip;
1718
+ }
1719
+
1720
+ $(nButton).hover( function () {
1721
+ $(nButton).addClass(oConfig.sButtonClassHover );
1722
+ }, function () {
1723
+ $(nButton).removeClass( oConfig.sButtonClassHover );
1724
+ } );
1725
+
1726
+ if ( oConfig.fnSelect !== null )
1727
+ {
1728
+ TableTools._fnEventListen( this, 'select', function (n) {
1729
+ oConfig.fnSelect.call( that, nButton, oConfig, n );
1730
+ } );
1731
+ }
1732
+
1733
+ $(nButton).click( function (e) {
1734
+ e.preventDefault();
1735
+
1736
+ that._fnPrintStart.call( that, e, oConfig);
1737
+
1738
+ if ( oConfig.fnClick !== null )
1739
+ {
1740
+ oConfig.fnClick.call( that, nButton, oConfig, null );
1741
+ }
1742
+
1743
+ /* Provide a complete function to match the behaviour of the flash elements */
1744
+ if ( oConfig.fnComplete !== null )
1745
+ {
1746
+ oConfig.fnComplete.call( that, nButton, oConfig, null, null );
1747
+ }
1748
+
1749
+ that._fnCollectionHide( nButton, oConfig );
1750
+ } );
1751
+ },
1752
+
1753
+ /**
1754
+ * Show print display
1755
+ * @method _fnPrintStart
1756
+ * @param {Event} e Event object
1757
+ * @param {Object} oConfig Button configuration object
1758
+ * @returns void
1759
+ * @private
1760
+ */
1761
+ "_fnPrintStart": function ( e, oConfig )
1762
+ {
1763
+ var that = this;
1764
+ var oSetDT = this.s.dt;
1765
+
1766
+ /* Parse through the DOM hiding everything that isn't needed for the table */
1767
+ this._fnPrintHideNodes( oSetDT.nTable );
1768
+
1769
+ /* Show the whole table */
1770
+ this.s.print.saveStart = oSetDT._iDisplayStart;
1771
+ this.s.print.saveLength = oSetDT._iDisplayLength;
1772
+
1773
+ if ( oConfig.bShowAll )
1774
+ {
1775
+ oSetDT._iDisplayStart = 0;
1776
+ oSetDT._iDisplayLength = -1;
1777
+ oSetDT.oApi._fnCalculateEnd( oSetDT );
1778
+ oSetDT.oApi._fnDraw( oSetDT );
1779
+ }
1780
+
1781
+ /* Adjust the display for scrolling which might be done by DataTables */
1782
+ if ( oSetDT.oScroll.sX !== "" || oSetDT.oScroll.sY !== "" )
1783
+ {
1784
+ this._fnPrintScrollStart( oSetDT );
1785
+ }
1786
+
1787
+ /* Remove the other DataTables feature nodes - but leave the table! and info div */
1788
+ var anFeature = oSetDT.aanFeatures;
1789
+ for ( var cFeature in anFeature )
1790
+ {
1791
+ if ( cFeature != 'i' && cFeature != 't' && cFeature.length == 1 )
1792
+ {
1793
+ for ( var i=0, iLen=anFeature[cFeature].length ; i<iLen ; i++ )
1794
+ {
1795
+ this.dom.print.hidden.push( {
1796
+ "node": anFeature[cFeature][i],
1797
+ "display": "block"
1798
+ } );
1799
+ anFeature[cFeature][i].style.display = "none";
1800
+ }
1801
+ }
1802
+ }
1803
+
1804
+ /* Print class can be used for styling */
1805
+ $(document.body).addClass( 'DTTT_Print' );
1806
+
1807
+ /* Add a node telling the user what is going on */
1808
+ if ( oConfig.sInfo !== "" )
1809
+ {
1810
+ var nInfo = document.createElement( "div" );
1811
+ nInfo.className = "DTTT_print_info";
1812
+ nInfo.innerHTML = oConfig.sInfo;
1813
+ document.body.appendChild( nInfo );
1814
+
1815
+ setTimeout( function() {
1816
+ $(nInfo).fadeOut( "normal", function() {
1817
+ document.body.removeChild( nInfo );
1818
+ } );
1819
+ }, 2000 );
1820
+ }
1821
+
1822
+ /* Add a message at the top of the page */
1823
+ if ( oConfig.sMessage !== "" )
1824
+ {
1825
+ this.dom.print.message = document.createElement( "div" );
1826
+ this.dom.print.message.className = "DTTT_PrintMessage";
1827
+ this.dom.print.message.innerHTML = oConfig.sMessage;
1828
+ document.body.insertBefore( this.dom.print.message, document.body.childNodes[0] );
1829
+ }
1830
+
1831
+ /* Cache the scrolling and the jump to the top of the t=page */
1832
+ this.s.print.saveScroll = $(window).scrollTop();
1833
+ window.scrollTo( 0, 0 );
1834
+
1835
+ this.s.print.funcEnd = function(e) {
1836
+ that._fnPrintEnd.call( that, e );
1837
+ };
1838
+ $(document).bind( "keydown", null, this.s.print.funcEnd );
1839
+ },
1840
+
1841
+
1842
+ /**
1843
+ * Printing is finished, resume normal display
1844
+ * @method _fnPrintEnd
1845
+ * @param {Event} e Event object
1846
+ * @returns void
1847
+ * @private
1848
+ */
1849
+ "_fnPrintEnd": function ( e )
1850
+ {
1851
+ /* Only interested in the escape key */
1852
+ if ( e.keyCode == 27 )
1853
+ {
1854
+ e.preventDefault();
1855
+
1856
+ var that = this;
1857
+ var oSetDT = this.s.dt;
1858
+ var oSetPrint = this.s.print;
1859
+ var oDomPrint = this.dom.print;
1860
+
1861
+ /* Show all hidden nodes */
1862
+ this._fnPrintShowNodes();
1863
+
1864
+ /* Restore DataTables' scrolling */
1865
+ if ( oSetDT.oScroll.sX !== "" || oSetDT.oScroll.sY !== "" )
1866
+ {
1867
+ this._fnPrintScrollEnd();
1868
+ }
1869
+
1870
+ /* Restore the scroll */
1871
+ window.scrollTo( 0, oSetPrint.saveScroll );
1872
+
1873
+ /* Drop the print message */
1874
+ if ( oDomPrint.message !== null )
1875
+ {
1876
+ document.body.removeChild( oDomPrint.message );
1877
+ oDomPrint.message = null;
1878
+ }
1879
+
1880
+ /* Styling class */
1881
+ $(document.body).removeClass( 'DTTT_Print' );
1882
+
1883
+ /* Restore the table length */
1884
+ oSetDT._iDisplayStart = oSetPrint.saveStart;
1885
+ oSetDT._iDisplayLength = oSetPrint.saveLength;
1886
+ oSetDT.oApi._fnCalculateEnd( oSetDT );
1887
+ oSetDT.oApi._fnDraw( oSetDT );
1888
+
1889
+ $(document).unbind( "keydown", this.s.print.funcEnd );
1890
+ this.s.print.funcEnd = null;
1891
+ }
1892
+ },
1893
+
1894
+
1895
+ /**
1896
+ * Take account of scrolling in DataTables by showing the full table
1897
+ * @returns void
1898
+ * @private
1899
+ */
1900
+ "_fnPrintScrollStart": function ()
1901
+ {
1902
+ var
1903
+ oSetDT = this.s.dt,
1904
+ nScrollHeadInner = oSetDT.nScrollHead.getElementsByTagName('div')[0],
1905
+ nScrollHeadTable = nScrollHeadInner.getElementsByTagName('table')[0],
1906
+ nScrollBody = oSetDT.nTable.parentNode;
1907
+
1908
+ /* Copy the header in the thead in the body table, this way we show one single table when
1909
+ * in print view. Note that this section of code is more or less verbatim from DT 1.7.0
1910
+ */
1911
+ var nTheadSize = oSetDT.nTable.getElementsByTagName('thead');
1912
+ if ( nTheadSize.length > 0 )
1913
+ {
1914
+ oSetDT.nTable.removeChild( nTheadSize[0] );
1915
+ }
1916
+
1917
+ if ( oSetDT.nTFoot !== null )
1918
+ {
1919
+ var nTfootSize = oSetDT.nTable.getElementsByTagName('tfoot');
1920
+ if ( nTfootSize.length > 0 )
1921
+ {
1922
+ oSetDT.nTable.removeChild( nTfootSize[0] );
1923
+ }
1924
+ }
1925
+
1926
+ nTheadSize = oSetDT.nTHead.cloneNode(true);
1927
+ oSetDT.nTable.insertBefore( nTheadSize, oSetDT.nTable.childNodes[0] );
1928
+
1929
+ if ( oSetDT.nTFoot !== null )
1930
+ {
1931
+ nTfootSize = oSetDT.nTFoot.cloneNode(true);
1932
+ oSetDT.nTable.insertBefore( nTfootSize, oSetDT.nTable.childNodes[1] );
1933
+ }
1934
+
1935
+ /* Now adjust the table's viewport so we can actually see it */
1936
+ if ( oSetDT.oScroll.sX !== "" )
1937
+ {
1938
+ oSetDT.nTable.style.width = $(oSetDT.nTable).outerWidth()+"px";
1939
+ nScrollBody.style.width = $(oSetDT.nTable).outerWidth()+"px";
1940
+ nScrollBody.style.overflow = "visible";
1941
+ }
1942
+
1943
+ if ( oSetDT.oScroll.sY !== "" )
1944
+ {
1945
+ nScrollBody.style.height = $(oSetDT.nTable).outerHeight()+"px";
1946
+ nScrollBody.style.overflow = "visible";
1947
+ }
1948
+ },
1949
+
1950
+
1951
+ /**
1952
+ * Take account of scrolling in DataTables by showing the full table. Note that the redraw of
1953
+ * the DataTable that we do will actually deal with the majority of the hardword here
1954
+ * @returns void
1955
+ * @private
1956
+ */
1957
+ "_fnPrintScrollEnd": function ()
1958
+ {
1959
+ var
1960
+ oSetDT = this.s.dt,
1961
+ nScrollBody = oSetDT.nTable.parentNode;
1962
+
1963
+ if ( oSetDT.oScroll.sX !== "" )
1964
+ {
1965
+ nScrollBody.style.width = oSetDT.oApi._fnStringToCss( oSetDT.oScroll.sX );
1966
+ nScrollBody.style.overflow = "auto";
1967
+ }
1968
+
1969
+ if ( oSetDT.oScroll.sY !== "" )
1970
+ {
1971
+ nScrollBody.style.height = oSetDT.oApi._fnStringToCss( oSetDT.oScroll.sY );
1972
+ nScrollBody.style.overflow = "auto";
1973
+ }
1974
+ },
1975
+
1976
+
1977
+ /**
1978
+ * Resume the display of all TableTools hidden nodes
1979
+ * @method _fnPrintShowNodes
1980
+ * @returns void
1981
+ * @private
1982
+ */
1983
+ "_fnPrintShowNodes": function ( )
1984
+ {
1985
+ var anHidden = this.dom.print.hidden;
1986
+
1987
+ for ( var i=0, iLen=anHidden.length ; i<iLen ; i++ )
1988
+ {
1989
+ anHidden[i].node.style.display = anHidden[i].display;
1990
+ }
1991
+ anHidden.splice( 0, anHidden.length );
1992
+ },
1993
+
1994
+
1995
+ /**
1996
+ * Hide nodes which are not needed in order to display the table. Note that this function is
1997
+ * recursive
1998
+ * @method _fnPrintHideNodes
1999
+ * @param {Node} nNode Element which should be showing in a 'print' display
2000
+ * @returns void
2001
+ * @private
2002
+ */
2003
+ "_fnPrintHideNodes": function ( nNode )
2004
+ {
2005
+ var anHidden = this.dom.print.hidden;
2006
+
2007
+ var nParent = nNode.parentNode;
2008
+ var nChildren = nParent.childNodes;
2009
+ for ( var i=0, iLen=nChildren.length ; i<iLen ; i++ )
2010
+ {
2011
+ if ( nChildren[i] != nNode && nChildren[i].nodeType == 1 )
2012
+ {
2013
+ /* If our node is shown (don't want to show nodes which were previously hidden) */
2014
+ var sDisplay = $(nChildren[i]).css("display");
2015
+ if ( sDisplay != "none" )
2016
+ {
2017
+ /* Cache the node and it's previous state so we can restore it */
2018
+ anHidden.push( {
2019
+ "node": nChildren[i],
2020
+ "display": sDisplay
2021
+ } );
2022
+ nChildren[i].style.display = "none";
2023
+ }
2024
+ }
2025
+ }
2026
+
2027
+ if ( nParent.nodeName != "BODY" )
2028
+ {
2029
+ this._fnPrintHideNodes( nParent );
2030
+ }
2031
+ }
2032
+ };
2033
+
2034
+
2035
+
2036
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2037
+ * Static variables
2038
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2039
+
2040
+ /**
2041
+ * Store of all instances that have been created of TableTools, so one can look up other (when
2042
+ * there is need of a master)
2043
+ * @property _aInstances
2044
+ * @type Array
2045
+ * @default []
2046
+ * @private
2047
+ */
2048
+ TableTools._aInstances = [];
2049
+
2050
+
2051
+ /**
2052
+ * Store of all listeners and their callback functions
2053
+ * @property _aListeners
2054
+ * @type Array
2055
+ * @default []
2056
+ */
2057
+ TableTools._aListeners = [];
2058
+
2059
+
2060
+
2061
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2062
+ * Static methods
2063
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2064
+
2065
+ /**
2066
+ * Get an array of all the master instances
2067
+ * @method fnGetMasters
2068
+ * @returns {Array} List of master TableTools instances
2069
+ * @static
2070
+ */
2071
+ TableTools.fnGetMasters = function ()
2072
+ {
2073
+ var a = [];
2074
+ for ( var i=0, iLen=TableTools._aInstances.length ; i<iLen ; i++ )
2075
+ {
2076
+ if ( TableTools._aInstances[i].s.master )
2077
+ {
2078
+ a.push( TableTools._aInstances[i] );
2079
+ }
2080
+ }
2081
+ return a;
2082
+ };
2083
+
2084
+ /**
2085
+ * Get the master instance for a table node (or id if a string is given)
2086
+ * @method fnGetInstance
2087
+ * @returns {Object} ID of table OR table node, for which we want the TableTools instance
2088
+ * @static
2089
+ */
2090
+ TableTools.fnGetInstance = function ( node )
2091
+ {
2092
+ if ( typeof node != 'object' )
2093
+ {
2094
+ node = document.getElementById(node);
2095
+ }
2096
+
2097
+ for ( var i=0, iLen=TableTools._aInstances.length ; i<iLen ; i++ )
2098
+ {
2099
+ if ( TableTools._aInstances[i].s.master && TableTools._aInstances[i].dom.table == node )
2100
+ {
2101
+ return TableTools._aInstances[i];
2102
+ }
2103
+ }
2104
+ return null;
2105
+ };
2106
+
2107
+
2108
+ /**
2109
+ * Add a listener for a specific event
2110
+ * @method _fnEventListen
2111
+ * @param {Object} that Scope of the listening function (i.e. 'this' in the caller)
2112
+ * @param {String} type Event type
2113
+ * @param {Function} fn Function
2114
+ * @returns void
2115
+ * @private
2116
+ * @static
2117
+ */
2118
+ TableTools._fnEventListen = function ( that, type, fn )
2119
+ {
2120
+ TableTools._aListeners.push( {
2121
+ "that": that,
2122
+ "type": type,
2123
+ "fn": fn
2124
+ } );
2125
+ };
2126
+
2127
+
2128
+ /**
2129
+ * An event has occured - look up every listener and fire it off. We check that the event we are
2130
+ * going to fire is attached to the same table (using the table node as reference) before firing
2131
+ * @method _fnEventDispatch
2132
+ * @param {Object} that Scope of the listening function (i.e. 'this' in the caller)
2133
+ * @param {String} type Event type
2134
+ * @param {Node} node Element that the event occured on (may be null)
2135
+ * @returns void
2136
+ * @private
2137
+ * @static
2138
+ */
2139
+ TableTools._fnEventDispatch = function ( that, type, node )
2140
+ {
2141
+ var listeners = TableTools._aListeners;
2142
+ for ( var i=0, iLen=listeners.length ; i<iLen ; i++ )
2143
+ {
2144
+ if ( that.dom.table == listeners[i].that.dom.table && listeners[i].type == type )
2145
+ {
2146
+ listeners[i].fn( node );
2147
+ }
2148
+ }
2149
+ };
2150
+
2151
+
2152
+
2153
+
2154
+
2155
+
2156
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2157
+ * Constants
2158
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2159
+
2160
+
2161
+ /**
2162
+ * @namespace Default button configurations
2163
+ */
2164
+ TableTools.BUTTONS = {
2165
+ "csv": {
2166
+ "sAction": "flash_save",
2167
+ "sCharSet": "utf8",
2168
+ "bBomInc": false,
2169
+ "sFileName": "*.csv",
2170
+ "sFieldBoundary": '"',
2171
+ "sFieldSeperator": ",",
2172
+ "sNewLine": "auto",
2173
+ "sTitle": "",
2174
+ "sToolTip": "",
2175
+ "sButtonClass": "DTTT_button_csv",
2176
+ "sButtonClassHover": "DTTT_button_csv_hover",
2177
+ "sButtonText": "CSV",
2178
+ "mColumns": "all", /* "all", "visible", "hidden" or array of column integers */
2179
+ "bHeader": true,
2180
+ "bFooter": true,
2181
+ "bSelectedOnly": false,
2182
+ "fnMouseover": null,
2183
+ "fnMouseout": null,
2184
+ "fnClick": function( nButton, oConfig, flash ) {
2185
+ this.fnSetText( flash, this.fnGetTableData(oConfig) );
2186
+ },
2187
+ "fnSelect": null,
2188
+ "fnComplete": null,
2189
+ "fnInit": null,
2190
+ "fnCellRender": null
2191
+ },
2192
+ "xls": {
2193
+ "sAction": "flash_save",
2194
+ "sCharSet": "utf16le",
2195
+ "bBomInc": true,
2196
+ "sFileName": "*.csv",
2197
+ "sFieldBoundary": "",
2198
+ "sFieldSeperator": "\t",
2199
+ "sNewLine": "auto",
2200
+ "sTitle": "",
2201
+ "sToolTip": "",
2202
+ "sButtonClass": "DTTT_button_xls",
2203
+ "sButtonClassHover": "DTTT_button_xls_hover",
2204
+ "sButtonText": "Excel",
2205
+ "mColumns": "all",
2206
+ "bHeader": true,
2207
+ "bFooter": true,
2208
+ "bSelectedOnly": false,
2209
+ "fnMouseover": null,
2210
+ "fnMouseout": null,
2211
+ "fnClick": function( nButton, oConfig, flash ) {
2212
+ this.fnSetText( flash, this.fnGetTableData(oConfig) );
2213
+ },
2214
+ "fnSelect": null,
2215
+ "fnComplete": null,
2216
+ "fnInit": null,
2217
+ "fnCellRender": null
2218
+ },
2219
+ "copy": {
2220
+ "sAction": "flash_copy",
2221
+ "sFieldBoundary": "",
2222
+ "sFieldSeperator": "\t",
2223
+ "sNewLine": "auto",
2224
+ "sToolTip": "",
2225
+ "sButtonClass": "DTTT_button_copy",
2226
+ "sButtonClassHover": "DTTT_button_copy_hover",
2227
+ "sButtonText": "Copy",
2228
+ "mColumns": "all",
2229
+ "bHeader": true,
2230
+ "bFooter": true,
2231
+ "bSelectedOnly": false,
2232
+ "fnMouseover": null,
2233
+ "fnMouseout": null,
2234
+ "fnClick": function( nButton, oConfig, flash ) {
2235
+ this.fnSetText( flash, this.fnGetTableData(oConfig) );
2236
+ },
2237
+ "fnSelect": null,
2238
+ "fnComplete": function(nButton, oConfig, flash, text) {
2239
+ var
2240
+ lines = text.split('\n').length,
2241
+ len = this.s.dt.nTFoot === null ? lines-1 : lines-2,
2242
+ plural = (len==1) ? "" : "s";
2243
+ alert( 'Copied '+len+' row'+plural+' to the clipboard' );
2244
+ },
2245
+ "fnInit": null,
2246
+ "fnCellRender": null
2247
+ },
2248
+ "pdf": {
2249
+ "sAction": "flash_pdf",
2250
+ "sFieldBoundary": "",
2251
+ "sFieldSeperator": "\t",
2252
+ "sNewLine": "\n",
2253
+ "sFileName": "*.pdf",
2254
+ "sToolTip": "",
2255
+ "sTitle": "",
2256
+ "sButtonClass": "DTTT_button_pdf",
2257
+ "sButtonClassHover": "DTTT_button_pdf_hover",
2258
+ "sButtonText": "PDF",
2259
+ "mColumns": "all",
2260
+ "bHeader": true,
2261
+ "bFooter": false,
2262
+ "bSelectedOnly": false,
2263
+ "fnMouseover": null,
2264
+ "fnMouseout": null,
2265
+ "sPdfOrientation": "portrait",
2266
+ "sPdfSize": "A4",
2267
+ "sPdfMessage": "",
2268
+ "fnClick": function( nButton, oConfig, flash ) {
2269
+ this.fnSetText( flash,
2270
+ "title:"+ this.fnGetTitle(oConfig) +"\n"+
2271
+ "message:"+ oConfig.sPdfMessage +"\n"+
2272
+ "colWidth:"+ this.fnCalcColRatios(oConfig) +"\n"+
2273
+ "orientation:"+ oConfig.sPdfOrientation +"\n"+
2274
+ "size:"+ oConfig.sPdfSize +"\n"+
2275
+ "--/TableToolsOpts--\n" +
2276
+ this.fnGetTableData(oConfig)
2277
+ );
2278
+ },
2279
+ "fnSelect": null,
2280
+ "fnComplete": null,
2281
+ "fnInit": null,
2282
+ "fnCellRender": null
2283
+ },
2284
+ "print": {
2285
+ "sAction": "print",
2286
+ "sInfo": "<h6>Print view</h6><p>Please use your browser's print function to "+
2287
+ "print this table. Press escape when finished.",
2288
+ "sMessage": "",
2289
+ "bShowAll": true,
2290
+ "sToolTip": "View print view",
2291
+ "sButtonClass": "DTTT_button_print",
2292
+ "sButtonClassHover": "DTTT_button_print_hover",
2293
+ "sButtonText": "Print",
2294
+ "fnMouseover": null,
2295
+ "fnMouseout": null,
2296
+ "fnClick": null,
2297
+ "fnSelect": null,
2298
+ "fnComplete": null,
2299
+ "fnInit": null,
2300
+ "fnCellRender": null
2301
+ },
2302
+ "text": {
2303
+ "sAction": "text",
2304
+ "sToolTip": "",
2305
+ "sButtonClass": "DTTT_button_text",
2306
+ "sButtonClassHover": "DTTT_button_text_hover",
2307
+ "sButtonText": "Text button",
2308
+ "mColumns": "all",
2309
+ "bHeader": true,
2310
+ "bFooter": true,
2311
+ "bSelectedOnly": false,
2312
+ "fnMouseover": null,
2313
+ "fnMouseout": null,
2314
+ "fnClick": null,
2315
+ "fnSelect": null,
2316
+ "fnComplete": null,
2317
+ "fnInit": null,
2318
+ "fnCellRender": null
2319
+ },
2320
+ "select": {
2321
+ "sAction": "text",
2322
+ "sToolTip": "",
2323
+ "sButtonClass": "DTTT_button_text",
2324
+ "sButtonClassHover": "DTTT_button_text_hover",
2325
+ "sButtonText": "Select button",
2326
+ "mColumns": "all",
2327
+ "bHeader": true,
2328
+ "bFooter": true,
2329
+ "fnMouseover": null,
2330
+ "fnMouseout": null,
2331
+ "fnClick": null,
2332
+ "fnSelect": function( nButton, oConfig ) {
2333
+ if ( this.fnGetSelected().length !== 0 ) {
2334
+ $(nButton).removeClass('DTTT_disabled');
2335
+ } else {
2336
+ $(nButton).addClass('DTTT_disabled');
2337
+ }
2338
+ },
2339
+ "fnComplete": null,
2340
+ "fnInit": function( nButton, oConfig ) {
2341
+ $(nButton).addClass('DTTT_disabled');
2342
+ },
2343
+ "fnCellRender": null
2344
+ },
2345
+ "select_single": {
2346
+ "sAction": "text",
2347
+ "sToolTip": "",
2348
+ "sButtonClass": "DTTT_button_text",
2349
+ "sButtonClassHover": "DTTT_button_text_hover",
2350
+ "sButtonText": "Select button",
2351
+ "mColumns": "all",
2352
+ "bHeader": true,
2353
+ "bFooter": true,
2354
+ "fnMouseover": null,
2355
+ "fnMouseout": null,
2356
+ "fnClick": null,
2357
+ "fnSelect": function( nButton, oConfig ) {
2358
+ var iSelected = this.fnGetSelected().length;
2359
+ if ( iSelected == 1 ) {
2360
+ $(nButton).removeClass('DTTT_disabled');
2361
+ } else {
2362
+ $(nButton).addClass('DTTT_disabled');
2363
+ }
2364
+ },
2365
+ "fnComplete": null,
2366
+ "fnInit": function( nButton, oConfig ) {
2367
+ $(nButton).addClass('DTTT_disabled');
2368
+ },
2369
+ "fnCellRender": null
2370
+ },
2371
+ "select_all": {
2372
+ "sAction": "text",
2373
+ "sToolTip": "",
2374
+ "sButtonClass": "DTTT_button_text",
2375
+ "sButtonClassHover": "DTTT_button_text_hover",
2376
+ "sButtonText": "Select all",
2377
+ "mColumns": "all",
2378
+ "bHeader": true,
2379
+ "bFooter": true,
2380
+ "fnMouseover": null,
2381
+ "fnMouseout": null,
2382
+ "fnClick": function( nButton, oConfig ) {
2383
+ this.fnSelectAll();
2384
+ },
2385
+ "fnSelect": function( nButton, oConfig ) {
2386
+ if ( this.fnGetSelected().length == this.s.dt.fnRecordsDisplay() ) {
2387
+ $(nButton).addClass('DTTT_disabled');
2388
+ } else {
2389
+ $(nButton).removeClass('DTTT_disabled');
2390
+ }
2391
+ },
2392
+ "fnComplete": null,
2393
+ "fnInit": null,
2394
+ "fnCellRender": null
2395
+ },
2396
+ "select_none": {
2397
+ "sAction": "text",
2398
+ "sToolTip": "",
2399
+ "sButtonClass": "DTTT_button_text",
2400
+ "sButtonClassHover": "DTTT_button_text_hover",
2401
+ "sButtonText": "Deselect all",
2402
+ "mColumns": "all",
2403
+ "bHeader": true,
2404
+ "bFooter": true,
2405
+ "fnMouseover": null,
2406
+ "fnMouseout": null,
2407
+ "fnClick": function( nButton, oConfig ) {
2408
+ this.fnSelectNone();
2409
+ },
2410
+ "fnSelect": function( nButton, oConfig ) {
2411
+ if ( this.fnGetSelected().length !== 0 ) {
2412
+ $(nButton).removeClass('DTTT_disabled');
2413
+ } else {
2414
+ $(nButton).addClass('DTTT_disabled');
2415
+ }
2416
+ },
2417
+ "fnComplete": null,
2418
+ "fnInit": function( nButton, oConfig ) {
2419
+ $(nButton).addClass('DTTT_disabled');
2420
+ },
2421
+ "fnCellRender": null
2422
+ },
2423
+ "ajax": {
2424
+ "sAction": "text",
2425
+ "sFieldBoundary": "",
2426
+ "sFieldSeperator": "\t",
2427
+ "sNewLine": "\n",
2428
+ "sAjaxUrl": "/xhr.php",
2429
+ "sToolTip": "",
2430
+ "sButtonClass": "DTTT_button_text",
2431
+ "sButtonClassHover": "DTTT_button_text_hover",
2432
+ "sButtonText": "Ajax button",
2433
+ "mColumns": "all",
2434
+ "bHeader": true,
2435
+ "bFooter": true,
2436
+ "bSelectedOnly": false,
2437
+ "fnMouseover": null,
2438
+ "fnMouseout": null,
2439
+ "fnClick": function( nButton, oConfig ) {
2440
+ var sData = this.fnGetTableData(oConfig);
2441
+ $.ajax( {
2442
+ "url": oConfig.sAjaxUrl,
2443
+ "data": [
2444
+ { "name": "tableData", "value": sData }
2445
+ ],
2446
+ "success": oConfig.fnAjaxComplete,
2447
+ "dataType": "json",
2448
+ "type": "POST",
2449
+ "cache": false,
2450
+ "error": function () {
2451
+ alert( "Error detected when sending table data to server" );
2452
+ }
2453
+ } );
2454
+ },
2455
+ "fnSelect": null,
2456
+ "fnComplete": null,
2457
+ "fnInit": null,
2458
+ "fnAjaxComplete": function( json ) {
2459
+ alert( 'Ajax complete' );
2460
+ },
2461
+ "fnCellRender": null
2462
+ },
2463
+ "div": {
2464
+ "sAction": "div",
2465
+ "sToolTip": "",
2466
+ "sButtonClass": "DTTT_nonbutton",
2467
+ "sButtonClassHover": "",
2468
+ "sButtonText": "Text button",
2469
+ "fnMouseover": null,
2470
+ "fnMouseout": null,
2471
+ "fnClick": null,
2472
+ "fnSelect": null,
2473
+ "fnComplete": null,
2474
+ "fnInit": null,
2475
+ "nContent": null,
2476
+ "fnCellRender": null
2477
+ },
2478
+ "collection": {
2479
+ "sAction": "collection",
2480
+ "sToolTip": "",
2481
+ "sButtonClass": "DTTT_button_collection",
2482
+ "sButtonClassHover": "DTTT_button_collection_hover",
2483
+ "sButtonText": "Collection",
2484
+ "fnMouseover": null,
2485
+ "fnMouseout": null,
2486
+ "fnClick": function( nButton, oConfig ) {
2487
+ this._fnCollectionShow(nButton, oConfig);
2488
+ },
2489
+ "fnSelect": null,
2490
+ "fnComplete": null,
2491
+ "fnInit": null,
2492
+ "fnCellRender": null
2493
+ }
2494
+ };
2495
+ /*
2496
+ * on* callback parameters:
2497
+ * 1. node - button element
2498
+ * 2. object - configuration object for this button
2499
+ * 3. object - ZeroClipboard reference (flash button only)
2500
+ * 4. string - Returned string from Flash (flash button only - and only on 'complete')
2501
+ */
2502
+
2503
+
2504
+ /**
2505
+ * @namespace TableTools default settings for initialisation
2506
+ */
2507
+ TableTools.DEFAULTS = {
2508
+ "sSwfPath": "media/swf/copy_cvs_xls_pdf.swf",
2509
+ "sRowSelect": "none",
2510
+ "sSelectedClass": "DTTT_selected",
2511
+ "fnPreRowSelect": null,
2512
+ "fnRowSelected": null,
2513
+ "fnRowDeselected": null,
2514
+ "aButtons": [ "copy", "csv", "xls", "pdf", "print" ]
2515
+ };
2516
+
2517
+
2518
+ /**
2519
+ * Name of this class
2520
+ * @constant CLASS
2521
+ * @type String
2522
+ * @default TableTools
2523
+ */
2524
+ TableTools.prototype.CLASS = "TableTools";
2525
+
2526
+
2527
+ /**
2528
+ * TableTools version
2529
+ * @constant VERSION
2530
+ * @type String
2531
+ * @default 2.0.3.dev
2532
+ */
2533
+ TableTools.VERSION = "2.0.3.dev";
2534
+ TableTools.prototype.VERSION = TableTools.VERSION;
2535
+
2536
+
2537
+
2538
+
2539
+ /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
2540
+ * Initialisation
2541
+ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2542
+
2543
+ /*
2544
+ * Register a new feature with DataTables
2545
+ */
2546
+ if ( typeof $.fn.dataTable == "function" &&
2547
+ typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
2548
+ $.fn.dataTableExt.fnVersionCheck('1.8.2') )
2549
+ {
2550
+ $.fn.dataTableExt.aoFeatures.push( {
2551
+ "fnInit": function( oDTSettings ) {
2552
+ var oOpts = typeof oDTSettings.oInit.oTableTools != 'undefined' ?
2553
+ oDTSettings.oInit.oTableTools : {};
2554
+
2555
+ var oTT = new TableTools( oDTSettings.oInstance, oOpts );
2556
+ TableTools._aInstances.push( oTT );
2557
+
2558
+ return oTT.dom.container;
2559
+ },
2560
+ "cFeature": "T",
2561
+ "sFeature": "TableTools"
2562
+ } );
2563
+ }
2564
+ else
2565
+ {
2566
+ alert( "Warning: TableTools 2 requires DataTables 1.8.2 or newer - www.datatables.net/download");
2567
+ }
2568
+
2569
+ })(jQuery, window, document);