1 |
|
2 |
/** |
3 |
* Return the settings object for a particular table |
4 |
* @param {node} nTable table we are using as a dataTable |
5 |
* @returns {object} Settings object - or null if not found |
6 |
* @memberof DataTable#oApi |
7 |
*/ |
8 |
function _fnSettingsFromNode ( nTable ) |
9 |
{ |
10 |
for ( var i=0 ; i<DataTable.settings.length ; i++ ) |
11 |
{ |
12 |
if ( DataTable.settings[i].nTable === nTable ) |
13 |
{ |
14 |
return DataTable.settings[i]; |
15 |
} |
16 |
} |
17 |
|
18 |
return null; |
19 |
} |
20 |
|
21 |
|
22 |
/** |
23 |
* Return an array with the TR nodes for the table |
24 |
* @param {object} oSettings dataTables settings object |
25 |
* @returns {array} TR array |
26 |
* @memberof DataTable#oApi |
27 |
*/ |
28 |
function _fnGetTrNodes ( oSettings ) |
29 |
{ |
30 |
var aNodes = []; |
31 |
var aoData = oSettings.aoData; |
32 |
for ( var i=0, iLen=aoData.length ; i<iLen ; i++ ) |
33 |
{ |
34 |
if ( aoData[i].nTr !== null ) |
35 |
{ |
36 |
aNodes.push( aoData[i].nTr ); |
37 |
} |
38 |
} |
39 |
return aNodes; |
40 |
} |
41 |
|
42 |
|
43 |
/** |
44 |
* Return an flat array with all TD nodes for the table, or row |
45 |
* @param {object} oSettings dataTables settings object |
46 |
* @param {int} [iIndividualRow] aoData index to get the nodes for - optional |
47 |
* if not given then the return array will contain all nodes for the table |
48 |
* @returns {array} TD array |
49 |
* @memberof DataTable#oApi |
50 |
*/ |
51 |
function _fnGetTdNodes ( oSettings, iIndividualRow ) |
52 |
{ |
53 |
var anReturn = []; |
54 |
var iCorrector; |
55 |
var anTds, nTd; |
56 |
var iRow, iRows=oSettings.aoData.length, |
57 |
iColumn, iColumns, oData, sNodeName, iStart=0, iEnd=iRows; |
58 |
|
59 |
/* Allow the collection to be limited to just one row */ |
60 |
if ( iIndividualRow !== undefined ) |
61 |
{ |
62 |
iStart = iIndividualRow; |
63 |
iEnd = iIndividualRow+1; |
64 |
} |
65 |
|
66 |
for ( iRow=iStart ; iRow<iEnd ; iRow++ ) |
67 |
{ |
68 |
oData = oSettings.aoData[iRow]; |
69 |
if ( oData.nTr !== null ) |
70 |
{ |
71 |
/* get the TD child nodes - taking into account text etc nodes */ |
72 |
anTds = []; |
73 |
nTd = oData.nTr.firstChild; |
74 |
while ( nTd ) |
75 |
{ |
76 |
sNodeName = nTd.nodeName.toLowerCase(); |
77 |
if ( sNodeName == 'td' || sNodeName == 'th' ) |
78 |
{ |
79 |
anTds.push( nTd ); |
80 |
} |
81 |
nTd = nTd.nextSibling; |
82 |
} |
83 |
|
84 |
iCorrector = 0; |
85 |
for ( iColumn=0, iColumns=oSettings.aoColumns.length ; iColumn<iColumns ; iColumn++ ) |
86 |
{ |
87 |
if ( oSettings.aoColumns[iColumn].bVisible ) |
88 |
{ |
89 |
anReturn.push( anTds[iColumn-iCorrector] ); |
90 |
} |
91 |
else |
92 |
{ |
93 |
anReturn.push( oData._anHidden[iColumn] ); |
94 |
iCorrector++; |
95 |
} |
96 |
} |
97 |
} |
98 |
} |
99 |
|
100 |
return anReturn; |
101 |
} |
102 |
|
103 |
|
104 |
/** |
105 |
* Log an error message |
106 |
* @param {object} oSettings dataTables settings object |
107 |
* @param {int} iLevel log error messages, or display them to the user |
108 |
* @param {string} sMesg error message |
109 |
* @memberof DataTable#oApi |
110 |
*/ |
111 |
function _fnLog( oSettings, iLevel, sMesg ) |
112 |
{ |
113 |
var sAlert = (oSettings===null) ? |
114 |
"DataTables warning: "+sMesg : |
115 |
"DataTables warning (table id = '"+oSettings.sTableId+"'): "+sMesg; |
116 |
|
117 |
if ( iLevel === 0 ) |
118 |
{ |
119 |
if ( DataTable.ext.sErrMode == 'alert' ) |
120 |
{ |
121 |
alert( sAlert ); |
122 |
} |
123 |
else |
124 |
{ |
125 |
throw new Error(sAlert); |
126 |
} |
127 |
return; |
128 |
} |
129 |
else if ( window.console && console.log ) |
130 |
{ |
131 |
console.log( sAlert ); |
132 |
} |
133 |
} |
134 |
|
135 |
|
136 |
/** |
137 |
* See if a property is defined on one object, if so assign it to the other object |
138 |
* @param {object} oRet target object |
139 |
* @param {object} oSrc source object |
140 |
* @param {string} sName property |
141 |
* @param {string} [sMappedName] name to map too - optional, sName used if not given |
142 |
* @memberof DataTable#oApi |
143 |
*/ |
144 |
function _fnMap( oRet, oSrc, sName, sMappedName ) |
145 |
{ |
146 |
if ( sMappedName === undefined ) |
147 |
{ |
148 |
sMappedName = sName; |
149 |
} |
150 |
if ( oSrc[sName] !== undefined ) |
151 |
{ |
152 |
oRet[sMappedName] = oSrc[sName]; |
153 |
} |
154 |
} |
155 |
|
156 |
|
157 |
/** |
158 |
* Extend objects - very similar to jQuery.extend, but deep copy objects, and shallow |
159 |
* copy arrays. The reason we need to do this, is that we don't want to deep copy array |
160 |
* init values (such as aaSorting) since the dev wouldn't be able to override them, but |
161 |
* we do want to deep copy arrays. |
162 |
* @param {object} oOut Object to extend |
163 |
* @param {object} oExtender Object from which the properties will be applied to oOut |
164 |
* @returns {object} oOut Reference, just for convenience - oOut === the return. |
165 |
* @memberof DataTable#oApi |
166 |
* @todo This doesn't take account of arrays inside the deep copied objects. |
167 |
*/ |
168 |
function _fnExtend( oOut, oExtender ) |
169 |
{ |
170 |
var val; |
171 |
|
172 |
for ( var prop in oExtender ) |
173 |
{ |
174 |
if ( oExtender.hasOwnProperty(prop) ) |
175 |
{ |
176 |
val = oExtender[prop]; |
177 |
|
178 |
if ( typeof oInit[prop] === 'object' && val !== null && $.isArray(val) === false ) |
179 |
{ |
180 |
$.extend( true, oOut[prop], val ); |
181 |
} |
182 |
else |
183 |
{ |
184 |
oOut[prop] = val; |
185 |
} |
186 |
} |
187 |
} |
188 |
|
189 |
return oOut; |
190 |
} |
191 |
|
192 |
|
193 |
/** |
194 |
* Bind an event handers to allow a click or return key to activate the callback. |
195 |
* This is good for accessibility since a return on the keyboard will have the |
196 |
* same effect as a click, if the element has focus. |
197 |
* @param {element} n Element to bind the action to |
198 |
* @param {object} oData Data object to pass to the triggered function |
199 |
* @param {function} fn Callback function for when the event is triggered |
200 |
* @memberof DataTable#oApi |
201 |
*/ |
202 |
function _fnBindAction( n, oData, fn ) |
203 |
{ |
204 |
$(n) |
205 |
.bind( 'click.DT', oData, function (e) { |
206 |
n.blur(); // Remove focus outline for mouse users |
207 |
fn(e); |
208 |
} ) |
209 |
.bind( 'keypress.DT', oData, function (e){ |
210 |
if ( e.which === 13 ) { |
211 |
fn(e); |
212 |
} } ) |
213 |
.bind( 'selectstart.DT', function () { |
214 |
/* Take the brutal approach to cancelling text selection */ |
215 |
return false; |
216 |
} ); |
217 |
} |
218 |
|
219 |
|
220 |
/** |
221 |
* Register a callback function. Easily allows a callback function to be added to |
222 |
* an array store of callback functions that can then all be called together. |
223 |
* @param {object} oSettings dataTables settings object |
224 |
* @param {string} sStore Name of the array storage for the callbacks in oSettings |
225 |
* @param {function} fn Function to be called back |
226 |
* @param {string} sName Identifying name for the callback (i.e. a label) |
227 |
* @memberof DataTable#oApi |
228 |
*/ |
229 |
function _fnCallbackReg( oSettings, sStore, fn, sName ) |
230 |
{ |
231 |
if ( fn ) |
232 |
{ |
233 |
oSettings[sStore].push( { |
234 |
"fn": fn, |
235 |
"sName": sName |
236 |
} ); |
237 |
} |
238 |
} |
239 |
|
240 |
|
241 |
/** |
242 |
* Fire callback functions and trigger events. Note that the loop over the callback |
243 |
* array store is done backwards! Further note that you do not want to fire off triggers |
244 |
* in time sensitive applications (for example cell creation) as its slow. |
245 |
* @param {object} oSettings dataTables settings object |
246 |
* @param {string} sStore Name of the array storage for the callbacks in oSettings |
247 |
* @param {string} sTrigger Name of the jQuery custom event to trigger. If null no trigger |
248 |
* is fired |
249 |
* @param {array} aArgs Array of arguments to pass to the callback function / trigger |
250 |
* @memberof DataTable#oApi |
251 |
*/ |
252 |
function _fnCallbackFire( oSettings, sStore, sTrigger, aArgs ) |
253 |
{ |
254 |
var aoStore = oSettings[sStore]; |
255 |
var aRet =[]; |
256 |
|
257 |
for ( var i=aoStore.length-1 ; i>=0 ; i-- ) |
258 |
{ |
259 |
aRet.push( aoStore[i].fn.apply( oSettings.oInstance, aArgs ) ); |
260 |
} |
261 |
|
262 |
if ( sTrigger !== null ) |
263 |
{ |
264 |
$(oSettings.oInstance).trigger(sTrigger, aArgs); |
265 |
} |
266 |
|
267 |
return aRet; |
268 |
} |
269 |
|
270 |
|
271 |
/** |
272 |
* JSON stringify. If JSON.stringify it provided by the browser, json2.js or any other |
273 |
* library, then we use that as it is fast, safe and accurate. If the function isn't |
274 |
* available then we need to built it ourselves - the inspiration for this function comes |
275 |
* from Craig Buckler ( http://www.sitepoint.com/javascript-json-serialization/ ). It is |
276 |
* not perfect and absolutely should not be used as a replacement to json2.js - but it does |
277 |
* do what we need, without requiring a dependency for DataTables. |
278 |
* @param {object} o JSON object to be converted |
279 |
* @returns {string} JSON string |
280 |
* @memberof DataTable#oApi |
281 |
*/ |
282 |
var _fnJsonString = (window.JSON) ? JSON.stringify : function( o ) |
283 |
{ |
284 |
/* Not an object or array */ |
285 |
var sType = typeof o; |
286 |
if (sType !== "object" || o === null) |
287 |
{ |
288 |
// simple data type |
289 |
if (sType === "string") |
290 |
{ |
291 |
o = '"'+o+'"'; |
292 |
} |
293 |
return o+""; |
294 |
} |
295 |
|
296 |
/* If object or array, need to recurse over it */ |
297 |
var |
298 |
sProp, mValue, |
299 |
json = [], |
300 |
bArr = $.isArray(o); |
301 |
|
302 |
for (sProp in o) |
303 |
{ |
304 |
mValue = o[sProp]; |
305 |
sType = typeof mValue; |
306 |
|
307 |
if (sType === "string") |
308 |
{ |
309 |
mValue = '"'+mValue+'"'; |
310 |
} |
311 |
else if (sType === "object" && mValue !== null) |
312 |
{ |
313 |
mValue = _fnJsonString(mValue); |
314 |
} |
315 |
|
316 |
json.push((bArr ? "" : '"'+sProp+'":') + mValue); |
317 |
} |
318 |
|
319 |
return (bArr ? "[" : "{") + json + (bArr ? "]" : "}"); |
320 |
}; |
321 |
|
322 |
|
323 |
/** |
324 |
* From some browsers (specifically IE6/7) we need special handling to work around browser |
325 |
* bugs - this function is used to detect when these workarounds are needed. |
326 |
* @param {object} oSettings dataTables settings object |
327 |
* @memberof DataTable#oApi |
328 |
*/ |
329 |
function _fnBrowserDetect( oSettings ) |
330 |
{ |
331 |
/* IE6/7 will oversize a width 100% element inside a scrolling element, to include the |
332 |
* width of the scrollbar, while other browsers ensure the inner element is contained |
333 |
* without forcing scrolling |
334 |
*/ |
335 |
var n = $( |
336 |
'<div style="position:absolute; top:0; left:0; height:1px; width:1px; overflow:hidden">'+ |
337 |
'<div style="position:absolute; top:1px; left:1px; width:100px; overflow:scroll;">'+ |
338 |
'<div id="DT_BrowserTest" style="width:100%; height:10px;"></div>'+ |
339 |
'</div>'+ |
340 |
'</div>')[0]; |
341 |
|
342 |
document.body.appendChild( n ); |
343 |
oSettings.oBrowser.bScrollOversize = $('#DT_BrowserTest', n)[0].offsetWidth === 100 ? true : false; |
344 |
document.body.removeChild( n ); |
345 |
} |