marbu 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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);