| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123111241112511126111271112811129111301113111132111331113411135111361113711138111391114011141111421114311144111451114611147111481114911150111511115211153111541115511156111571115811159111601116111162111631116411165111661116711168111691117011171111721117311174111751117611177111781117911180111811118211183111841118511186111871118811189111901119111192111931119411195111961119711198111991120011201112021120311204112051120611207112081120911210112111121211213112141121511216112171121811219112201122111222112231122411225112261122711228112291123011231112321123311234112351123611237112381123911240112411124211243112441124511246112471124811249112501125111252112531125411255112561125711258112591126011261112621126311264112651126611267112681126911270112711127211273112741127511276112771127811279112801128111282112831128411285112861128711288112891129011291112921129311294112951129611297112981129911300113011130211303113041130511306113071130811309113101131111312113131131411315113161131711318113191132011321113221132311324113251132611327113281132911330113311133211333113341133511336113371133811339113401134111342113431134411345113461134711348113491135011351113521135311354113551135611357113581135911360113611136211363113641136511366113671136811369113701137111372113731137411375113761137711378113791138011381113821138311384113851138611387113881138911390113911139211393113941139511396113971139811399114001140111402114031140411405114061140711408114091141011411114121141311414114151141611417114181141911420114211142211423114241142511426114271142811429114301143111432114331143411435114361143711438114391144011441114421144311444114451144611447114481144911450114511145211453114541145511456114571145811459114601146111462114631146411465114661146711468114691147011471114721147311474114751147611477114781147911480114811148211483114841148511486114871148811489114901149111492114931149411495114961149711498114991150011501115021150311504115051150611507115081150911510115111151211513115141151511516115171151811519115201152111522115231152411525115261152711528115291153011531115321153311534115351153611537115381153911540115411154211543115441154511546115471154811549115501155111552115531155411555115561155711558115591156011561115621156311564115651156611567115681156911570115711157211573115741157511576115771157811579115801158111582115831158411585115861158711588115891159011591115921159311594115951159611597115981159911600116011160211603116041160511606116071160811609116101161111612116131161411615116161161711618116191162011621116221162311624116251162611627116281162911630116311163211633116341163511636116371163811639116401164111642116431164411645116461164711648116491165011651116521165311654116551165611657116581165911660116611166211663116641166511666116671166811669116701167111672116731167411675116761167711678116791168011681116821168311684116851168611687116881168911690116911169211693116941169511696116971169811699117001170111702117031170411705117061170711708117091171011711117121171311714117151171611717117181171911720117211172211723117241172511726117271172811729117301173111732117331173411735117361173711738117391174011741117421174311744117451174611747117481174911750117511175211753117541175511756117571175811759117601176111762117631176411765117661176711768117691177011771117721177311774117751177611777117781177911780117811178211783117841178511786117871178811789117901179111792117931179411795117961179711798117991180011801118021180311804118051180611807118081180911810118111181211813118141181511816118171181811819118201182111822118231182411825118261182711828118291183011831118321183311834118351183611837118381183911840118411184211843118441184511846118471184811849118501185111852118531185411855118561185711858118591186011861118621186311864118651186611867118681186911870118711187211873118741187511876118771187811879118801188111882118831188411885118861188711888118891189011891118921189311894118951189611897118981189911900119011190211903119041190511906119071190811909119101191111912119131191411915119161191711918119191192011921119221192311924119251192611927119281192911930119311193211933119341193511936119371193811939119401194111942119431194411945119461194711948119491195011951119521195311954119551195611957119581195911960119611196211963119641196511966119671196811969119701197111972119731197411975119761197711978119791198011981119821198311984119851198611987119881198911990119911199211993119941199511996119971199811999120001200112002120031200412005120061200712008120091201012011120121201312014120151201612017120181201912020120211202212023120241202512026120271202812029120301203112032120331203412035120361203712038120391204012041120421204312044120451204612047120481204912050120511205212053120541205512056120571205812059120601206112062120631206412065120661206712068120691207012071120721207312074120751207612077120781207912080120811208212083120841208512086120871208812089120901209112092120931209412095120961209712098120991210012101121021210312104121051210612107121081210912110121111211212113121141211512116121171211812119121201212112122121231212412125121261212712128121291213012131121321213312134121351213612137121381213912140121411214212143121441214512146121471214812149121501215112152121531215412155121561215712158121591216012161121621216312164121651216612167121681216912170121711217212173121741217512176121771217812179121801218112182121831218412185121861218712188121891219012191121921219312194121951219612197121981219912200122011220212203122041220512206122071220812209122101221112212122131221412215122161221712218122191222012221122221222312224122251222612227122281222912230122311223212233122341223512236122371223812239122401224112242122431224412245122461224712248122491225012251122521225312254122551225612257122581225912260122611226212263122641226512266122671226812269122701227112272122731227412275122761227712278122791228012281122821228312284122851228612287122881228912290122911229212293122941229512296122971229812299123001230112302123031230412305123061230712308123091231012311123121231312314123151231612317123181231912320123211232212323123241232512326123271232812329123301233112332123331233412335123361233712338123391234012341123421234312344123451234612347123481234912350123511235212353123541235512356123571235812359123601236112362123631236412365123661236712368123691237012371123721237312374123751237612377123781237912380123811238212383123841238512386123871238812389123901239112392123931239412395123961239712398123991240012401124021240312404124051240612407124081240912410124111241212413124141241512416124171241812419124201242112422124231242412425124261242712428124291243012431124321243312434124351243612437124381243912440124411244212443124441244512446124471244812449124501245112452124531245412455124561245712458124591246012461124621246312464124651246612467124681246912470124711247212473124741247512476124771247812479124801248112482124831248412485124861248712488124891249012491124921249312494124951249612497124981249912500125011250212503125041250512506125071250812509125101251112512125131251412515125161251712518125191252012521125221252312524125251252612527125281252912530125311253212533125341253512536125371253812539125401254112542125431254412545125461254712548125491255012551125521255312554125551255612557125581255912560125611256212563125641256512566125671256812569125701257112572125731257412575125761257712578125791258012581125821258312584125851258612587125881258912590125911259212593125941259512596125971259812599126001260112602126031260412605126061260712608126091261012611126121261312614126151261612617126181261912620126211262212623126241262512626126271262812629126301263112632126331263412635126361263712638126391264012641126421264312644126451264612647126481264912650126511265212653126541265512656126571265812659126601266112662126631266412665126661266712668126691267012671126721267312674126751267612677126781267912680126811268212683126841268512686126871268812689126901269112692126931269412695126961269712698126991270012701127021270312704127051270612707127081270912710127111271212713127141271512716127171271812719127201272112722127231272412725127261272712728127291273012731127321273312734127351273612737127381273912740127411274212743127441274512746127471274812749127501275112752127531275412755127561275712758127591276012761127621276312764127651276612767127681276912770127711277212773127741277512776127771277812779127801278112782127831278412785127861278712788127891279012791127921279312794127951279612797127981279912800128011280212803128041280512806128071280812809128101281112812128131281412815128161281712818128191282012821128221282312824128251282612827128281282912830128311283212833128341283512836128371283812839128401284112842128431284412845128461284712848128491285012851128521285312854128551285612857128581285912860128611286212863128641286512866128671286812869128701287112872128731287412875128761287712878128791288012881128821288312884128851288612887128881288912890128911289212893128941289512896128971289812899129001290112902129031290412905129061290712908129091291012911129121291312914129151291612917129181291912920129211292212923129241292512926129271292812929129301293112932129331293412935129361293712938129391294012941129421294312944129451294612947129481294912950129511295212953129541295512956129571295812959129601296112962129631296412965129661296712968129691297012971129721297312974129751297612977129781297912980129811298212983129841298512986129871298812989129901299112992129931299412995129961299712998129991300013001130021300313004130051300613007130081300913010130111301213013130141301513016130171301813019130201302113022130231302413025130261302713028130291303013031130321303313034130351303613037130381303913040130411304213043130441304513046130471304813049130501305113052130531305413055130561305713058130591306013061130621306313064130651306613067130681306913070130711307213073130741307513076130771307813079130801308113082130831308413085130861308713088130891309013091130921309313094130951309613097130981309913100131011310213103131041310513106131071310813109131101311113112131131311413115131161311713118131191312013121131221312313124131251312613127131281312913130131311313213133131341313513136131371313813139131401314113142131431314413145131461314713148131491315013151131521315313154131551315613157131581315913160131611316213163131641316513166131671316813169131701317113172131731317413175131761317713178131791318013181131821318313184131851318613187131881318913190131911319213193131941319513196131971319813199132001320113202132031320413205132061320713208132091321013211132121321313214132151321613217132181321913220132211322213223132241322513226132271322813229132301323113232132331323413235132361323713238132391324013241132421324313244132451324613247132481324913250132511325213253132541325513256132571325813259132601326113262132631326413265132661326713268132691327013271132721327313274132751327613277132781327913280132811328213283132841328513286132871328813289132901329113292132931329413295132961329713298132991330013301133021330313304133051330613307133081330913310133111331213313133141331513316133171331813319133201332113322133231332413325133261332713328133291333013331133321333313334133351333613337133381333913340133411334213343133441334513346133471334813349133501335113352133531335413355133561335713358133591336013361133621336313364133651336613367133681336913370133711337213373133741337513376133771337813379133801338113382133831338413385133861338713388133891339013391133921339313394133951339613397133981339913400134011340213403134041340513406134071340813409134101341113412134131341413415134161341713418134191342013421134221342313424134251342613427134281342913430134311343213433134341343513436134371343813439134401344113442134431344413445134461344713448134491345013451134521345313454134551345613457134581345913460134611346213463134641346513466134671346813469134701347113472134731347413475134761347713478134791348013481134821348313484134851348613487134881348913490134911349213493134941349513496134971349813499135001350113502135031350413505135061350713508135091351013511135121351313514135151351613517135181351913520135211352213523135241352513526135271352813529135301353113532135331353413535135361353713538135391354013541135421354313544135451354613547135481354913550135511355213553135541355513556135571355813559135601356113562135631356413565135661356713568135691357013571135721357313574135751357613577135781357913580135811358213583135841358513586135871358813589135901359113592135931359413595135961359713598135991360013601136021360313604136051360613607136081360913610136111361213613136141361513616136171361813619136201362113622136231362413625136261362713628136291363013631136321363313634136351363613637136381363913640136411364213643136441364513646136471364813649136501365113652136531365413655136561365713658136591366013661136621366313664136651366613667136681366913670136711367213673136741367513676136771367813679136801368113682136831368413685136861368713688136891369013691136921369313694136951369613697136981369913700137011370213703137041370513706137071370813709137101371113712137131371413715137161371713718137191372013721137221372313724137251372613727137281372913730137311373213733137341373513736137371373813739137401374113742137431374413745137461374713748137491375013751137521375313754137551375613757137581375913760137611376213763137641376513766137671376813769137701377113772137731377413775137761377713778137791378013781137821378313784137851378613787137881378913790137911379213793137941379513796137971379813799138001380113802138031380413805138061380713808138091381013811138121381313814138151381613817138181381913820138211382213823138241382513826138271382813829138301383113832138331383413835138361383713838138391384013841138421384313844138451384613847138481384913850138511385213853138541385513856138571385813859138601386113862138631386413865138661386713868138691387013871138721387313874138751387613877138781387913880138811388213883138841388513886138871388813889138901389113892138931389413895138961389713898138991390013901139021390313904139051390613907139081390913910139111391213913139141391513916139171391813919139201392113922139231392413925139261392713928139291393013931139321393313934139351393613937139381393913940139411394213943139441394513946139471394813949139501395113952139531395413955139561395713958139591396013961139621396313964139651396613967139681396913970139711397213973139741397513976139771397813979139801398113982139831398413985139861398713988139891399013991139921399313994139951399613997139981399914000140011400214003140041400514006140071400814009140101401114012140131401414015140161401714018140191402014021140221402314024140251402614027140281402914030140311403214033140341403514036140371403814039140401404114042140431404414045140461404714048140491405014051140521405314054140551405614057140581405914060140611406214063140641406514066140671406814069140701407114072140731407414075140761407714078140791408014081140821408314084140851408614087140881408914090140911409214093140941409514096140971409814099141001410114102141031410414105141061410714108141091411014111141121411314114141151411614117141181411914120141211412214123141241412514126141271412814129141301413114132141331413414135141361413714138141391414014141141421414314144141451414614147141481414914150141511415214153141541415514156141571415814159141601416114162141631416414165141661416714168141691417014171141721417314174141751417614177141781417914180141811418214183141841418514186141871418814189141901419114192141931419414195141961419714198141991420014201142021420314204142051420614207142081420914210142111421214213142141421514216142171421814219142201422114222142231422414225142261422714228142291423014231142321423314234142351423614237142381423914240142411424214243142441424514246142471424814249142501425114252142531425414255142561425714258142591426014261142621426314264142651426614267142681426914270142711427214273142741427514276142771427814279142801428114282142831428414285142861428714288142891429014291142921429314294142951429614297142981429914300143011430214303143041430514306143071430814309143101431114312143131431414315143161431714318143191432014321143221432314324143251432614327143281432914330143311433214333143341433514336143371433814339143401434114342143431434414345143461434714348143491435014351143521435314354143551435614357143581435914360143611436214363143641436514366143671436814369143701437114372143731437414375143761437714378143791438014381143821438314384143851438614387143881438914390143911439214393143941439514396143971439814399144001440114402144031440414405144061440714408144091441014411144121441314414144151441614417144181441914420144211442214423144241442514426144271442814429144301443114432144331443414435144361443714438144391444014441144421444314444144451444614447144481444914450144511445214453144541445514456144571445814459144601446114462144631446414465144661446714468144691447014471144721447314474144751447614477144781447914480144811448214483144841448514486144871448814489144901449114492144931449414495144961449714498144991450014501145021450314504145051450614507145081450914510145111451214513145141451514516145171451814519145201452114522145231452414525145261452714528145291453014531145321453314534145351453614537145381453914540145411454214543145441454514546145471454814549145501455114552145531455414555145561455714558145591456014561145621456314564145651456614567145681456914570145711457214573145741457514576145771457814579145801458114582145831458414585145861458714588145891459014591145921459314594145951459614597145981459914600146011460214603146041460514606146071460814609146101461114612146131461414615146161461714618146191462014621146221462314624146251462614627146281462914630146311463214633146341463514636146371463814639146401464114642146431464414645146461464714648146491465014651146521465314654146551465614657146581465914660146611466214663146641466514666146671466814669146701467114672146731467414675146761467714678146791468014681146821468314684146851468614687146881468914690146911469214693146941469514696146971469814699147001470114702147031470414705147061470714708147091471014711147121471314714147151471614717147181471914720147211472214723147241472514726147271472814729147301473114732147331473414735147361473714738147391474014741147421474314744147451474614747147481474914750147511475214753147541475514756147571475814759147601476114762147631476414765147661476714768147691477014771147721477314774147751477614777147781477914780147811478214783147841478514786147871478814789147901479114792147931479414795147961479714798147991480014801148021480314804148051480614807148081480914810148111481214813148141481514816148171481814819148201482114822148231482414825148261482714828148291483014831148321483314834148351483614837148381483914840148411484214843148441484514846148471484814849148501485114852148531485414855148561485714858148591486014861148621486314864148651486614867148681486914870148711487214873148741487514876148771487814879148801488114882148831488414885148861488714888148891489014891148921489314894148951489614897148981489914900149011490214903149041490514906149071490814909149101491114912149131491414915149161491714918149191492014921149221492314924149251492614927149281492914930149311493214933149341493514936149371493814939149401494114942149431494414945149461494714948149491495014951149521495314954149551495614957149581495914960149611496214963149641496514966149671496814969149701497114972149731497414975149761497714978149791498014981149821498314984149851498614987149881498914990149911499214993149941499514996149971499814999150001500115002150031500415005150061500715008150091501015011150121501315014150151501615017150181501915020150211502215023150241502515026150271502815029150301503115032150331503415035150361503715038150391504015041150421504315044150451504615047150481504915050150511505215053150541505515056150571505815059150601506115062150631506415065150661506715068150691507015071150721507315074150751507615077150781507915080150811508215083150841508515086150871508815089150901509115092150931509415095150961509715098150991510015101151021510315104151051510615107151081510915110151111511215113151141511515116151171511815119151201512115122151231512415125151261512715128151291513015131151321513315134151351513615137151381513915140151411514215143151441514515146151471514815149151501515115152151531515415155151561515715158151591516015161151621516315164151651516615167151681516915170151711517215173151741517515176151771517815179151801518115182151831518415185151861518715188151891519015191151921519315194151951519615197151981519915200152011520215203152041520515206152071520815209152101521115212152131521415215152161521715218152191522015221152221522315224152251522615227152281522915230152311523215233152341523515236152371523815239152401524115242152431524415245152461524715248152491525015251152521525315254152551525615257152581525915260152611526215263152641526515266152671526815269152701527115272152731527415275152761527715278152791528015281152821528315284152851528615287152881528915290152911529215293152941529515296152971529815299153001530115302153031530415305153061530715308153091531015311153121531315314153151531615317153181531915320153211532215323153241532515326153271532815329153301533115332153331533415335153361533715338153391534015341153421534315344153451534615347153481534915350153511535215353153541535515356153571535815359153601536115362153631536415365153661536715368153691537015371153721537315374153751537615377153781537915380153811538215383153841538515386153871538815389153901539115392153931539415395153961539715398153991540015401154021540315404154051540615407154081540915410154111541215413154141541515416154171541815419154201542115422154231542415425154261542715428154291543015431154321543315434154351543615437154381543915440154411544215443154441544515446154471544815449154501545115452154531545415455154561545715458154591546015461154621546315464154651546615467154681546915470154711547215473154741547515476154771547815479154801548115482154831548415485154861548715488154891549015491154921549315494154951549615497154981549915500155011550215503155041550515506155071550815509155101551115512155131551415515155161551715518155191552015521155221552315524155251552615527155281552915530155311553215533155341553515536155371553815539155401554115542155431554415545155461554715548155491555015551155521555315554155551555615557155581555915560155611556215563155641556515566155671556815569155701557115572155731557415575155761557715578155791558015581155821558315584155851558615587155881558915590155911559215593155941559515596155971559815599156001560115602156031560415605156061560715608156091561015611156121561315614156151561615617156181561915620156211562215623156241562515626156271562815629156301563115632156331563415635156361563715638156391564015641156421564315644156451564615647156481564915650156511565215653156541565515656156571565815659156601566115662156631566415665156661566715668156691567015671156721567315674156751567615677156781567915680156811568215683156841568515686156871568815689156901569115692156931569415695156961569715698156991570015701157021570315704157051570615707157081570915710157111571215713157141571515716157171571815719157201572115722157231572415725157261572715728157291573015731157321573315734157351573615737157381573915740157411574215743157441574515746157471574815749157501575115752157531575415755157561575715758157591576015761157621576315764157651576615767157681576915770157711577215773157741577515776157771577815779157801578115782157831578415785157861578715788157891579015791157921579315794157951579615797157981579915800158011580215803158041580515806158071580815809158101581115812158131581415815158161581715818158191582015821158221582315824158251582615827158281582915830158311583215833158341583515836158371583815839158401584115842158431584415845158461584715848158491585015851158521585315854158551585615857158581585915860158611586215863158641586515866158671586815869158701587115872158731587415875158761587715878158791588015881158821588315884158851588615887158881588915890158911589215893158941589515896158971589815899159001590115902159031590415905159061590715908159091591015911159121591315914159151591615917159181591915920159211592215923159241592515926159271592815929159301593115932159331593415935159361593715938159391594015941159421594315944159451594615947159481594915950159511595215953159541595515956159571595815959159601596115962159631596415965159661596715968159691597015971159721597315974159751597615977159781597915980159811598215983159841598515986159871598815989159901599115992159931599415995159961599715998159991600016001160021600316004160051600616007160081600916010160111601216013160141601516016160171601816019160201602116022160231602416025160261602716028160291603016031160321603316034160351603616037160381603916040160411604216043160441604516046160471604816049160501605116052160531605416055160561605716058160591606016061160621606316064160651606616067160681606916070160711607216073160741607516076160771607816079160801608116082160831608416085160861608716088160891609016091160921609316094160951609616097160981609916100161011610216103161041610516106161071610816109161101611116112161131611416115161161611716118161191612016121161221612316124161251612616127161281612916130161311613216133161341613516136161371613816139161401614116142161431614416145161461614716148161491615016151161521615316154161551615616157161581615916160161611616216163161641616516166161671616816169161701617116172161731617416175161761617716178161791618016181161821618316184161851618616187161881618916190161911619216193161941619516196161971619816199162001620116202162031620416205162061620716208162091621016211162121621316214162151621616217162181621916220162211622216223162241622516226162271622816229162301623116232162331623416235162361623716238162391624016241162421624316244162451624616247162481624916250162511625216253162541625516256162571625816259162601626116262162631626416265162661626716268162691627016271162721627316274162751627616277162781627916280162811628216283162841628516286162871628816289162901629116292162931629416295162961629716298162991630016301163021630316304163051630616307163081630916310163111631216313163141631516316163171631816319163201632116322163231632416325163261632716328163291633016331163321633316334163351633616337163381633916340163411634216343163441634516346163471634816349163501635116352163531635416355163561635716358163591636016361163621636316364163651636616367163681636916370163711637216373163741637516376163771637816379163801638116382163831638416385163861638716388163891639016391163921639316394163951639616397163981639916400164011640216403164041640516406164071640816409164101641116412164131641416415164161641716418164191642016421164221642316424164251642616427164281642916430164311643216433164341643516436164371643816439164401644116442164431644416445164461644716448164491645016451164521645316454164551645616457164581645916460164611646216463164641646516466164671646816469164701647116472164731647416475164761647716478164791648016481164821648316484164851648616487164881648916490164911649216493164941649516496164971649816499165001650116502165031650416505165061650716508165091651016511165121651316514165151651616517165181651916520165211652216523165241652516526165271652816529165301653116532165331653416535165361653716538165391654016541165421654316544165451654616547165481654916550165511655216553165541655516556165571655816559165601656116562165631656416565165661656716568165691657016571165721657316574165751657616577165781657916580165811658216583165841658516586165871658816589165901659116592165931659416595165961659716598165991660016601166021660316604166051660616607166081660916610166111661216613166141661516616166171661816619166201662116622166231662416625166261662716628166291663016631166321663316634166351663616637166381663916640166411664216643166441664516646166471664816649166501665116652166531665416655166561665716658166591666016661166621666316664166651666616667166681666916670166711667216673166741667516676166771667816679166801668116682166831668416685166861668716688166891669016691166921669316694166951669616697166981669916700167011670216703167041670516706167071670816709167101671116712167131671416715167161671716718167191672016721167221672316724167251672616727167281672916730167311673216733167341673516736167371673816739167401674116742167431674416745167461674716748167491675016751167521675316754167551675616757167581675916760167611676216763167641676516766167671676816769167701677116772167731677416775167761677716778167791678016781167821678316784167851678616787167881678916790167911679216793167941679516796167971679816799168001680116802168031680416805168061680716808168091681016811168121681316814168151681616817168181681916820168211682216823168241682516826168271682816829168301683116832168331683416835168361683716838168391684016841168421684316844168451684616847168481684916850168511685216853168541685516856168571685816859168601686116862168631686416865168661686716868168691687016871168721687316874168751687616877168781687916880168811688216883168841688516886168871688816889168901689116892168931689416895168961689716898168991690016901169021690316904169051690616907169081690916910169111691216913169141691516916169171691816919169201692116922169231692416925169261692716928169291693016931169321693316934169351693616937169381693916940169411694216943169441694516946169471694816949169501695116952169531695416955169561695716958169591696016961169621696316964169651696616967169681696916970169711697216973169741697516976169771697816979169801698116982169831698416985169861698716988169891699016991169921699316994169951699616997169981699917000170011700217003170041700517006170071700817009170101701117012170131701417015170161701717018170191702017021170221702317024170251702617027170281702917030170311703217033170341703517036170371703817039170401704117042170431704417045170461704717048170491705017051170521705317054170551705617057170581705917060170611706217063170641706517066170671706817069170701707117072170731707417075170761707717078170791708017081170821708317084170851708617087170881708917090170911709217093170941709517096170971709817099171001710117102171031710417105171061710717108171091711017111171121711317114171151711617117171181711917120171211712217123171241712517126171271712817129171301713117132171331713417135171361713717138171391714017141171421714317144171451714617147171481714917150171511715217153171541715517156171571715817159171601716117162171631716417165171661716717168171691717017171171721717317174171751717617177171781717917180171811718217183171841718517186171871718817189171901719117192171931719417195171961719717198171991720017201172021720317204172051720617207172081720917210172111721217213172141721517216172171721817219172201722117222172231722417225172261722717228172291723017231172321723317234172351723617237172381723917240172411724217243172441724517246172471724817249172501725117252172531725417255172561725717258172591726017261172621726317264172651726617267172681726917270172711727217273172741727517276172771727817279172801728117282172831728417285172861728717288172891729017291172921729317294172951729617297172981729917300173011730217303173041730517306173071730817309173101731117312173131731417315173161731717318173191732017321173221732317324173251732617327173281732917330173311733217333173341733517336173371733817339173401734117342173431734417345173461734717348173491735017351173521735317354173551735617357173581735917360173611736217363173641736517366173671736817369173701737117372173731737417375173761737717378173791738017381173821738317384173851738617387173881738917390173911739217393173941739517396173971739817399174001740117402174031740417405174061740717408174091741017411174121741317414174151741617417174181741917420174211742217423174241742517426174271742817429174301743117432174331743417435174361743717438174391744017441174421744317444174451744617447174481744917450174511745217453174541745517456174571745817459174601746117462174631746417465174661746717468174691747017471174721747317474174751747617477174781747917480174811748217483174841748517486174871748817489174901749117492174931749417495174961749717498174991750017501175021750317504175051750617507175081750917510175111751217513175141751517516175171751817519175201752117522175231752417525175261752717528175291753017531175321753317534175351753617537175381753917540175411754217543175441754517546175471754817549175501755117552175531755417555175561755717558175591756017561175621756317564175651756617567175681756917570175711757217573175741757517576175771757817579175801758117582175831758417585175861758717588175891759017591175921759317594175951759617597175981759917600176011760217603176041760517606176071760817609176101761117612176131761417615176161761717618176191762017621176221762317624176251762617627176281762917630176311763217633176341763517636176371763817639176401764117642176431764417645176461764717648176491765017651176521765317654176551765617657176581765917660176611766217663176641766517666176671766817669176701767117672176731767417675176761767717678176791768017681176821768317684176851768617687176881768917690176911769217693176941769517696176971769817699177001770117702177031770417705177061770717708177091771017711177121771317714177151771617717177181771917720177211772217723177241772517726177271772817729177301773117732177331773417735177361773717738177391774017741177421774317744177451774617747177481774917750177511775217753177541775517756177571775817759177601776117762177631776417765177661776717768177691777017771177721777317774177751777617777177781777917780177811778217783177841778517786177871778817789177901779117792177931779417795177961779717798177991780017801178021780317804178051780617807178081780917810178111781217813178141781517816178171781817819178201782117822178231782417825178261782717828178291783017831178321783317834178351783617837178381783917840178411784217843178441784517846178471784817849178501785117852178531785417855178561785717858178591786017861178621786317864178651786617867178681786917870178711787217873178741787517876178771787817879178801788117882178831788417885178861788717888178891789017891178921789317894178951789617897178981789917900179011790217903179041790517906179071790817909179101791117912179131791417915179161791717918179191792017921179221792317924179251792617927179281792917930179311793217933179341793517936179371793817939179401794117942179431794417945179461794717948179491795017951179521795317954179551795617957179581795917960179611796217963179641796517966179671796817969179701797117972179731797417975179761797717978179791798017981179821798317984179851798617987179881798917990179911799217993179941799517996179971799817999180001800118002180031800418005180061800718008180091801018011180121801318014180151801618017180181801918020180211802218023180241802518026180271802818029180301803118032180331803418035180361803718038180391804018041180421804318044180451804618047180481804918050180511805218053180541805518056180571805818059180601806118062180631806418065180661806718068180691807018071180721807318074180751807618077180781807918080180811808218083180841808518086180871808818089180901809118092180931809418095180961809718098180991810018101181021810318104181051810618107181081810918110181111811218113181141811518116181171811818119181201812118122181231812418125181261812718128181291813018131181321813318134181351813618137181381813918140181411814218143181441814518146181471814818149181501815118152181531815418155181561815718158181591816018161181621816318164181651816618167181681816918170181711817218173181741817518176181771817818179181801818118182181831818418185181861818718188181891819018191181921819318194181951819618197181981819918200182011820218203182041820518206182071820818209182101821118212182131821418215182161821718218182191822018221182221822318224182251822618227182281822918230182311823218233182341823518236182371823818239182401824118242182431824418245182461824718248182491825018251182521825318254182551825618257182581825918260182611826218263182641826518266182671826818269182701827118272182731827418275182761827718278182791828018281182821828318284182851828618287182881828918290182911829218293182941829518296182971829818299183001830118302183031830418305183061830718308183091831018311183121831318314183151831618317183181831918320183211832218323183241832518326183271832818329183301833118332183331833418335183361833718338183391834018341183421834318344183451834618347183481834918350183511835218353183541835518356183571835818359183601836118362183631836418365183661836718368183691837018371183721837318374183751837618377183781837918380183811838218383183841838518386183871838818389183901839118392183931839418395183961839718398183991840018401184021840318404184051840618407184081840918410184111841218413184141841518416184171841818419184201842118422184231842418425184261842718428184291843018431184321843318434184351843618437184381843918440184411844218443184441844518446184471844818449184501845118452184531845418455184561845718458184591846018461184621846318464184651846618467184681846918470184711847218473184741847518476184771847818479184801848118482184831848418485184861848718488184891849018491184921849318494184951849618497184981849918500185011850218503185041850518506185071850818509185101851118512185131851418515185161851718518185191852018521185221852318524185251852618527185281852918530185311853218533185341853518536185371853818539185401854118542185431854418545185461854718548185491855018551185521855318554185551855618557185581855918560185611856218563185641856518566185671856818569185701857118572185731857418575185761857718578185791858018581185821858318584185851858618587185881858918590185911859218593185941859518596185971859818599186001860118602186031860418605186061860718608186091861018611186121861318614186151861618617186181861918620186211862218623186241862518626186271862818629186301863118632186331863418635186361863718638186391864018641186421864318644186451864618647186481864918650186511865218653186541865518656186571865818659186601866118662186631866418665186661866718668186691867018671186721867318674186751867618677186781867918680186811868218683186841868518686186871868818689186901869118692186931869418695186961869718698186991870018701187021870318704187051870618707187081870918710187111871218713187141871518716187171871818719187201872118722187231872418725187261872718728187291873018731187321873318734187351873618737187381873918740187411874218743187441874518746187471874818749187501875118752187531875418755187561875718758187591876018761187621876318764187651876618767187681876918770187711877218773187741877518776187771877818779187801878118782187831878418785187861878718788187891879018791187921879318794187951879618797187981879918800188011880218803188041880518806188071880818809188101881118812188131881418815188161881718818188191882018821188221882318824188251882618827188281882918830188311883218833188341883518836188371883818839188401884118842188431884418845188461884718848188491885018851188521885318854188551885618857188581885918860188611886218863188641886518866188671886818869188701887118872188731887418875188761887718878188791888018881188821888318884188851888618887188881888918890188911889218893188941889518896188971889818899189001890118902189031890418905189061890718908189091891018911189121891318914189151891618917189181891918920189211892218923189241892518926189271892818929189301893118932189331893418935189361893718938189391894018941189421894318944189451894618947189481894918950189511895218953189541895518956189571895818959189601896118962189631896418965189661896718968189691897018971189721897318974189751897618977189781897918980189811898218983189841898518986189871898818989189901899118992189931899418995189961899718998189991900019001190021900319004190051900619007190081900919010190111901219013190141901519016190171901819019190201902119022190231902419025190261902719028190291903019031190321903319034190351903619037190381903919040190411904219043190441904519046190471904819049190501905119052190531905419055190561905719058190591906019061190621906319064190651906619067190681906919070190711907219073190741907519076190771907819079190801908119082190831908419085190861908719088190891909019091190921909319094190951909619097190981909919100191011910219103191041910519106191071910819109191101911119112191131911419115191161911719118191191912019121191221912319124191251912619127191281912919130191311913219133191341913519136191371913819139191401914119142191431914419145191461914719148191491915019151191521915319154191551915619157191581915919160191611916219163191641916519166191671916819169191701917119172191731917419175191761917719178191791918019181191821918319184191851918619187191881918919190191911919219193191941919519196191971919819199192001920119202192031920419205192061920719208192091921019211192121921319214192151921619217192181921919220192211922219223192241922519226192271922819229192301923119232192331923419235192361923719238192391924019241192421924319244192451924619247192481924919250192511925219253192541925519256192571925819259192601926119262192631926419265192661926719268192691927019271192721927319274192751927619277192781927919280192811928219283192841928519286192871928819289192901929119292192931929419295192961929719298192991930019301193021930319304193051930619307193081930919310193111931219313193141931519316193171931819319193201932119322193231932419325193261932719328193291933019331193321933319334193351933619337193381933919340193411934219343193441934519346193471934819349193501935119352193531935419355193561935719358193591936019361193621936319364193651936619367193681936919370193711937219373193741937519376193771937819379193801938119382193831938419385193861938719388193891939019391193921939319394193951939619397193981939919400194011940219403194041940519406194071940819409194101941119412194131941419415194161941719418194191942019421194221942319424194251942619427194281942919430194311943219433194341943519436194371943819439194401944119442194431944419445194461944719448194491945019451194521945319454194551945619457194581945919460194611946219463194641946519466194671946819469194701947119472194731947419475194761947719478194791948019481194821948319484194851948619487194881948919490194911949219493194941949519496194971949819499195001950119502195031950419505195061950719508195091951019511195121951319514195151951619517195181951919520195211952219523195241952519526195271952819529195301953119532195331953419535195361953719538195391954019541195421954319544195451954619547195481954919550195511955219553195541955519556195571955819559195601956119562195631956419565195661956719568195691957019571195721957319574195751957619577195781957919580195811958219583195841958519586195871958819589195901959119592195931959419595195961959719598195991960019601196021960319604196051960619607196081960919610196111961219613196141961519616196171961819619196201962119622196231962419625196261962719628196291963019631196321963319634196351963619637196381963919640196411964219643196441964519646196471964819649196501965119652196531965419655196561965719658196591966019661196621966319664196651966619667196681966919670196711967219673196741967519676196771967819679196801968119682196831968419685196861968719688196891969019691196921969319694196951969619697196981969919700197011970219703197041970519706197071970819709197101971119712197131971419715197161971719718197191972019721197221972319724197251972619727197281972919730197311973219733197341973519736197371973819739197401974119742197431974419745197461974719748197491975019751197521975319754197551975619757197581975919760197611976219763197641976519766197671976819769197701977119772197731977419775197761977719778197791978019781197821978319784197851978619787197881978919790197911979219793197941979519796197971979819799198001980119802198031980419805198061980719808198091981019811198121981319814198151981619817198181981919820198211982219823198241982519826198271982819829198301983119832198331983419835198361983719838198391984019841198421984319844198451984619847198481984919850198511985219853198541985519856198571985819859198601986119862198631986419865198661986719868198691987019871198721987319874198751987619877198781987919880198811988219883198841988519886198871988819889198901989119892198931989419895198961989719898198991990019901199021990319904199051990619907199081990919910199111991219913199141991519916199171991819919199201992119922199231992419925199261992719928199291993019931199321993319934199351993619937199381993919940199411994219943199441994519946199471994819949199501995119952199531995419955199561995719958199591996019961199621996319964199651996619967199681996919970199711997219973199741997519976199771997819979199801998119982199831998419985199861998719988199891999019991199921999319994199951999619997199981999920000200012000220003200042000520006200072000820009200102001120012200132001420015200162001720018200192002020021200222002320024200252002620027200282002920030200312003220033200342003520036200372003820039200402004120042200432004420045200462004720048200492005020051200522005320054200552005620057200582005920060200612006220063200642006520066200672006820069200702007120072200732007420075200762007720078200792008020081200822008320084200852008620087200882008920090200912009220093200942009520096200972009820099201002010120102201032010420105201062010720108201092011020111201122011320114201152011620117201182011920120201212012220123201242012520126201272012820129201302013120132201332013420135201362013720138201392014020141201422014320144201452014620147201482014920150201512015220153201542015520156201572015820159201602016120162201632016420165201662016720168201692017020171201722017320174201752017620177201782017920180201812018220183201842018520186201872018820189201902019120192201932019420195201962019720198201992020020201202022020320204202052020620207202082020920210202112021220213202142021520216202172021820219202202022120222202232022420225202262022720228202292023020231202322023320234202352023620237202382023920240202412024220243202442024520246202472024820249202502025120252202532025420255202562025720258202592026020261202622026320264202652026620267202682026920270202712027220273202742027520276202772027820279202802028120282202832028420285202862028720288202892029020291202922029320294202952029620297202982029920300203012030220303203042030520306203072030820309203102031120312203132031420315203162031720318203192032020321203222032320324203252032620327203282032920330203312033220333203342033520336203372033820339203402034120342203432034420345203462034720348203492035020351203522035320354203552035620357203582035920360203612036220363203642036520366203672036820369203702037120372203732037420375203762037720378203792038020381203822038320384203852038620387203882038920390203912039220393203942039520396203972039820399204002040120402204032040420405204062040720408204092041020411204122041320414204152041620417204182041920420204212042220423204242042520426204272042820429204302043120432204332043420435204362043720438204392044020441204422044320444204452044620447204482044920450204512045220453204542045520456204572045820459204602046120462204632046420465204662046720468204692047020471204722047320474204752047620477204782047920480204812048220483204842048520486204872048820489204902049120492204932049420495204962049720498204992050020501205022050320504205052050620507205082050920510205112051220513205142051520516205172051820519205202052120522205232052420525205262052720528205292053020531205322053320534205352053620537205382053920540205412054220543205442054520546205472054820549205502055120552205532055420555205562055720558205592056020561205622056320564205652056620567205682056920570205712057220573205742057520576205772057820579205802058120582205832058420585205862058720588205892059020591205922059320594205952059620597205982059920600206012060220603206042060520606206072060820609206102061120612206132061420615206162061720618206192062020621206222062320624206252062620627206282062920630206312063220633206342063520636206372063820639206402064120642206432064420645206462064720648206492065020651206522065320654206552065620657206582065920660206612066220663206642066520666206672066820669206702067120672206732067420675206762067720678206792068020681206822068320684206852068620687206882068920690206912069220693206942069520696206972069820699207002070120702207032070420705207062070720708207092071020711207122071320714207152071620717207182071920720207212072220723207242072520726207272072820729207302073120732207332073420735207362073720738207392074020741207422074320744207452074620747207482074920750207512075220753207542075520756207572075820759207602076120762207632076420765207662076720768207692077020771207722077320774207752077620777207782077920780207812078220783207842078520786207872078820789207902079120792207932079420795207962079720798207992080020801208022080320804208052080620807208082080920810208112081220813208142081520816208172081820819208202082120822208232082420825208262082720828208292083020831208322083320834208352083620837208382083920840208412084220843208442084520846208472084820849208502085120852208532085420855208562085720858208592086020861208622086320864208652086620867208682086920870208712087220873208742087520876208772087820879208802088120882208832088420885208862088720888208892089020891208922089320894208952089620897208982089920900209012090220903209042090520906209072090820909209102091120912209132091420915209162091720918209192092020921209222092320924209252092620927209282092920930209312093220933209342093520936209372093820939209402094120942209432094420945209462094720948209492095020951209522095320954209552095620957209582095920960209612096220963209642096520966209672096820969209702097120972209732097420975209762097720978209792098020981209822098320984209852098620987209882098920990209912099220993209942099520996209972099820999210002100121002210032100421005210062100721008210092101021011210122101321014210152101621017210182101921020210212102221023210242102521026210272102821029210302103121032210332103421035210362103721038210392104021041210422104321044210452104621047210482104921050210512105221053210542105521056210572105821059210602106121062210632106421065210662106721068210692107021071210722107321074210752107621077210782107921080210812108221083210842108521086210872108821089210902109121092210932109421095210962109721098210992110021101211022110321104211052110621107211082110921110211112111221113211142111521116211172111821119211202112121122211232112421125211262112721128211292113021131211322113321134211352113621137211382113921140211412114221143211442114521146211472114821149211502115121152211532115421155211562115721158211592116021161211622116321164211652116621167211682116921170211712117221173211742117521176211772117821179211802118121182211832118421185211862118721188211892119021191211922119321194211952119621197211982119921200212012120221203212042120521206212072120821209212102121121212212132121421215212162121721218212192122021221212222122321224212252122621227212282122921230212312123221233212342123521236212372123821239212402124121242212432124421245212462124721248212492125021251212522125321254212552125621257212582125921260212612126221263212642126521266212672126821269212702127121272212732127421275212762127721278212792128021281212822128321284212852128621287212882128921290212912129221293212942129521296212972129821299213002130121302213032130421305213062130721308213092131021311213122131321314213152131621317213182131921320213212132221323213242132521326213272132821329213302133121332213332133421335213362133721338213392134021341213422134321344213452134621347213482134921350213512135221353213542135521356213572135821359213602136121362213632136421365213662136721368213692137021371213722137321374213752137621377213782137921380213812138221383213842138521386213872138821389213902139121392213932139421395213962139721398213992140021401214022140321404214052140621407214082140921410214112141221413214142141521416214172141821419214202142121422214232142421425214262142721428214292143021431214322143321434214352143621437214382143921440214412144221443214442144521446214472144821449214502145121452214532145421455214562145721458214592146021461214622146321464214652146621467214682146921470214712147221473214742147521476214772147821479214802148121482214832148421485214862148721488214892149021491214922149321494214952149621497214982149921500215012150221503215042150521506215072150821509215102151121512215132151421515215162151721518215192152021521215222152321524215252152621527215282152921530215312153221533215342153521536215372153821539215402154121542215432154421545215462154721548215492155021551215522155321554215552155621557215582155921560215612156221563215642156521566215672156821569215702157121572215732157421575215762157721578215792158021581215822158321584215852158621587215882158921590215912159221593215942159521596215972159821599216002160121602216032160421605216062160721608216092161021611216122161321614216152161621617216182161921620216212162221623216242162521626216272162821629216302163121632216332163421635216362163721638216392164021641216422164321644216452164621647216482164921650216512165221653216542165521656216572165821659216602166121662216632166421665216662166721668216692167021671216722167321674216752167621677216782167921680216812168221683216842168521686216872168821689216902169121692216932169421695216962169721698216992170021701217022170321704217052170621707217082170921710217112171221713217142171521716217172171821719217202172121722217232172421725217262172721728217292173021731217322173321734217352173621737217382173921740217412174221743217442174521746217472174821749217502175121752217532175421755217562175721758217592176021761217622176321764217652176621767217682176921770217712177221773217742177521776217772177821779217802178121782217832178421785217862178721788217892179021791217922179321794217952179621797217982179921800218012180221803218042180521806218072180821809218102181121812218132181421815218162181721818218192182021821218222182321824218252182621827218282182921830218312183221833218342183521836218372183821839218402184121842218432184421845218462184721848218492185021851218522185321854218552185621857218582185921860218612186221863218642186521866218672186821869218702187121872218732187421875218762187721878218792188021881218822188321884218852188621887218882188921890218912189221893218942189521896218972189821899219002190121902219032190421905219062190721908219092191021911219122191321914219152191621917219182191921920219212192221923219242192521926219272192821929219302193121932219332193421935219362193721938219392194021941219422194321944219452194621947219482194921950219512195221953219542195521956219572195821959219602196121962219632196421965219662196721968219692197021971219722197321974219752197621977219782197921980219812198221983219842198521986219872198821989219902199121992219932199421995219962199721998219992200022001220022200322004220052200622007220082200922010220112201222013220142201522016220172201822019220202202122022220232202422025220262202722028220292203022031220322203322034220352203622037220382203922040220412204222043220442204522046220472204822049220502205122052220532205422055220562205722058220592206022061220622206322064220652206622067220682206922070220712207222073220742207522076220772207822079220802208122082220832208422085220862208722088220892209022091220922209322094220952209622097220982209922100221012210222103221042210522106221072210822109221102211122112221132211422115221162211722118221192212022121221222212322124221252212622127221282212922130221312213222133221342213522136221372213822139221402214122142221432214422145221462214722148221492215022151221522215322154221552215622157221582215922160221612216222163221642216522166221672216822169221702217122172221732217422175221762217722178221792218022181221822218322184221852218622187221882218922190221912219222193221942219522196221972219822199222002220122202222032220422205222062220722208222092221022211222122221322214222152221622217222182221922220222212222222223222242222522226222272222822229222302223122232222332223422235222362223722238222392224022241222422224322244222452224622247222482224922250222512225222253222542225522256222572225822259222602226122262222632226422265222662226722268222692227022271222722227322274222752227622277222782227922280222812228222283222842228522286222872228822289222902229122292222932229422295222962229722298222992230022301223022230322304223052230622307223082230922310223112231222313223142231522316223172231822319223202232122322223232232422325223262232722328223292233022331223322233322334223352233622337223382233922340223412234222343223442234522346223472234822349223502235122352223532235422355223562235722358223592236022361223622236322364223652236622367223682236922370223712237222373223742237522376223772237822379223802238122382223832238422385223862238722388223892239022391223922239322394223952239622397223982239922400224012240222403224042240522406224072240822409224102241122412224132241422415224162241722418224192242022421224222242322424224252242622427224282242922430224312243222433224342243522436224372243822439224402244122442224432244422445224462244722448224492245022451224522245322454224552245622457224582245922460224612246222463224642246522466224672246822469224702247122472224732247422475224762247722478224792248022481224822248322484224852248622487224882248922490224912249222493224942249522496224972249822499225002250122502225032250422505225062250722508225092251022511225122251322514225152251622517225182251922520225212252222523225242252522526225272252822529225302253122532225332253422535225362253722538225392254022541225422254322544225452254622547225482254922550225512255222553225542255522556225572255822559225602256122562225632256422565225662256722568225692257022571225722257322574225752257622577225782257922580225812258222583225842258522586225872258822589225902259122592225932259422595225962259722598225992260022601226022260322604226052260622607226082260922610226112261222613226142261522616226172261822619226202262122622226232262422625226262262722628226292263022631226322263322634226352263622637226382263922640226412264222643226442264522646226472264822649226502265122652226532265422655226562265722658226592266022661226622266322664226652266622667226682266922670226712267222673226742267522676226772267822679226802268122682226832268422685226862268722688226892269022691226922269322694226952269622697226982269922700227012270222703227042270522706227072270822709227102271122712227132271422715227162271722718227192272022721227222272322724227252272622727227282272922730227312273222733227342273522736227372273822739227402274122742227432274422745227462274722748227492275022751227522275322754227552275622757227582275922760227612276222763227642276522766227672276822769227702277122772227732277422775227762277722778227792278022781227822278322784227852278622787227882278922790227912279222793227942279522796227972279822799228002280122802228032280422805228062280722808228092281022811228122281322814228152281622817228182281922820228212282222823228242282522826228272282822829228302283122832228332283422835228362283722838228392284022841228422284322844228452284622847228482284922850228512285222853228542285522856228572285822859228602286122862228632286422865228662286722868228692287022871228722287322874228752287622877228782287922880228812288222883228842288522886228872288822889228902289122892228932289422895228962289722898228992290022901229022290322904229052290622907229082290922910229112291222913229142291522916229172291822919229202292122922229232292422925229262292722928229292293022931229322293322934229352293622937229382293922940229412294222943229442294522946229472294822949229502295122952229532295422955229562295722958229592296022961229622296322964229652296622967229682296922970229712297222973229742297522976229772297822979229802298122982229832298422985229862298722988229892299022991229922299322994229952299622997229982299923000230012300223003230042300523006230072300823009230102301123012230132301423015230162301723018230192302023021230222302323024230252302623027230282302923030230312303223033230342303523036230372303823039230402304123042230432304423045230462304723048230492305023051230522305323054230552305623057230582305923060230612306223063230642306523066230672306823069230702307123072230732307423075230762307723078230792308023081230822308323084230852308623087230882308923090230912309223093230942309523096230972309823099231002310123102231032310423105231062310723108231092311023111231122311323114231152311623117231182311923120231212312223123231242312523126231272312823129231302313123132231332313423135231362313723138231392314023141231422314323144231452314623147231482314923150231512315223153231542315523156231572315823159231602316123162231632316423165231662316723168231692317023171231722317323174231752317623177231782317923180231812318223183231842318523186231872318823189231902319123192231932319423195231962319723198231992320023201232022320323204232052320623207232082320923210232112321223213232142321523216232172321823219232202322123222232232322423225232262322723228232292323023231232322323323234232352323623237232382323923240232412324223243232442324523246232472324823249232502325123252232532325423255232562325723258232592326023261232622326323264232652326623267232682326923270232712327223273232742327523276232772327823279232802328123282232832328423285232862328723288232892329023291232922329323294232952329623297232982329923300233012330223303233042330523306233072330823309233102331123312233132331423315233162331723318233192332023321233222332323324233252332623327233282332923330233312333223333233342333523336233372333823339233402334123342233432334423345233462334723348233492335023351233522335323354233552335623357233582335923360233612336223363233642336523366233672336823369233702337123372233732337423375233762337723378233792338023381233822338323384233852338623387233882338923390233912339223393233942339523396233972339823399234002340123402234032340423405234062340723408234092341023411234122341323414234152341623417234182341923420234212342223423234242342523426234272342823429234302343123432234332343423435234362343723438234392344023441234422344323444234452344623447234482344923450234512345223453234542345523456234572345823459234602346123462234632346423465234662346723468234692347023471234722347323474234752347623477234782347923480234812348223483234842348523486234872348823489234902349123492234932349423495234962349723498234992350023501235022350323504235052350623507235082350923510235112351223513235142351523516235172351823519235202352123522235232352423525235262352723528235292353023531235322353323534235352353623537235382353923540235412354223543235442354523546235472354823549235502355123552235532355423555235562355723558235592356023561235622356323564235652356623567235682356923570235712357223573235742357523576235772357823579235802358123582235832358423585235862358723588235892359023591235922359323594235952359623597235982359923600236012360223603236042360523606236072360823609236102361123612236132361423615236162361723618236192362023621236222362323624236252362623627236282362923630236312363223633236342363523636236372363823639236402364123642236432364423645236462364723648236492365023651236522365323654236552365623657236582365923660236612366223663236642366523666236672366823669236702367123672236732367423675236762367723678236792368023681236822368323684236852368623687236882368923690236912369223693236942369523696236972369823699237002370123702237032370423705237062370723708237092371023711237122371323714237152371623717237182371923720237212372223723237242372523726237272372823729237302373123732237332373423735237362373723738237392374023741237422374323744237452374623747237482374923750237512375223753237542375523756237572375823759237602376123762237632376423765237662376723768237692377023771237722377323774237752377623777237782377923780237812378223783237842378523786237872378823789237902379123792237932379423795237962379723798237992380023801238022380323804238052380623807238082380923810238112381223813238142381523816238172381823819238202382123822238232382423825238262382723828238292383023831238322383323834238352383623837238382383923840238412384223843238442384523846238472384823849238502385123852238532385423855238562385723858238592386023861238622386323864238652386623867238682386923870238712387223873238742387523876238772387823879238802388123882238832388423885238862388723888238892389023891238922389323894238952389623897238982389923900239012390223903239042390523906239072390823909239102391123912239132391423915239162391723918239192392023921239222392323924239252392623927239282392923930239312393223933239342393523936239372393823939239402394123942239432394423945239462394723948239492395023951239522395323954239552395623957239582395923960239612396223963239642396523966239672396823969239702397123972239732397423975239762397723978239792398023981239822398323984239852398623987239882398923990239912399223993239942399523996239972399823999240002400124002240032400424005240062400724008240092401024011240122401324014240152401624017240182401924020240212402224023240242402524026240272402824029240302403124032240332403424035240362403724038240392404024041240422404324044240452404624047240482404924050240512405224053240542405524056240572405824059240602406124062240632406424065240662406724068240692407024071240722407324074240752407624077240782407924080240812408224083240842408524086240872408824089240902409124092240932409424095240962409724098240992410024101241022410324104241052410624107241082410924110241112411224113241142411524116241172411824119241202412124122241232412424125241262412724128241292413024131241322413324134241352413624137241382413924140241412414224143241442414524146241472414824149241502415124152241532415424155241562415724158241592416024161241622416324164241652416624167241682416924170241712417224173241742417524176241772417824179241802418124182241832418424185241862418724188241892419024191241922419324194241952419624197241982419924200242012420224203242042420524206242072420824209242102421124212242132421424215242162421724218242192422024221242222422324224242252422624227242282422924230242312423224233242342423524236242372423824239242402424124242242432424424245242462424724248242492425024251242522425324254242552425624257242582425924260242612426224263242642426524266242672426824269242702427124272242732427424275242762427724278242792428024281242822428324284242852428624287242882428924290242912429224293242942429524296242972429824299243002430124302243032430424305243062430724308243092431024311243122431324314243152431624317243182431924320243212432224323243242432524326243272432824329243302433124332243332433424335243362433724338243392434024341243422434324344243452434624347243482434924350243512435224353243542435524356243572435824359243602436124362243632436424365243662436724368243692437024371243722437324374243752437624377243782437924380243812438224383243842438524386243872438824389243902439124392243932439424395243962439724398243992440024401244022440324404244052440624407244082440924410244112441224413244142441524416244172441824419244202442124422244232442424425244262442724428244292443024431244322443324434244352443624437244382443924440244412444224443244442444524446244472444824449244502445124452244532445424455244562445724458244592446024461244622446324464244652446624467244682446924470244712447224473244742447524476244772447824479244802448124482244832448424485244862448724488244892449024491244922449324494244952449624497244982449924500245012450224503245042450524506245072450824509245102451124512245132451424515245162451724518245192452024521245222452324524245252452624527245282452924530245312453224533245342453524536245372453824539245402454124542245432454424545245462454724548245492455024551245522455324554245552455624557245582455924560245612456224563245642456524566245672456824569245702457124572245732457424575245762457724578245792458024581245822458324584245852458624587245882458924590245912459224593245942459524596245972459824599246002460124602246032460424605246062460724608246092461024611246122461324614246152461624617246182461924620246212462224623246242462524626246272462824629246302463124632246332463424635246362463724638246392464024641246422464324644246452464624647246482464924650246512465224653246542465524656246572465824659246602466124662246632466424665246662466724668246692467024671246722467324674246752467624677246782467924680246812468224683246842468524686246872468824689246902469124692246932469424695246962469724698246992470024701247022470324704247052470624707247082470924710247112471224713247142471524716247172471824719247202472124722247232472424725247262472724728247292473024731247322473324734247352473624737247382473924740247412474224743247442474524746247472474824749247502475124752247532475424755247562475724758247592476024761247622476324764247652476624767247682476924770247712477224773247742477524776247772477824779247802478124782247832478424785247862478724788247892479024791247922479324794247952479624797247982479924800248012480224803248042480524806248072480824809248102481124812248132481424815248162481724818248192482024821248222482324824248252482624827248282482924830248312483224833248342483524836248372483824839248402484124842248432484424845248462484724848248492485024851248522485324854248552485624857248582485924860248612486224863248642486524866248672486824869248702487124872248732487424875248762487724878248792488024881248822488324884248852488624887248882488924890248912489224893248942489524896248972489824899249002490124902249032490424905249062490724908249092491024911249122491324914249152491624917249182491924920249212492224923249242492524926249272492824929249302493124932249332493424935249362493724938249392494024941249422494324944249452494624947249482494924950249512495224953249542495524956249572495824959249602496124962249632496424965249662496724968249692497024971249722497324974249752497624977249782497924980249812498224983249842498524986249872498824989249902499124992249932499424995249962499724998249992500025001250022500325004250052500625007250082500925010250112501225013250142501525016250172501825019250202502125022250232502425025250262502725028250292503025031250322503325034250352503625037250382503925040250412504225043250442504525046250472504825049250502505125052250532505425055250562505725058250592506025061250622506325064250652506625067250682506925070250712507225073250742507525076250772507825079250802508125082250832508425085250862508725088250892509025091250922509325094250952509625097250982509925100251012510225103251042510525106251072510825109251102511125112251132511425115251162511725118251192512025121251222512325124251252512625127251282512925130251312513225133251342513525136251372513825139251402514125142251432514425145251462514725148251492515025151251522515325154251552515625157251582515925160251612516225163251642516525166251672516825169251702517125172251732517425175251762517725178251792518025181251822518325184251852518625187251882518925190251912519225193251942519525196251972519825199252002520125202252032520425205252062520725208252092521025211252122521325214252152521625217252182521925220252212522225223252242522525226252272522825229252302523125232252332523425235252362523725238252392524025241252422524325244252452524625247252482524925250252512525225253252542525525256252572525825259252602526125262252632526425265252662526725268252692527025271252722527325274252752527625277252782527925280252812528225283252842528525286252872528825289252902529125292252932529425295252962529725298252992530025301253022530325304253052530625307253082530925310253112531225313253142531525316253172531825319253202532125322253232532425325253262532725328253292533025331253322533325334253352533625337253382533925340253412534225343253442534525346253472534825349253502535125352253532535425355253562535725358253592536025361253622536325364253652536625367253682536925370253712537225373253742537525376253772537825379253802538125382253832538425385253862538725388253892539025391253922539325394253952539625397253982539925400254012540225403254042540525406254072540825409254102541125412254132541425415254162541725418254192542025421254222542325424254252542625427254282542925430254312543225433254342543525436254372543825439254402544125442254432544425445254462544725448254492545025451254522545325454254552545625457254582545925460254612546225463254642546525466254672546825469254702547125472254732547425475254762547725478254792548025481254822548325484254852548625487254882548925490254912549225493254942549525496254972549825499255002550125502255032550425505255062550725508255092551025511255122551325514255152551625517255182551925520255212552225523255242552525526255272552825529255302553125532255332553425535255362553725538255392554025541255422554325544255452554625547255482554925550255512555225553255542555525556255572555825559255602556125562255632556425565255662556725568255692557025571255722557325574255752557625577255782557925580255812558225583255842558525586255872558825589255902559125592255932559425595255962559725598255992560025601256022560325604256052560625607256082560925610256112561225613256142561525616256172561825619256202562125622256232562425625256262562725628256292563025631256322563325634256352563625637256382563925640256412564225643256442564525646256472564825649256502565125652256532565425655256562565725658256592566025661256622566325664256652566625667256682566925670256712567225673256742567525676256772567825679256802568125682256832568425685256862568725688256892569025691256922569325694256952569625697256982569925700257012570225703257042570525706257072570825709257102571125712257132571425715257162571725718257192572025721257222572325724257252572625727257282572925730257312573225733257342573525736257372573825739257402574125742257432574425745257462574725748257492575025751257522575325754257552575625757257582575925760257612576225763257642576525766257672576825769257702577125772257732577425775257762577725778257792578025781257822578325784257852578625787257882578925790257912579225793257942579525796257972579825799258002580125802258032580425805258062580725808258092581025811258122581325814258152581625817258182581925820258212582225823258242582525826258272582825829258302583125832258332583425835258362583725838258392584025841258422584325844258452584625847258482584925850258512585225853258542585525856258572585825859258602586125862258632586425865258662586725868258692587025871258722587325874258752587625877258782587925880258812588225883258842588525886258872588825889258902589125892258932589425895258962589725898258992590025901259022590325904259052590625907259082590925910259112591225913259142591525916259172591825919259202592125922259232592425925259262592725928259292593025931259322593325934259352593625937259382593925940259412594225943259442594525946259472594825949259502595125952259532595425955259562595725958259592596025961259622596325964259652596625967259682596925970259712597225973259742597525976259772597825979259802598125982259832598425985259862598725988259892599025991259922599325994259952599625997259982599926000260012600226003260042600526006260072600826009260102601126012260132601426015260162601726018260192602026021260222602326024260252602626027260282602926030260312603226033260342603526036260372603826039260402604126042260432604426045260462604726048260492605026051260522605326054260552605626057260582605926060260612606226063260642606526066260672606826069260702607126072260732607426075260762607726078260792608026081260822608326084260852608626087260882608926090260912609226093260942609526096260972609826099261002610126102261032610426105261062610726108261092611026111261122611326114261152611626117261182611926120261212612226123261242612526126261272612826129261302613126132261332613426135261362613726138261392614026141261422614326144261452614626147261482614926150261512615226153261542615526156261572615826159261602616126162261632616426165261662616726168261692617026171261722617326174261752617626177261782617926180261812618226183261842618526186261872618826189261902619126192261932619426195261962619726198261992620026201262022620326204262052620626207262082620926210262112621226213262142621526216262172621826219262202622126222262232622426225262262622726228262292623026231262322623326234262352623626237262382623926240262412624226243262442624526246262472624826249262502625126252262532625426255262562625726258262592626026261262622626326264262652626626267262682626926270262712627226273262742627526276262772627826279262802628126282262832628426285262862628726288262892629026291262922629326294262952629626297262982629926300263012630226303263042630526306263072630826309263102631126312263132631426315263162631726318263192632026321263222632326324263252632626327263282632926330263312633226333263342633526336263372633826339263402634126342263432634426345263462634726348263492635026351263522635326354263552635626357263582635926360263612636226363263642636526366263672636826369263702637126372263732637426375263762637726378263792638026381263822638326384263852638626387263882638926390263912639226393263942639526396263972639826399264002640126402264032640426405264062640726408264092641026411264122641326414264152641626417264182641926420264212642226423264242642526426264272642826429264302643126432264332643426435264362643726438264392644026441264422644326444264452644626447264482644926450264512645226453264542645526456264572645826459264602646126462264632646426465264662646726468264692647026471264722647326474264752647626477264782647926480264812648226483264842648526486264872648826489264902649126492264932649426495264962649726498264992650026501265022650326504265052650626507265082650926510265112651226513265142651526516265172651826519265202652126522265232652426525265262652726528265292653026531265322653326534265352653626537265382653926540265412654226543265442654526546265472654826549265502655126552265532655426555265562655726558265592656026561265622656326564265652656626567265682656926570265712657226573265742657526576265772657826579265802658126582265832658426585265862658726588265892659026591265922659326594265952659626597265982659926600266012660226603266042660526606266072660826609266102661126612266132661426615266162661726618266192662026621266222662326624266252662626627266282662926630266312663226633266342663526636266372663826639266402664126642266432664426645266462664726648266492665026651266522665326654266552665626657266582665926660266612666226663266642666526666266672666826669266702667126672266732667426675266762667726678266792668026681266822668326684266852668626687266882668926690266912669226693266942669526696266972669826699267002670126702267032670426705267062670726708267092671026711267122671326714267152671626717267182671926720267212672226723267242672526726267272672826729267302673126732267332673426735267362673726738267392674026741267422674326744267452674626747267482674926750267512675226753267542675526756267572675826759267602676126762267632676426765267662676726768267692677026771267722677326774267752677626777267782677926780267812678226783267842678526786267872678826789267902679126792267932679426795267962679726798267992680026801268022680326804268052680626807268082680926810268112681226813268142681526816268172681826819268202682126822268232682426825268262682726828268292683026831268322683326834268352683626837268382683926840268412684226843268442684526846268472684826849268502685126852268532685426855268562685726858268592686026861268622686326864268652686626867268682686926870268712687226873268742687526876268772687826879268802688126882268832688426885268862688726888268892689026891268922689326894268952689626897268982689926900269012690226903269042690526906269072690826909269102691126912269132691426915269162691726918269192692026921269222692326924269252692626927269282692926930269312693226933269342693526936269372693826939269402694126942269432694426945269462694726948269492695026951269522695326954269552695626957269582695926960269612696226963269642696526966269672696826969269702697126972269732697426975269762697726978269792698026981269822698326984269852698626987269882698926990269912699226993269942699526996269972699826999270002700127002270032700427005270062700727008270092701027011270122701327014270152701627017270182701927020270212702227023270242702527026270272702827029270302703127032270332703427035270362703727038270392704027041270422704327044270452704627047270482704927050270512705227053270542705527056270572705827059270602706127062270632706427065270662706727068270692707027071270722707327074270752707627077270782707927080270812708227083270842708527086270872708827089270902709127092270932709427095270962709727098270992710027101271022710327104271052710627107271082710927110271112711227113271142711527116271172711827119271202712127122271232712427125271262712727128271292713027131271322713327134271352713627137271382713927140271412714227143271442714527146271472714827149271502715127152271532715427155271562715727158271592716027161271622716327164271652716627167271682716927170271712717227173271742717527176271772717827179271802718127182271832718427185271862718727188271892719027191271922719327194271952719627197271982719927200272012720227203272042720527206272072720827209272102721127212272132721427215272162721727218272192722027221272222722327224272252722627227272282722927230272312723227233272342723527236272372723827239272402724127242272432724427245272462724727248272492725027251272522725327254272552725627257272582725927260272612726227263272642726527266272672726827269272702727127272272732727427275272762727727278272792728027281272822728327284272852728627287272882728927290272912729227293272942729527296272972729827299273002730127302273032730427305273062730727308273092731027311273122731327314273152731627317273182731927320273212732227323273242732527326273272732827329273302733127332273332733427335273362733727338273392734027341273422734327344273452734627347273482734927350273512735227353273542735527356273572735827359273602736127362273632736427365273662736727368273692737027371273722737327374273752737627377273782737927380273812738227383273842738527386273872738827389273902739127392273932739427395273962739727398273992740027401274022740327404274052740627407274082740927410274112741227413274142741527416274172741827419274202742127422274232742427425274262742727428274292743027431274322743327434274352743627437274382743927440274412744227443274442744527446274472744827449274502745127452274532745427455274562745727458274592746027461274622746327464274652746627467274682746927470274712747227473274742747527476274772747827479274802748127482274832748427485274862748727488274892749027491274922749327494274952749627497274982749927500275012750227503275042750527506275072750827509275102751127512275132751427515275162751727518275192752027521275222752327524275252752627527275282752927530275312753227533275342753527536275372753827539275402754127542275432754427545275462754727548275492755027551275522755327554275552755627557275582755927560275612756227563275642756527566275672756827569275702757127572275732757427575275762757727578275792758027581275822758327584275852758627587275882758927590275912759227593275942759527596275972759827599276002760127602276032760427605276062760727608276092761027611276122761327614276152761627617276182761927620276212762227623276242762527626276272762827629276302763127632276332763427635276362763727638276392764027641276422764327644276452764627647276482764927650276512765227653276542765527656276572765827659276602766127662276632766427665276662766727668276692767027671276722767327674276752767627677276782767927680276812768227683276842768527686276872768827689276902769127692276932769427695276962769727698276992770027701277022770327704277052770627707277082770927710277112771227713277142771527716277172771827719277202772127722277232772427725277262772727728277292773027731277322773327734277352773627737277382773927740277412774227743277442774527746277472774827749277502775127752277532775427755277562775727758277592776027761277622776327764277652776627767277682776927770277712777227773277742777527776277772777827779277802778127782277832778427785277862778727788 |
- /*
- * This combined file was created by the DataTables downloader builder:
- * https://datatables.net/download
- *
- * To rebuild or modify this file with the latest versions of the included
- * software please visit:
- * https://datatables.net/download/#dt/dt-1.10.16/af-2.2.2/b-1.5.1/cr-1.4.1/fc-3.2.4/fh-3.1.3/kt-2.3.2/r-2.2.1/rr-1.2.3/sc-1.4.4/sl-1.2.5
- *
- * Included libraries:
- * DataTables 1.10.16, AutoFill 2.2.2, Buttons 1.5.1, ColReorder 1.4.1, FixedColumns 3.2.4, FixedHeader 3.1.3, KeyTable 2.3.2, Responsive 2.2.1, RowReorder 1.2.3, Scroller 1.4.4, Select 1.2.5
- */
- /*! DataTables 1.10.16
- * ©2008-2017 SpryMedia Ltd - datatables.net/license
- */
- /**
- * @summary DataTables
- * @description Paginate, search and order HTML tables
- * @version 1.10.16
- * @file jquery.dataTables.js
- * @author SpryMedia Ltd
- * @contact www.datatables.net
- * @copyright Copyright 2008-2017 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net
- */
- /*jslint evil: true, undef: true, browser: true */
- /*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/
- (function( factory ) {
- "use strict";
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- // CommonJS environments without a window global must pass a
- // root. This will give an error otherwise
- root = window;
- }
- if ( ! $ ) {
- $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
- require('jquery') :
- require('jquery')( root );
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }
- (function( $, window, document, undefined ) {
- "use strict";
- /**
- * DataTables is a plug-in for the jQuery Javascript library. It is a highly
- * flexible tool, based upon the foundations of progressive enhancement,
- * which will add advanced interaction controls to any HTML table. For a
- * full list of features please refer to
- * [DataTables.net](href="http://datatables.net).
- *
- * Note that the `DataTable` object is not a global variable but is aliased
- * to `jQuery.fn.DataTable` and `jQuery.fn.dataTable` through which it may
- * be accessed.
- *
- * @class
- * @param {object} [init={}] Configuration object for DataTables. Options
- * are defined by {@link DataTable.defaults}
- * @requires jQuery 1.7+
- *
- * @example
- * // Basic initialisation
- * $(document).ready( function {
- * $('#example').dataTable();
- * } );
- *
- * @example
- * // Initialisation with configuration options - in this case, disable
- * // pagination and sorting.
- * $(document).ready( function {
- * $('#example').dataTable( {
- * "paginate": false,
- * "sort": false
- * } );
- * } );
- */
- var DataTable = function ( options )
- {
- /**
- * Perform a jQuery selector action on the table's TR elements (from the tbody) and
- * return the resulting jQuery object.
- * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
- * @param {object} [oOpts] Optional parameters for modifying the rows to be included
- * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter
- * criterion ("applied") or all TR elements (i.e. no filter).
- * @param {string} [oOpts.order=current] Order of the TR elements in the processed array.
- * Can be either 'current', whereby the current sorting of the table is used, or
- * 'original' whereby the original order the data was read into the table is used.
- * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
- * ("current") or not ("all"). If 'current' is given, then order is assumed to be
- * 'current' and filter is 'applied', regardless of what they might be given as.
- * @returns {object} jQuery object, filtered by the given selector.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Highlight every second row
- * oTable.$('tr:odd').css('backgroundColor', 'blue');
- * } );
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Filter to rows with 'Webkit' in them, add a background colour and then
- * // remove the filter, thus highlighting the 'Webkit' rows only.
- * oTable.fnFilter('Webkit');
- * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue');
- * oTable.fnFilter('');
- * } );
- */
- this.$ = function ( sSelector, oOpts )
- {
- return this.api(true).$( sSelector, oOpts );
- };
-
-
- /**
- * Almost identical to $ in operation, but in this case returns the data for the matched
- * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
- * rather than any descendants, so the data can be obtained for the row/cell. If matching
- * rows are found, the data returned is the original data array/object that was used to
- * create the row (or a generated array if from a DOM source).
- *
- * This method is often useful in-combination with $ where both functions are given the
- * same parameters and the array indexes will match identically.
- * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
- * @param {object} [oOpts] Optional parameters for modifying the rows to be included
- * @param {string} [oOpts.filter=none] Select elements that meet the current filter
- * criterion ("applied") or all elements (i.e. no filter).
- * @param {string} [oOpts.order=current] Order of the data in the processed array.
- * Can be either 'current', whereby the current sorting of the table is used, or
- * 'original' whereby the original order the data was read into the table is used.
- * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
- * ("current") or not ("all"). If 'current' is given, then order is assumed to be
- * 'current' and filter is 'applied', regardless of what they might be given as.
- * @returns {array} Data for the matched elements. If any elements, as a result of the
- * selector, were not TR, TD or TH elements in the DataTable, they will have a null
- * entry in the array.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Get the data from the first row in the table
- * var data = oTable._('tr:first');
- *
- * // Do something useful with the data
- * alert( "First cell is: "+data[0] );
- * } );
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Filter to 'Webkit' and get all data for
- * oTable.fnFilter('Webkit');
- * var data = oTable._('tr', {"search": "applied"});
- *
- * // Do something with the data
- * alert( data.length+" rows matched the search" );
- * } );
- */
- this._ = function ( sSelector, oOpts )
- {
- return this.api(true).rows( sSelector, oOpts ).data();
- };
-
-
- /**
- * Create a DataTables Api instance, with the currently selected tables for
- * the Api's context.
- * @param {boolean} [traditional=false] Set the API instance's context to be
- * only the table referred to by the `DataTable.ext.iApiIndex` option, as was
- * used in the API presented by DataTables 1.9- (i.e. the traditional mode),
- * or if all tables captured in the jQuery object should be used.
- * @return {DataTables.Api}
- */
- this.api = function ( traditional )
- {
- return traditional ?
- new _Api(
- _fnSettingsFromNode( this[ _ext.iApiIndex ] )
- ) :
- new _Api( this );
- };
-
-
- /**
- * Add a single new row or multiple rows of data to the table. Please note
- * that this is suitable for client-side processing only - if you are using
- * server-side processing (i.e. "bServerSide": true), then to add data, you
- * must add it to the data source, i.e. the server-side, through an Ajax call.
- * @param {array|object} data The data to be added to the table. This can be:
- * <ul>
- * <li>1D array of data - add a single row with the data provided</li>
- * <li>2D array of arrays - add multiple rows in a single call</li>
- * <li>object - data object when using <i>mData</i></li>
- * <li>array of objects - multiple data objects when using <i>mData</i></li>
- * </ul>
- * @param {bool} [redraw=true] redraw the table or not
- * @returns {array} An array of integers, representing the list of indexes in
- * <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to
- * the table.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * // Global var for counter
- * var giCount = 2;
- *
- * $(document).ready(function() {
- * $('#example').dataTable();
- * } );
- *
- * function fnClickAddRow() {
- * $('#example').dataTable().fnAddData( [
- * giCount+".1",
- * giCount+".2",
- * giCount+".3",
- * giCount+".4" ]
- * );
- *
- * giCount++;
- * }
- */
- this.fnAddData = function( data, redraw )
- {
- var api = this.api( true );
-
- /* Check if we want to add multiple rows or not */
- var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ?
- api.rows.add( data ) :
- api.row.add( data );
-
- if ( redraw === undefined || redraw ) {
- api.draw();
- }
-
- return rows.flatten().toArray();
- };
-
-
- /**
- * This function will make DataTables recalculate the column sizes, based on the data
- * contained in the table and the sizes applied to the columns (in the DOM, CSS or
- * through the sWidth parameter). This can be useful when the width of the table's
- * parent element changes (for example a window resize).
- * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable( {
- * "sScrollY": "200px",
- * "bPaginate": false
- * } );
- *
- * $(window).on('resize', function () {
- * oTable.fnAdjustColumnSizing();
- * } );
- * } );
- */
- this.fnAdjustColumnSizing = function ( bRedraw )
- {
- var api = this.api( true ).columns.adjust();
- var settings = api.settings()[0];
- var scroll = settings.oScroll;
-
- if ( bRedraw === undefined || bRedraw ) {
- api.draw( false );
- }
- else if ( scroll.sX !== "" || scroll.sY !== "" ) {
- /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
- _fnScrollDraw( settings );
- }
- };
-
-
- /**
- * Quickly and simply clear a table
- * @param {bool} [bRedraw=true] redraw the table or not
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)
- * oTable.fnClearTable();
- * } );
- */
- this.fnClearTable = function( bRedraw )
- {
- var api = this.api( true ).clear();
-
- if ( bRedraw === undefined || bRedraw ) {
- api.draw();
- }
- };
-
-
- /**
- * The exact opposite of 'opening' a row, this function will close any rows which
- * are currently 'open'.
- * @param {node} nTr the table row to 'close'
- * @returns {int} 0 on success, or 1 if failed (can't find the row)
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable;
- *
- * // 'open' an information row when a row is clicked on
- * $('#example tbody tr').click( function () {
- * if ( oTable.fnIsOpen(this) ) {
- * oTable.fnClose( this );
- * } else {
- * oTable.fnOpen( this, "Temporary row opened", "info_row" );
- * }
- * } );
- *
- * oTable = $('#example').dataTable();
- * } );
- */
- this.fnClose = function( nTr )
- {
- this.api( true ).row( nTr ).child.hide();
- };
-
-
- /**
- * Remove a row for the table
- * @param {mixed} target The index of the row from aoData to be deleted, or
- * the TR element you want to delete
- * @param {function|null} [callBack] Callback function
- * @param {bool} [redraw=true] Redraw the table or not
- * @returns {array} The row that was deleted
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Immediately remove the first row
- * oTable.fnDeleteRow( 0 );
- * } );
- */
- this.fnDeleteRow = function( target, callback, redraw )
- {
- var api = this.api( true );
- var rows = api.rows( target );
- var settings = rows.settings()[0];
- var data = settings.aoData[ rows[0][0] ];
-
- rows.remove();
-
- if ( callback ) {
- callback.call( this, settings, data );
- }
-
- if ( redraw === undefined || redraw ) {
- api.draw();
- }
-
- return data;
- };
-
-
- /**
- * Restore the table to it's original state in the DOM by removing all of DataTables
- * enhancements, alterations to the DOM structure of the table and event listeners.
- * @param {boolean} [remove=false] Completely remove the table from the DOM
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * // This example is fairly pointless in reality, but shows how fnDestroy can be used
- * var oTable = $('#example').dataTable();
- * oTable.fnDestroy();
- * } );
- */
- this.fnDestroy = function ( remove )
- {
- this.api( true ).destroy( remove );
- };
-
-
- /**
- * Redraw the table
- * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Re-draw the table - you wouldn't want to do it here, but it's an example :-)
- * oTable.fnDraw();
- * } );
- */
- this.fnDraw = function( complete )
- {
- // Note that this isn't an exact match to the old call to _fnDraw - it takes
- // into account the new data, but can hold position.
- this.api( true ).draw( complete );
- };
-
-
- /**
- * Filter the input based on data
- * @param {string} sInput String to filter the table on
- * @param {int|null} [iColumn] Column to limit filtering to
- * @param {bool} [bRegex=false] Treat as regular expression or not
- * @param {bool} [bSmart=true] Perform smart filtering or not
- * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)
- * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Sometime later - filter...
- * oTable.fnFilter( 'test string' );
- * } );
- */
- this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
- {
- var api = this.api( true );
-
- if ( iColumn === null || iColumn === undefined ) {
- api.search( sInput, bRegex, bSmart, bCaseInsensitive );
- }
- else {
- api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );
- }
-
- api.draw();
- };
-
-
- /**
- * Get the data for the whole table, an individual row or an individual cell based on the
- * provided parameters.
- * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as
- * a TR node then the data source for the whole row will be returned. If given as a
- * TD/TH cell node then iCol will be automatically calculated and the data for the
- * cell returned. If given as an integer, then this is treated as the aoData internal
- * data index for the row (see fnGetPosition) and the data for that row used.
- * @param {int} [col] Optional column index that you want the data of.
- * @returns {array|object|string} If mRow is undefined, then the data for all rows is
- * returned. If mRow is defined, just data for that row, and is iCol is
- * defined, only data for the designated cell is returned.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * // Row data
- * $(document).ready(function() {
- * oTable = $('#example').dataTable();
- *
- * oTable.$('tr').click( function () {
- * var data = oTable.fnGetData( this );
- * // ... do something with the array / object of data for the row
- * } );
- * } );
- *
- * @example
- * // Individual cell data
- * $(document).ready(function() {
- * oTable = $('#example').dataTable();
- *
- * oTable.$('td').click( function () {
- * var sData = oTable.fnGetData( this );
- * alert( 'The cell clicked on had the value of '+sData );
- * } );
- * } );
- */
- this.fnGetData = function( src, col )
- {
- var api = this.api( true );
-
- if ( src !== undefined ) {
- var type = src.nodeName ? src.nodeName.toLowerCase() : '';
-
- return col !== undefined || type == 'td' || type == 'th' ?
- api.cell( src, col ).data() :
- api.row( src ).data() || null;
- }
-
- return api.data().toArray();
- };
-
-
- /**
- * Get an array of the TR nodes that are used in the table's body. Note that you will
- * typically want to use the '$' API method in preference to this as it is more
- * flexible.
- * @param {int} [iRow] Optional row index for the TR element you want
- * @returns {array|node} If iRow is undefined, returns an array of all TR elements
- * in the table's body, or iRow is defined, just the TR element requested.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Get the nodes from the table
- * var nNodes = oTable.fnGetNodes( );
- * } );
- */
- this.fnGetNodes = function( iRow )
- {
- var api = this.api( true );
-
- return iRow !== undefined ?
- api.row( iRow ).node() :
- api.rows().nodes().flatten().toArray();
- };
-
-
- /**
- * Get the array indexes of a particular cell from it's DOM element
- * and column index including hidden columns
- * @param {node} node this can either be a TR, TD or TH in the table's body
- * @returns {int} If nNode is given as a TR, then a single index is returned, or
- * if given as a cell, an array of [row index, column index (visible),
- * column index (all)] is given.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * $('#example tbody td').click( function () {
- * // Get the position of the current data from the node
- * var aPos = oTable.fnGetPosition( this );
- *
- * // Get the data array for this row
- * var aData = oTable.fnGetData( aPos[0] );
- *
- * // Update the data array and return the value
- * aData[ aPos[1] ] = 'clicked';
- * this.innerHTML = 'clicked';
- * } );
- *
- * // Init DataTables
- * oTable = $('#example').dataTable();
- * } );
- */
- this.fnGetPosition = function( node )
- {
- var api = this.api( true );
- var nodeName = node.nodeName.toUpperCase();
-
- if ( nodeName == 'TR' ) {
- return api.row( node ).index();
- }
- else if ( nodeName == 'TD' || nodeName == 'TH' ) {
- var cell = api.cell( node ).index();
-
- return [
- cell.row,
- cell.columnVisible,
- cell.column
- ];
- }
- return null;
- };
-
-
- /**
- * Check to see if a row is 'open' or not.
- * @param {node} nTr the table row to check
- * @returns {boolean} true if the row is currently open, false otherwise
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable;
- *
- * // 'open' an information row when a row is clicked on
- * $('#example tbody tr').click( function () {
- * if ( oTable.fnIsOpen(this) ) {
- * oTable.fnClose( this );
- * } else {
- * oTable.fnOpen( this, "Temporary row opened", "info_row" );
- * }
- * } );
- *
- * oTable = $('#example').dataTable();
- * } );
- */
- this.fnIsOpen = function( nTr )
- {
- return this.api( true ).row( nTr ).child.isShown();
- };
-
-
- /**
- * This function will place a new row directly after a row which is currently
- * on display on the page, with the HTML contents that is passed into the
- * function. This can be used, for example, to ask for confirmation that a
- * particular record should be deleted.
- * @param {node} nTr The table row to 'open'
- * @param {string|node|jQuery} mHtml The HTML to put into the row
- * @param {string} sClass Class to give the new TD cell
- * @returns {node} The row opened. Note that if the table row passed in as the
- * first parameter, is not found in the table, this method will silently
- * return.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable;
- *
- * // 'open' an information row when a row is clicked on
- * $('#example tbody tr').click( function () {
- * if ( oTable.fnIsOpen(this) ) {
- * oTable.fnClose( this );
- * } else {
- * oTable.fnOpen( this, "Temporary row opened", "info_row" );
- * }
- * } );
- *
- * oTable = $('#example').dataTable();
- * } );
- */
- this.fnOpen = function( nTr, mHtml, sClass )
- {
- return this.api( true )
- .row( nTr )
- .child( mHtml, sClass )
- .show()
- .child()[0];
- };
-
-
- /**
- * Change the pagination - provides the internal logic for pagination in a simple API
- * function. With this function you can have a DataTables table go to the next,
- * previous, first or last pages.
- * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
- * or page number to jump to (integer), note that page 0 is the first page.
- * @param {bool} [bRedraw=true] Redraw the table or not
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- * oTable.fnPageChange( 'next' );
- * } );
- */
- this.fnPageChange = function ( mAction, bRedraw )
- {
- var api = this.api( true ).page( mAction );
-
- if ( bRedraw === undefined || bRedraw ) {
- api.draw(false);
- }
- };
-
-
- /**
- * Show a particular column
- * @param {int} iCol The column whose display should be changed
- * @param {bool} bShow Show (true) or hide (false) the column
- * @param {bool} [bRedraw=true] Redraw the table or not
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Hide the second column after initialisation
- * oTable.fnSetColumnVis( 1, false );
- * } );
- */
- this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
- {
- var api = this.api( true ).column( iCol ).visible( bShow );
-
- if ( bRedraw === undefined || bRedraw ) {
- api.columns.adjust().draw();
- }
- };
-
-
- /**
- * Get the settings for a particular table for external manipulation
- * @returns {object} DataTables settings object. See
- * {@link DataTable.models.oSettings}
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- * var oSettings = oTable.fnSettings();
- *
- * // Show an example parameter from the settings
- * alert( oSettings._iDisplayStart );
- * } );
- */
- this.fnSettings = function()
- {
- return _fnSettingsFromNode( this[_ext.iApiIndex] );
- };
-
-
- /**
- * Sort the table by a particular column
- * @param {int} iCol the data index to sort on. Note that this will not match the
- * 'display index' if you have hidden data entries
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Sort immediately with columns 0 and 1
- * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );
- * } );
- */
- this.fnSort = function( aaSort )
- {
- this.api( true ).order( aaSort ).draw();
- };
-
-
- /**
- * Attach a sort listener to an element for a given column
- * @param {node} nNode the element to attach the sort listener to
- * @param {int} iColumn the column that a click on this node will sort on
- * @param {function} [fnCallback] callback function when sort is run
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Sort on column 1, when 'sorter' is clicked on
- * oTable.fnSortListener( document.getElementById('sorter'), 1 );
- * } );
- */
- this.fnSortListener = function( nNode, iColumn, fnCallback )
- {
- this.api( true ).order.listener( nNode, iColumn, fnCallback );
- };
-
-
- /**
- * Update a table cell or row - this method will accept either a single value to
- * update the cell with, an array of values with one element for each column or
- * an object in the same format as the original data source. The function is
- * self-referencing in order to make the multi column updates easier.
- * @param {object|array|string} mData Data to update the cell/row with
- * @param {node|int} mRow TR element you want to update or the aoData index
- * @param {int} [iColumn] The column to update, give as null or undefined to
- * update a whole row.
- * @param {bool} [bRedraw=true] Redraw the table or not
- * @param {bool} [bAction=true] Perform pre-draw actions or not
- * @returns {int} 0 on success, 1 on error
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell
- * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row
- * } );
- */
- this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
- {
- var api = this.api( true );
-
- if ( iColumn === undefined || iColumn === null ) {
- api.row( mRow ).data( mData );
- }
- else {
- api.cell( mRow, iColumn ).data( mData );
- }
-
- if ( bAction === undefined || bAction ) {
- api.columns.adjust();
- }
-
- if ( bRedraw === undefined || bRedraw ) {
- api.draw();
- }
- return 0;
- };
-
-
- /**
- * Provide a common method for plug-ins to check the version of DataTables being used, in order
- * to ensure compatibility.
- * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
- * formats "X" and "X.Y" are also acceptable.
- * @returns {boolean} true if this version of DataTables is greater or equal to the required
- * version, or false if this version of DataTales is not suitable
- * @method
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- * alert( oTable.fnVersionCheck( '1.9.0' ) );
- * } );
- */
- this.fnVersionCheck = _ext.fnVersionCheck;
-
- var _that = this;
- var emptyInit = options === undefined;
- var len = this.length;
- if ( emptyInit ) {
- options = {};
- }
- this.oApi = this.internal = _ext.internal;
- // Extend with old style plug-in API methods
- for ( var fn in DataTable.ext.internal ) {
- if ( fn ) {
- this[fn] = _fnExternApiFunc(fn);
- }
- }
- this.each(function() {
- // For each initialisation we want to give it a clean initialisation
- // object that can be bashed around
- var o = {};
- var oInit = len > 1 ? // optimisation for single table case
- _fnExtend( o, options, true ) :
- options;
- /*global oInit,_that,emptyInit*/
- var i=0, iLen, j, jLen, k, kLen;
- var sId = this.getAttribute( 'id' );
- var bInitHandedOff = false;
- var defaults = DataTable.defaults;
- var $this = $(this);
-
-
- /* Sanity check */
- if ( this.nodeName.toLowerCase() != 'table' )
- {
- _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
- return;
- }
-
- /* Backwards compatibility for the defaults */
- _fnCompatOpts( defaults );
- _fnCompatCols( defaults.column );
-
- /* Convert the camel-case defaults to Hungarian */
- _fnCamelToHungarian( defaults, defaults, true );
- _fnCamelToHungarian( defaults.column, defaults.column, true );
-
- /* Setting up the initialisation object */
- _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ) );
-
-
-
- /* Check to see if we are re-initialising a table */
- var allSettings = DataTable.settings;
- for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
- {
- var s = allSettings[i];
-
- /* Base check on table node */
- if ( s.nTable == this || s.nTHead.parentNode == this || (s.nTFoot && s.nTFoot.parentNode == this) )
- {
- var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
- var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
-
- if ( emptyInit || bRetrieve )
- {
- return s.oInstance;
- }
- else if ( bDestroy )
- {
- s.oInstance.fnDestroy();
- break;
- }
- else
- {
- _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 );
- return;
- }
- }
-
- /* If the element we are initialising has the same ID as a table which was previously
- * initialised, but the table nodes don't match (from before) then we destroy the old
- * instance by simply deleting it. This is under the assumption that the table has been
- * destroyed by other methods. Anyone using non-id selectors will need to do this manually
- */
- if ( s.sTableId == this.id )
- {
- allSettings.splice( i, 1 );
- break;
- }
- }
-
- /* Ensure the table has an ID - required for accessibility */
- if ( sId === null || sId === "" )
- {
- sId = "DataTables_Table_"+(DataTable.ext._unique++);
- this.id = sId;
- }
-
- /* Create the settings object for this table and set some of the default parameters */
- var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
- "sDestroyWidth": $this[0].style.width,
- "sInstance": sId,
- "sTableId": sId
- } );
- oSettings.nTable = this;
- oSettings.oApi = _that.internal;
- oSettings.oInit = oInit;
-
- allSettings.push( oSettings );
-
- // Need to add the instance after the instance after the settings object has been added
- // to the settings array, so we can self reference the table instance if more than one
- oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();
-
- // Backwards compatibility, before we apply all the defaults
- _fnCompatOpts( oInit );
-
- if ( oInit.oLanguage )
- {
- _fnLanguageCompat( oInit.oLanguage );
- }
-
- // If the length menu is given, but the init display length is not, use the length menu
- if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
- {
- oInit.iDisplayLength = $.isArray( oInit.aLengthMenu[0] ) ?
- oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
- }
-
- // Apply the defaults and init options to make a single init object will all
- // options defined from defaults and instance options.
- oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
-
-
- // Map the initialisation options onto the settings object
- _fnMap( oSettings.oFeatures, oInit, [
- "bPaginate",
- "bLengthChange",
- "bFilter",
- "bSort",
- "bSortMulti",
- "bInfo",
- "bProcessing",
- "bAutoWidth",
- "bSortClasses",
- "bServerSide",
- "bDeferRender"
- ] );
- _fnMap( oSettings, oInit, [
- "asStripeClasses",
- "ajax",
- "fnServerData",
- "fnFormatNumber",
- "sServerMethod",
- "aaSorting",
- "aaSortingFixed",
- "aLengthMenu",
- "sPaginationType",
- "sAjaxSource",
- "sAjaxDataProp",
- "iStateDuration",
- "sDom",
- "bSortCellsTop",
- "iTabIndex",
- "fnStateLoadCallback",
- "fnStateSaveCallback",
- "renderer",
- "searchDelay",
- "rowId",
- [ "iCookieDuration", "iStateDuration" ], // backwards compat
- [ "oSearch", "oPreviousSearch" ],
- [ "aoSearchCols", "aoPreSearchCols" ],
- [ "iDisplayLength", "_iDisplayLength" ]
- ] );
- _fnMap( oSettings.oScroll, oInit, [
- [ "sScrollX", "sX" ],
- [ "sScrollXInner", "sXInner" ],
- [ "sScrollY", "sY" ],
- [ "bScrollCollapse", "bCollapse" ]
- ] );
- _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
-
- /* Callback functions which are array driven */
- _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' );
- _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' );
- _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user' );
- _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user' );
- _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user' );
- _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user' );
- _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user' );
- _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user' );
- _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' );
- _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' );
- _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' );
-
- oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
-
- /* Browser support detection */
- _fnBrowserDetect( oSettings );
-
- var oClasses = oSettings.oClasses;
-
- $.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
- $this.addClass( oClasses.sTable );
-
-
- if ( oSettings.iInitDisplayStart === undefined )
- {
- /* Display start point, taking into account the save saving */
- oSettings.iInitDisplayStart = oInit.iDisplayStart;
- oSettings._iDisplayStart = oInit.iDisplayStart;
- }
-
- if ( oInit.iDeferLoading !== null )
- {
- oSettings.bDeferLoading = true;
- var tmp = $.isArray( oInit.iDeferLoading );
- oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
- oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
- }
-
- /* Language definitions */
- var oLanguage = oSettings.oLanguage;
- $.extend( true, oLanguage, oInit.oLanguage );
-
- if ( oLanguage.sUrl )
- {
- /* Get the language definitions from a file - because this Ajax call makes the language
- * get async to the remainder of this function we use bInitHandedOff to indicate that
- * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
- */
- $.ajax( {
- dataType: 'json',
- url: oLanguage.sUrl,
- success: function ( json ) {
- _fnLanguageCompat( json );
- _fnCamelToHungarian( defaults.oLanguage, json );
- $.extend( true, oLanguage, json );
- _fnInitialise( oSettings );
- },
- error: function () {
- // Error occurred loading language file, continue on as best we can
- _fnInitialise( oSettings );
- }
- } );
- bInitHandedOff = true;
- }
-
- /*
- * Stripes
- */
- if ( oInit.asStripeClasses === null )
- {
- oSettings.asStripeClasses =[
- oClasses.sStripeOdd,
- oClasses.sStripeEven
- ];
- }
-
- /* Remove row stripe classes if they are already on the table row */
- var stripeClasses = oSettings.asStripeClasses;
- var rowOne = $this.children('tbody').find('tr').eq(0);
- if ( $.inArray( true, $.map( stripeClasses, function(el, i) {
- return rowOne.hasClass(el);
- } ) ) !== -1 ) {
- $('tbody tr', this).removeClass( stripeClasses.join(' ') );
- oSettings.asDestroyStripes = stripeClasses.slice();
- }
-
- /*
- * Columns
- * See if we should load columns automatically or use defined ones
- */
- var anThs = [];
- var aoColumnsInit;
- var nThead = this.getElementsByTagName('thead');
- if ( nThead.length !== 0 )
- {
- _fnDetectHeader( oSettings.aoHeader, nThead[0] );
- anThs = _fnGetUniqueThs( oSettings );
- }
-
- /* If not given a column array, generate one with nulls */
- if ( oInit.aoColumns === null )
- {
- aoColumnsInit = [];
- for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
- {
- aoColumnsInit.push( null );
- }
- }
- else
- {
- aoColumnsInit = oInit.aoColumns;
- }
-
- /* Add the columns */
- for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
- {
- _fnAddColumn( oSettings, anThs ? anThs[i] : null );
- }
-
- /* Apply the column definitions */
- _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
- _fnColumnOptions( oSettings, iCol, oDef );
- } );
-
- /* HTML5 attribute detection - build an mData object automatically if the
- * attributes are found
- */
- if ( rowOne.length ) {
- var a = function ( cell, name ) {
- return cell.getAttribute( 'data-'+name ) !== null ? name : null;
- };
-
- $( rowOne[0] ).children('th, td').each( function (i, cell) {
- var col = oSettings.aoColumns[i];
-
- if ( col.mData === i ) {
- var sort = a( cell, 'sort' ) || a( cell, 'order' );
- var filter = a( cell, 'filter' ) || a( cell, 'search' );
-
- if ( sort !== null || filter !== null ) {
- col.mData = {
- _: i+'.display',
- sort: sort !== null ? i+'.@data-'+sort : undefined,
- type: sort !== null ? i+'.@data-'+sort : undefined,
- filter: filter !== null ? i+'.@data-'+filter : undefined
- };
-
- _fnColumnOptions( oSettings, i );
- }
- }
- } );
- }
-
- var features = oSettings.oFeatures;
- var loadedInit = function () {
- /*
- * Sorting
- * @todo For modularisation (1.11) this needs to do into a sort start up handler
- */
-
- // If aaSorting is not defined, then we use the first indicator in asSorting
- // in case that has been altered, so the default sort reflects that option
- if ( oInit.aaSorting === undefined ) {
- var sorting = oSettings.aaSorting;
- for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) {
- sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
- }
- }
-
- /* Do a first pass on the sorting classes (allows any size changes to be taken into
- * account, and also will apply sorting disabled classes if disabled
- */
- _fnSortingClasses( oSettings );
-
- if ( features.bSort ) {
- _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
- if ( oSettings.bSorted ) {
- var aSort = _fnSortFlatten( oSettings );
- var sortedColumns = {};
-
- $.each( aSort, function (i, val) {
- sortedColumns[ val.src ] = val.dir;
- } );
-
- _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );
- _fnSortAria( oSettings );
- }
- } );
- }
-
- _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
- if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
- _fnSortingClasses( oSettings );
- }
- }, 'sc' );
-
-
- /*
- * Final init
- * Cache the header, body and footer as required, creating them if needed
- */
-
- // Work around for Webkit bug 83867 - store the caption-side before removing from doc
- var captions = $this.children('caption').each( function () {
- this._captionSide = $(this).css('caption-side');
- } );
-
- var thead = $this.children('thead');
- if ( thead.length === 0 ) {
- thead = $('<thead/>').appendTo($this);
- }
- oSettings.nTHead = thead[0];
-
- var tbody = $this.children('tbody');
- if ( tbody.length === 0 ) {
- tbody = $('<tbody/>').appendTo($this);
- }
- oSettings.nTBody = tbody[0];
-
- var tfoot = $this.children('tfoot');
- if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) {
- // If we are a scrolling table, and no footer has been given, then we need to create
- // a tfoot element for the caption element to be appended to
- tfoot = $('<tfoot/>').appendTo($this);
- }
-
- if ( tfoot.length === 0 || tfoot.children().length === 0 ) {
- $this.addClass( oClasses.sNoFooter );
- }
- else if ( tfoot.length > 0 ) {
- oSettings.nTFoot = tfoot[0];
- _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
- }
-
- /* Check if there is data passing into the constructor */
- if ( oInit.aaData ) {
- for ( i=0 ; i<oInit.aaData.length ; i++ ) {
- _fnAddData( oSettings, oInit.aaData[ i ] );
- }
- }
- else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' ) {
- /* Grab the data from the page - only do this when deferred loading or no Ajax
- * source since there is no point in reading the DOM data if we are then going
- * to replace it with Ajax data
- */
- _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
- }
-
- /* Copy the data index array */
- oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
-
- /* Initialisation complete - table can be drawn */
- oSettings.bInitialised = true;
-
- /* Check if we need to initialise the table (it might not have been handed off to the
- * language processor)
- */
- if ( bInitHandedOff === false ) {
- _fnInitialise( oSettings );
- }
- };
-
- /* Must be done after everything which can be overridden by the state saving! */
- if ( oInit.bStateSave )
- {
- features.bStateSave = true;
- _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
- _fnLoadState( oSettings, oInit, loadedInit );
- }
- else {
- loadedInit();
- }
-
- } );
- _that = null;
- return this;
- };
-
- /*
- * It is useful to have variables which are scoped locally so only the
- * DataTables functions can access them and they don't leak into global space.
- * At the same time these functions are often useful over multiple files in the
- * core and API, so we list, or at least document, all variables which are used
- * by DataTables as private variables here. This also ensures that there is no
- * clashing of variable names and that they can easily referenced for reuse.
- */
-
-
- // Defined else where
- // _selector_run
- // _selector_opts
- // _selector_first
- // _selector_row_indexes
-
- var _ext; // DataTable.ext
- var _Api; // DataTable.Api
- var _api_register; // DataTable.Api.register
- var _api_registerPlural; // DataTable.Api.registerPlural
-
- var _re_dic = {};
- var _re_new_lines = /[\r\n]/g;
- var _re_html = /<.*?>/g;
-
- // This is not strict ISO8601 - Date.parse() is quite lax, although
- // implementations differ between browsers.
- var _re_date = /^\d{2,4}[\.\/\-]\d{1,2}[\.\/\-]\d{1,2}([T ]{1}\d{1,2}[:\.]\d{2}([\.:]\d{2})?)?$/;
-
- // Escape regular expression special characters
- var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
-
- // http://en.wikipedia.org/wiki/Foreign_exchange_market
- // - \u20BD - Russian ruble.
- // - \u20a9 - South Korean Won
- // - \u20BA - Turkish Lira
- // - \u20B9 - Indian Rupee
- // - R - Brazil (R$) and South Africa
- // - fr - Swiss Franc
- // - kr - Swedish krona, Norwegian krone and Danish krone
- // - \u2009 is thin space and \u202F is narrow no-break space, both used in many
- // standards as thousands separators.
- var _re_formatted_numeric = /[',$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfk]/gi;
-
-
- var _empty = function ( d ) {
- return !d || d === true || d === '-' ? true : false;
- };
-
-
- var _intVal = function ( s ) {
- var integer = parseInt( s, 10 );
- return !isNaN(integer) && isFinite(s) ? integer : null;
- };
-
- // Convert from a formatted number with characters other than `.` as the
- // decimal place, to a Javascript number
- var _numToDecimal = function ( num, decimalPoint ) {
- // Cache created regular expressions for speed as this function is called often
- if ( ! _re_dic[ decimalPoint ] ) {
- _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' );
- }
- return typeof num === 'string' && decimalPoint !== '.' ?
- num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :
- num;
- };
-
-
- var _isNumber = function ( d, decimalPoint, formatted ) {
- var strType = typeof d === 'string';
-
- // If empty return immediately so there must be a number if it is a
- // formatted string (this stops the string "k", or "kr", etc being detected
- // as a formatted number for currency
- if ( _empty( d ) ) {
- return true;
- }
-
- if ( decimalPoint && strType ) {
- d = _numToDecimal( d, decimalPoint );
- }
-
- if ( formatted && strType ) {
- d = d.replace( _re_formatted_numeric, '' );
- }
-
- return !isNaN( parseFloat(d) ) && isFinite( d );
- };
-
-
- // A string without HTML in it can be considered to be HTML still
- var _isHtml = function ( d ) {
- return _empty( d ) || typeof d === 'string';
- };
-
-
- var _htmlNumeric = function ( d, decimalPoint, formatted ) {
- if ( _empty( d ) ) {
- return true;
- }
-
- var html = _isHtml( d );
- return ! html ?
- null :
- _isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
- true :
- null;
- };
-
-
- var _pluck = function ( a, prop, prop2 ) {
- var out = [];
- var i=0, ien=a.length;
-
- // Could have the test in the loop for slightly smaller code, but speed
- // is essential here
- if ( prop2 !== undefined ) {
- for ( ; i<ien ; i++ ) {
- if ( a[i] && a[i][ prop ] ) {
- out.push( a[i][ prop ][ prop2 ] );
- }
- }
- }
- else {
- for ( ; i<ien ; i++ ) {
- if ( a[i] ) {
- out.push( a[i][ prop ] );
- }
- }
- }
-
- return out;
- };
-
-
- // Basically the same as _pluck, but rather than looping over `a` we use `order`
- // as the indexes to pick from `a`
- var _pluck_order = function ( a, order, prop, prop2 )
- {
- var out = [];
- var i=0, ien=order.length;
-
- // Could have the test in the loop for slightly smaller code, but speed
- // is essential here
- if ( prop2 !== undefined ) {
- for ( ; i<ien ; i++ ) {
- if ( a[ order[i] ][ prop ] ) {
- out.push( a[ order[i] ][ prop ][ prop2 ] );
- }
- }
- }
- else {
- for ( ; i<ien ; i++ ) {
- out.push( a[ order[i] ][ prop ] );
- }
- }
-
- return out;
- };
-
-
- var _range = function ( len, start )
- {
- var out = [];
- var end;
-
- if ( start === undefined ) {
- start = 0;
- end = len;
- }
- else {
- end = start;
- start = len;
- }
-
- for ( var i=start ; i<end ; i++ ) {
- out.push( i );
- }
-
- return out;
- };
-
-
- var _removeEmpty = function ( a )
- {
- var out = [];
-
- for ( var i=0, ien=a.length ; i<ien ; i++ ) {
- if ( a[i] ) { // careful - will remove all falsy values!
- out.push( a[i] );
- }
- }
-
- return out;
- };
-
-
- var _stripHtml = function ( d ) {
- return d.replace( _re_html, '' );
- };
-
-
- /**
- * Determine if all values in the array are unique. This means we can short
- * cut the _unique method at the cost of a single loop. A sorted array is used
- * to easily check the values.
- *
- * @param {array} src Source array
- * @return {boolean} true if all unique, false otherwise
- * @ignore
- */
- var _areAllUnique = function ( src ) {
- if ( src.length < 2 ) {
- return true;
- }
-
- var sorted = src.slice().sort();
- var last = sorted[0];
-
- for ( var i=1, ien=sorted.length ; i<ien ; i++ ) {
- if ( sorted[i] === last ) {
- return false;
- }
-
- last = sorted[i];
- }
-
- return true;
- };
-
-
- /**
- * Find the unique elements in a source array.
- *
- * @param {array} src Source array
- * @return {array} Array of unique items
- * @ignore
- */
- var _unique = function ( src )
- {
- if ( _areAllUnique( src ) ) {
- return src.slice();
- }
-
- // A faster unique method is to use object keys to identify used values,
- // but this doesn't work with arrays or objects, which we must also
- // consider. See jsperf.com/compare-array-unique-versions/4 for more
- // information.
- var
- out = [],
- val,
- i, ien=src.length,
- j, k=0;
-
- again: for ( i=0 ; i<ien ; i++ ) {
- val = src[i];
-
- for ( j=0 ; j<k ; j++ ) {
- if ( out[j] === val ) {
- continue again;
- }
- }
-
- out.push( val );
- k++;
- }
-
- return out;
- };
-
-
- /**
- * DataTables utility methods
- *
- * This namespace provides helper methods that DataTables uses internally to
- * create a DataTable, but which are not exclusively used only for DataTables.
- * These methods can be used by extension authors to save the duplication of
- * code.
- *
- * @namespace
- */
- DataTable.util = {
- /**
- * Throttle the calls to a function. Arguments and context are maintained
- * for the throttled function.
- *
- * @param {function} fn Function to be called
- * @param {integer} freq Call frequency in mS
- * @return {function} Wrapped function
- */
- throttle: function ( fn, freq ) {
- var
- frequency = freq !== undefined ? freq : 200,
- last,
- timer;
-
- return function () {
- var
- that = this,
- now = +new Date(),
- args = arguments;
-
- if ( last && now < last + frequency ) {
- clearTimeout( timer );
-
- timer = setTimeout( function () {
- last = undefined;
- fn.apply( that, args );
- }, frequency );
- }
- else {
- last = now;
- fn.apply( that, args );
- }
- };
- },
-
-
- /**
- * Escape a string such that it can be used in a regular expression
- *
- * @param {string} val string to escape
- * @returns {string} escaped string
- */
- escapeRegex: function ( val ) {
- return val.replace( _re_escape_regex, '\\$1' );
- }
- };
-
-
-
- /**
- * Create a mapping object that allows camel case parameters to be looked up
- * for their Hungarian counterparts. The mapping is stored in a private
- * parameter called `_hungarianMap` which can be accessed on the source object.
- * @param {object} o
- * @memberof DataTable#oApi
- */
- function _fnHungarianMap ( o )
- {
- var
- hungarian = 'a aa ai ao as b fn i m o s ',
- match,
- newKey,
- map = {};
-
- $.each( o, function (key, val) {
- match = key.match(/^([^A-Z]+?)([A-Z])/);
-
- if ( match && hungarian.indexOf(match[1]+' ') !== -1 )
- {
- newKey = key.replace( match[0], match[2].toLowerCase() );
- map[ newKey ] = key;
-
- if ( match[1] === 'o' )
- {
- _fnHungarianMap( o[key] );
- }
- }
- } );
-
- o._hungarianMap = map;
- }
-
-
- /**
- * Convert from camel case parameters to Hungarian, based on a Hungarian map
- * created by _fnHungarianMap.
- * @param {object} src The model object which holds all parameters that can be
- * mapped.
- * @param {object} user The object to convert from camel case to Hungarian.
- * @param {boolean} force When set to `true`, properties which already have a
- * Hungarian value in the `user` object will be overwritten. Otherwise they
- * won't be.
- * @memberof DataTable#oApi
- */
- function _fnCamelToHungarian ( src, user, force )
- {
- if ( ! src._hungarianMap ) {
- _fnHungarianMap( src );
- }
-
- var hungarianKey;
-
- $.each( user, function (key, val) {
- hungarianKey = src._hungarianMap[ key ];
-
- if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )
- {
- // For objects, we need to buzz down into the object to copy parameters
- if ( hungarianKey.charAt(0) === 'o' )
- {
- // Copy the camelCase options over to the hungarian
- if ( ! user[ hungarianKey ] ) {
- user[ hungarianKey ] = {};
- }
- $.extend( true, user[hungarianKey], user[key] );
-
- _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );
- }
- else {
- user[hungarianKey] = user[ key ];
- }
- }
- } );
- }
-
-
- /**
- * Language compatibility - when certain options are given, and others aren't, we
- * need to duplicate the values over, in order to provide backwards compatibility
- * with older language files.
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnLanguageCompat( lang )
- {
- var defaults = DataTable.defaults.oLanguage;
- var zeroRecords = lang.sZeroRecords;
-
- /* Backwards compatibility - if there is no sEmptyTable given, then use the same as
- * sZeroRecords - assuming that is given.
- */
- if ( ! lang.sEmptyTable && zeroRecords &&
- defaults.sEmptyTable === "No data available in table" )
- {
- _fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' );
- }
-
- /* Likewise with loading records */
- if ( ! lang.sLoadingRecords && zeroRecords &&
- defaults.sLoadingRecords === "Loading..." )
- {
- _fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' );
- }
-
- // Old parameter name of the thousands separator mapped onto the new
- if ( lang.sInfoThousands ) {
- lang.sThousands = lang.sInfoThousands;
- }
-
- var decimal = lang.sDecimal;
- if ( decimal ) {
- _addNumericSort( decimal );
- }
- }
-
-
- /**
- * Map one parameter onto another
- * @param {object} o Object to map
- * @param {*} knew The new parameter name
- * @param {*} old The old parameter name
- */
- var _fnCompatMap = function ( o, knew, old ) {
- if ( o[ knew ] !== undefined ) {
- o[ old ] = o[ knew ];
- }
- };
-
-
- /**
- * Provide backwards compatibility for the main DT options. Note that the new
- * options are mapped onto the old parameters, so this is an external interface
- * change only.
- * @param {object} init Object to map
- */
- function _fnCompatOpts ( init )
- {
- _fnCompatMap( init, 'ordering', 'bSort' );
- _fnCompatMap( init, 'orderMulti', 'bSortMulti' );
- _fnCompatMap( init, 'orderClasses', 'bSortClasses' );
- _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' );
- _fnCompatMap( init, 'order', 'aaSorting' );
- _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' );
- _fnCompatMap( init, 'paging', 'bPaginate' );
- _fnCompatMap( init, 'pagingType', 'sPaginationType' );
- _fnCompatMap( init, 'pageLength', 'iDisplayLength' );
- _fnCompatMap( init, 'searching', 'bFilter' );
-
- // Boolean initialisation of x-scrolling
- if ( typeof init.sScrollX === 'boolean' ) {
- init.sScrollX = init.sScrollX ? '100%' : '';
- }
- if ( typeof init.scrollX === 'boolean' ) {
- init.scrollX = init.scrollX ? '100%' : '';
- }
-
- // Column search objects are in an array, so it needs to be converted
- // element by element
- var searchCols = init.aoSearchCols;
-
- if ( searchCols ) {
- for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {
- if ( searchCols[i] ) {
- _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );
- }
- }
- }
- }
-
-
- /**
- * Provide backwards compatibility for column options. Note that the new options
- * are mapped onto the old parameters, so this is an external interface change
- * only.
- * @param {object} init Object to map
- */
- function _fnCompatCols ( init )
- {
- _fnCompatMap( init, 'orderable', 'bSortable' );
- _fnCompatMap( init, 'orderData', 'aDataSort' );
- _fnCompatMap( init, 'orderSequence', 'asSorting' );
- _fnCompatMap( init, 'orderDataType', 'sortDataType' );
-
- // orderData can be given as an integer
- var dataSort = init.aDataSort;
- if ( typeof dataSort === 'number' && ! $.isArray( dataSort ) ) {
- init.aDataSort = [ dataSort ];
- }
- }
-
-
- /**
- * Browser feature detection for capabilities, quirks
- * @param {object} settings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnBrowserDetect( settings )
- {
- // We don't need to do this every time DataTables is constructed, the values
- // calculated are specific to the browser and OS configuration which we
- // don't expect to change between initialisations
- if ( ! DataTable.__browser ) {
- var browser = {};
- DataTable.__browser = browser;
-
- // Scrolling feature / quirks detection
- var n = $('<div/>')
- .css( {
- position: 'fixed',
- top: 0,
- left: $(window).scrollLeft()*-1, // allow for scrolling
- height: 1,
- width: 1,
- overflow: 'hidden'
- } )
- .append(
- $('<div/>')
- .css( {
- position: 'absolute',
- top: 1,
- left: 1,
- width: 100,
- overflow: 'scroll'
- } )
- .append(
- $('<div/>')
- .css( {
- width: '100%',
- height: 10
- } )
- )
- )
- .appendTo( 'body' );
-
- var outer = n.children();
- var inner = outer.children();
-
- // Numbers below, in order, are:
- // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth
- //
- // IE6 XP: 100 100 100 83
- // IE7 Vista: 100 100 100 83
- // IE 8+ Windows: 83 83 100 83
- // Evergreen Windows: 83 83 100 83
- // Evergreen Mac with scrollbars: 85 85 100 85
- // Evergreen Mac without scrollbars: 100 100 100 100
-
- // Get scrollbar width
- browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;
-
- // IE6/7 will oversize a width 100% element inside a scrolling element, to
- // include the width of the scrollbar, while other browsers ensure the inner
- // element is contained without forcing scrolling
- browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100;
-
- // In rtl text layout, some browsers (most, but not all) will place the
- // scrollbar on the left, rather than the right.
- browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1;
-
- // IE8- don't provide height and width for getBoundingClientRect
- browser.bBounding = n[0].getBoundingClientRect().width ? true : false;
-
- n.remove();
- }
-
- $.extend( settings.oBrowser, DataTable.__browser );
- settings.oScroll.iBarWidth = DataTable.__browser.barWidth;
- }
-
-
- /**
- * Array.prototype reduce[Right] method, used for browsers which don't support
- * JS 1.6. Done this way to reduce code size, since we iterate either way
- * @param {object} settings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnReduce ( that, fn, init, start, end, inc )
- {
- var
- i = start,
- value,
- isSet = false;
-
- if ( init !== undefined ) {
- value = init;
- isSet = true;
- }
-
- while ( i !== end ) {
- if ( ! that.hasOwnProperty(i) ) {
- continue;
- }
-
- value = isSet ?
- fn( value, that[i], i, that ) :
- that[i];
-
- isSet = true;
- i += inc;
- }
-
- return value;
- }
-
- /**
- * Add a column to the list used for the table with default values
- * @param {object} oSettings dataTables settings object
- * @param {node} nTh The th element for this column
- * @memberof DataTable#oApi
- */
- function _fnAddColumn( oSettings, nTh )
- {
- // Add column to aoColumns array
- var oDefaults = DataTable.defaults.column;
- var iCol = oSettings.aoColumns.length;
- var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
- "nTh": nTh ? nTh : document.createElement('th'),
- "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '',
- "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
- "mData": oDefaults.mData ? oDefaults.mData : iCol,
- idx: iCol
- } );
- oSettings.aoColumns.push( oCol );
-
- // Add search object for column specific search. Note that the `searchCols[ iCol ]`
- // passed into extend can be undefined. This allows the user to give a default
- // with only some of the parameters defined, and also not give a default
- var searchCols = oSettings.aoPreSearchCols;
- searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );
-
- // Use the default column options function to initialise classes etc
- _fnColumnOptions( oSettings, iCol, $(nTh).data() );
- }
-
-
- /**
- * Apply options for a column
- * @param {object} oSettings dataTables settings object
- * @param {int} iCol column index to consider
- * @param {object} oOptions object with sType, bVisible and bSearchable etc
- * @memberof DataTable#oApi
- */
- function _fnColumnOptions( oSettings, iCol, oOptions )
- {
- var oCol = oSettings.aoColumns[ iCol ];
- var oClasses = oSettings.oClasses;
- var th = $(oCol.nTh);
-
- // Try to get width information from the DOM. We can't get it from CSS
- // as we'd need to parse the CSS stylesheet. `width` option can override
- if ( ! oCol.sWidthOrig ) {
- // Width attribute
- oCol.sWidthOrig = th.attr('width') || null;
-
- // Style attribute
- var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/);
- if ( t ) {
- oCol.sWidthOrig = t[1];
- }
- }
-
- /* User specified column options */
- if ( oOptions !== undefined && oOptions !== null )
- {
- // Backwards compatibility
- _fnCompatCols( oOptions );
-
- // Map camel case parameters to their Hungarian counterparts
- _fnCamelToHungarian( DataTable.defaults.column, oOptions );
-
- /* Backwards compatibility for mDataProp */
- if ( oOptions.mDataProp !== undefined && !oOptions.mData )
- {
- oOptions.mData = oOptions.mDataProp;
- }
-
- if ( oOptions.sType )
- {
- oCol._sManualType = oOptions.sType;
- }
-
- // `class` is a reserved word in Javascript, so we need to provide
- // the ability to use a valid name for the camel case input
- if ( oOptions.className && ! oOptions.sClass )
- {
- oOptions.sClass = oOptions.className;
- }
- if ( oOptions.sClass ) {
- th.addClass( oOptions.sClass );
- }
-
- $.extend( oCol, oOptions );
- _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
-
- /* iDataSort to be applied (backwards compatibility), but aDataSort will take
- * priority if defined
- */
- if ( oOptions.iDataSort !== undefined )
- {
- oCol.aDataSort = [ oOptions.iDataSort ];
- }
- _fnMap( oCol, oOptions, "aDataSort" );
- }
-
- /* Cache the data get and set functions for speed */
- var mDataSrc = oCol.mData;
- var mData = _fnGetObjectDataFn( mDataSrc );
- var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
-
- var attrTest = function( src ) {
- return typeof src === 'string' && src.indexOf('@') !== -1;
- };
- oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
- attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
- );
- oCol._setter = null;
-
- oCol.fnGetData = function (rowData, type, meta) {
- var innerData = mData( rowData, type, undefined, meta );
-
- return mRender && type ?
- mRender( innerData, type, rowData, meta ) :
- innerData;
- };
- oCol.fnSetData = function ( rowData, val, meta ) {
- return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );
- };
-
- // Indicate if DataTables should read DOM data as an object or array
- // Used in _fnGetRowElements
- if ( typeof mDataSrc !== 'number' ) {
- oSettings._rowReadObject = true;
- }
-
- /* Feature sorting overrides column specific when off */
- if ( !oSettings.oFeatures.bSort )
- {
- oCol.bSortable = false;
- th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called
- }
-
- /* Check that the class assignment is correct for sorting */
- var bAsc = $.inArray('asc', oCol.asSorting) !== -1;
- var bDesc = $.inArray('desc', oCol.asSorting) !== -1;
- if ( !oCol.bSortable || (!bAsc && !bDesc) )
- {
- oCol.sSortingClass = oClasses.sSortableNone;
- oCol.sSortingClassJUI = "";
- }
- else if ( bAsc && !bDesc )
- {
- oCol.sSortingClass = oClasses.sSortableAsc;
- oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;
- }
- else if ( !bAsc && bDesc )
- {
- oCol.sSortingClass = oClasses.sSortableDesc;
- oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;
- }
- else
- {
- oCol.sSortingClass = oClasses.sSortable;
- oCol.sSortingClassJUI = oClasses.sSortJUI;
- }
- }
-
-
- /**
- * Adjust the table column widths for new data. Note: you would probably want to
- * do a redraw after calling this function!
- * @param {object} settings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnAdjustColumnSizing ( settings )
- {
- /* Not interested in doing column width calculation if auto-width is disabled */
- if ( settings.oFeatures.bAutoWidth !== false )
- {
- var columns = settings.aoColumns;
-
- _fnCalculateColumnWidths( settings );
- for ( var i=0 , iLen=columns.length ; i<iLen ; i++ )
- {
- columns[i].nTh.style.width = columns[i].sWidth;
- }
- }
-
- var scroll = settings.oScroll;
- if ( scroll.sY !== '' || scroll.sX !== '')
- {
- _fnScrollDraw( settings );
- }
-
- _fnCallbackFire( settings, null, 'column-sizing', [settings] );
- }
-
-
- /**
- * Covert the index of a visible column to the index in the data array (take account
- * of hidden columns)
- * @param {object} oSettings dataTables settings object
- * @param {int} iMatch Visible column index to lookup
- * @returns {int} i the data index
- * @memberof DataTable#oApi
- */
- function _fnVisibleToColumnIndex( oSettings, iMatch )
- {
- var aiVis = _fnGetColumns( oSettings, 'bVisible' );
-
- return typeof aiVis[iMatch] === 'number' ?
- aiVis[iMatch] :
- null;
- }
-
-
- /**
- * Covert the index of an index in the data array and convert it to the visible
- * column index (take account of hidden columns)
- * @param {int} iMatch Column index to lookup
- * @param {object} oSettings dataTables settings object
- * @returns {int} i the data index
- * @memberof DataTable#oApi
- */
- function _fnColumnIndexToVisible( oSettings, iMatch )
- {
- var aiVis = _fnGetColumns( oSettings, 'bVisible' );
- var iPos = $.inArray( iMatch, aiVis );
-
- return iPos !== -1 ? iPos : null;
- }
-
-
- /**
- * Get the number of visible columns
- * @param {object} oSettings dataTables settings object
- * @returns {int} i the number of visible columns
- * @memberof DataTable#oApi
- */
- function _fnVisbleColumns( oSettings )
- {
- var vis = 0;
-
- // No reduce in IE8, use a loop for now
- $.each( oSettings.aoColumns, function ( i, col ) {
- if ( col.bVisible && $(col.nTh).css('display') !== 'none' ) {
- vis++;
- }
- } );
-
- return vis;
- }
-
-
- /**
- * Get an array of column indexes that match a given property
- * @param {object} oSettings dataTables settings object
- * @param {string} sParam Parameter in aoColumns to look for - typically
- * bVisible or bSearchable
- * @returns {array} Array of indexes with matched properties
- * @memberof DataTable#oApi
- */
- function _fnGetColumns( oSettings, sParam )
- {
- var a = [];
-
- $.map( oSettings.aoColumns, function(val, i) {
- if ( val[sParam] ) {
- a.push( i );
- }
- } );
-
- return a;
- }
-
-
- /**
- * Calculate the 'type' of a column
- * @param {object} settings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnColumnTypes ( settings )
- {
- var columns = settings.aoColumns;
- var data = settings.aoData;
- var types = DataTable.ext.type.detect;
- var i, ien, j, jen, k, ken;
- var col, cell, detectedType, cache;
-
- // For each column, spin over the
- for ( i=0, ien=columns.length ; i<ien ; i++ ) {
- col = columns[i];
- cache = [];
-
- if ( ! col.sType && col._sManualType ) {
- col.sType = col._sManualType;
- }
- else if ( ! col.sType ) {
- for ( j=0, jen=types.length ; j<jen ; j++ ) {
- for ( k=0, ken=data.length ; k<ken ; k++ ) {
- // Use a cache array so we only need to get the type data
- // from the formatter once (when using multiple detectors)
- if ( cache[k] === undefined ) {
- cache[k] = _fnGetCellData( settings, k, i, 'type' );
- }
-
- detectedType = types[j]( cache[k], settings );
-
- // If null, then this type can't apply to this column, so
- // rather than testing all cells, break out. There is an
- // exception for the last type which is `html`. We need to
- // scan all rows since it is possible to mix string and HTML
- // types
- if ( ! detectedType && j !== types.length-1 ) {
- break;
- }
-
- // Only a single match is needed for html type since it is
- // bottom of the pile and very similar to string
- if ( detectedType === 'html' ) {
- break;
- }
- }
-
- // Type is valid for all data points in the column - use this
- // type
- if ( detectedType ) {
- col.sType = detectedType;
- break;
- }
- }
-
- // Fall back - if no type was detected, always use string
- if ( ! col.sType ) {
- col.sType = 'string';
- }
- }
- }
- }
-
-
- /**
- * Take the column definitions and static columns arrays and calculate how
- * they relate to column indexes. The callback function will then apply the
- * definition found for a column to a suitable configuration object.
- * @param {object} oSettings dataTables settings object
- * @param {array} aoColDefs The aoColumnDefs array that is to be applied
- * @param {array} aoCols The aoColumns array that defines columns individually
- * @param {function} fn Callback function - takes two parameters, the calculated
- * column index and the definition for that column.
- * @memberof DataTable#oApi
- */
- function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
- {
- var i, iLen, j, jLen, k, kLen, def;
- var columns = oSettings.aoColumns;
-
- // Column definitions with aTargets
- if ( aoColDefs )
- {
- /* Loop over the definitions array - loop in reverse so first instance has priority */
- for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
- {
- def = aoColDefs[i];
-
- /* Each definition can target multiple columns, as it is an array */
- var aTargets = def.targets !== undefined ?
- def.targets :
- def.aTargets;
-
- if ( ! $.isArray( aTargets ) )
- {
- aTargets = [ aTargets ];
- }
-
- for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
- {
- if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )
- {
- /* Add columns that we don't yet know about */
- while( columns.length <= aTargets[j] )
- {
- _fnAddColumn( oSettings );
- }
-
- /* Integer, basic index */
- fn( aTargets[j], def );
- }
- else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )
- {
- /* Negative integer, right to left column counting */
- fn( columns.length+aTargets[j], def );
- }
- else if ( typeof aTargets[j] === 'string' )
- {
- /* Class name matching on TH element */
- for ( k=0, kLen=columns.length ; k<kLen ; k++ )
- {
- if ( aTargets[j] == "_all" ||
- $(columns[k].nTh).hasClass( aTargets[j] ) )
- {
- fn( k, def );
- }
- }
- }
- }
- }
- }
-
- // Statically defined columns array
- if ( aoCols )
- {
- for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
- {
- fn( i, aoCols[i] );
- }
- }
- }
-
- /**
- * Add a data array to the table, creating DOM node etc. This is the parallel to
- * _fnGatherData, but for adding rows from a Javascript source, rather than a
- * DOM source.
- * @param {object} oSettings dataTables settings object
- * @param {array} aData data array to be added
- * @param {node} [nTr] TR element to add to the table - optional. If not given,
- * DataTables will create a row automatically
- * @param {array} [anTds] Array of TD|TH elements for the row - must be given
- * if nTr is.
- * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
- * @memberof DataTable#oApi
- */
- function _fnAddData ( oSettings, aDataIn, nTr, anTds )
- {
- /* Create the object for storing information about this new row */
- var iRow = oSettings.aoData.length;
- var oData = $.extend( true, {}, DataTable.models.oRow, {
- src: nTr ? 'dom' : 'data',
- idx: iRow
- } );
-
- oData._aData = aDataIn;
- oSettings.aoData.push( oData );
-
- /* Create the cells */
- var nTd, sThisType;
- var columns = oSettings.aoColumns;
-
- // Invalidate the column types as the new data needs to be revalidated
- for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
- {
- columns[i].sType = null;
- }
-
- /* Add to the display array */
- oSettings.aiDisplayMaster.push( iRow );
-
- var id = oSettings.rowIdFn( aDataIn );
- if ( id !== undefined ) {
- oSettings.aIds[ id ] = oData;
- }
-
- /* Create the DOM information, or register it if already present */
- if ( nTr || ! oSettings.oFeatures.bDeferRender )
- {
- _fnCreateTr( oSettings, iRow, nTr, anTds );
- }
-
- return iRow;
- }
-
-
- /**
- * Add one or more TR elements to the table. Generally we'd expect to
- * use this for reading data from a DOM sourced table, but it could be
- * used for an TR element. Note that if a TR is given, it is used (i.e.
- * it is not cloned).
- * @param {object} settings dataTables settings object
- * @param {array|node|jQuery} trs The TR element(s) to add to the table
- * @returns {array} Array of indexes for the added rows
- * @memberof DataTable#oApi
- */
- function _fnAddTr( settings, trs )
- {
- var row;
-
- // Allow an individual node to be passed in
- if ( ! (trs instanceof $) ) {
- trs = $(trs);
- }
-
- return trs.map( function (i, el) {
- row = _fnGetRowElements( settings, el );
- return _fnAddData( settings, row.data, el, row.cells );
- } );
- }
-
-
- /**
- * Take a TR element and convert it to an index in aoData
- * @param {object} oSettings dataTables settings object
- * @param {node} n the TR element to find
- * @returns {int} index if the node is found, null if not
- * @memberof DataTable#oApi
- */
- function _fnNodeToDataIndex( oSettings, n )
- {
- return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
- }
-
-
- /**
- * Take a TD element and convert it into a column data index (not the visible index)
- * @param {object} oSettings dataTables settings object
- * @param {int} iRow The row number the TD/TH can be found in
- * @param {node} n The TD/TH element to find
- * @returns {int} index if the node is found, -1 if not
- * @memberof DataTable#oApi
- */
- function _fnNodeToColumnIndex( oSettings, iRow, n )
- {
- return $.inArray( n, oSettings.aoData[ iRow ].anCells );
- }
-
-
- /**
- * Get the data for a given cell from the internal cache, taking into account data mapping
- * @param {object} settings dataTables settings object
- * @param {int} rowIdx aoData row id
- * @param {int} colIdx Column index
- * @param {string} type data get type ('display', 'type' 'filter' 'sort')
- * @returns {*} Cell data
- * @memberof DataTable#oApi
- */
- function _fnGetCellData( settings, rowIdx, colIdx, type )
- {
- var draw = settings.iDraw;
- var col = settings.aoColumns[colIdx];
- var rowData = settings.aoData[rowIdx]._aData;
- var defaultContent = col.sDefaultContent;
- var cellData = col.fnGetData( rowData, type, {
- settings: settings,
- row: rowIdx,
- col: colIdx
- } );
-
- if ( cellData === undefined ) {
- if ( settings.iDrawError != draw && defaultContent === null ) {
- _fnLog( settings, 0, "Requested unknown parameter "+
- (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+
- " for row "+rowIdx+", column "+colIdx, 4 );
- settings.iDrawError = draw;
- }
- return defaultContent;
- }
-
- // When the data source is null and a specific data type is requested (i.e.
- // not the original data), we can use default column data
- if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) {
- cellData = defaultContent;
- }
- else if ( typeof cellData === 'function' ) {
- // If the data source is a function, then we run it and use the return,
- // executing in the scope of the data object (for instances)
- return cellData.call( rowData );
- }
-
- if ( cellData === null && type == 'display' ) {
- return '';
- }
- return cellData;
- }
-
-
- /**
- * Set the value for a specific cell, into the internal data cache
- * @param {object} settings dataTables settings object
- * @param {int} rowIdx aoData row id
- * @param {int} colIdx Column index
- * @param {*} val Value to set
- * @memberof DataTable#oApi
- */
- function _fnSetCellData( settings, rowIdx, colIdx, val )
- {
- var col = settings.aoColumns[colIdx];
- var rowData = settings.aoData[rowIdx]._aData;
-
- col.fnSetData( rowData, val, {
- settings: settings,
- row: rowIdx,
- col: colIdx
- } );
- }
-
-
- // Private variable that is used to match action syntax in the data property object
- var __reArray = /\[.*?\]$/;
- var __reFn = /\(\)$/;
-
- /**
- * Split string on periods, taking into account escaped periods
- * @param {string} str String to split
- * @return {array} Split string
- */
- function _fnSplitObjNotation( str )
- {
- return $.map( str.match(/(\\.|[^\.])+/g) || [''], function ( s ) {
- return s.replace(/\\\./g, '.');
- } );
- }
-
-
- /**
- * Return a function that can be used to get data from a source object, taking
- * into account the ability to use nested objects as a source
- * @param {string|int|function} mSource The data source for the object
- * @returns {function} Data get function
- * @memberof DataTable#oApi
- */
- function _fnGetObjectDataFn( mSource )
- {
- if ( $.isPlainObject( mSource ) )
- {
- /* Build an object of get functions, and wrap them in a single call */
- var o = {};
- $.each( mSource, function (key, val) {
- if ( val ) {
- o[key] = _fnGetObjectDataFn( val );
- }
- } );
-
- return function (data, type, row, meta) {
- var t = o[type] || o._;
- return t !== undefined ?
- t(data, type, row, meta) :
- data;
- };
- }
- else if ( mSource === null )
- {
- /* Give an empty string for rendering / sorting etc */
- return function (data) { // type, row and meta also passed, but not used
- return data;
- };
- }
- else if ( typeof mSource === 'function' )
- {
- return function (data, type, row, meta) {
- return mSource( data, type, row, meta );
- };
- }
- else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
- mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
- {
- /* If there is a . in the source string then the data source is in a
- * nested object so we loop over the data for each level to get the next
- * level down. On each loop we test for undefined, and if found immediately
- * return. This allows entire objects to be missing and sDefaultContent to
- * be used if defined, rather than throwing an error
- */
- var fetchData = function (data, type, src) {
- var arrayNotation, funcNotation, out, innerSrc;
-
- if ( src !== "" )
- {
- var a = _fnSplitObjNotation( src );
-
- for ( var i=0, iLen=a.length ; i<iLen ; i++ )
- {
- // Check if we are dealing with special notation
- arrayNotation = a[i].match(__reArray);
- funcNotation = a[i].match(__reFn);
-
- if ( arrayNotation )
- {
- // Array notation
- a[i] = a[i].replace(__reArray, '');
-
- // Condition allows simply [] to be passed in
- if ( a[i] !== "" ) {
- data = data[ a[i] ];
- }
- out = [];
-
- // Get the remainder of the nested object to get
- a.splice( 0, i+1 );
- innerSrc = a.join('.');
-
- // Traverse each entry in the array getting the properties requested
- if ( $.isArray( data ) ) {
- for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
- out.push( fetchData( data[j], type, innerSrc ) );
- }
- }
-
- // If a string is given in between the array notation indicators, that
- // is used to join the strings together, otherwise an array is returned
- var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
- data = (join==="") ? out : out.join(join);
-
- // The inner call to fetchData has already traversed through the remainder
- // of the source requested, so we exit from the loop
- break;
- }
- else if ( funcNotation )
- {
- // Function call
- a[i] = a[i].replace(__reFn, '');
- data = data[ a[i] ]();
- continue;
- }
-
- if ( data === null || data[ a[i] ] === undefined )
- {
- return undefined;
- }
- data = data[ a[i] ];
- }
- }
-
- return data;
- };
-
- return function (data, type) { // row and meta also passed, but not used
- return fetchData( data, type, mSource );
- };
- }
- else
- {
- /* Array or flat object mapping */
- return function (data, type) { // row and meta also passed, but not used
- return data[mSource];
- };
- }
- }
-
-
- /**
- * Return a function that can be used to set data from a source object, taking
- * into account the ability to use nested objects as a source
- * @param {string|int|function} mSource The data source for the object
- * @returns {function} Data set function
- * @memberof DataTable#oApi
- */
- function _fnSetObjectDataFn( mSource )
- {
- if ( $.isPlainObject( mSource ) )
- {
- /* Unlike get, only the underscore (global) option is used for for
- * setting data since we don't know the type here. This is why an object
- * option is not documented for `mData` (which is read/write), but it is
- * for `mRender` which is read only.
- */
- return _fnSetObjectDataFn( mSource._ );
- }
- else if ( mSource === null )
- {
- /* Nothing to do when the data source is null */
- return function () {};
- }
- else if ( typeof mSource === 'function' )
- {
- return function (data, val, meta) {
- mSource( data, 'set', val, meta );
- };
- }
- else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
- mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
- {
- /* Like the get, we need to get data from a nested object */
- var setData = function (data, val, src) {
- var a = _fnSplitObjNotation( src ), b;
- var aLast = a[a.length-1];
- var arrayNotation, funcNotation, o, innerSrc;
-
- for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ )
- {
- // Check if we are dealing with an array notation request
- arrayNotation = a[i].match(__reArray);
- funcNotation = a[i].match(__reFn);
-
- if ( arrayNotation )
- {
- a[i] = a[i].replace(__reArray, '');
- data[ a[i] ] = [];
-
- // Get the remainder of the nested object to set so we can recurse
- b = a.slice();
- b.splice( 0, i+1 );
- innerSrc = b.join('.');
-
- // Traverse each entry in the array setting the properties requested
- if ( $.isArray( val ) )
- {
- for ( var j=0, jLen=val.length ; j<jLen ; j++ )
- {
- o = {};
- setData( o, val[j], innerSrc );
- data[ a[i] ].push( o );
- }
- }
- else
- {
- // We've been asked to save data to an array, but it
- // isn't array data to be saved. Best that can be done
- // is to just save the value.
- data[ a[i] ] = val;
- }
-
- // The inner call to setData has already traversed through the remainder
- // of the source and has set the data, thus we can exit here
- return;
- }
- else if ( funcNotation )
- {
- // Function call
- a[i] = a[i].replace(__reFn, '');
- data = data[ a[i] ]( val );
- }
-
- // If the nested object doesn't currently exist - since we are
- // trying to set the value - create it
- if ( data[ a[i] ] === null || data[ a[i] ] === undefined )
- {
- data[ a[i] ] = {};
- }
- data = data[ a[i] ];
- }
-
- // Last item in the input - i.e, the actual set
- if ( aLast.match(__reFn ) )
- {
- // Function call
- data = data[ aLast.replace(__reFn, '') ]( val );
- }
- else
- {
- // If array notation is used, we just want to strip it and use the property name
- // and assign the value. If it isn't used, then we get the result we want anyway
- data[ aLast.replace(__reArray, '') ] = val;
- }
- };
-
- return function (data, val) { // meta is also passed in, but not used
- return setData( data, val, mSource );
- };
- }
- else
- {
- /* Array or flat object mapping */
- return function (data, val) { // meta is also passed in, but not used
- data[mSource] = val;
- };
- }
- }
-
-
- /**
- * Return an array with the full table data
- * @param {object} oSettings dataTables settings object
- * @returns array {array} aData Master data array
- * @memberof DataTable#oApi
- */
- function _fnGetDataMaster ( settings )
- {
- return _pluck( settings.aoData, '_aData' );
- }
-
-
- /**
- * Nuke the table
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnClearTable( settings )
- {
- settings.aoData.length = 0;
- settings.aiDisplayMaster.length = 0;
- settings.aiDisplay.length = 0;
- settings.aIds = {};
- }
-
-
- /**
- * Take an array of integers (index array) and remove a target integer (value - not
- * the key!)
- * @param {array} a Index array to target
- * @param {int} iTarget value to find
- * @memberof DataTable#oApi
- */
- function _fnDeleteIndex( a, iTarget, splice )
- {
- var iTargetIndex = -1;
-
- for ( var i=0, iLen=a.length ; i<iLen ; i++ )
- {
- if ( a[i] == iTarget )
- {
- iTargetIndex = i;
- }
- else if ( a[i] > iTarget )
- {
- a[i]--;
- }
- }
-
- if ( iTargetIndex != -1 && splice === undefined )
- {
- a.splice( iTargetIndex, 1 );
- }
- }
-
-
- /**
- * Mark cached data as invalid such that a re-read of the data will occur when
- * the cached data is next requested. Also update from the data source object.
- *
- * @param {object} settings DataTables settings object
- * @param {int} rowIdx Row index to invalidate
- * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom'
- * or 'data'
- * @param {int} [colIdx] Column index to invalidate. If undefined the whole
- * row will be invalidated
- * @memberof DataTable#oApi
- *
- * @todo For the modularisation of v1.11 this will need to become a callback, so
- * the sort and filter methods can subscribe to it. That will required
- * initialisation options for sorting, which is why it is not already baked in
- */
- function _fnInvalidate( settings, rowIdx, src, colIdx )
- {
- var row = settings.aoData[ rowIdx ];
- var i, ien;
- var cellWrite = function ( cell, col ) {
- // This is very frustrating, but in IE if you just write directly
- // to innerHTML, and elements that are overwritten are GC'ed,
- // even if there is a reference to them elsewhere
- while ( cell.childNodes.length ) {
- cell.removeChild( cell.firstChild );
- }
-
- cell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' );
- };
-
- // Are we reading last data from DOM or the data object?
- if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {
- // Read the data from the DOM
- row._aData = _fnGetRowElements(
- settings, row, colIdx, colIdx === undefined ? undefined : row._aData
- )
- .data;
- }
- else {
- // Reading from data object, update the DOM
- var cells = row.anCells;
-
- if ( cells ) {
- if ( colIdx !== undefined ) {
- cellWrite( cells[colIdx], colIdx );
- }
- else {
- for ( i=0, ien=cells.length ; i<ien ; i++ ) {
- cellWrite( cells[i], i );
- }
- }
- }
- }
-
- // For both row and cell invalidation, the cached data for sorting and
- // filtering is nulled out
- row._aSortData = null;
- row._aFilterData = null;
-
- // Invalidate the type for a specific column (if given) or all columns since
- // the data might have changed
- var cols = settings.aoColumns;
- if ( colIdx !== undefined ) {
- cols[ colIdx ].sType = null;
- }
- else {
- for ( i=0, ien=cols.length ; i<ien ; i++ ) {
- cols[i].sType = null;
- }
-
- // Update DataTables special `DT_*` attributes for the row
- _fnRowAttributes( settings, row );
- }
- }
-
-
- /**
- * Build a data source object from an HTML row, reading the contents of the
- * cells that are in the row.
- *
- * @param {object} settings DataTables settings object
- * @param {node|object} TR element from which to read data or existing row
- * object from which to re-read the data from the cells
- * @param {int} [colIdx] Optional column index
- * @param {array|object} [d] Data source object. If `colIdx` is given then this
- * parameter should also be given and will be used to write the data into.
- * Only the column in question will be written
- * @returns {object} Object with two parameters: `data` the data read, in
- * document order, and `cells` and array of nodes (they can be useful to the
- * caller, so rather than needing a second traversal to get them, just return
- * them from here).
- * @memberof DataTable#oApi
- */
- function _fnGetRowElements( settings, row, colIdx, d )
- {
- var
- tds = [],
- td = row.firstChild,
- name, col, o, i=0, contents,
- columns = settings.aoColumns,
- objectRead = settings._rowReadObject;
-
- // Allow the data object to be passed in, or construct
- d = d !== undefined ?
- d :
- objectRead ?
- {} :
- [];
-
- var attr = function ( str, td ) {
- if ( typeof str === 'string' ) {
- var idx = str.indexOf('@');
-
- if ( idx !== -1 ) {
- var attr = str.substring( idx+1 );
- var setter = _fnSetObjectDataFn( str );
- setter( d, td.getAttribute( attr ) );
- }
- }
- };
-
- // Read data from a cell and store into the data object
- var cellProcess = function ( cell ) {
- if ( colIdx === undefined || colIdx === i ) {
- col = columns[i];
- contents = $.trim(cell.innerHTML);
-
- if ( col && col._bAttrSrc ) {
- var setter = _fnSetObjectDataFn( col.mData._ );
- setter( d, contents );
-
- attr( col.mData.sort, cell );
- attr( col.mData.type, cell );
- attr( col.mData.filter, cell );
- }
- else {
- // Depending on the `data` option for the columns the data can
- // be read to either an object or an array.
- if ( objectRead ) {
- if ( ! col._setter ) {
- // Cache the setter function
- col._setter = _fnSetObjectDataFn( col.mData );
- }
- col._setter( d, contents );
- }
- else {
- d[i] = contents;
- }
- }
- }
-
- i++;
- };
-
- if ( td ) {
- // `tr` element was passed in
- while ( td ) {
- name = td.nodeName.toUpperCase();
-
- if ( name == "TD" || name == "TH" ) {
- cellProcess( td );
- tds.push( td );
- }
-
- td = td.nextSibling;
- }
- }
- else {
- // Existing row object passed in
- tds = row.anCells;
-
- for ( var j=0, jen=tds.length ; j<jen ; j++ ) {
- cellProcess( tds[j] );
- }
- }
-
- // Read the ID from the DOM if present
- var rowNode = row.firstChild ? row : row.nTr;
-
- if ( rowNode ) {
- var id = rowNode.getAttribute( 'id' );
-
- if ( id ) {
- _fnSetObjectDataFn( settings.rowId )( d, id );
- }
- }
-
- return {
- data: d,
- cells: tds
- };
- }
- /**
- * Create a new TR element (and it's TD children) for a row
- * @param {object} oSettings dataTables settings object
- * @param {int} iRow Row to consider
- * @param {node} [nTrIn] TR element to add to the table - optional. If not given,
- * DataTables will create a row automatically
- * @param {array} [anTds] Array of TD|TH elements for the row - must be given
- * if nTr is.
- * @memberof DataTable#oApi
- */
- function _fnCreateTr ( oSettings, iRow, nTrIn, anTds )
- {
- var
- row = oSettings.aoData[iRow],
- rowData = row._aData,
- cells = [],
- nTr, nTd, oCol,
- i, iLen;
-
- if ( row.nTr === null )
- {
- nTr = nTrIn || document.createElement('tr');
-
- row.nTr = nTr;
- row.anCells = cells;
-
- /* Use a private property on the node to allow reserve mapping from the node
- * to the aoData array for fast look up
- */
- nTr._DT_RowIndex = iRow;
-
- /* Special parameters can be given by the data source to be used on the row */
- _fnRowAttributes( oSettings, row );
-
- /* Process each column */
- for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
- {
- oCol = oSettings.aoColumns[i];
-
- nTd = nTrIn ? anTds[i] : document.createElement( oCol.sCellType );
- nTd._DT_CellIndex = {
- row: iRow,
- column: i
- };
-
- cells.push( nTd );
-
- // Need to create the HTML if new, or if a rendering function is defined
- if ( (!nTrIn || oCol.mRender || oCol.mData !== i) &&
- (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')
- ) {
- nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );
- }
-
- /* Add user defined class */
- if ( oCol.sClass )
- {
- nTd.className += ' '+oCol.sClass;
- }
-
- // Visibility - add or remove as required
- if ( oCol.bVisible && ! nTrIn )
- {
- nTr.appendChild( nTd );
- }
- else if ( ! oCol.bVisible && nTrIn )
- {
- nTd.parentNode.removeChild( nTd );
- }
-
- if ( oCol.fnCreatedCell )
- {
- oCol.fnCreatedCell.call( oSettings.oInstance,
- nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
- );
- }
- }
-
- _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow] );
- }
-
- // Remove once webkit bug 131819 and Chromium bug 365619 have been resolved
- // and deployed
- row.nTr.setAttribute( 'role', 'row' );
- }
-
-
- /**
- * Add attributes to a row based on the special `DT_*` parameters in a data
- * source object.
- * @param {object} settings DataTables settings object
- * @param {object} DataTables row object for the row to be modified
- * @memberof DataTable#oApi
- */
- function _fnRowAttributes( settings, row )
- {
- var tr = row.nTr;
- var data = row._aData;
-
- if ( tr ) {
- var id = settings.rowIdFn( data );
-
- if ( id ) {
- tr.id = id;
- }
-
- if ( data.DT_RowClass ) {
- // Remove any classes added by DT_RowClass before
- var a = data.DT_RowClass.split(' ');
- row.__rowc = row.__rowc ?
- _unique( row.__rowc.concat( a ) ) :
- a;
-
- $(tr)
- .removeClass( row.__rowc.join(' ') )
- .addClass( data.DT_RowClass );
- }
-
- if ( data.DT_RowAttr ) {
- $(tr).attr( data.DT_RowAttr );
- }
-
- if ( data.DT_RowData ) {
- $(tr).data( data.DT_RowData );
- }
- }
- }
-
-
- /**
- * Create the HTML header for the table
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnBuildHead( oSettings )
- {
- var i, ien, cell, row, column;
- var thead = oSettings.nTHead;
- var tfoot = oSettings.nTFoot;
- var createHeader = $('th, td', thead).length === 0;
- var classes = oSettings.oClasses;
- var columns = oSettings.aoColumns;
-
- if ( createHeader ) {
- row = $('<tr/>').appendTo( thead );
- }
-
- for ( i=0, ien=columns.length ; i<ien ; i++ ) {
- column = columns[i];
- cell = $( column.nTh ).addClass( column.sClass );
-
- if ( createHeader ) {
- cell.appendTo( row );
- }
-
- // 1.11 move into sorting
- if ( oSettings.oFeatures.bSort ) {
- cell.addClass( column.sSortingClass );
-
- if ( column.bSortable !== false ) {
- cell
- .attr( 'tabindex', oSettings.iTabIndex )
- .attr( 'aria-controls', oSettings.sTableId );
-
- _fnSortAttachListener( oSettings, column.nTh, i );
- }
- }
-
- if ( column.sTitle != cell[0].innerHTML ) {
- cell.html( column.sTitle );
- }
-
- _fnRenderer( oSettings, 'header' )(
- oSettings, cell, column, classes
- );
- }
-
- if ( createHeader ) {
- _fnDetectHeader( oSettings.aoHeader, thead );
- }
-
- /* ARIA role for the rows */
- $(thead).find('>tr').attr('role', 'row');
-
- /* Deal with the footer - add classes if required */
- $(thead).find('>tr>th, >tr>td').addClass( classes.sHeaderTH );
- $(tfoot).find('>tr>th, >tr>td').addClass( classes.sFooterTH );
-
- // Cache the footer cells. Note that we only take the cells from the first
- // row in the footer. If there is more than one row the user wants to
- // interact with, they need to use the table().foot() method. Note also this
- // allows cells to be used for multiple columns using colspan
- if ( tfoot !== null ) {
- var cells = oSettings.aoFooter[0];
-
- for ( i=0, ien=cells.length ; i<ien ; i++ ) {
- column = columns[i];
- column.nTf = cells[i].cell;
-
- if ( column.sClass ) {
- $(column.nTf).addClass( column.sClass );
- }
- }
- }
- }
-
-
- /**
- * Draw the header (or footer) element based on the column visibility states. The
- * methodology here is to use the layout array from _fnDetectHeader, modified for
- * the instantaneous column visibility, to construct the new layout. The grid is
- * traversed over cell at a time in a rows x columns grid fashion, although each
- * cell insert can cover multiple elements in the grid - which is tracks using the
- * aApplied array. Cell inserts in the grid will only occur where there isn't
- * already a cell in that position.
- * @param {object} oSettings dataTables settings object
- * @param array {objects} aoSource Layout array from _fnDetectHeader
- * @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc,
- * @memberof DataTable#oApi
- */
- function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
- {
- var i, iLen, j, jLen, k, kLen, n, nLocalTr;
- var aoLocal = [];
- var aApplied = [];
- var iColumns = oSettings.aoColumns.length;
- var iRowspan, iColspan;
-
- if ( ! aoSource )
- {
- return;
- }
-
- if ( bIncludeHidden === undefined )
- {
- bIncludeHidden = false;
- }
-
- /* Make a copy of the master layout array, but without the visible columns in it */
- for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
- {
- aoLocal[i] = aoSource[i].slice();
- aoLocal[i].nTr = aoSource[i].nTr;
-
- /* Remove any columns which are currently hidden */
- for ( j=iColumns-1 ; j>=0 ; j-- )
- {
- if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
- {
- aoLocal[i].splice( j, 1 );
- }
- }
-
- /* Prep the applied array - it needs an element for each row */
- aApplied.push( [] );
- }
-
- for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
- {
- nLocalTr = aoLocal[i].nTr;
-
- /* All cells are going to be replaced, so empty out the row */
- if ( nLocalTr )
- {
- while( (n = nLocalTr.firstChild) )
- {
- nLocalTr.removeChild( n );
- }
- }
-
- for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
- {
- iRowspan = 1;
- iColspan = 1;
-
- /* Check to see if there is already a cell (row/colspan) covering our target
- * insert point. If there is, then there is nothing to do.
- */
- if ( aApplied[i][j] === undefined )
- {
- nLocalTr.appendChild( aoLocal[i][j].cell );
- aApplied[i][j] = 1;
-
- /* Expand the cell to cover as many rows as needed */
- while ( aoLocal[i+iRowspan] !== undefined &&
- aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
- {
- aApplied[i+iRowspan][j] = 1;
- iRowspan++;
- }
-
- /* Expand the cell to cover as many columns as needed */
- while ( aoLocal[i][j+iColspan] !== undefined &&
- aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
- {
- /* Must update the applied array over the rows for the columns */
- for ( k=0 ; k<iRowspan ; k++ )
- {
- aApplied[i+k][j+iColspan] = 1;
- }
- iColspan++;
- }
-
- /* Do the actual expansion in the DOM */
- $(aoLocal[i][j].cell)
- .attr('rowspan', iRowspan)
- .attr('colspan', iColspan);
- }
- }
- }
- }
-
-
- /**
- * Insert the required TR nodes into the table for display
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnDraw( oSettings )
- {
- /* Provide a pre-callback function which can be used to cancel the draw is false is returned */
- var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
- if ( $.inArray( false, aPreDraw ) !== -1 )
- {
- _fnProcessingDisplay( oSettings, false );
- return;
- }
-
- var i, iLen, n;
- var anRows = [];
- var iRowCount = 0;
- var asStripeClasses = oSettings.asStripeClasses;
- var iStripes = asStripeClasses.length;
- var iOpenRows = oSettings.aoOpenRows.length;
- var oLang = oSettings.oLanguage;
- var iInitDisplayStart = oSettings.iInitDisplayStart;
- var bServerSide = _fnDataSource( oSettings ) == 'ssp';
- var aiDisplay = oSettings.aiDisplay;
-
- oSettings.bDrawing = true;
-
- /* Check and see if we have an initial draw position from state saving */
- if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 )
- {
- oSettings._iDisplayStart = bServerSide ?
- iInitDisplayStart :
- iInitDisplayStart >= oSettings.fnRecordsDisplay() ?
- 0 :
- iInitDisplayStart;
-
- oSettings.iInitDisplayStart = -1;
- }
-
- var iDisplayStart = oSettings._iDisplayStart;
- var iDisplayEnd = oSettings.fnDisplayEnd();
-
- /* Server-side processing draw intercept */
- if ( oSettings.bDeferLoading )
- {
- oSettings.bDeferLoading = false;
- oSettings.iDraw++;
- _fnProcessingDisplay( oSettings, false );
- }
- else if ( !bServerSide )
- {
- oSettings.iDraw++;
- }
- else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) )
- {
- return;
- }
-
- if ( aiDisplay.length !== 0 )
- {
- var iStart = bServerSide ? 0 : iDisplayStart;
- var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;
-
- for ( var j=iStart ; j<iEnd ; j++ )
- {
- var iDataIndex = aiDisplay[j];
- var aoData = oSettings.aoData[ iDataIndex ];
- if ( aoData.nTr === null )
- {
- _fnCreateTr( oSettings, iDataIndex );
- }
-
- var nRow = aoData.nTr;
-
- /* Remove the old striping classes and then add the new one */
- if ( iStripes !== 0 )
- {
- var sStripe = asStripeClasses[ iRowCount % iStripes ];
- if ( aoData._sRowStripe != sStripe )
- {
- $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
- aoData._sRowStripe = sStripe;
- }
- }
-
- // Row callback functions - might want to manipulate the row
- // iRowCount and j are not currently documented. Are they at all
- // useful?
- _fnCallbackFire( oSettings, 'aoRowCallback', null,
- [nRow, aoData._aData, iRowCount, j] );
-
- anRows.push( nRow );
- iRowCount++;
- }
- }
- else
- {
- /* Table is empty - create a row with an empty message in it */
- var sZero = oLang.sZeroRecords;
- if ( oSettings.iDraw == 1 && _fnDataSource( oSettings ) == 'ajax' )
- {
- sZero = oLang.sLoadingRecords;
- }
- else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )
- {
- sZero = oLang.sEmptyTable;
- }
-
- anRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } )
- .append( $('<td />', {
- 'valign': 'top',
- 'colSpan': _fnVisbleColumns( oSettings ),
- 'class': oSettings.oClasses.sRowEmpty
- } ).html( sZero ) )[0];
- }
-
- /* Header and footer callbacks */
- _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],
- _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
-
- _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],
- _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
-
- var body = $(oSettings.nTBody);
-
- body.children().detach();
- body.append( $(anRows) );
-
- /* Call all required callback functions for the end of a draw */
- _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );
-
- /* Draw is complete, sorting and filtering must be as well */
- oSettings.bSorted = false;
- oSettings.bFiltered = false;
- oSettings.bDrawing = false;
- }
-
-
- /**
- * Redraw the table - taking account of the various features which are enabled
- * @param {object} oSettings dataTables settings object
- * @param {boolean} [holdPosition] Keep the current paging position. By default
- * the paging is reset to the first page
- * @memberof DataTable#oApi
- */
- function _fnReDraw( settings, holdPosition )
- {
- var
- features = settings.oFeatures,
- sort = features.bSort,
- filter = features.bFilter;
-
- if ( sort ) {
- _fnSort( settings );
- }
-
- if ( filter ) {
- _fnFilterComplete( settings, settings.oPreviousSearch );
- }
- else {
- // No filtering, so we want to just use the display master
- settings.aiDisplay = settings.aiDisplayMaster.slice();
- }
-
- if ( holdPosition !== true ) {
- settings._iDisplayStart = 0;
- }
-
- // Let any modules know about the draw hold position state (used by
- // scrolling internally)
- settings._drawHold = holdPosition;
-
- _fnDraw( settings );
-
- settings._drawHold = false;
- }
-
-
- /**
- * Add the options to the page HTML for the table
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnAddOptionsHtml ( oSettings )
- {
- var classes = oSettings.oClasses;
- var table = $(oSettings.nTable);
- var holding = $('<div/>').insertBefore( table ); // Holding element for speed
- var features = oSettings.oFeatures;
-
- // All DataTables are wrapped in a div
- var insert = $('<div/>', {
- id: oSettings.sTableId+'_wrapper',
- 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter)
- } );
-
- oSettings.nHolding = holding[0];
- oSettings.nTableWrapper = insert[0];
- oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
-
- /* Loop over the user set positioning and place the elements as needed */
- var aDom = oSettings.sDom.split('');
- var featureNode, cOption, nNewNode, cNext, sAttr, j;
- for ( var i=0 ; i<aDom.length ; i++ )
- {
- featureNode = null;
- cOption = aDom[i];
-
- if ( cOption == '<' )
- {
- /* New container div */
- nNewNode = $('<div/>')[0];
-
- /* Check to see if we should append an id and/or a class name to the container */
- cNext = aDom[i+1];
- if ( cNext == "'" || cNext == '"' )
- {
- sAttr = "";
- j = 2;
- while ( aDom[i+j] != cNext )
- {
- sAttr += aDom[i+j];
- j++;
- }
-
- /* Replace jQuery UI constants @todo depreciated */
- if ( sAttr == "H" )
- {
- sAttr = classes.sJUIHeader;
- }
- else if ( sAttr == "F" )
- {
- sAttr = classes.sJUIFooter;
- }
-
- /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
- * breaks the string into parts and applies them as needed
- */
- if ( sAttr.indexOf('.') != -1 )
- {
- var aSplit = sAttr.split('.');
- nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
- nNewNode.className = aSplit[1];
- }
- else if ( sAttr.charAt(0) == "#" )
- {
- nNewNode.id = sAttr.substr(1, sAttr.length-1);
- }
- else
- {
- nNewNode.className = sAttr;
- }
-
- i += j; /* Move along the position array */
- }
-
- insert.append( nNewNode );
- insert = $(nNewNode);
- }
- else if ( cOption == '>' )
- {
- /* End container div */
- insert = insert.parent();
- }
- // @todo Move options into their own plugins?
- else if ( cOption == 'l' && features.bPaginate && features.bLengthChange )
- {
- /* Length */
- featureNode = _fnFeatureHtmlLength( oSettings );
- }
- else if ( cOption == 'f' && features.bFilter )
- {
- /* Filter */
- featureNode = _fnFeatureHtmlFilter( oSettings );
- }
- else if ( cOption == 'r' && features.bProcessing )
- {
- /* pRocessing */
- featureNode = _fnFeatureHtmlProcessing( oSettings );
- }
- else if ( cOption == 't' )
- {
- /* Table */
- featureNode = _fnFeatureHtmlTable( oSettings );
- }
- else if ( cOption == 'i' && features.bInfo )
- {
- /* Info */
- featureNode = _fnFeatureHtmlInfo( oSettings );
- }
- else if ( cOption == 'p' && features.bPaginate )
- {
- /* Pagination */
- featureNode = _fnFeatureHtmlPaginate( oSettings );
- }
- else if ( DataTable.ext.feature.length !== 0 )
- {
- /* Plug-in features */
- var aoFeatures = DataTable.ext.feature;
- for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
- {
- if ( cOption == aoFeatures[k].cFeature )
- {
- featureNode = aoFeatures[k].fnInit( oSettings );
- break;
- }
- }
- }
-
- /* Add to the 2D features array */
- if ( featureNode )
- {
- var aanFeatures = oSettings.aanFeatures;
-
- if ( ! aanFeatures[cOption] )
- {
- aanFeatures[cOption] = [];
- }
-
- aanFeatures[cOption].push( featureNode );
- insert.append( featureNode );
- }
- }
-
- /* Built our DOM structure - replace the holding div with what we want */
- holding.replaceWith( insert );
- oSettings.nHolding = null;
- }
-
-
- /**
- * Use the DOM source to create up an array of header cells. The idea here is to
- * create a layout grid (array) of rows x columns, which contains a reference
- * to the cell that that point in the grid (regardless of col/rowspan), such that
- * any column / row could be removed and the new grid constructed
- * @param array {object} aLayout Array to store the calculated layout in
- * @param {node} nThead The header/footer element for the table
- * @memberof DataTable#oApi
- */
- function _fnDetectHeader ( aLayout, nThead )
- {
- var nTrs = $(nThead).children('tr');
- var nTr, nCell;
- var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
- var bUnique;
- var fnShiftCol = function ( a, i, j ) {
- var k = a[i];
- while ( k[j] ) {
- j++;
- }
- return j;
- };
-
- aLayout.splice( 0, aLayout.length );
-
- /* We know how many rows there are in the layout - so prep it */
- for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
- {
- aLayout.push( [] );
- }
-
- /* Calculate a layout array */
- for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
- {
- nTr = nTrs[i];
- iColumn = 0;
-
- /* For every cell in the row... */
- nCell = nTr.firstChild;
- while ( nCell ) {
- if ( nCell.nodeName.toUpperCase() == "TD" ||
- nCell.nodeName.toUpperCase() == "TH" )
- {
- /* Get the col and rowspan attributes from the DOM and sanitise them */
- iColspan = nCell.getAttribute('colspan') * 1;
- iRowspan = nCell.getAttribute('rowspan') * 1;
- iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
- iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
-
- /* There might be colspan cells already in this row, so shift our target
- * accordingly
- */
- iColShifted = fnShiftCol( aLayout, i, iColumn );
-
- /* Cache calculation for unique columns */
- bUnique = iColspan === 1 ? true : false;
-
- /* If there is col / rowspan, copy the information into the layout grid */
- for ( l=0 ; l<iColspan ; l++ )
- {
- for ( k=0 ; k<iRowspan ; k++ )
- {
- aLayout[i+k][iColShifted+l] = {
- "cell": nCell,
- "unique": bUnique
- };
- aLayout[i+k].nTr = nTr;
- }
- }
- }
- nCell = nCell.nextSibling;
- }
- }
- }
-
-
- /**
- * Get an array of unique th elements, one for each column
- * @param {object} oSettings dataTables settings object
- * @param {node} nHeader automatically detect the layout from this node - optional
- * @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional
- * @returns array {node} aReturn list of unique th's
- * @memberof DataTable#oApi
- */
- function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
- {
- var aReturn = [];
- if ( !aLayout )
- {
- aLayout = oSettings.aoHeader;
- if ( nHeader )
- {
- aLayout = [];
- _fnDetectHeader( aLayout, nHeader );
- }
- }
-
- for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
- {
- for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
- {
- if ( aLayout[i][j].unique &&
- (!aReturn[j] || !oSettings.bSortCellsTop) )
- {
- aReturn[j] = aLayout[i][j].cell;
- }
- }
- }
-
- return aReturn;
- }
-
- /**
- * Create an Ajax call based on the table's settings, taking into account that
- * parameters can have multiple forms, and backwards compatibility.
- *
- * @param {object} oSettings dataTables settings object
- * @param {array} data Data to send to the server, required by
- * DataTables - may be augmented by developer callbacks
- * @param {function} fn Callback function to run when data is obtained
- */
- function _fnBuildAjax( oSettings, data, fn )
- {
- // Compatibility with 1.9-, allow fnServerData and event to manipulate
- _fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] );
-
- // Convert to object based for 1.10+ if using the old array scheme which can
- // come from server-side processing or serverParams
- if ( data && $.isArray(data) ) {
- var tmp = {};
- var rbracket = /(.*?)\[\]$/;
-
- $.each( data, function (key, val) {
- var match = val.name.match(rbracket);
-
- if ( match ) {
- // Support for arrays
- var name = match[0];
-
- if ( ! tmp[ name ] ) {
- tmp[ name ] = [];
- }
- tmp[ name ].push( val.value );
- }
- else {
- tmp[val.name] = val.value;
- }
- } );
- data = tmp;
- }
-
- var ajaxData;
- var ajax = oSettings.ajax;
- var instance = oSettings.oInstance;
- var callback = function ( json ) {
- _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] );
- fn( json );
- };
-
- if ( $.isPlainObject( ajax ) && ajax.data )
- {
- ajaxData = ajax.data;
-
- var newData = $.isFunction( ajaxData ) ?
- ajaxData( data, oSettings ) : // fn can manipulate data or return
- ajaxData; // an object object or array to merge
-
- // If the function returned something, use that alone
- data = $.isFunction( ajaxData ) && newData ?
- newData :
- $.extend( true, data, newData );
-
- // Remove the data property as we've resolved it already and don't want
- // jQuery to do it again (it is restored at the end of the function)
- delete ajax.data;
- }
-
- var baseAjax = {
- "data": data,
- "success": function (json) {
- var error = json.error || json.sError;
- if ( error ) {
- _fnLog( oSettings, 0, error );
- }
-
- oSettings.json = json;
- callback( json );
- },
- "dataType": "json",
- "cache": false,
- "type": oSettings.sServerMethod,
- "error": function (xhr, error, thrown) {
- var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] );
-
- if ( $.inArray( true, ret ) === -1 ) {
- if ( error == "parsererror" ) {
- _fnLog( oSettings, 0, 'Invalid JSON response', 1 );
- }
- else if ( xhr.readyState === 4 ) {
- _fnLog( oSettings, 0, 'Ajax error', 7 );
- }
- }
-
- _fnProcessingDisplay( oSettings, false );
- }
- };
-
- // Store the data submitted for the API
- oSettings.oAjaxData = data;
-
- // Allow plug-ins and external processes to modify the data
- _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] );
-
- if ( oSettings.fnServerData )
- {
- // DataTables 1.9- compatibility
- oSettings.fnServerData.call( instance,
- oSettings.sAjaxSource,
- $.map( data, function (val, key) { // Need to convert back to 1.9 trad format
- return { name: key, value: val };
- } ),
- callback,
- oSettings
- );
- }
- else if ( oSettings.sAjaxSource || typeof ajax === 'string' )
- {
- // DataTables 1.9- compatibility
- oSettings.jqXHR = $.ajax( $.extend( baseAjax, {
- url: ajax || oSettings.sAjaxSource
- } ) );
- }
- else if ( $.isFunction( ajax ) )
- {
- // Is a function - let the caller define what needs to be done
- oSettings.jqXHR = ajax.call( instance, data, callback, oSettings );
- }
- else
- {
- // Object to extend the base settings
- oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) );
-
- // Restore for next time around
- ajax.data = ajaxData;
- }
- }
-
-
- /**
- * Update the table using an Ajax call
- * @param {object} settings dataTables settings object
- * @returns {boolean} Block the table drawing or not
- * @memberof DataTable#oApi
- */
- function _fnAjaxUpdate( settings )
- {
- if ( settings.bAjaxDataGet ) {
- settings.iDraw++;
- _fnProcessingDisplay( settings, true );
-
- _fnBuildAjax(
- settings,
- _fnAjaxParameters( settings ),
- function(json) {
- _fnAjaxUpdateDraw( settings, json );
- }
- );
-
- return false;
- }
- return true;
- }
-
-
- /**
- * Build up the parameters in an object needed for a server-side processing
- * request. Note that this is basically done twice, is different ways - a modern
- * method which is used by default in DataTables 1.10 which uses objects and
- * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if
- * the sAjaxSource option is used in the initialisation, or the legacyAjax
- * option is set.
- * @param {object} oSettings dataTables settings object
- * @returns {bool} block the table drawing or not
- * @memberof DataTable#oApi
- */
- function _fnAjaxParameters( settings )
- {
- var
- columns = settings.aoColumns,
- columnCount = columns.length,
- features = settings.oFeatures,
- preSearch = settings.oPreviousSearch,
- preColSearch = settings.aoPreSearchCols,
- i, data = [], dataProp, column, columnSearch,
- sort = _fnSortFlatten( settings ),
- displayStart = settings._iDisplayStart,
- displayLength = features.bPaginate !== false ?
- settings._iDisplayLength :
- -1;
-
- var param = function ( name, value ) {
- data.push( { 'name': name, 'value': value } );
- };
-
- // DataTables 1.9- compatible method
- param( 'sEcho', settings.iDraw );
- param( 'iColumns', columnCount );
- param( 'sColumns', _pluck( columns, 'sName' ).join(',') );
- param( 'iDisplayStart', displayStart );
- param( 'iDisplayLength', displayLength );
-
- // DataTables 1.10+ method
- var d = {
- draw: settings.iDraw,
- columns: [],
- order: [],
- start: displayStart,
- length: displayLength,
- search: {
- value: preSearch.sSearch,
- regex: preSearch.bRegex
- }
- };
-
- for ( i=0 ; i<columnCount ; i++ ) {
- column = columns[i];
- columnSearch = preColSearch[i];
- dataProp = typeof column.mData=="function" ? 'function' : column.mData ;
-
- d.columns.push( {
- data: dataProp,
- name: column.sName,
- searchable: column.bSearchable,
- orderable: column.bSortable,
- search: {
- value: columnSearch.sSearch,
- regex: columnSearch.bRegex
- }
- } );
-
- param( "mDataProp_"+i, dataProp );
-
- if ( features.bFilter ) {
- param( 'sSearch_'+i, columnSearch.sSearch );
- param( 'bRegex_'+i, columnSearch.bRegex );
- param( 'bSearchable_'+i, column.bSearchable );
- }
-
- if ( features.bSort ) {
- param( 'bSortable_'+i, column.bSortable );
- }
- }
-
- if ( features.bFilter ) {
- param( 'sSearch', preSearch.sSearch );
- param( 'bRegex', preSearch.bRegex );
- }
-
- if ( features.bSort ) {
- $.each( sort, function ( i, val ) {
- d.order.push( { column: val.col, dir: val.dir } );
-
- param( 'iSortCol_'+i, val.col );
- param( 'sSortDir_'+i, val.dir );
- } );
-
- param( 'iSortingCols', sort.length );
- }
-
- // If the legacy.ajax parameter is null, then we automatically decide which
- // form to use, based on sAjaxSource
- var legacy = DataTable.ext.legacy.ajax;
- if ( legacy === null ) {
- return settings.sAjaxSource ? data : d;
- }
-
- // Otherwise, if legacy has been specified then we use that to decide on the
- // form
- return legacy ? data : d;
- }
-
-
- /**
- * Data the data from the server (nuking the old) and redraw the table
- * @param {object} oSettings dataTables settings object
- * @param {object} json json data return from the server.
- * @param {string} json.sEcho Tracking flag for DataTables to match requests
- * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
- * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
- * @param {array} json.aaData The data to display on this page
- * @param {string} [json.sColumns] Column ordering (sName, comma separated)
- * @memberof DataTable#oApi
- */
- function _fnAjaxUpdateDraw ( settings, json )
- {
- // v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.
- // Support both
- var compat = function ( old, modern ) {
- return json[old] !== undefined ? json[old] : json[modern];
- };
-
- var data = _fnAjaxDataSrc( settings, json );
- var draw = compat( 'sEcho', 'draw' );
- var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' );
- var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );
-
- if ( draw ) {
- // Protect against out of sequence returns
- if ( draw*1 < settings.iDraw ) {
- return;
- }
- settings.iDraw = draw * 1;
- }
-
- _fnClearTable( settings );
- settings._iRecordsTotal = parseInt(recordsTotal, 10);
- settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
-
- for ( var i=0, ien=data.length ; i<ien ; i++ ) {
- _fnAddData( settings, data[i] );
- }
- settings.aiDisplay = settings.aiDisplayMaster.slice();
-
- settings.bAjaxDataGet = false;
- _fnDraw( settings );
-
- if ( ! settings._bInitComplete ) {
- _fnInitComplete( settings, json );
- }
-
- settings.bAjaxDataGet = true;
- _fnProcessingDisplay( settings, false );
- }
-
-
- /**
- * Get the data from the JSON data source to use for drawing a table. Using
- * `_fnGetObjectDataFn` allows the data to be sourced from a property of the
- * source object, or from a processing function.
- * @param {object} oSettings dataTables settings object
- * @param {object} json Data source object / array from the server
- * @return {array} Array of data to use
- */
- function _fnAjaxDataSrc ( oSettings, json )
- {
- var dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ?
- oSettings.ajax.dataSrc :
- oSettings.sAjaxDataProp; // Compatibility with 1.9-.
-
- // Compatibility with 1.9-. In order to read from aaData, check if the
- // default has been changed, if not, check for aaData
- if ( dataSrc === 'data' ) {
- return json.aaData || json[dataSrc];
- }
-
- return dataSrc !== "" ?
- _fnGetObjectDataFn( dataSrc )( json ) :
- json;
- }
-
- /**
- * Generate the node required for filtering text
- * @returns {node} Filter control element
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlFilter ( settings )
- {
- var classes = settings.oClasses;
- var tableId = settings.sTableId;
- var language = settings.oLanguage;
- var previousSearch = settings.oPreviousSearch;
- var features = settings.aanFeatures;
- var input = '<input type="search" class="'+classes.sFilterInput+'"/>';
-
- var str = language.sSearch;
- str = str.match(/_INPUT_/) ?
- str.replace('_INPUT_', input) :
- str+input;
-
- var filter = $('<div/>', {
- 'id': ! features.f ? tableId+'_filter' : null,
- 'class': classes.sFilter
- } )
- .append( $('<label/>' ).append( str ) );
-
- var searchFn = function() {
- /* Update all other filter input elements for the new display */
- var n = features.f;
- var val = !this.value ? "" : this.value; // mental IE8 fix :-(
-
- /* Now do the filter */
- if ( val != previousSearch.sSearch ) {
- _fnFilterComplete( settings, {
- "sSearch": val,
- "bRegex": previousSearch.bRegex,
- "bSmart": previousSearch.bSmart ,
- "bCaseInsensitive": previousSearch.bCaseInsensitive
- } );
-
- // Need to redraw, without resorting
- settings._iDisplayStart = 0;
- _fnDraw( settings );
- }
- };
-
- var searchDelay = settings.searchDelay !== null ?
- settings.searchDelay :
- _fnDataSource( settings ) === 'ssp' ?
- 400 :
- 0;
-
- var jqFilter = $('input', filter)
- .val( previousSearch.sSearch )
- .attr( 'placeholder', language.sSearchPlaceholder )
- .on(
- 'keyup.DT search.DT input.DT paste.DT cut.DT',
- searchDelay ?
- _fnThrottle( searchFn, searchDelay ) :
- searchFn
- )
- .on( 'keypress.DT', function(e) {
- /* Prevent form submission */
- if ( e.keyCode == 13 ) {
- return false;
- }
- } )
- .attr('aria-controls', tableId);
-
- // Update the input elements whenever the table is filtered
- $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {
- if ( settings === s ) {
- // IE9 throws an 'unknown error' if document.activeElement is used
- // inside an iframe or frame...
- try {
- if ( jqFilter[0] !== document.activeElement ) {
- jqFilter.val( previousSearch.sSearch );
- }
- }
- catch ( e ) {}
- }
- } );
-
- return filter[0];
- }
-
-
- /**
- * Filter the table using both the global filter and column based filtering
- * @param {object} oSettings dataTables settings object
- * @param {object} oSearch search information
- * @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)
- * @memberof DataTable#oApi
- */
- function _fnFilterComplete ( oSettings, oInput, iForce )
- {
- var oPrevSearch = oSettings.oPreviousSearch;
- var aoPrevSearch = oSettings.aoPreSearchCols;
- var fnSaveFilter = function ( oFilter ) {
- /* Save the filtering values */
- oPrevSearch.sSearch = oFilter.sSearch;
- oPrevSearch.bRegex = oFilter.bRegex;
- oPrevSearch.bSmart = oFilter.bSmart;
- oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
- };
- var fnRegex = function ( o ) {
- // Backwards compatibility with the bEscapeRegex option
- return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex;
- };
-
- // Resolve any column types that are unknown due to addition or invalidation
- // @todo As per sort - can this be moved into an event handler?
- _fnColumnTypes( oSettings );
-
- /* In server-side processing all filtering is done by the server, so no point hanging around here */
- if ( _fnDataSource( oSettings ) != 'ssp' )
- {
- /* Global filter */
- _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive );
- fnSaveFilter( oInput );
-
- /* Now do the individual column filter */
- for ( var i=0 ; i<aoPrevSearch.length ; i++ )
- {
- _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]),
- aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );
- }
-
- /* Custom filtering */
- _fnFilterCustom( oSettings );
- }
- else
- {
- fnSaveFilter( oInput );
- }
-
- /* Tell the draw function we have been filtering */
- oSettings.bFiltered = true;
- _fnCallbackFire( oSettings, null, 'search', [oSettings] );
- }
-
-
- /**
- * Apply custom filtering functions
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnFilterCustom( settings )
- {
- var filters = DataTable.ext.search;
- var displayRows = settings.aiDisplay;
- var row, rowIdx;
-
- for ( var i=0, ien=filters.length ; i<ien ; i++ ) {
- var rows = [];
-
- // Loop over each row and see if it should be included
- for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {
- rowIdx = displayRows[ j ];
- row = settings.aoData[ rowIdx ];
-
- if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {
- rows.push( rowIdx );
- }
- }
-
- // So the array reference doesn't break set the results into the
- // existing array
- displayRows.length = 0;
- $.merge( displayRows, rows );
- }
- }
-
-
- /**
- * Filter the table on a per-column basis
- * @param {object} oSettings dataTables settings object
- * @param {string} sInput string to filter on
- * @param {int} iColumn column to filter
- * @param {bool} bRegex treat search string as a regular expression or not
- * @param {bool} bSmart use smart filtering or not
- * @param {bool} bCaseInsensitive Do case insenstive matching or not
- * @memberof DataTable#oApi
- */
- function _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive )
- {
- if ( searchStr === '' ) {
- return;
- }
-
- var data;
- var out = [];
- var display = settings.aiDisplay;
- var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive );
-
- for ( var i=0 ; i<display.length ; i++ ) {
- data = settings.aoData[ display[i] ]._aFilterData[ colIdx ];
-
- if ( rpSearch.test( data ) ) {
- out.push( display[i] );
- }
- }
-
- settings.aiDisplay = out;
- }
-
-
- /**
- * Filter the data table based on user input and draw the table
- * @param {object} settings dataTables settings object
- * @param {string} input string to filter on
- * @param {int} force optional - force a research of the master array (1) or not (undefined or 0)
- * @param {bool} regex treat as a regular expression or not
- * @param {bool} smart perform smart filtering or not
- * @param {bool} caseInsensitive Do case insenstive matching or not
- * @memberof DataTable#oApi
- */
- function _fnFilter( settings, input, force, regex, smart, caseInsensitive )
- {
- var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive );
- var prevSearch = settings.oPreviousSearch.sSearch;
- var displayMaster = settings.aiDisplayMaster;
- var display, invalidated, i;
- var filtered = [];
-
- // Need to take account of custom filtering functions - always filter
- if ( DataTable.ext.search.length !== 0 ) {
- force = true;
- }
-
- // Check if any of the rows were invalidated
- invalidated = _fnFilterData( settings );
-
- // If the input is blank - we just want the full data set
- if ( input.length <= 0 ) {
- settings.aiDisplay = displayMaster.slice();
- }
- else {
- // New search - start from the master array
- if ( invalidated ||
- force ||
- prevSearch.length > input.length ||
- input.indexOf(prevSearch) !== 0 ||
- settings.bSorted // On resort, the display master needs to be
- // re-filtered since indexes will have changed
- ) {
- settings.aiDisplay = displayMaster.slice();
- }
-
- // Search the display array
- display = settings.aiDisplay;
-
- for ( i=0 ; i<display.length ; i++ ) {
- if ( rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) {
- filtered.push( display[i] );
- }
- }
-
- settings.aiDisplay = filtered;
- }
- }
-
-
- /**
- * Build a regular expression object suitable for searching a table
- * @param {string} sSearch string to search for
- * @param {bool} bRegex treat as a regular expression or not
- * @param {bool} bSmart perform smart filtering or not
- * @param {bool} bCaseInsensitive Do case insensitive matching or not
- * @returns {RegExp} constructed object
- * @memberof DataTable#oApi
- */
- function _fnFilterCreateSearch( search, regex, smart, caseInsensitive )
- {
- search = regex ?
- search :
- _fnEscapeRegex( search );
-
- if ( smart ) {
- /* For smart filtering we want to allow the search to work regardless of
- * word order. We also want double quoted text to be preserved, so word
- * order is important - a la google. So this is what we want to
- * generate:
- *
- * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
- */
- var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || [''], function ( word ) {
- if ( word.charAt(0) === '"' ) {
- var m = word.match( /^"(.*)"$/ );
- word = m ? m[1] : word;
- }
-
- return word.replace('"', '');
- } );
-
- search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$';
- }
-
- return new RegExp( search, caseInsensitive ? 'i' : '' );
- }
-
-
- /**
- * Escape a string such that it can be used in a regular expression
- * @param {string} sVal string to escape
- * @returns {string} escaped string
- * @memberof DataTable#oApi
- */
- var _fnEscapeRegex = DataTable.util.escapeRegex;
-
- var __filter_div = $('<div>')[0];
- var __filter_div_textContent = __filter_div.textContent !== undefined;
-
- // Update the filtering data for each row if needed (by invalidation or first run)
- function _fnFilterData ( settings )
- {
- var columns = settings.aoColumns;
- var column;
- var i, j, ien, jen, filterData, cellData, row;
- var fomatters = DataTable.ext.type.search;
- var wasInvalidated = false;
-
- for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
- row = settings.aoData[i];
-
- if ( ! row._aFilterData ) {
- filterData = [];
-
- for ( j=0, jen=columns.length ; j<jen ; j++ ) {
- column = columns[j];
-
- if ( column.bSearchable ) {
- cellData = _fnGetCellData( settings, i, j, 'filter' );
-
- if ( fomatters[ column.sType ] ) {
- cellData = fomatters[ column.sType ]( cellData );
- }
-
- // Search in DataTables 1.10 is string based. In 1.11 this
- // should be altered to also allow strict type checking.
- if ( cellData === null ) {
- cellData = '';
- }
-
- if ( typeof cellData !== 'string' && cellData.toString ) {
- cellData = cellData.toString();
- }
- }
- else {
- cellData = '';
- }
-
- // If it looks like there is an HTML entity in the string,
- // attempt to decode it so sorting works as expected. Note that
- // we could use a single line of jQuery to do this, but the DOM
- // method used here is much faster http://jsperf.com/html-decode
- if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {
- __filter_div.innerHTML = cellData;
- cellData = __filter_div_textContent ?
- __filter_div.textContent :
- __filter_div.innerText;
- }
-
- if ( cellData.replace ) {
- cellData = cellData.replace(/[\r\n]/g, '');
- }
-
- filterData.push( cellData );
- }
-
- row._aFilterData = filterData;
- row._sFilterRow = filterData.join(' ');
- wasInvalidated = true;
- }
- }
-
- return wasInvalidated;
- }
-
-
- /**
- * Convert from the internal Hungarian notation to camelCase for external
- * interaction
- * @param {object} obj Object to convert
- * @returns {object} Inverted object
- * @memberof DataTable#oApi
- */
- function _fnSearchToCamel ( obj )
- {
- return {
- search: obj.sSearch,
- smart: obj.bSmart,
- regex: obj.bRegex,
- caseInsensitive: obj.bCaseInsensitive
- };
- }
-
-
-
- /**
- * Convert from camelCase notation to the internal Hungarian. We could use the
- * Hungarian convert function here, but this is cleaner
- * @param {object} obj Object to convert
- * @returns {object} Inverted object
- * @memberof DataTable#oApi
- */
- function _fnSearchToHung ( obj )
- {
- return {
- sSearch: obj.search,
- bSmart: obj.smart,
- bRegex: obj.regex,
- bCaseInsensitive: obj.caseInsensitive
- };
- }
-
- /**
- * Generate the node required for the info display
- * @param {object} oSettings dataTables settings object
- * @returns {node} Information element
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlInfo ( settings )
- {
- var
- tid = settings.sTableId,
- nodes = settings.aanFeatures.i,
- n = $('<div/>', {
- 'class': settings.oClasses.sInfo,
- 'id': ! nodes ? tid+'_info' : null
- } );
-
- if ( ! nodes ) {
- // Update display on each draw
- settings.aoDrawCallback.push( {
- "fn": _fnUpdateInfo,
- "sName": "information"
- } );
-
- n
- .attr( 'role', 'status' )
- .attr( 'aria-live', 'polite' );
-
- // Table is described by our info div
- $(settings.nTable).attr( 'aria-describedby', tid+'_info' );
- }
-
- return n[0];
- }
-
-
- /**
- * Update the information elements in the display
- * @param {object} settings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnUpdateInfo ( settings )
- {
- /* Show information about the table */
- var nodes = settings.aanFeatures.i;
- if ( nodes.length === 0 ) {
- return;
- }
-
- var
- lang = settings.oLanguage,
- start = settings._iDisplayStart+1,
- end = settings.fnDisplayEnd(),
- max = settings.fnRecordsTotal(),
- total = settings.fnRecordsDisplay(),
- out = total ?
- lang.sInfo :
- lang.sInfoEmpty;
-
- if ( total !== max ) {
- /* Record set after filtering */
- out += ' ' + lang.sInfoFiltered;
- }
-
- // Convert the macros
- out += lang.sInfoPostFix;
- out = _fnInfoMacros( settings, out );
-
- var callback = lang.fnInfoCallback;
- if ( callback !== null ) {
- out = callback.call( settings.oInstance,
- settings, start, end, max, total, out
- );
- }
-
- $(nodes).html( out );
- }
-
-
- function _fnInfoMacros ( settings, str )
- {
- // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
- // internally
- var
- formatter = settings.fnFormatNumber,
- start = settings._iDisplayStart+1,
- len = settings._iDisplayLength,
- vis = settings.fnRecordsDisplay(),
- all = len === -1;
-
- return str.
- replace(/_START_/g, formatter.call( settings, start ) ).
- replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ).
- replace(/_MAX_/g, formatter.call( settings, settings.fnRecordsTotal() ) ).
- replace(/_TOTAL_/g, formatter.call( settings, vis ) ).
- replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).
- replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) );
- }
-
-
-
- /**
- * Draw the table for the first time, adding all required features
- * @param {object} settings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnInitialise ( settings )
- {
- var i, iLen, iAjaxStart=settings.iInitDisplayStart;
- var columns = settings.aoColumns, column;
- var features = settings.oFeatures;
- var deferLoading = settings.bDeferLoading; // value modified by the draw
-
- /* Ensure that the table data is fully initialised */
- if ( ! settings.bInitialised ) {
- setTimeout( function(){ _fnInitialise( settings ); }, 200 );
- return;
- }
-
- /* Show the display HTML options */
- _fnAddOptionsHtml( settings );
-
- /* Build and draw the header / footer for the table */
- _fnBuildHead( settings );
- _fnDrawHead( settings, settings.aoHeader );
- _fnDrawHead( settings, settings.aoFooter );
-
- /* Okay to show that something is going on now */
- _fnProcessingDisplay( settings, true );
-
- /* Calculate sizes for columns */
- if ( features.bAutoWidth ) {
- _fnCalculateColumnWidths( settings );
- }
-
- for ( i=0, iLen=columns.length ; i<iLen ; i++ ) {
- column = columns[i];
-
- if ( column.sWidth ) {
- column.nTh.style.width = _fnStringToCss( column.sWidth );
- }
- }
-
- _fnCallbackFire( settings, null, 'preInit', [settings] );
-
- // If there is default sorting required - let's do it. The sort function
- // will do the drawing for us. Otherwise we draw the table regardless of the
- // Ajax source - this allows the table to look initialised for Ajax sourcing
- // data (show 'loading' message possibly)
- _fnReDraw( settings );
-
- // Server-side processing init complete is done by _fnAjaxUpdateDraw
- var dataSrc = _fnDataSource( settings );
- if ( dataSrc != 'ssp' || deferLoading ) {
- // if there is an ajax source load the data
- if ( dataSrc == 'ajax' ) {
- _fnBuildAjax( settings, [], function(json) {
- var aData = _fnAjaxDataSrc( settings, json );
-
- // Got the data - add it to the table
- for ( i=0 ; i<aData.length ; i++ ) {
- _fnAddData( settings, aData[i] );
- }
-
- // Reset the init display for cookie saving. We've already done
- // a filter, and therefore cleared it before. So we need to make
- // it appear 'fresh'
- settings.iInitDisplayStart = iAjaxStart;
-
- _fnReDraw( settings );
-
- _fnProcessingDisplay( settings, false );
- _fnInitComplete( settings, json );
- }, settings );
- }
- else {
- _fnProcessingDisplay( settings, false );
- _fnInitComplete( settings );
- }
- }
- }
-
-
- /**
- * Draw the table for the first time, adding all required features
- * @param {object} oSettings dataTables settings object
- * @param {object} [json] JSON from the server that completed the table, if using Ajax source
- * with client-side processing (optional)
- * @memberof DataTable#oApi
- */
- function _fnInitComplete ( settings, json )
- {
- settings._bInitComplete = true;
-
- // When data was added after the initialisation (data or Ajax) we need to
- // calculate the column sizing
- if ( json || settings.oInit.aaData ) {
- _fnAdjustColumnSizing( settings );
- }
-
- _fnCallbackFire( settings, null, 'plugin-init', [settings, json] );
- _fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] );
- }
-
-
- function _fnLengthChange ( settings, val )
- {
- var len = parseInt( val, 10 );
- settings._iDisplayLength = len;
-
- _fnLengthOverflow( settings );
-
- // Fire length change event
- _fnCallbackFire( settings, null, 'length', [settings, len] );
- }
-
-
- /**
- * Generate the node required for user display length changing
- * @param {object} settings dataTables settings object
- * @returns {node} Display length feature node
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlLength ( settings )
- {
- var
- classes = settings.oClasses,
- tableId = settings.sTableId,
- menu = settings.aLengthMenu,
- d2 = $.isArray( menu[0] ),
- lengths = d2 ? menu[0] : menu,
- language = d2 ? menu[1] : menu;
-
- var select = $('<select/>', {
- 'name': tableId+'_length',
- 'aria-controls': tableId,
- 'class': classes.sLengthSelect
- } );
-
- for ( var i=0, ien=lengths.length ; i<ien ; i++ ) {
- select[0][ i ] = new Option(
- typeof language[i] === 'number' ?
- settings.fnFormatNumber( language[i] ) :
- language[i],
- lengths[i]
- );
- }
-
- var div = $('<div><label/></div>').addClass( classes.sLength );
- if ( ! settings.aanFeatures.l ) {
- div[0].id = tableId+'_length';
- }
-
- div.children().append(
- settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML )
- );
-
- // Can't use `select` variable as user might provide their own and the
- // reference is broken by the use of outerHTML
- $('select', div)
- .val( settings._iDisplayLength )
- .on( 'change.DT', function(e) {
- _fnLengthChange( settings, $(this).val() );
- _fnDraw( settings );
- } );
-
- // Update node value whenever anything changes the table's length
- $(settings.nTable).on( 'length.dt.DT', function (e, s, len) {
- if ( settings === s ) {
- $('select', div).val( len );
- }
- } );
-
- return div[0];
- }
-
-
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Note that most of the paging logic is done in
- * DataTable.ext.pager
- */
-
- /**
- * Generate the node required for default pagination
- * @param {object} oSettings dataTables settings object
- * @returns {node} Pagination feature node
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlPaginate ( settings )
- {
- var
- type = settings.sPaginationType,
- plugin = DataTable.ext.pager[ type ],
- modern = typeof plugin === 'function',
- redraw = function( settings ) {
- _fnDraw( settings );
- },
- node = $('<div/>').addClass( settings.oClasses.sPaging + type )[0],
- features = settings.aanFeatures;
-
- if ( ! modern ) {
- plugin.fnInit( settings, node, redraw );
- }
-
- /* Add a draw callback for the pagination on first instance, to update the paging display */
- if ( ! features.p )
- {
- node.id = settings.sTableId+'_paginate';
-
- settings.aoDrawCallback.push( {
- "fn": function( settings ) {
- if ( modern ) {
- var
- start = settings._iDisplayStart,
- len = settings._iDisplayLength,
- visRecords = settings.fnRecordsDisplay(),
- all = len === -1,
- page = all ? 0 : Math.ceil( start / len ),
- pages = all ? 1 : Math.ceil( visRecords / len ),
- buttons = plugin(page, pages),
- i, ien;
-
- for ( i=0, ien=features.p.length ; i<ien ; i++ ) {
- _fnRenderer( settings, 'pageButton' )(
- settings, features.p[i], i, buttons, page, pages
- );
- }
- }
- else {
- plugin.fnUpdate( settings, redraw );
- }
- },
- "sName": "pagination"
- } );
- }
-
- return node;
- }
-
-
- /**
- * Alter the display settings to change the page
- * @param {object} settings DataTables settings object
- * @param {string|int} action Paging action to take: "first", "previous",
- * "next" or "last" or page number to jump to (integer)
- * @param [bool] redraw Automatically draw the update or not
- * @returns {bool} true page has changed, false - no change
- * @memberof DataTable#oApi
- */
- function _fnPageChange ( settings, action, redraw )
- {
- var
- start = settings._iDisplayStart,
- len = settings._iDisplayLength,
- records = settings.fnRecordsDisplay();
-
- if ( records === 0 || len === -1 )
- {
- start = 0;
- }
- else if ( typeof action === "number" )
- {
- start = action * len;
-
- if ( start > records )
- {
- start = 0;
- }
- }
- else if ( action == "first" )
- {
- start = 0;
- }
- else if ( action == "previous" )
- {
- start = len >= 0 ?
- start - len :
- 0;
-
- if ( start < 0 )
- {
- start = 0;
- }
- }
- else if ( action == "next" )
- {
- if ( start + len < records )
- {
- start += len;
- }
- }
- else if ( action == "last" )
- {
- start = Math.floor( (records-1) / len) * len;
- }
- else
- {
- _fnLog( settings, 0, "Unknown paging action: "+action, 5 );
- }
-
- var changed = settings._iDisplayStart !== start;
- settings._iDisplayStart = start;
-
- if ( changed ) {
- _fnCallbackFire( settings, null, 'page', [settings] );
-
- if ( redraw ) {
- _fnDraw( settings );
- }
- }
-
- return changed;
- }
-
-
-
- /**
- * Generate the node required for the processing node
- * @param {object} settings dataTables settings object
- * @returns {node} Processing element
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlProcessing ( settings )
- {
- return $('<div/>', {
- 'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null,
- 'class': settings.oClasses.sProcessing
- } )
- .html( settings.oLanguage.sProcessing )
- .insertBefore( settings.nTable )[0];
- }
-
-
- /**
- * Display or hide the processing indicator
- * @param {object} settings dataTables settings object
- * @param {bool} show Show the processing indicator (true) or not (false)
- * @memberof DataTable#oApi
- */
- function _fnProcessingDisplay ( settings, show )
- {
- if ( settings.oFeatures.bProcessing ) {
- $(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' );
- }
-
- _fnCallbackFire( settings, null, 'processing', [settings, show] );
- }
-
- /**
- * Add any control elements for the table - specifically scrolling
- * @param {object} settings dataTables settings object
- * @returns {node} Node to add to the DOM
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlTable ( settings )
- {
- var table = $(settings.nTable);
-
- // Add the ARIA grid role to the table
- table.attr( 'role', 'grid' );
-
- // Scrolling from here on in
- var scroll = settings.oScroll;
-
- if ( scroll.sX === '' && scroll.sY === '' ) {
- return settings.nTable;
- }
-
- var scrollX = scroll.sX;
- var scrollY = scroll.sY;
- var classes = settings.oClasses;
- var caption = table.children('caption');
- var captionSide = caption.length ? caption[0]._captionSide : null;
- var headerClone = $( table[0].cloneNode(false) );
- var footerClone = $( table[0].cloneNode(false) );
- var footer = table.children('tfoot');
- var _div = '<div/>';
- var size = function ( s ) {
- return !s ? null : _fnStringToCss( s );
- };
-
- if ( ! footer.length ) {
- footer = null;
- }
-
- /*
- * The HTML structure that we want to generate in this function is:
- * div - scroller
- * div - scroll head
- * div - scroll head inner
- * table - scroll head table
- * thead - thead
- * div - scroll body
- * table - table (master table)
- * thead - thead clone for sizing
- * tbody - tbody
- * div - scroll foot
- * div - scroll foot inner
- * table - scroll foot table
- * tfoot - tfoot
- */
- var scroller = $( _div, { 'class': classes.sScrollWrapper } )
- .append(
- $(_div, { 'class': classes.sScrollHead } )
- .css( {
- overflow: 'hidden',
- position: 'relative',
- border: 0,
- width: scrollX ? size(scrollX) : '100%'
- } )
- .append(
- $(_div, { 'class': classes.sScrollHeadInner } )
- .css( {
- 'box-sizing': 'content-box',
- width: scroll.sXInner || '100%'
- } )
- .append(
- headerClone
- .removeAttr('id')
- .css( 'margin-left', 0 )
- .append( captionSide === 'top' ? caption : null )
- .append(
- table.children('thead')
- )
- )
- )
- )
- .append(
- $(_div, { 'class': classes.sScrollBody } )
- .css( {
- position: 'relative',
- overflow: 'auto',
- width: size( scrollX )
- } )
- .append( table )
- );
-
- if ( footer ) {
- scroller.append(
- $(_div, { 'class': classes.sScrollFoot } )
- .css( {
- overflow: 'hidden',
- border: 0,
- width: scrollX ? size(scrollX) : '100%'
- } )
- .append(
- $(_div, { 'class': classes.sScrollFootInner } )
- .append(
- footerClone
- .removeAttr('id')
- .css( 'margin-left', 0 )
- .append( captionSide === 'bottom' ? caption : null )
- .append(
- table.children('tfoot')
- )
- )
- )
- );
- }
-
- var children = scroller.children();
- var scrollHead = children[0];
- var scrollBody = children[1];
- var scrollFoot = footer ? children[2] : null;
-
- // When the body is scrolled, then we also want to scroll the headers
- if ( scrollX ) {
- $(scrollBody).on( 'scroll.DT', function (e) {
- var scrollLeft = this.scrollLeft;
-
- scrollHead.scrollLeft = scrollLeft;
-
- if ( footer ) {
- scrollFoot.scrollLeft = scrollLeft;
- }
- } );
- }
-
- $(scrollBody).css(
- scrollY && scroll.bCollapse ? 'max-height' : 'height',
- scrollY
- );
-
- settings.nScrollHead = scrollHead;
- settings.nScrollBody = scrollBody;
- settings.nScrollFoot = scrollFoot;
-
- // On redraw - align columns
- settings.aoDrawCallback.push( {
- "fn": _fnScrollDraw,
- "sName": "scrolling"
- } );
-
- return scroller[0];
- }
-
-
-
- /**
- * Update the header, footer and body tables for resizing - i.e. column
- * alignment.
- *
- * Welcome to the most horrible function DataTables. The process that this
- * function follows is basically:
- * 1. Re-create the table inside the scrolling div
- * 2. Take live measurements from the DOM
- * 3. Apply the measurements to align the columns
- * 4. Clean up
- *
- * @param {object} settings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnScrollDraw ( settings )
- {
- // Given that this is such a monster function, a lot of variables are use
- // to try and keep the minimised size as small as possible
- var
- scroll = settings.oScroll,
- scrollX = scroll.sX,
- scrollXInner = scroll.sXInner,
- scrollY = scroll.sY,
- barWidth = scroll.iBarWidth,
- divHeader = $(settings.nScrollHead),
- divHeaderStyle = divHeader[0].style,
- divHeaderInner = divHeader.children('div'),
- divHeaderInnerStyle = divHeaderInner[0].style,
- divHeaderTable = divHeaderInner.children('table'),
- divBodyEl = settings.nScrollBody,
- divBody = $(divBodyEl),
- divBodyStyle = divBodyEl.style,
- divFooter = $(settings.nScrollFoot),
- divFooterInner = divFooter.children('div'),
- divFooterTable = divFooterInner.children('table'),
- header = $(settings.nTHead),
- table = $(settings.nTable),
- tableEl = table[0],
- tableStyle = tableEl.style,
- footer = settings.nTFoot ? $(settings.nTFoot) : null,
- browser = settings.oBrowser,
- ie67 = browser.bScrollOversize,
- dtHeaderCells = _pluck( settings.aoColumns, 'nTh' ),
- headerTrgEls, footerTrgEls,
- headerSrcEls, footerSrcEls,
- headerCopy, footerCopy,
- headerWidths=[], footerWidths=[],
- headerContent=[], footerContent=[],
- idx, correction, sanityWidth,
- zeroOut = function(nSizer) {
- var style = nSizer.style;
- style.paddingTop = "0";
- style.paddingBottom = "0";
- style.borderTopWidth = "0";
- style.borderBottomWidth = "0";
- style.height = 0;
- };
-
- // If the scrollbar visibility has changed from the last draw, we need to
- // adjust the column sizes as the table width will have changed to account
- // for the scrollbar
- var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight;
-
- if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) {
- settings.scrollBarVis = scrollBarVis;
- _fnAdjustColumnSizing( settings );
- return; // adjust column sizing will call this function again
- }
- else {
- settings.scrollBarVis = scrollBarVis;
- }
-
- /*
- * 1. Re-create the table inside the scrolling div
- */
-
- // Remove the old minimised thead and tfoot elements in the inner table
- table.children('thead, tfoot').remove();
-
- if ( footer ) {
- footerCopy = footer.clone().prependTo( table );
- footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized
- footerSrcEls = footerCopy.find('tr');
- }
-
- // Clone the current header and footer elements and then place it into the inner table
- headerCopy = header.clone().prependTo( table );
- headerTrgEls = header.find('tr'); // original header is in its own table
- headerSrcEls = headerCopy.find('tr');
- headerCopy.find('th, td').removeAttr('tabindex');
-
-
- /*
- * 2. Take live measurements from the DOM - do not alter the DOM itself!
- */
-
- // Remove old sizing and apply the calculated column widths
- // Get the unique column headers in the newly created (cloned) header. We want to apply the
- // calculated sizes to this header
- if ( ! scrollX )
- {
- divBodyStyle.width = '100%';
- divHeader[0].style.width = '100%';
- }
-
- $.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) {
- idx = _fnVisibleToColumnIndex( settings, i );
- el.style.width = settings.aoColumns[idx].sWidth;
- } );
-
- if ( footer ) {
- _fnApplyToChildren( function(n) {
- n.style.width = "";
- }, footerSrcEls );
- }
-
- // Size the table as a whole
- sanityWidth = table.outerWidth();
- if ( scrollX === "" ) {
- // No x scrolling
- tableStyle.width = "100%";
-
- // IE7 will make the width of the table when 100% include the scrollbar
- // - which is shouldn't. When there is a scrollbar we need to take this
- // into account.
- if ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight ||
- divBody.css('overflow-y') == "scroll")
- ) {
- tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth);
- }
-
- // Recalculate the sanity width
- sanityWidth = table.outerWidth();
- }
- else if ( scrollXInner !== "" ) {
- // legacy x scroll inner has been given - use it
- tableStyle.width = _fnStringToCss(scrollXInner);
-
- // Recalculate the sanity width
- sanityWidth = table.outerWidth();
- }
-
- // Hidden header should have zero height, so remove padding and borders. Then
- // set the width based on the real headers
-
- // Apply all styles in one pass
- _fnApplyToChildren( zeroOut, headerSrcEls );
-
- // Read all widths in next pass
- _fnApplyToChildren( function(nSizer) {
- headerContent.push( nSizer.innerHTML );
- headerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
- }, headerSrcEls );
-
- // Apply all widths in final pass
- _fnApplyToChildren( function(nToSize, i) {
- // Only apply widths to the DataTables detected header cells - this
- // prevents complex headers from having contradictory sizes applied
- if ( $.inArray( nToSize, dtHeaderCells ) !== -1 ) {
- nToSize.style.width = headerWidths[i];
- }
- }, headerTrgEls );
-
- $(headerSrcEls).height(0);
-
- /* Same again with the footer if we have one */
- if ( footer )
- {
- _fnApplyToChildren( zeroOut, footerSrcEls );
-
- _fnApplyToChildren( function(nSizer) {
- footerContent.push( nSizer.innerHTML );
- footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
- }, footerSrcEls );
-
- _fnApplyToChildren( function(nToSize, i) {
- nToSize.style.width = footerWidths[i];
- }, footerTrgEls );
-
- $(footerSrcEls).height(0);
- }
-
-
- /*
- * 3. Apply the measurements
- */
-
- // "Hide" the header and footer that we used for the sizing. We need to keep
- // the content of the cell so that the width applied to the header and body
- // both match, but we want to hide it completely. We want to also fix their
- // width to what they currently are
- _fnApplyToChildren( function(nSizer, i) {
- nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+headerContent[i]+'</div>';
- nSizer.style.width = headerWidths[i];
- }, headerSrcEls );
-
- if ( footer )
- {
- _fnApplyToChildren( function(nSizer, i) {
- nSizer.innerHTML = '<div class="dataTables_sizing" style="height:0;overflow:hidden;">'+footerContent[i]+'</div>';
- nSizer.style.width = footerWidths[i];
- }, footerSrcEls );
- }
-
- // Sanity check that the table is of a sensible width. If not then we are going to get
- // misalignment - try to prevent this by not allowing the table to shrink below its min width
- if ( table.outerWidth() < sanityWidth )
- {
- // The min width depends upon if we have a vertical scrollbar visible or not */
- correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight ||
- divBody.css('overflow-y') == "scroll")) ?
- sanityWidth+barWidth :
- sanityWidth;
-
- // IE6/7 are a law unto themselves...
- if ( ie67 && (divBodyEl.scrollHeight >
- divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll")
- ) {
- tableStyle.width = _fnStringToCss( correction-barWidth );
- }
-
- // And give the user a warning that we've stopped the table getting too small
- if ( scrollX === "" || scrollXInner !== "" ) {
- _fnLog( settings, 1, 'Possible column misalignment', 6 );
- }
- }
- else
- {
- correction = '100%';
- }
-
- // Apply to the container elements
- divBodyStyle.width = _fnStringToCss( correction );
- divHeaderStyle.width = _fnStringToCss( correction );
-
- if ( footer ) {
- settings.nScrollFoot.style.width = _fnStringToCss( correction );
- }
-
-
- /*
- * 4. Clean up
- */
- if ( ! scrollY ) {
- /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
- * the scrollbar height from the visible display, rather than adding it on. We need to
- * set the height in order to sort this. Don't want to do it in any other browsers.
- */
- if ( ie67 ) {
- divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth );
- }
- }
-
- /* Finally set the width's of the header and footer tables */
- var iOuterWidth = table.outerWidth();
- divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth );
- divHeaderInnerStyle.width = _fnStringToCss( iOuterWidth );
-
- // Figure out if there are scrollbar present - if so then we need a the header and footer to
- // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
- var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
- var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
- divHeaderInnerStyle[ padding ] = bScrolling ? barWidth+"px" : "0px";
-
- if ( footer ) {
- divFooterTable[0].style.width = _fnStringToCss( iOuterWidth );
- divFooterInner[0].style.width = _fnStringToCss( iOuterWidth );
- divFooterInner[0].style[padding] = bScrolling ? barWidth+"px" : "0px";
- }
-
- // Correct DOM ordering for colgroup - comes before the thead
- table.children('colgroup').insertBefore( table.children('thead') );
-
- /* Adjust the position of the header in case we loose the y-scrollbar */
- divBody.scroll();
-
- // If sorting or filtering has occurred, jump the scrolling back to the top
- // only if we aren't holding the position
- if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {
- divBodyEl.scrollTop = 0;
- }
- }
-
-
-
- /**
- * Apply a given function to the display child nodes of an element array (typically
- * TD children of TR rows
- * @param {function} fn Method to apply to the objects
- * @param array {nodes} an1 List of elements to look through for display children
- * @param array {nodes} an2 Another list (identical structure to the first) - optional
- * @memberof DataTable#oApi
- */
- function _fnApplyToChildren( fn, an1, an2 )
- {
- var index=0, i=0, iLen=an1.length;
- var nNode1, nNode2;
-
- while ( i < iLen ) {
- nNode1 = an1[i].firstChild;
- nNode2 = an2 ? an2[i].firstChild : null;
-
- while ( nNode1 ) {
- if ( nNode1.nodeType === 1 ) {
- if ( an2 ) {
- fn( nNode1, nNode2, index );
- }
- else {
- fn( nNode1, index );
- }
-
- index++;
- }
-
- nNode1 = nNode1.nextSibling;
- nNode2 = an2 ? nNode2.nextSibling : null;
- }
-
- i++;
- }
- }
-
-
-
- var __re_html_remove = /<.*?>/g;
-
-
- /**
- * Calculate the width of columns for the table
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnCalculateColumnWidths ( oSettings )
- {
- var
- table = oSettings.nTable,
- columns = oSettings.aoColumns,
- scroll = oSettings.oScroll,
- scrollY = scroll.sY,
- scrollX = scroll.sX,
- scrollXInner = scroll.sXInner,
- columnCount = columns.length,
- visibleColumns = _fnGetColumns( oSettings, 'bVisible' ),
- headerCells = $('th', oSettings.nTHead),
- tableWidthAttr = table.getAttribute('width'), // from DOM element
- tableContainer = table.parentNode,
- userInputs = false,
- i, column, columnIdx, width, outerWidth,
- browser = oSettings.oBrowser,
- ie67 = browser.bScrollOversize;
-
- var styleWidth = table.style.width;
- if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
- tableWidthAttr = styleWidth;
- }
-
- /* Convert any user input sizes into pixel sizes */
- for ( i=0 ; i<visibleColumns.length ; i++ ) {
- column = columns[ visibleColumns[i] ];
-
- if ( column.sWidth !== null ) {
- column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );
-
- userInputs = true;
- }
- }
-
- /* If the number of columns in the DOM equals the number that we have to
- * process in DataTables, then we can use the offsets that are created by
- * the web- browser. No custom sizes can be set in order for this to happen,
- * nor scrolling used
- */
- if ( ie67 || ! userInputs && ! scrollX && ! scrollY &&
- columnCount == _fnVisbleColumns( oSettings ) &&
- columnCount == headerCells.length
- ) {
- for ( i=0 ; i<columnCount ; i++ ) {
- var colIdx = _fnVisibleToColumnIndex( oSettings, i );
-
- if ( colIdx !== null ) {
- columns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() );
- }
- }
- }
- else
- {
- // Otherwise construct a single row, worst case, table with the widest
- // node in the data, assign any user defined widths, then insert it into
- // the DOM and allow the browser to do all the hard work of calculating
- // table widths
- var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
- .css( 'visibility', 'hidden' )
- .removeAttr( 'id' );
-
- // Clean up the table body
- tmpTable.find('tbody tr').remove();
- var tr = $('<tr/>').appendTo( tmpTable.find('tbody') );
-
- // Clone the table header and footer - we can't use the header / footer
- // from the cloned table, since if scrolling is active, the table's
- // real header and footer are contained in different table tags
- tmpTable.find('thead, tfoot').remove();
- tmpTable
- .append( $(oSettings.nTHead).clone() )
- .append( $(oSettings.nTFoot).clone() );
-
- // Remove any assigned widths from the footer (from scrolling)
- tmpTable.find('tfoot th, tfoot td').css('width', '');
-
- // Apply custom sizing to the cloned header
- headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );
-
- for ( i=0 ; i<visibleColumns.length ; i++ ) {
- column = columns[ visibleColumns[i] ];
-
- headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
- _fnStringToCss( column.sWidthOrig ) :
- '';
-
- // For scrollX we need to force the column width otherwise the
- // browser will collapse it. If this width is smaller than the
- // width the column requires, then it will have no effect
- if ( column.sWidthOrig && scrollX ) {
- $( headerCells[i] ).append( $('<div/>').css( {
- width: column.sWidthOrig,
- margin: 0,
- padding: 0,
- border: 0,
- height: 1
- } ) );
- }
- }
-
- // Find the widest cell for each column and put it into the table
- if ( oSettings.aoData.length ) {
- for ( i=0 ; i<visibleColumns.length ; i++ ) {
- columnIdx = visibleColumns[i];
- column = columns[ columnIdx ];
-
- $( _fnGetWidestNode( oSettings, columnIdx ) )
- .clone( false )
- .append( column.sContentPadding )
- .appendTo( tr );
- }
- }
-
- // Tidy the temporary table - remove name attributes so there aren't
- // duplicated in the dom (radio elements for example)
- $('[name]', tmpTable).removeAttr('name');
-
- // Table has been built, attach to the document so we can work with it.
- // A holding element is used, positioned at the top of the container
- // with minimal height, so it has no effect on if the container scrolls
- // or not. Otherwise it might trigger scrolling when it actually isn't
- // needed
- var holder = $('<div/>').css( scrollX || scrollY ?
- {
- position: 'absolute',
- top: 0,
- left: 0,
- height: 1,
- right: 0,
- overflow: 'hidden'
- } :
- {}
- )
- .append( tmpTable )
- .appendTo( tableContainer );
-
- // When scrolling (X or Y) we want to set the width of the table as
- // appropriate. However, when not scrolling leave the table width as it
- // is. This results in slightly different, but I think correct behaviour
- if ( scrollX && scrollXInner ) {
- tmpTable.width( scrollXInner );
- }
- else if ( scrollX ) {
- tmpTable.css( 'width', 'auto' );
- tmpTable.removeAttr('width');
-
- // If there is no width attribute or style, then allow the table to
- // collapse
- if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) {
- tmpTable.width( tableContainer.clientWidth );
- }
- }
- else if ( scrollY ) {
- tmpTable.width( tableContainer.clientWidth );
- }
- else if ( tableWidthAttr ) {
- tmpTable.width( tableWidthAttr );
- }
-
- // Get the width of each column in the constructed table - we need to
- // know the inner width (so it can be assigned to the other table's
- // cells) and the outer width so we can calculate the full width of the
- // table. This is safe since DataTables requires a unique cell for each
- // column, but if ever a header can span multiple columns, this will
- // need to be modified.
- var total = 0;
- for ( i=0 ; i<visibleColumns.length ; i++ ) {
- var cell = $(headerCells[i]);
- var border = cell.outerWidth() - cell.width();
-
- // Use getBounding... where possible (not IE8-) because it can give
- // sub-pixel accuracy, which we then want to round up!
- var bounding = browser.bBounding ?
- Math.ceil( headerCells[i].getBoundingClientRect().width ) :
- cell.outerWidth();
-
- // Total is tracked to remove any sub-pixel errors as the outerWidth
- // of the table might not equal the total given here (IE!).
- total += bounding;
-
- // Width for each column to use
- columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding - border );
- }
-
- table.style.width = _fnStringToCss( total );
-
- // Finished with the table - ditch it
- holder.remove();
- }
-
- // If there is a width attr, we want to attach an event listener which
- // allows the table sizing to automatically adjust when the window is
- // resized. Use the width attr rather than CSS, since we can't know if the
- // CSS is a relative value or absolute - DOM read is always px.
- if ( tableWidthAttr ) {
- table.style.width = _fnStringToCss( tableWidthAttr );
- }
-
- if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {
- var bindResize = function () {
- $(window).on('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
- _fnAdjustColumnSizing( oSettings );
- } ) );
- };
-
- // IE6/7 will crash if we bind a resize event handler on page load.
- // To be removed in 1.11 which drops IE6/7 support
- if ( ie67 ) {
- setTimeout( bindResize, 1000 );
- }
- else {
- bindResize();
- }
-
- oSettings._reszEvt = true;
- }
- }
-
-
- /**
- * Throttle the calls to a function. Arguments and context are maintained for
- * the throttled function
- * @param {function} fn Function to be called
- * @param {int} [freq=200] call frequency in mS
- * @returns {function} wrapped function
- * @memberof DataTable#oApi
- */
- var _fnThrottle = DataTable.util.throttle;
-
-
- /**
- * Convert a CSS unit width to pixels (e.g. 2em)
- * @param {string} width width to be converted
- * @param {node} parent parent to get the with for (required for relative widths) - optional
- * @returns {int} width in pixels
- * @memberof DataTable#oApi
- */
- function _fnConvertToWidth ( width, parent )
- {
- if ( ! width ) {
- return 0;
- }
-
- var n = $('<div/>')
- .css( 'width', _fnStringToCss( width ) )
- .appendTo( parent || document.body );
-
- var val = n[0].offsetWidth;
- n.remove();
-
- return val;
- }
-
-
- /**
- * Get the widest node
- * @param {object} settings dataTables settings object
- * @param {int} colIdx column of interest
- * @returns {node} widest table node
- * @memberof DataTable#oApi
- */
- function _fnGetWidestNode( settings, colIdx )
- {
- var idx = _fnGetMaxLenString( settings, colIdx );
- if ( idx < 0 ) {
- return null;
- }
-
- var data = settings.aoData[ idx ];
- return ! data.nTr ? // Might not have been created when deferred rendering
- $('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :
- data.anCells[ colIdx ];
- }
-
-
- /**
- * Get the maximum strlen for each data column
- * @param {object} settings dataTables settings object
- * @param {int} colIdx column of interest
- * @returns {string} max string length for each column
- * @memberof DataTable#oApi
- */
- function _fnGetMaxLenString( settings, colIdx )
- {
- var s, max=-1, maxIdx = -1;
-
- for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
- s = _fnGetCellData( settings, i, colIdx, 'display' )+'';
- s = s.replace( __re_html_remove, '' );
- s = s.replace( / /g, ' ' );
-
- if ( s.length > max ) {
- max = s.length;
- maxIdx = i;
- }
- }
-
- return maxIdx;
- }
-
-
- /**
- * Append a CSS unit (only if required) to a string
- * @param {string} value to css-ify
- * @returns {string} value with css unit
- * @memberof DataTable#oApi
- */
- function _fnStringToCss( s )
- {
- if ( s === null ) {
- return '0px';
- }
-
- if ( typeof s == 'number' ) {
- return s < 0 ?
- '0px' :
- s+'px';
- }
-
- // Check it has a unit character already
- return s.match(/\d$/) ?
- s+'px' :
- s;
- }
-
-
-
- function _fnSortFlatten ( settings )
- {
- var
- i, iLen, k, kLen,
- aSort = [],
- aiOrig = [],
- aoColumns = settings.aoColumns,
- aDataSort, iCol, sType, srcCol,
- fixed = settings.aaSortingFixed,
- fixedObj = $.isPlainObject( fixed ),
- nestedSort = [],
- add = function ( a ) {
- if ( a.length && ! $.isArray( a[0] ) ) {
- // 1D array
- nestedSort.push( a );
- }
- else {
- // 2D array
- $.merge( nestedSort, a );
- }
- };
-
- // Build the sort array, with pre-fix and post-fix options if they have been
- // specified
- if ( $.isArray( fixed ) ) {
- add( fixed );
- }
-
- if ( fixedObj && fixed.pre ) {
- add( fixed.pre );
- }
-
- add( settings.aaSorting );
-
- if (fixedObj && fixed.post ) {
- add( fixed.post );
- }
-
- for ( i=0 ; i<nestedSort.length ; i++ )
- {
- srcCol = nestedSort[i][0];
- aDataSort = aoColumns[ srcCol ].aDataSort;
-
- for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
- {
- iCol = aDataSort[k];
- sType = aoColumns[ iCol ].sType || 'string';
-
- if ( nestedSort[i]._idx === undefined ) {
- nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting );
- }
-
- aSort.push( {
- src: srcCol,
- col: iCol,
- dir: nestedSort[i][1],
- index: nestedSort[i]._idx,
- type: sType,
- formatter: DataTable.ext.type.order[ sType+"-pre" ]
- } );
- }
- }
-
- return aSort;
- }
-
- /**
- * Change the order of the table
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- * @todo This really needs split up!
- */
- function _fnSort ( oSettings )
- {
- var
- i, ien, iLen, j, jLen, k, kLen,
- sDataType, nTh,
- aiOrig = [],
- oExtSort = DataTable.ext.type.order,
- aoData = oSettings.aoData,
- aoColumns = oSettings.aoColumns,
- aDataSort, data, iCol, sType, oSort,
- formatters = 0,
- sortCol,
- displayMaster = oSettings.aiDisplayMaster,
- aSort;
-
- // Resolve any column types that are unknown due to addition or invalidation
- // @todo Can this be moved into a 'data-ready' handler which is called when
- // data is going to be used in the table?
- _fnColumnTypes( oSettings );
-
- aSort = _fnSortFlatten( oSettings );
-
- for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
- sortCol = aSort[i];
-
- // Track if we can use the fast sort algorithm
- if ( sortCol.formatter ) {
- formatters++;
- }
-
- // Load the data needed for the sort, for each cell
- _fnSortData( oSettings, sortCol.col );
- }
-
- /* No sorting required if server-side or no sorting array */
- if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )
- {
- // Create a value - key array of the current row positions such that we can use their
- // current position during the sort, if values match, in order to perform stable sorting
- for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
- aiOrig[ displayMaster[i] ] = i;
- }
-
- /* Do the sort - here we want multi-column sorting based on a given data source (column)
- * and sorting function (from oSort) in a certain direction. It's reasonably complex to
- * follow on it's own, but this is what we want (example two column sorting):
- * fnLocalSorting = function(a,b){
- * var iTest;
- * iTest = oSort['string-asc']('data11', 'data12');
- * if (iTest !== 0)
- * return iTest;
- * iTest = oSort['numeric-desc']('data21', 'data22');
- * if (iTest !== 0)
- * return iTest;
- * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
- * }
- * Basically we have a test for each sorting column, if the data in that column is equal,
- * test the next column. If all columns match, then we use a numeric sort on the row
- * positions in the original data array to provide a stable sort.
- *
- * Note - I know it seems excessive to have two sorting methods, but the first is around
- * 15% faster, so the second is only maintained for backwards compatibility with sorting
- * methods which do not have a pre-sort formatting function.
- */
- if ( formatters === aSort.length ) {
- // All sort types have formatting functions
- displayMaster.sort( function ( a, b ) {
- var
- x, y, k, test, sort,
- len=aSort.length,
- dataA = aoData[a]._aSortData,
- dataB = aoData[b]._aSortData;
-
- for ( k=0 ; k<len ; k++ ) {
- sort = aSort[k];
-
- x = dataA[ sort.col ];
- y = dataB[ sort.col ];
-
- test = x<y ? -1 : x>y ? 1 : 0;
- if ( test !== 0 ) {
- return sort.dir === 'asc' ? test : -test;
- }
- }
-
- x = aiOrig[a];
- y = aiOrig[b];
- return x<y ? -1 : x>y ? 1 : 0;
- } );
- }
- else {
- // Depreciated - remove in 1.11 (providing a plug-in option)
- // Not all sort types have formatting methods, so we have to call their sorting
- // methods.
- displayMaster.sort( function ( a, b ) {
- var
- x, y, k, l, test, sort, fn,
- len=aSort.length,
- dataA = aoData[a]._aSortData,
- dataB = aoData[b]._aSortData;
-
- for ( k=0 ; k<len ; k++ ) {
- sort = aSort[k];
-
- x = dataA[ sort.col ];
- y = dataB[ sort.col ];
-
- fn = oExtSort[ sort.type+"-"+sort.dir ] || oExtSort[ "string-"+sort.dir ];
- test = fn( x, y );
- if ( test !== 0 ) {
- return test;
- }
- }
-
- x = aiOrig[a];
- y = aiOrig[b];
- return x<y ? -1 : x>y ? 1 : 0;
- } );
- }
- }
-
- /* Tell the draw function that we have sorted the data */
- oSettings.bSorted = true;
- }
-
-
- function _fnSortAria ( settings )
- {
- var label;
- var nextSort;
- var columns = settings.aoColumns;
- var aSort = _fnSortFlatten( settings );
- var oAria = settings.oLanguage.oAria;
-
- // ARIA attributes - need to loop all columns, to update all (removing old
- // attributes as needed)
- for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
- {
- var col = columns[i];
- var asSorting = col.asSorting;
- var sTitle = col.sTitle.replace( /<.*?>/g, "" );
- var th = col.nTh;
-
- // IE7 is throwing an error when setting these properties with jQuery's
- // attr() and removeAttr() methods...
- th.removeAttribute('aria-sort');
-
- /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
- if ( col.bSortable ) {
- if ( aSort.length > 0 && aSort[0].col == i ) {
- th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" );
- nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0];
- }
- else {
- nextSort = asSorting[0];
- }
-
- label = sTitle + ( nextSort === "asc" ?
- oAria.sSortAscending :
- oAria.sSortDescending
- );
- }
- else {
- label = sTitle;
- }
-
- th.setAttribute('aria-label', label);
- }
- }
-
-
- /**
- * Function to run on user sort request
- * @param {object} settings dataTables settings object
- * @param {node} attachTo node to attach the handler to
- * @param {int} colIdx column sorting index
- * @param {boolean} [append=false] Append the requested sort to the existing
- * sort if true (i.e. multi-column sort)
- * @param {function} [callback] callback function
- * @memberof DataTable#oApi
- */
- function _fnSortListener ( settings, colIdx, append, callback )
- {
- var col = settings.aoColumns[ colIdx ];
- var sorting = settings.aaSorting;
- var asSorting = col.asSorting;
- var nextSortIdx;
- var next = function ( a, overflow ) {
- var idx = a._idx;
- if ( idx === undefined ) {
- idx = $.inArray( a[1], asSorting );
- }
-
- return idx+1 < asSorting.length ?
- idx+1 :
- overflow ?
- null :
- 0;
- };
-
- // Convert to 2D array if needed
- if ( typeof sorting[0] === 'number' ) {
- sorting = settings.aaSorting = [ sorting ];
- }
-
- // If appending the sort then we are multi-column sorting
- if ( append && settings.oFeatures.bSortMulti ) {
- // Are we already doing some kind of sort on this column?
- var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') );
-
- if ( sortIdx !== -1 ) {
- // Yes, modify the sort
- nextSortIdx = next( sorting[sortIdx], true );
-
- if ( nextSortIdx === null && sorting.length === 1 ) {
- nextSortIdx = 0; // can't remove sorting completely
- }
-
- if ( nextSortIdx === null ) {
- sorting.splice( sortIdx, 1 );
- }
- else {
- sorting[sortIdx][1] = asSorting[ nextSortIdx ];
- sorting[sortIdx]._idx = nextSortIdx;
- }
- }
- else {
- // No sort on this column yet
- sorting.push( [ colIdx, asSorting[0], 0 ] );
- sorting[sorting.length-1]._idx = 0;
- }
- }
- else if ( sorting.length && sorting[0][0] == colIdx ) {
- // Single column - already sorting on this column, modify the sort
- nextSortIdx = next( sorting[0] );
-
- sorting.length = 1;
- sorting[0][1] = asSorting[ nextSortIdx ];
- sorting[0]._idx = nextSortIdx;
- }
- else {
- // Single column - sort only on this column
- sorting.length = 0;
- sorting.push( [ colIdx, asSorting[0] ] );
- sorting[0]._idx = 0;
- }
-
- // Run the sort by calling a full redraw
- _fnReDraw( settings );
-
- // callback used for async user interaction
- if ( typeof callback == 'function' ) {
- callback( settings );
- }
- }
-
-
- /**
- * Attach a sort handler (click) to a node
- * @param {object} settings dataTables settings object
- * @param {node} attachTo node to attach the handler to
- * @param {int} colIdx column sorting index
- * @param {function} [callback] callback function
- * @memberof DataTable#oApi
- */
- function _fnSortAttachListener ( settings, attachTo, colIdx, callback )
- {
- var col = settings.aoColumns[ colIdx ];
-
- _fnBindAction( attachTo, {}, function (e) {
- /* If the column is not sortable - don't to anything */
- if ( col.bSortable === false ) {
- return;
- }
-
- // If processing is enabled use a timeout to allow the processing
- // display to be shown - otherwise to it synchronously
- if ( settings.oFeatures.bProcessing ) {
- _fnProcessingDisplay( settings, true );
-
- setTimeout( function() {
- _fnSortListener( settings, colIdx, e.shiftKey, callback );
-
- // In server-side processing, the draw callback will remove the
- // processing display
- if ( _fnDataSource( settings ) !== 'ssp' ) {
- _fnProcessingDisplay( settings, false );
- }
- }, 0 );
- }
- else {
- _fnSortListener( settings, colIdx, e.shiftKey, callback );
- }
- } );
- }
-
-
- /**
- * Set the sorting classes on table's body, Note: it is safe to call this function
- * when bSort and bSortClasses are false
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnSortingClasses( settings )
- {
- var oldSort = settings.aLastSort;
- var sortClass = settings.oClasses.sSortColumn;
- var sort = _fnSortFlatten( settings );
- var features = settings.oFeatures;
- var i, ien, colIdx;
-
- if ( features.bSort && features.bSortClasses ) {
- // Remove old sorting classes
- for ( i=0, ien=oldSort.length ; i<ien ; i++ ) {
- colIdx = oldSort[i].src;
-
- // Remove column sorting
- $( _pluck( settings.aoData, 'anCells', colIdx ) )
- .removeClass( sortClass + (i<2 ? i+1 : 3) );
- }
-
- // Add new column sorting
- for ( i=0, ien=sort.length ; i<ien ; i++ ) {
- colIdx = sort[i].src;
-
- $( _pluck( settings.aoData, 'anCells', colIdx ) )
- .addClass( sortClass + (i<2 ? i+1 : 3) );
- }
- }
-
- settings.aLastSort = sort;
- }
-
-
- // Get the data to sort a column, be it from cache, fresh (populating the
- // cache), or from a sort formatter
- function _fnSortData( settings, idx )
- {
- // Custom sorting function - provided by the sort data type
- var column = settings.aoColumns[ idx ];
- var customSort = DataTable.ext.order[ column.sSortDataType ];
- var customData;
-
- if ( customSort ) {
- customData = customSort.call( settings.oInstance, settings, idx,
- _fnColumnIndexToVisible( settings, idx )
- );
- }
-
- // Use / populate cache
- var row, cellData;
- var formatter = DataTable.ext.type.order[ column.sType+"-pre" ];
-
- for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
- row = settings.aoData[i];
-
- if ( ! row._aSortData ) {
- row._aSortData = [];
- }
-
- if ( ! row._aSortData[idx] || customSort ) {
- cellData = customSort ?
- customData[i] : // If there was a custom sort function, use data from there
- _fnGetCellData( settings, i, idx, 'sort' );
-
- row._aSortData[ idx ] = formatter ?
- formatter( cellData ) :
- cellData;
- }
- }
- }
-
-
-
- /**
- * Save the state of a table
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnSaveState ( settings )
- {
- if ( !settings.oFeatures.bStateSave || settings.bDestroying )
- {
- return;
- }
-
- /* Store the interesting variables */
- var state = {
- time: +new Date(),
- start: settings._iDisplayStart,
- length: settings._iDisplayLength,
- order: $.extend( true, [], settings.aaSorting ),
- search: _fnSearchToCamel( settings.oPreviousSearch ),
- columns: $.map( settings.aoColumns, function ( col, i ) {
- return {
- visible: col.bVisible,
- search: _fnSearchToCamel( settings.aoPreSearchCols[i] )
- };
- } )
- };
-
- _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] );
-
- settings.oSavedState = state;
- settings.fnStateSaveCallback.call( settings.oInstance, settings, state );
- }
-
-
- /**
- * Attempt to load a saved table state
- * @param {object} oSettings dataTables settings object
- * @param {object} oInit DataTables init object so we can override settings
- * @param {function} callback Callback to execute when the state has been loaded
- * @memberof DataTable#oApi
- */
- function _fnLoadState ( settings, oInit, callback )
- {
- var i, ien;
- var columns = settings.aoColumns;
- var loaded = function ( s ) {
- if ( ! s || ! s.time ) {
- callback();
- return;
- }
-
- // Allow custom and plug-in manipulation functions to alter the saved data set and
- // cancelling of loading by returning false
- var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, s] );
- if ( $.inArray( false, abStateLoad ) !== -1 ) {
- callback();
- return;
- }
-
- // Reject old data
- var duration = settings.iStateDuration;
- if ( duration > 0 && s.time < +new Date() - (duration*1000) ) {
- callback();
- return;
- }
-
- // Number of columns have changed - all bets are off, no restore of settings
- if ( s.columns && columns.length !== s.columns.length ) {
- callback();
- return;
- }
-
- // Store the saved state so it might be accessed at any time
- settings.oLoadedState = $.extend( true, {}, s );
-
- // Restore key features - todo - for 1.11 this needs to be done by
- // subscribed events
- if ( s.start !== undefined ) {
- settings._iDisplayStart = s.start;
- settings.iInitDisplayStart = s.start;
- }
- if ( s.length !== undefined ) {
- settings._iDisplayLength = s.length;
- }
-
- // Order
- if ( s.order !== undefined ) {
- settings.aaSorting = [];
- $.each( s.order, function ( i, col ) {
- settings.aaSorting.push( col[0] >= columns.length ?
- [ 0, col[1] ] :
- col
- );
- } );
- }
-
- // Search
- if ( s.search !== undefined ) {
- $.extend( settings.oPreviousSearch, _fnSearchToHung( s.search ) );
- }
-
- // Columns
- //
- if ( s.columns ) {
- for ( i=0, ien=s.columns.length ; i<ien ; i++ ) {
- var col = s.columns[i];
-
- // Visibility
- if ( col.visible !== undefined ) {
- columns[i].bVisible = col.visible;
- }
-
- // Search
- if ( col.search !== undefined ) {
- $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );
- }
- }
- }
-
- _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, s] );
- callback();
- }
-
- if ( ! settings.oFeatures.bStateSave ) {
- callback();
- return;
- }
-
- var state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded );
-
- if ( state !== undefined ) {
- loaded( state );
- }
- // otherwise, wait for the loaded callback to be executed
- }
-
-
- /**
- * Return the settings object for a particular table
- * @param {node} table table we are using as a dataTable
- * @returns {object} Settings object - or null if not found
- * @memberof DataTable#oApi
- */
- function _fnSettingsFromNode ( table )
- {
- var settings = DataTable.settings;
- var idx = $.inArray( table, _pluck( settings, 'nTable' ) );
-
- return idx !== -1 ?
- settings[ idx ] :
- null;
- }
-
-
- /**
- * Log an error message
- * @param {object} settings dataTables settings object
- * @param {int} level log error messages, or display them to the user
- * @param {string} msg error message
- * @param {int} tn Technical note id to get more information about the error.
- * @memberof DataTable#oApi
- */
- function _fnLog( settings, level, msg, tn )
- {
- msg = 'DataTables warning: '+
- (settings ? 'table id='+settings.sTableId+' - ' : '')+msg;
-
- if ( tn ) {
- msg += '. For more information about this error, please see '+
- 'http://datatables.net/tn/'+tn;
- }
-
- if ( ! level ) {
- // Backwards compatibility pre 1.10
- var ext = DataTable.ext;
- var type = ext.sErrMode || ext.errMode;
-
- if ( settings ) {
- _fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] );
- }
-
- if ( type == 'alert' ) {
- alert( msg );
- }
- else if ( type == 'throw' ) {
- throw new Error(msg);
- }
- else if ( typeof type == 'function' ) {
- type( settings, tn, msg );
- }
- }
- else if ( window.console && console.log ) {
- console.log( msg );
- }
- }
-
-
- /**
- * See if a property is defined on one object, if so assign it to the other object
- * @param {object} ret target object
- * @param {object} src source object
- * @param {string} name property
- * @param {string} [mappedName] name to map too - optional, name used if not given
- * @memberof DataTable#oApi
- */
- function _fnMap( ret, src, name, mappedName )
- {
- if ( $.isArray( name ) ) {
- $.each( name, function (i, val) {
- if ( $.isArray( val ) ) {
- _fnMap( ret, src, val[0], val[1] );
- }
- else {
- _fnMap( ret, src, val );
- }
- } );
-
- return;
- }
-
- if ( mappedName === undefined ) {
- mappedName = name;
- }
-
- if ( src[name] !== undefined ) {
- ret[mappedName] = src[name];
- }
- }
-
-
- /**
- * Extend objects - very similar to jQuery.extend, but deep copy objects, and
- * shallow copy arrays. The reason we need to do this, is that we don't want to
- * deep copy array init values (such as aaSorting) since the dev wouldn't be
- * able to override them, but we do want to deep copy arrays.
- * @param {object} out Object to extend
- * @param {object} extender Object from which the properties will be applied to
- * out
- * @param {boolean} breakRefs If true, then arrays will be sliced to take an
- * independent copy with the exception of the `data` or `aaData` parameters
- * if they are present. This is so you can pass in a collection to
- * DataTables and have that used as your data source without breaking the
- * references
- * @returns {object} out Reference, just for convenience - out === the return.
- * @memberof DataTable#oApi
- * @todo This doesn't take account of arrays inside the deep copied objects.
- */
- function _fnExtend( out, extender, breakRefs )
- {
- var val;
-
- for ( var prop in extender ) {
- if ( extender.hasOwnProperty(prop) ) {
- val = extender[prop];
-
- if ( $.isPlainObject( val ) ) {
- if ( ! $.isPlainObject( out[prop] ) ) {
- out[prop] = {};
- }
- $.extend( true, out[prop], val );
- }
- else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && $.isArray(val) ) {
- out[prop] = val.slice();
- }
- else {
- out[prop] = val;
- }
- }
- }
-
- return out;
- }
-
-
- /**
- * Bind an event handers to allow a click or return key to activate the callback.
- * This is good for accessibility since a return on the keyboard will have the
- * same effect as a click, if the element has focus.
- * @param {element} n Element to bind the action to
- * @param {object} oData Data object to pass to the triggered function
- * @param {function} fn Callback function for when the event is triggered
- * @memberof DataTable#oApi
- */
- function _fnBindAction( n, oData, fn )
- {
- $(n)
- .on( 'click.DT', oData, function (e) {
- n.blur(); // Remove focus outline for mouse users
- fn(e);
- } )
- .on( 'keypress.DT', oData, function (e){
- if ( e.which === 13 ) {
- e.preventDefault();
- fn(e);
- }
- } )
- .on( 'selectstart.DT', function () {
- /* Take the brutal approach to cancelling text selection */
- return false;
- } );
- }
-
-
- /**
- * Register a callback function. Easily allows a callback function to be added to
- * an array store of callback functions that can then all be called together.
- * @param {object} oSettings dataTables settings object
- * @param {string} sStore Name of the array storage for the callbacks in oSettings
- * @param {function} fn Function to be called back
- * @param {string} sName Identifying name for the callback (i.e. a label)
- * @memberof DataTable#oApi
- */
- function _fnCallbackReg( oSettings, sStore, fn, sName )
- {
- if ( fn )
- {
- oSettings[sStore].push( {
- "fn": fn,
- "sName": sName
- } );
- }
- }
-
-
- /**
- * Fire callback functions and trigger events. Note that the loop over the
- * callback array store is done backwards! Further note that you do not want to
- * fire off triggers in time sensitive applications (for example cell creation)
- * as its slow.
- * @param {object} settings dataTables settings object
- * @param {string} callbackArr Name of the array storage for the callbacks in
- * oSettings
- * @param {string} eventName Name of the jQuery custom event to trigger. If
- * null no trigger is fired
- * @param {array} args Array of arguments to pass to the callback function /
- * trigger
- * @memberof DataTable#oApi
- */
- function _fnCallbackFire( settings, callbackArr, eventName, args )
- {
- var ret = [];
-
- if ( callbackArr ) {
- ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) {
- return val.fn.apply( settings.oInstance, args );
- } );
- }
-
- if ( eventName !== null ) {
- var e = $.Event( eventName+'.dt' );
-
- $(settings.nTable).trigger( e, args );
-
- ret.push( e.result );
- }
-
- return ret;
- }
-
-
- function _fnLengthOverflow ( settings )
- {
- var
- start = settings._iDisplayStart,
- end = settings.fnDisplayEnd(),
- len = settings._iDisplayLength;
-
- /* If we have space to show extra rows (backing up from the end point - then do so */
- if ( start >= end )
- {
- start = end - len;
- }
-
- // Keep the start record on the current page
- start -= (start % len);
-
- if ( len === -1 || start < 0 )
- {
- start = 0;
- }
-
- settings._iDisplayStart = start;
- }
-
-
- function _fnRenderer( settings, type )
- {
- var renderer = settings.renderer;
- var host = DataTable.ext.renderer[type];
-
- if ( $.isPlainObject( renderer ) && renderer[type] ) {
- // Specific renderer for this type. If available use it, otherwise use
- // the default.
- return host[renderer[type]] || host._;
- }
- else if ( typeof renderer === 'string' ) {
- // Common renderer - if there is one available for this type use it,
- // otherwise use the default
- return host[renderer] || host._;
- }
-
- // Use the default
- return host._;
- }
-
-
- /**
- * Detect the data source being used for the table. Used to simplify the code
- * a little (ajax) and to make it compress a little smaller.
- *
- * @param {object} settings dataTables settings object
- * @returns {string} Data source
- * @memberof DataTable#oApi
- */
- function _fnDataSource ( settings )
- {
- if ( settings.oFeatures.bServerSide ) {
- return 'ssp';
- }
- else if ( settings.ajax || settings.sAjaxSource ) {
- return 'ajax';
- }
- return 'dom';
- }
-
-
-
- /**
- * Computed structure of the DataTables API, defined by the options passed to
- * `DataTable.Api.register()` when building the API.
- *
- * The structure is built in order to speed creation and extension of the Api
- * objects since the extensions are effectively pre-parsed.
- *
- * The array is an array of objects with the following structure, where this
- * base array represents the Api prototype base:
- *
- * [
- * {
- * name: 'data' -- string - Property name
- * val: function () {}, -- function - Api method (or undefined if just an object
- * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
- * propExt: [ ... ] -- array - Array of Api object definitions to extend the property
- * },
- * {
- * name: 'row'
- * val: {},
- * methodExt: [ ... ],
- * propExt: [
- * {
- * name: 'data'
- * val: function () {},
- * methodExt: [ ... ],
- * propExt: [ ... ]
- * },
- * ...
- * ]
- * }
- * ]
- *
- * @type {Array}
- * @ignore
- */
- var __apiStruct = [];
-
-
- /**
- * `Array.prototype` reference.
- *
- * @type object
- * @ignore
- */
- var __arrayProto = Array.prototype;
-
-
- /**
- * Abstraction for `context` parameter of the `Api` constructor to allow it to
- * take several different forms for ease of use.
- *
- * Each of the input parameter types will be converted to a DataTables settings
- * object where possible.
- *
- * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one
- * of:
- *
- * * `string` - jQuery selector. Any DataTables' matching the given selector
- * with be found and used.
- * * `node` - `TABLE` node which has already been formed into a DataTable.
- * * `jQuery` - A jQuery object of `TABLE` nodes.
- * * `object` - DataTables settings object
- * * `DataTables.Api` - API instance
- * @return {array|null} Matching DataTables settings objects. `null` or
- * `undefined` is returned if no matching DataTable is found.
- * @ignore
- */
- var _toSettings = function ( mixed )
- {
- var idx, jq;
- var settings = DataTable.settings;
- var tables = $.map( settings, function (el, i) {
- return el.nTable;
- } );
-
- if ( ! mixed ) {
- return [];
- }
- else if ( mixed.nTable && mixed.oApi ) {
- // DataTables settings object
- return [ mixed ];
- }
- else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) {
- // Table node
- idx = $.inArray( mixed, tables );
- return idx !== -1 ? [ settings[idx] ] : null;
- }
- else if ( mixed && typeof mixed.settings === 'function' ) {
- return mixed.settings().toArray();
- }
- else if ( typeof mixed === 'string' ) {
- // jQuery selector
- jq = $(mixed);
- }
- else if ( mixed instanceof $ ) {
- // jQuery object (also DataTables instance)
- jq = mixed;
- }
-
- if ( jq ) {
- return jq.map( function(i) {
- idx = $.inArray( this, tables );
- return idx !== -1 ? settings[idx] : null;
- } ).toArray();
- }
- };
-
-
- /**
- * DataTables API class - used to control and interface with one or more
- * DataTables enhanced tables.
- *
- * The API class is heavily based on jQuery, presenting a chainable interface
- * that you can use to interact with tables. Each instance of the API class has
- * a "context" - i.e. the tables that it will operate on. This could be a single
- * table, all tables on a page or a sub-set thereof.
- *
- * Additionally the API is designed to allow you to easily work with the data in
- * the tables, retrieving and manipulating it as required. This is done by
- * presenting the API class as an array like interface. The contents of the
- * array depend upon the actions requested by each method (for example
- * `rows().nodes()` will return an array of nodes, while `rows().data()` will
- * return an array of objects or arrays depending upon your table's
- * configuration). The API object has a number of array like methods (`push`,
- * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`,
- * `unique` etc) to assist your working with the data held in a table.
- *
- * Most methods (those which return an Api instance) are chainable, which means
- * the return from a method call also has all of the methods available that the
- * top level object had. For example, these two calls are equivalent:
- *
- * // Not chained
- * api.row.add( {...} );
- * api.draw();
- *
- * // Chained
- * api.row.add( {...} ).draw();
- *
- * @class DataTable.Api
- * @param {array|object|string|jQuery} context DataTable identifier. This is
- * used to define which DataTables enhanced tables this API will operate on.
- * Can be one of:
- *
- * * `string` - jQuery selector. Any DataTables' matching the given selector
- * with be found and used.
- * * `node` - `TABLE` node which has already been formed into a DataTable.
- * * `jQuery` - A jQuery object of `TABLE` nodes.
- * * `object` - DataTables settings object
- * @param {array} [data] Data to initialise the Api instance with.
- *
- * @example
- * // Direct initialisation during DataTables construction
- * var api = $('#example').DataTable();
- *
- * @example
- * // Initialisation using a DataTables jQuery object
- * var api = $('#example').dataTable().api();
- *
- * @example
- * // Initialisation as a constructor
- * var api = new $.fn.DataTable.Api( 'table.dataTable' );
- */
- _Api = function ( context, data )
- {
- if ( ! (this instanceof _Api) ) {
- return new _Api( context, data );
- }
-
- var settings = [];
- var ctxSettings = function ( o ) {
- var a = _toSettings( o );
- if ( a ) {
- settings = settings.concat( a );
- }
- };
-
- if ( $.isArray( context ) ) {
- for ( var i=0, ien=context.length ; i<ien ; i++ ) {
- ctxSettings( context[i] );
- }
- }
- else {
- ctxSettings( context );
- }
-
- // Remove duplicates
- this.context = _unique( settings );
-
- // Initial data
- if ( data ) {
- $.merge( this, data );
- }
-
- // selector
- this.selector = {
- rows: null,
- cols: null,
- opts: null
- };
-
- _Api.extend( this, this, __apiStruct );
- };
-
- DataTable.Api = _Api;
-
- // Don't destroy the existing prototype, just extend it. Required for jQuery 2's
- // isPlainObject.
- $.extend( _Api.prototype, {
- any: function ()
- {
- return this.count() !== 0;
- },
-
-
- concat: __arrayProto.concat,
-
-
- context: [], // array of table settings objects
-
-
- count: function ()
- {
- return this.flatten().length;
- },
-
-
- each: function ( fn )
- {
- for ( var i=0, ien=this.length ; i<ien; i++ ) {
- fn.call( this, this[i], i, this );
- }
-
- return this;
- },
-
-
- eq: function ( idx )
- {
- var ctx = this.context;
-
- return ctx.length > idx ?
- new _Api( ctx[idx], this[idx] ) :
- null;
- },
-
-
- filter: function ( fn )
- {
- var a = [];
-
- if ( __arrayProto.filter ) {
- a = __arrayProto.filter.call( this, fn, this );
- }
- else {
- // Compatibility for browsers without EMCA-252-5 (JS 1.6)
- for ( var i=0, ien=this.length ; i<ien ; i++ ) {
- if ( fn.call( this, this[i], i, this ) ) {
- a.push( this[i] );
- }
- }
- }
-
- return new _Api( this.context, a );
- },
-
-
- flatten: function ()
- {
- var a = [];
- return new _Api( this.context, a.concat.apply( a, this.toArray() ) );
- },
-
-
- join: __arrayProto.join,
-
-
- indexOf: __arrayProto.indexOf || function (obj, start)
- {
- for ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) {
- if ( this[i] === obj ) {
- return i;
- }
- }
- return -1;
- },
-
- iterator: function ( flatten, type, fn, alwaysNew ) {
- var
- a = [], ret,
- i, ien, j, jen,
- context = this.context,
- rows, items, item,
- selector = this.selector;
-
- // Argument shifting
- if ( typeof flatten === 'string' ) {
- alwaysNew = fn;
- fn = type;
- type = flatten;
- flatten = false;
- }
-
- for ( i=0, ien=context.length ; i<ien ; i++ ) {
- var apiInst = new _Api( context[i] );
-
- if ( type === 'table' ) {
- ret = fn.call( apiInst, context[i], i );
-
- if ( ret !== undefined ) {
- a.push( ret );
- }
- }
- else if ( type === 'columns' || type === 'rows' ) {
- // this has same length as context - one entry for each table
- ret = fn.call( apiInst, context[i], this[i], i );
-
- if ( ret !== undefined ) {
- a.push( ret );
- }
- }
- else if ( type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) {
- // columns and rows share the same structure.
- // 'this' is an array of column indexes for each context
- items = this[i];
-
- if ( type === 'column-rows' ) {
- rows = _selector_row_indexes( context[i], selector.opts );
- }
-
- for ( j=0, jen=items.length ; j<jen ; j++ ) {
- item = items[j];
-
- if ( type === 'cell' ) {
- ret = fn.call( apiInst, context[i], item.row, item.column, i, j );
- }
- else {
- ret = fn.call( apiInst, context[i], item, i, j, rows );
- }
-
- if ( ret !== undefined ) {
- a.push( ret );
- }
- }
- }
- }
-
- if ( a.length || alwaysNew ) {
- var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );
- var apiSelector = api.selector;
- apiSelector.rows = selector.rows;
- apiSelector.cols = selector.cols;
- apiSelector.opts = selector.opts;
- return api;
- }
- return this;
- },
-
-
- lastIndexOf: __arrayProto.lastIndexOf || function (obj, start)
- {
- // Bit cheeky...
- return this.indexOf.apply( this.toArray.reverse(), arguments );
- },
-
-
- length: 0,
-
-
- map: function ( fn )
- {
- var a = [];
-
- if ( __arrayProto.map ) {
- a = __arrayProto.map.call( this, fn, this );
- }
- else {
- // Compatibility for browsers without EMCA-252-5 (JS 1.6)
- for ( var i=0, ien=this.length ; i<ien ; i++ ) {
- a.push( fn.call( this, this[i], i ) );
- }
- }
-
- return new _Api( this.context, a );
- },
-
-
- pluck: function ( prop )
- {
- return this.map( function ( el ) {
- return el[ prop ];
- } );
- },
-
- pop: __arrayProto.pop,
-
-
- push: __arrayProto.push,
-
-
- // Does not return an API instance
- reduce: __arrayProto.reduce || function ( fn, init )
- {
- return _fnReduce( this, fn, init, 0, this.length, 1 );
- },
-
-
- reduceRight: __arrayProto.reduceRight || function ( fn, init )
- {
- return _fnReduce( this, fn, init, this.length-1, -1, -1 );
- },
-
-
- reverse: __arrayProto.reverse,
-
-
- // Object with rows, columns and opts
- selector: null,
-
-
- shift: __arrayProto.shift,
-
-
- slice: function () {
- return new _Api( this.context, this );
- },
-
-
- sort: __arrayProto.sort, // ? name - order?
-
-
- splice: __arrayProto.splice,
-
-
- toArray: function ()
- {
- return __arrayProto.slice.call( this );
- },
-
-
- to$: function ()
- {
- return $( this );
- },
-
-
- toJQuery: function ()
- {
- return $( this );
- },
-
-
- unique: function ()
- {
- return new _Api( this.context, _unique(this) );
- },
-
-
- unshift: __arrayProto.unshift
- } );
-
-
- _Api.extend = function ( scope, obj, ext )
- {
- // Only extend API instances and static properties of the API
- if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) {
- return;
- }
-
- var
- i, ien,
- j, jen,
- struct, inner,
- methodScoping = function ( scope, fn, struc ) {
- return function () {
- var ret = fn.apply( scope, arguments );
-
- // Method extension
- _Api.extend( ret, ret, struc.methodExt );
- return ret;
- };
- };
-
- for ( i=0, ien=ext.length ; i<ien ; i++ ) {
- struct = ext[i];
-
- // Value
- obj[ struct.name ] = typeof struct.val === 'function' ?
- methodScoping( scope, struct.val, struct ) :
- $.isPlainObject( struct.val ) ?
- {} :
- struct.val;
-
- obj[ struct.name ].__dt_wrapper = true;
-
- // Property extension
- _Api.extend( scope, obj[ struct.name ], struct.propExt );
- }
- };
-
-
- // @todo - Is there need for an augment function?
- // _Api.augment = function ( inst, name )
- // {
- // // Find src object in the structure from the name
- // var parts = name.split('.');
-
- // _Api.extend( inst, obj );
- // };
-
-
- // [
- // {
- // name: 'data' -- string - Property name
- // val: function () {}, -- function - Api method (or undefined if just an object
- // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
- // propExt: [ ... ] -- array - Array of Api object definitions to extend the property
- // },
- // {
- // name: 'row'
- // val: {},
- // methodExt: [ ... ],
- // propExt: [
- // {
- // name: 'data'
- // val: function () {},
- // methodExt: [ ... ],
- // propExt: [ ... ]
- // },
- // ...
- // ]
- // }
- // ]
-
- _Api.register = _api_register = function ( name, val )
- {
- if ( $.isArray( name ) ) {
- for ( var j=0, jen=name.length ; j<jen ; j++ ) {
- _Api.register( name[j], val );
- }
- return;
- }
-
- var
- i, ien,
- heir = name.split('.'),
- struct = __apiStruct,
- key, method;
-
- var find = function ( src, name ) {
- for ( var i=0, ien=src.length ; i<ien ; i++ ) {
- if ( src[i].name === name ) {
- return src[i];
- }
- }
- return null;
- };
-
- for ( i=0, ien=heir.length ; i<ien ; i++ ) {
- method = heir[i].indexOf('()') !== -1;
- key = method ?
- heir[i].replace('()', '') :
- heir[i];
-
- var src = find( struct, key );
- if ( ! src ) {
- src = {
- name: key,
- val: {},
- methodExt: [],
- propExt: []
- };
- struct.push( src );
- }
-
- if ( i === ien-1 ) {
- src.val = val;
- }
- else {
- struct = method ?
- src.methodExt :
- src.propExt;
- }
- }
- };
-
-
- _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {
- _Api.register( pluralName, val );
-
- _Api.register( singularName, function () {
- var ret = val.apply( this, arguments );
-
- if ( ret === this ) {
- // Returned item is the API instance that was passed in, return it
- return this;
- }
- else if ( ret instanceof _Api ) {
- // New API instance returned, want the value from the first item
- // in the returned array for the singular result.
- return ret.length ?
- $.isArray( ret[0] ) ?
- new _Api( ret.context, ret[0] ) : // Array results are 'enhanced'
- ret[0] :
- undefined;
- }
-
- // Non-API return - just fire it back
- return ret;
- } );
- };
-
-
- /**
- * Selector for HTML tables. Apply the given selector to the give array of
- * DataTables settings objects.
- *
- * @param {string|integer} [selector] jQuery selector string or integer
- * @param {array} Array of DataTables settings objects to be filtered
- * @return {array}
- * @ignore
- */
- var __table_selector = function ( selector, a )
- {
- // Integer is used to pick out a table by index
- if ( typeof selector === 'number' ) {
- return [ a[ selector ] ];
- }
-
- // Perform a jQuery selector on the table nodes
- var nodes = $.map( a, function (el, i) {
- return el.nTable;
- } );
-
- return $(nodes)
- .filter( selector )
- .map( function (i) {
- // Need to translate back from the table node to the settings
- var idx = $.inArray( this, nodes );
- return a[ idx ];
- } )
- .toArray();
- };
-
-
-
- /**
- * Context selector for the API's context (i.e. the tables the API instance
- * refers to.
- *
- * @name DataTable.Api#tables
- * @param {string|integer} [selector] Selector to pick which tables the iterator
- * should operate on. If not given, all tables in the current context are
- * used. This can be given as a jQuery selector (for example `':gt(0)'`) to
- * select multiple tables or as an integer to select a single table.
- * @returns {DataTable.Api} Returns a new API instance if a selector is given.
- */
- _api_register( 'tables()', function ( selector ) {
- // A new instance is created if there was a selector specified
- return selector ?
- new _Api( __table_selector( selector, this.context ) ) :
- this;
- } );
-
-
- _api_register( 'table()', function ( selector ) {
- var tables = this.tables( selector );
- var ctx = tables.context;
-
- // Truncate to the first matched table
- return ctx.length ?
- new _Api( ctx[0] ) :
- tables;
- } );
-
-
- _api_registerPlural( 'tables().nodes()', 'table().node()' , function () {
- return this.iterator( 'table', function ( ctx ) {
- return ctx.nTable;
- }, 1 );
- } );
-
-
- _api_registerPlural( 'tables().body()', 'table().body()' , function () {
- return this.iterator( 'table', function ( ctx ) {
- return ctx.nTBody;
- }, 1 );
- } );
-
-
- _api_registerPlural( 'tables().header()', 'table().header()' , function () {
- return this.iterator( 'table', function ( ctx ) {
- return ctx.nTHead;
- }, 1 );
- } );
-
-
- _api_registerPlural( 'tables().footer()', 'table().footer()' , function () {
- return this.iterator( 'table', function ( ctx ) {
- return ctx.nTFoot;
- }, 1 );
- } );
-
-
- _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
- return this.iterator( 'table', function ( ctx ) {
- return ctx.nTableWrapper;
- }, 1 );
- } );
-
-
-
- /**
- * Redraw the tables in the current context.
- */
- _api_register( 'draw()', function ( paging ) {
- return this.iterator( 'table', function ( settings ) {
- if ( paging === 'page' ) {
- _fnDraw( settings );
- }
- else {
- if ( typeof paging === 'string' ) {
- paging = paging === 'full-hold' ?
- false :
- true;
- }
-
- _fnReDraw( settings, paging===false );
- }
- } );
- } );
-
-
-
- /**
- * Get the current page index.
- *
- * @return {integer} Current page index (zero based)
- *//**
- * Set the current page.
- *
- * Note that if you attempt to show a page which does not exist, DataTables will
- * not throw an error, but rather reset the paging.
- *
- * @param {integer|string} action The paging action to take. This can be one of:
- * * `integer` - The page index to jump to
- * * `string` - An action to take:
- * * `first` - Jump to first page.
- * * `next` - Jump to the next page
- * * `previous` - Jump to previous page
- * * `last` - Jump to the last page.
- * @returns {DataTables.Api} this
- */
- _api_register( 'page()', function ( action ) {
- if ( action === undefined ) {
- return this.page.info().page; // not an expensive call
- }
-
- // else, have an action to take on all tables
- return this.iterator( 'table', function ( settings ) {
- _fnPageChange( settings, action );
- } );
- } );
-
-
- /**
- * Paging information for the first table in the current context.
- *
- * If you require paging information for another table, use the `table()` method
- * with a suitable selector.
- *
- * @return {object} Object with the following properties set:
- * * `page` - Current page index (zero based - i.e. the first page is `0`)
- * * `pages` - Total number of pages
- * * `start` - Display index for the first record shown on the current page
- * * `end` - Display index for the last record shown on the current page
- * * `length` - Display length (number of records). Note that generally `start
- * + length = end`, but this is not always true, for example if there are
- * only 2 records to show on the final page, with a length of 10.
- * * `recordsTotal` - Full data set length
- * * `recordsDisplay` - Data set length once the current filtering criterion
- * are applied.
- */
- _api_register( 'page.info()', function ( action ) {
- if ( this.context.length === 0 ) {
- return undefined;
- }
-
- var
- settings = this.context[0],
- start = settings._iDisplayStart,
- len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1,
- visRecords = settings.fnRecordsDisplay(),
- all = len === -1;
-
- return {
- "page": all ? 0 : Math.floor( start / len ),
- "pages": all ? 1 : Math.ceil( visRecords / len ),
- "start": start,
- "end": settings.fnDisplayEnd(),
- "length": len,
- "recordsTotal": settings.fnRecordsTotal(),
- "recordsDisplay": visRecords,
- "serverSide": _fnDataSource( settings ) === 'ssp'
- };
- } );
-
-
- /**
- * Get the current page length.
- *
- * @return {integer} Current page length. Note `-1` indicates that all records
- * are to be shown.
- *//**
- * Set the current page length.
- *
- * @param {integer} Page length to set. Use `-1` to show all records.
- * @returns {DataTables.Api} this
- */
- _api_register( 'page.len()', function ( len ) {
- // Note that we can't call this function 'length()' because `length`
- // is a Javascript property of functions which defines how many arguments
- // the function expects.
- if ( len === undefined ) {
- return this.context.length !== 0 ?
- this.context[0]._iDisplayLength :
- undefined;
- }
-
- // else, set the page length
- return this.iterator( 'table', function ( settings ) {
- _fnLengthChange( settings, len );
- } );
- } );
-
-
-
- var __reload = function ( settings, holdPosition, callback ) {
- // Use the draw event to trigger a callback
- if ( callback ) {
- var api = new _Api( settings );
-
- api.one( 'draw', function () {
- callback( api.ajax.json() );
- } );
- }
-
- if ( _fnDataSource( settings ) == 'ssp' ) {
- _fnReDraw( settings, holdPosition );
- }
- else {
- _fnProcessingDisplay( settings, true );
-
- // Cancel an existing request
- var xhr = settings.jqXHR;
- if ( xhr && xhr.readyState !== 4 ) {
- xhr.abort();
- }
-
- // Trigger xhr
- _fnBuildAjax( settings, [], function( json ) {
- _fnClearTable( settings );
-
- var data = _fnAjaxDataSrc( settings, json );
- for ( var i=0, ien=data.length ; i<ien ; i++ ) {
- _fnAddData( settings, data[i] );
- }
-
- _fnReDraw( settings, holdPosition );
- _fnProcessingDisplay( settings, false );
- } );
- }
- };
-
-
- /**
- * Get the JSON response from the last Ajax request that DataTables made to the
- * server. Note that this returns the JSON from the first table in the current
- * context.
- *
- * @return {object} JSON received from the server.
- */
- _api_register( 'ajax.json()', function () {
- var ctx = this.context;
-
- if ( ctx.length > 0 ) {
- return ctx[0].json;
- }
-
- // else return undefined;
- } );
-
-
- /**
- * Get the data submitted in the last Ajax request
- */
- _api_register( 'ajax.params()', function () {
- var ctx = this.context;
-
- if ( ctx.length > 0 ) {
- return ctx[0].oAjaxData;
- }
-
- // else return undefined;
- } );
-
-
- /**
- * Reload tables from the Ajax data source. Note that this function will
- * automatically re-draw the table when the remote data has been loaded.
- *
- * @param {boolean} [reset=true] Reset (default) or hold the current paging
- * position. A full re-sort and re-filter is performed when this method is
- * called, which is why the pagination reset is the default action.
- * @returns {DataTables.Api} this
- */
- _api_register( 'ajax.reload()', function ( callback, resetPaging ) {
- return this.iterator( 'table', function (settings) {
- __reload( settings, resetPaging===false, callback );
- } );
- } );
-
-
- /**
- * Get the current Ajax URL. Note that this returns the URL from the first
- * table in the current context.
- *
- * @return {string} Current Ajax source URL
- *//**
- * Set the Ajax URL. Note that this will set the URL for all tables in the
- * current context.
- *
- * @param {string} url URL to set.
- * @returns {DataTables.Api} this
- */
- _api_register( 'ajax.url()', function ( url ) {
- var ctx = this.context;
-
- if ( url === undefined ) {
- // get
- if ( ctx.length === 0 ) {
- return undefined;
- }
- ctx = ctx[0];
-
- return ctx.ajax ?
- $.isPlainObject( ctx.ajax ) ?
- ctx.ajax.url :
- ctx.ajax :
- ctx.sAjaxSource;
- }
-
- // set
- return this.iterator( 'table', function ( settings ) {
- if ( $.isPlainObject( settings.ajax ) ) {
- settings.ajax.url = url;
- }
- else {
- settings.ajax = url;
- }
- // No need to consider sAjaxSource here since DataTables gives priority
- // to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any
- // value of `sAjaxSource` redundant.
- } );
- } );
-
-
- /**
- * Load data from the newly set Ajax URL. Note that this method is only
- * available when `ajax.url()` is used to set a URL. Additionally, this method
- * has the same effect as calling `ajax.reload()` but is provided for
- * convenience when setting a new URL. Like `ajax.reload()` it will
- * automatically redraw the table once the remote data has been loaded.
- *
- * @returns {DataTables.Api} this
- */
- _api_register( 'ajax.url().load()', function ( callback, resetPaging ) {
- // Same as a reload, but makes sense to present it for easy access after a
- // url change
- return this.iterator( 'table', function ( ctx ) {
- __reload( ctx, resetPaging===false, callback );
- } );
- } );
-
-
-
-
- var _selector_run = function ( type, selector, selectFn, settings, opts )
- {
- var
- out = [], res,
- a, i, ien, j, jen,
- selectorType = typeof selector;
-
- // Can't just check for isArray here, as an API or jQuery instance might be
- // given with their array like look
- if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {
- selector = [ selector ];
- }
-
- for ( i=0, ien=selector.length ; i<ien ; i++ ) {
- // Only split on simple strings - complex expressions will be jQuery selectors
- a = selector[i] && selector[i].split && ! selector[i].match(/[\[\(:]/) ?
- selector[i].split(',') :
- [ selector[i] ];
-
- for ( j=0, jen=a.length ; j<jen ; j++ ) {
- res = selectFn( typeof a[j] === 'string' ? $.trim(a[j]) : a[j] );
-
- if ( res && res.length ) {
- out = out.concat( res );
- }
- }
- }
-
- // selector extensions
- var ext = _ext.selector[ type ];
- if ( ext.length ) {
- for ( i=0, ien=ext.length ; i<ien ; i++ ) {
- out = ext[i]( settings, opts, out );
- }
- }
-
- return _unique( out );
- };
-
-
- var _selector_opts = function ( opts )
- {
- if ( ! opts ) {
- opts = {};
- }
-
- // Backwards compatibility for 1.9- which used the terminology filter rather
- // than search
- if ( opts.filter && opts.search === undefined ) {
- opts.search = opts.filter;
- }
-
- return $.extend( {
- search: 'none',
- order: 'current',
- page: 'all'
- }, opts );
- };
-
-
- var _selector_first = function ( inst )
- {
- // Reduce the API instance to the first item found
- for ( var i=0, ien=inst.length ; i<ien ; i++ ) {
- if ( inst[i].length > 0 ) {
- // Assign the first element to the first item in the instance
- // and truncate the instance and context
- inst[0] = inst[i];
- inst[0].length = 1;
- inst.length = 1;
- inst.context = [ inst.context[i] ];
-
- return inst;
- }
- }
-
- // Not found - return an empty instance
- inst.length = 0;
- return inst;
- };
-
-
- var _selector_row_indexes = function ( settings, opts )
- {
- var
- i, ien, tmp, a=[],
- displayFiltered = settings.aiDisplay,
- displayMaster = settings.aiDisplayMaster;
-
- var
- search = opts.search, // none, applied, removed
- order = opts.order, // applied, current, index (original - compatibility with 1.9)
- page = opts.page; // all, current
-
- if ( _fnDataSource( settings ) == 'ssp' ) {
- // In server-side processing mode, most options are irrelevant since
- // rows not shown don't exist and the index order is the applied order
- // Removed is a special case - for consistency just return an empty
- // array
- return search === 'removed' ?
- [] :
- _range( 0, displayMaster.length );
- }
- else if ( page == 'current' ) {
- // Current page implies that order=current and fitler=applied, since it is
- // fairly senseless otherwise, regardless of what order and search actually
- // are
- for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) {
- a.push( displayFiltered[i] );
- }
- }
- else if ( order == 'current' || order == 'applied' ) {
- a = search == 'none' ?
- displayMaster.slice() : // no search
- search == 'applied' ?
- displayFiltered.slice() : // applied search
- $.map( displayMaster, function (el, i) { // removed search
- return $.inArray( el, displayFiltered ) === -1 ? el : null;
- } );
- }
- else if ( order == 'index' || order == 'original' ) {
- for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
- if ( search == 'none' ) {
- a.push( i );
- }
- else { // applied | removed
- tmp = $.inArray( i, displayFiltered );
-
- if ((tmp === -1 && search == 'removed') ||
- (tmp >= 0 && search == 'applied') )
- {
- a.push( i );
- }
- }
- }
- }
-
- return a;
- };
-
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Rows
- *
- * {} - no selector - use all available rows
- * {integer} - row aoData index
- * {node} - TR node
- * {string} - jQuery selector to apply to the TR elements
- * {array} - jQuery array of nodes, or simply an array of TR nodes
- *
- */
-
-
- var __row_selector = function ( settings, selector, opts )
- {
- var rows;
- var run = function ( sel ) {
- var selInt = _intVal( sel );
- var i, ien;
-
- // Short cut - selector is a number and no options provided (default is
- // all records, so no need to check if the index is in there, since it
- // must be - dev error if the index doesn't exist).
- if ( selInt !== null && ! opts ) {
- return [ selInt ];
- }
-
- if ( ! rows ) {
- rows = _selector_row_indexes( settings, opts );
- }
-
- if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) {
- // Selector - integer
- return [ selInt ];
- }
- else if ( sel === null || sel === undefined || sel === '' ) {
- // Selector - none
- return rows;
- }
-
- // Selector - function
- if ( typeof sel === 'function' ) {
- return $.map( rows, function (idx) {
- var row = settings.aoData[ idx ];
- return sel( idx, row._aData, row.nTr ) ? idx : null;
- } );
- }
-
- // Get nodes in the order from the `rows` array with null values removed
- var nodes = _removeEmpty(
- _pluck_order( settings.aoData, rows, 'nTr' )
- );
-
- // Selector - node
- if ( sel.nodeName ) {
- if ( sel._DT_RowIndex !== undefined ) {
- return [ sel._DT_RowIndex ]; // Property added by DT for fast lookup
- }
- else if ( sel._DT_CellIndex ) {
- return [ sel._DT_CellIndex.row ];
- }
- else {
- var host = $(sel).closest('*[data-dt-row]');
- return host.length ?
- [ host.data('dt-row') ] :
- [];
- }
- }
-
- // ID selector. Want to always be able to select rows by id, regardless
- // of if the tr element has been created or not, so can't rely upon
- // jQuery here - hence a custom implementation. This does not match
- // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything,
- // but to select it using a CSS selector engine (like Sizzle or
- // querySelect) it would need to need to be escaped for some characters.
- // DataTables simplifies this for row selectors since you can select
- // only a row. A # indicates an id any anything that follows is the id -
- // unescaped.
- if ( typeof sel === 'string' && sel.charAt(0) === '#' ) {
- // get row index from id
- var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ];
- if ( rowObj !== undefined ) {
- return [ rowObj.idx ];
- }
-
- // need to fall through to jQuery in case there is DOM id that
- // matches
- }
-
- // Selector - jQuery selector string, array of nodes or jQuery object/
- // As jQuery's .filter() allows jQuery objects to be passed in filter,
- // it also allows arrays, so this will cope with all three options
- return $(nodes)
- .filter( sel )
- .map( function () {
- return this._DT_RowIndex;
- } )
- .toArray();
- };
-
- return _selector_run( 'row', selector, run, settings, opts );
- };
-
-
- _api_register( 'rows()', function ( selector, opts ) {
- // argument shifting
- if ( selector === undefined ) {
- selector = '';
- }
- else if ( $.isPlainObject( selector ) ) {
- opts = selector;
- selector = '';
- }
-
- opts = _selector_opts( opts );
-
- var inst = this.iterator( 'table', function ( settings ) {
- return __row_selector( settings, selector, opts );
- }, 1 );
-
- // Want argument shifting here and in __row_selector?
- inst.selector.rows = selector;
- inst.selector.opts = opts;
-
- return inst;
- } );
-
- _api_register( 'rows().nodes()', function () {
- return this.iterator( 'row', function ( settings, row ) {
- return settings.aoData[ row ].nTr || undefined;
- }, 1 );
- } );
-
- _api_register( 'rows().data()', function () {
- return this.iterator( true, 'rows', function ( settings, rows ) {
- return _pluck_order( settings.aoData, rows, '_aData' );
- }, 1 );
- } );
-
- _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {
- return this.iterator( 'row', function ( settings, row ) {
- var r = settings.aoData[ row ];
- return type === 'search' ? r._aFilterData : r._aSortData;
- }, 1 );
- } );
-
- _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {
- return this.iterator( 'row', function ( settings, row ) {
- _fnInvalidate( settings, row, src );
- } );
- } );
-
- _api_registerPlural( 'rows().indexes()', 'row().index()', function () {
- return this.iterator( 'row', function ( settings, row ) {
- return row;
- }, 1 );
- } );
-
- _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) {
- var a = [];
- var context = this.context;
-
- // `iterator` will drop undefined values, but in this case we want them
- for ( var i=0, ien=context.length ; i<ien ; i++ ) {
- for ( var j=0, jen=this[i].length ; j<jen ; j++ ) {
- var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData );
- a.push( (hash === true ? '#' : '' )+ id );
- }
- }
-
- return new _Api( context, a );
- } );
-
- _api_registerPlural( 'rows().remove()', 'row().remove()', function () {
- var that = this;
-
- this.iterator( 'row', function ( settings, row, thatIdx ) {
- var data = settings.aoData;
- var rowData = data[ row ];
- var i, ien, j, jen;
- var loopRow, loopCells;
-
- data.splice( row, 1 );
-
- // Update the cached indexes
- for ( i=0, ien=data.length ; i<ien ; i++ ) {
- loopRow = data[i];
- loopCells = loopRow.anCells;
-
- // Rows
- if ( loopRow.nTr !== null ) {
- loopRow.nTr._DT_RowIndex = i;
- }
-
- // Cells
- if ( loopCells !== null ) {
- for ( j=0, jen=loopCells.length ; j<jen ; j++ ) {
- loopCells[j]._DT_CellIndex.row = i;
- }
- }
- }
-
- // Delete from the display arrays
- _fnDeleteIndex( settings.aiDisplayMaster, row );
- _fnDeleteIndex( settings.aiDisplay, row );
- _fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes
-
- // For server-side processing tables - subtract the deleted row from the count
- if ( settings._iRecordsDisplay > 0 ) {
- settings._iRecordsDisplay--;
- }
-
- // Check for an 'overflow' they case for displaying the table
- _fnLengthOverflow( settings );
-
- // Remove the row's ID reference if there is one
- var id = settings.rowIdFn( rowData._aData );
- if ( id !== undefined ) {
- delete settings.aIds[ id ];
- }
- } );
-
- this.iterator( 'table', function ( settings ) {
- for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
- settings.aoData[i].idx = i;
- }
- } );
-
- return this;
- } );
-
-
- _api_register( 'rows.add()', function ( rows ) {
- var newRows = this.iterator( 'table', function ( settings ) {
- var row, i, ien;
- var out = [];
-
- for ( i=0, ien=rows.length ; i<ien ; i++ ) {
- row = rows[i];
-
- if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
- out.push( _fnAddTr( settings, row )[0] );
- }
- else {
- out.push( _fnAddData( settings, row ) );
- }
- }
-
- return out;
- }, 1 );
-
- // Return an Api.rows() extended instance, so rows().nodes() etc can be used
- var modRows = this.rows( -1 );
- modRows.pop();
- $.merge( modRows, newRows );
-
- return modRows;
- } );
-
-
-
-
-
- /**
- *
- */
- _api_register( 'row()', function ( selector, opts ) {
- return _selector_first( this.rows( selector, opts ) );
- } );
-
-
- _api_register( 'row().data()', function ( data ) {
- var ctx = this.context;
-
- if ( data === undefined ) {
- // Get
- return ctx.length && this.length ?
- ctx[0].aoData[ this[0] ]._aData :
- undefined;
- }
-
- // Set
- ctx[0].aoData[ this[0] ]._aData = data;
-
- // Automatically invalidate
- _fnInvalidate( ctx[0], this[0], 'data' );
-
- return this;
- } );
-
-
- _api_register( 'row().node()', function () {
- var ctx = this.context;
-
- return ctx.length && this.length ?
- ctx[0].aoData[ this[0] ].nTr || null :
- null;
- } );
-
-
- _api_register( 'row.add()', function ( row ) {
- // Allow a jQuery object to be passed in - only a single row is added from
- // it though - the first element in the set
- if ( row instanceof $ && row.length ) {
- row = row[0];
- }
-
- var rows = this.iterator( 'table', function ( settings ) {
- if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
- return _fnAddTr( settings, row )[0];
- }
- return _fnAddData( settings, row );
- } );
-
- // Return an Api.rows() extended instance, with the newly added row selected
- return this.row( rows[0] );
- } );
-
-
-
- var __details_add = function ( ctx, row, data, klass )
- {
- // Convert to array of TR elements
- var rows = [];
- var addRow = function ( r, k ) {
- // Recursion to allow for arrays of jQuery objects
- if ( $.isArray( r ) || r instanceof $ ) {
- for ( var i=0, ien=r.length ; i<ien ; i++ ) {
- addRow( r[i], k );
- }
- return;
- }
-
- // If we get a TR element, then just add it directly - up to the dev
- // to add the correct number of columns etc
- if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {
- rows.push( r );
- }
- else {
- // Otherwise create a row with a wrapper
- var created = $('<tr><td/></tr>').addClass( k );
- $('td', created)
- .addClass( k )
- .html( r )
- [0].colSpan = _fnVisbleColumns( ctx );
-
- rows.push( created[0] );
- }
- };
-
- addRow( data, klass );
-
- if ( row._details ) {
- row._details.detach();
- }
-
- row._details = $(rows);
-
- // If the children were already shown, that state should be retained
- if ( row._detailsShow ) {
- row._details.insertAfter( row.nTr );
- }
- };
-
-
- var __details_remove = function ( api, idx )
- {
- var ctx = api.context;
-
- if ( ctx.length ) {
- var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
-
- if ( row && row._details ) {
- row._details.remove();
-
- row._detailsShow = undefined;
- row._details = undefined;
- }
- }
- };
-
-
- var __details_display = function ( api, show ) {
- var ctx = api.context;
-
- if ( ctx.length && api.length ) {
- var row = ctx[0].aoData[ api[0] ];
-
- if ( row._details ) {
- row._detailsShow = show;
-
- if ( show ) {
- row._details.insertAfter( row.nTr );
- }
- else {
- row._details.detach();
- }
-
- __details_events( ctx[0] );
- }
- }
- };
-
-
- var __details_events = function ( settings )
- {
- var api = new _Api( settings );
- var namespace = '.dt.DT_details';
- var drawEvent = 'draw'+namespace;
- var colvisEvent = 'column-visibility'+namespace;
- var destroyEvent = 'destroy'+namespace;
- var data = settings.aoData;
-
- api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );
-
- if ( _pluck( data, '_details' ).length > 0 ) {
- // On each draw, insert the required elements into the document
- api.on( drawEvent, function ( e, ctx ) {
- if ( settings !== ctx ) {
- return;
- }
-
- api.rows( {page:'current'} ).eq(0).each( function (idx) {
- // Internal data grab
- var row = data[ idx ];
-
- if ( row._detailsShow ) {
- row._details.insertAfter( row.nTr );
- }
- } );
- } );
-
- // Column visibility change - update the colspan
- api.on( colvisEvent, function ( e, ctx, idx, vis ) {
- if ( settings !== ctx ) {
- return;
- }
-
- // Update the colspan for the details rows (note, only if it already has
- // a colspan)
- var row, visible = _fnVisbleColumns( ctx );
-
- for ( var i=0, ien=data.length ; i<ien ; i++ ) {
- row = data[i];
-
- if ( row._details ) {
- row._details.children('td[colspan]').attr('colspan', visible );
- }
- }
- } );
-
- // Table destroyed - nuke any child rows
- api.on( destroyEvent, function ( e, ctx ) {
- if ( settings !== ctx ) {
- return;
- }
-
- for ( var i=0, ien=data.length ; i<ien ; i++ ) {
- if ( data[i]._details ) {
- __details_remove( api, i );
- }
- }
- } );
- }
- };
-
- // Strings for the method names to help minification
- var _emp = '';
- var _child_obj = _emp+'row().child';
- var _child_mth = _child_obj+'()';
-
- // data can be:
- // tr
- // string
- // jQuery or array of any of the above
- _api_register( _child_mth, function ( data, klass ) {
- var ctx = this.context;
-
- if ( data === undefined ) {
- // get
- return ctx.length && this.length ?
- ctx[0].aoData[ this[0] ]._details :
- undefined;
- }
- else if ( data === true ) {
- // show
- this.child.show();
- }
- else if ( data === false ) {
- // remove
- __details_remove( this );
- }
- else if ( ctx.length && this.length ) {
- // set
- __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );
- }
-
- return this;
- } );
-
-
- _api_register( [
- _child_obj+'.show()',
- _child_mth+'.show()' // only when `child()` was called with parameters (without
- ], function ( show ) { // it returns an object and this method is not executed)
- __details_display( this, true );
- return this;
- } );
-
-
- _api_register( [
- _child_obj+'.hide()',
- _child_mth+'.hide()' // only when `child()` was called with parameters (without
- ], function () { // it returns an object and this method is not executed)
- __details_display( this, false );
- return this;
- } );
-
-
- _api_register( [
- _child_obj+'.remove()',
- _child_mth+'.remove()' // only when `child()` was called with parameters (without
- ], function () { // it returns an object and this method is not executed)
- __details_remove( this );
- return this;
- } );
-
-
- _api_register( _child_obj+'.isShown()', function () {
- var ctx = this.context;
-
- if ( ctx.length && this.length ) {
- // _detailsShown as false or undefined will fall through to return false
- return ctx[0].aoData[ this[0] ]._detailsShow || false;
- }
- return false;
- } );
-
-
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Columns
- *
- * {integer} - column index (>=0 count from left, <0 count from right)
- * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right)
- * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right)
- * "{string}:name" - column name
- * "{string}" - jQuery selector on column header nodes
- *
- */
-
- // can be an array of these items, comma separated list, or an array of comma
- // separated lists
-
- var __re_column_selector = /^([^:]+):(name|visIdx|visible)$/;
-
-
- // r1 and r2 are redundant - but it means that the parameters match for the
- // iterator callback in columns().data()
- var __columnData = function ( settings, column, r1, r2, rows ) {
- var a = [];
- for ( var row=0, ien=rows.length ; row<ien ; row++ ) {
- a.push( _fnGetCellData( settings, rows[row], column ) );
- }
- return a;
- };
-
-
- var __column_selector = function ( settings, selector, opts )
- {
- var
- columns = settings.aoColumns,
- names = _pluck( columns, 'sName' ),
- nodes = _pluck( columns, 'nTh' );
-
- var run = function ( s ) {
- var selInt = _intVal( s );
-
- // Selector - all
- if ( s === '' ) {
- return _range( columns.length );
- }
-
- // Selector - index
- if ( selInt !== null ) {
- return [ selInt >= 0 ?
- selInt : // Count from left
- columns.length + selInt // Count from right (+ because its a negative value)
- ];
- }
-
- // Selector = function
- if ( typeof s === 'function' ) {
- var rows = _selector_row_indexes( settings, opts );
-
- return $.map( columns, function (col, idx) {
- return s(
- idx,
- __columnData( settings, idx, 0, 0, rows ),
- nodes[ idx ]
- ) ? idx : null;
- } );
- }
-
- // jQuery or string selector
- var match = typeof s === 'string' ?
- s.match( __re_column_selector ) :
- '';
-
- if ( match ) {
- switch( match[2] ) {
- case 'visIdx':
- case 'visible':
- var idx = parseInt( match[1], 10 );
- // Visible index given, convert to column index
- if ( idx < 0 ) {
- // Counting from the right
- var visColumns = $.map( columns, function (col,i) {
- return col.bVisible ? i : null;
- } );
- return [ visColumns[ visColumns.length + idx ] ];
- }
- // Counting from the left
- return [ _fnVisibleToColumnIndex( settings, idx ) ];
-
- case 'name':
- // match by name. `names` is column index complete and in order
- return $.map( names, function (name, i) {
- return name === match[1] ? i : null;
- } );
-
- default:
- return [];
- }
- }
-
- // Cell in the table body
- if ( s.nodeName && s._DT_CellIndex ) {
- return [ s._DT_CellIndex.column ];
- }
-
- // jQuery selector on the TH elements for the columns
- var jqResult = $( nodes )
- .filter( s )
- .map( function () {
- return $.inArray( this, nodes ); // `nodes` is column index complete and in order
- } )
- .toArray();
-
- if ( jqResult.length || ! s.nodeName ) {
- return jqResult;
- }
-
- // Otherwise a node which might have a `dt-column` data attribute, or be
- // a child or such an element
- var host = $(s).closest('*[data-dt-column]');
- return host.length ?
- [ host.data('dt-column') ] :
- [];
- };
-
- return _selector_run( 'column', selector, run, settings, opts );
- };
-
-
- var __setColumnVis = function ( settings, column, vis ) {
- var
- cols = settings.aoColumns,
- col = cols[ column ],
- data = settings.aoData,
- row, cells, i, ien, tr;
-
- // Get
- if ( vis === undefined ) {
- return col.bVisible;
- }
-
- // Set
- // No change
- if ( col.bVisible === vis ) {
- return;
- }
-
- if ( vis ) {
- // Insert column
- // Need to decide if we should use appendChild or insertBefore
- var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 );
-
- for ( i=0, ien=data.length ; i<ien ; i++ ) {
- tr = data[i].nTr;
- cells = data[i].anCells;
-
- if ( tr ) {
- // insertBefore can act like appendChild if 2nd arg is null
- tr.insertBefore( cells[ column ], cells[ insertBefore ] || null );
- }
- }
- }
- else {
- // Remove column
- $( _pluck( settings.aoData, 'anCells', column ) ).detach();
- }
-
- // Common actions
- col.bVisible = vis;
- _fnDrawHead( settings, settings.aoHeader );
- _fnDrawHead( settings, settings.aoFooter );
-
- _fnSaveState( settings );
- };
-
-
- _api_register( 'columns()', function ( selector, opts ) {
- // argument shifting
- if ( selector === undefined ) {
- selector = '';
- }
- else if ( $.isPlainObject( selector ) ) {
- opts = selector;
- selector = '';
- }
-
- opts = _selector_opts( opts );
-
- var inst = this.iterator( 'table', function ( settings ) {
- return __column_selector( settings, selector, opts );
- }, 1 );
-
- // Want argument shifting here and in _row_selector?
- inst.selector.cols = selector;
- inst.selector.opts = opts;
-
- return inst;
- } );
-
- _api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) {
- return this.iterator( 'column', function ( settings, column ) {
- return settings.aoColumns[column].nTh;
- }, 1 );
- } );
-
- _api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) {
- return this.iterator( 'column', function ( settings, column ) {
- return settings.aoColumns[column].nTf;
- }, 1 );
- } );
-
- _api_registerPlural( 'columns().data()', 'column().data()', function () {
- return this.iterator( 'column-rows', __columnData, 1 );
- } );
-
- _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {
- return this.iterator( 'column', function ( settings, column ) {
- return settings.aoColumns[column].mData;
- }, 1 );
- } );
-
- _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {
- return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
- return _pluck_order( settings.aoData, rows,
- type === 'search' ? '_aFilterData' : '_aSortData', column
- );
- }, 1 );
- } );
-
- _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
- return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
- return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
- }, 1 );
- } );
-
- _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {
- var ret = this.iterator( 'column', function ( settings, column ) {
- if ( vis === undefined ) {
- return settings.aoColumns[ column ].bVisible;
- } // else
- __setColumnVis( settings, column, vis );
- } );
-
- // Group the column visibility changes
- if ( vis !== undefined ) {
- // Second loop once the first is done for events
- this.iterator( 'column', function ( settings, column ) {
- _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] );
- } );
-
- if ( calc === undefined || calc ) {
- this.columns.adjust();
- }
- }
-
- return ret;
- } );
-
- _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
- return this.iterator( 'column', function ( settings, column ) {
- return type === 'visible' ?
- _fnColumnIndexToVisible( settings, column ) :
- column;
- }, 1 );
- } );
-
- _api_register( 'columns.adjust()', function () {
- return this.iterator( 'table', function ( settings ) {
- _fnAdjustColumnSizing( settings );
- }, 1 );
- } );
-
- _api_register( 'column.index()', function ( type, idx ) {
- if ( this.context.length !== 0 ) {
- var ctx = this.context[0];
-
- if ( type === 'fromVisible' || type === 'toData' ) {
- return _fnVisibleToColumnIndex( ctx, idx );
- }
- else if ( type === 'fromData' || type === 'toVisible' ) {
- return _fnColumnIndexToVisible( ctx, idx );
- }
- }
- } );
-
- _api_register( 'column()', function ( selector, opts ) {
- return _selector_first( this.columns( selector, opts ) );
- } );
-
-
-
- var __cell_selector = function ( settings, selector, opts )
- {
- var data = settings.aoData;
- var rows = _selector_row_indexes( settings, opts );
- var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) );
- var allCells = $( [].concat.apply([], cells) );
- var row;
- var columns = settings.aoColumns.length;
- var a, i, ien, j, o, host;
-
- var run = function ( s ) {
- var fnSelector = typeof s === 'function';
-
- if ( s === null || s === undefined || fnSelector ) {
- // All cells and function selectors
- a = [];
-
- for ( i=0, ien=rows.length ; i<ien ; i++ ) {
- row = rows[i];
-
- for ( j=0 ; j<columns ; j++ ) {
- o = {
- row: row,
- column: j
- };
-
- if ( fnSelector ) {
- // Selector - function
- host = data[ row ];
-
- if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) {
- a.push( o );
- }
- }
- else {
- // Selector - all
- a.push( o );
- }
- }
- }
-
- return a;
- }
-
- // Selector - index
- if ( $.isPlainObject( s ) ) {
- return [s];
- }
-
- // Selector - jQuery filtered cells
- var jqResult = allCells
- .filter( s )
- .map( function (i, el) {
- return { // use a new object, in case someone changes the values
- row: el._DT_CellIndex.row,
- column: el._DT_CellIndex.column
- };
- } )
- .toArray();
-
- if ( jqResult.length || ! s.nodeName ) {
- return jqResult;
- }
-
- // Otherwise the selector is a node, and there is one last option - the
- // element might be a child of an element which has dt-row and dt-column
- // data attributes
- host = $(s).closest('*[data-dt-row]');
- return host.length ?
- [ {
- row: host.data('dt-row'),
- column: host.data('dt-column')
- } ] :
- [];
- };
-
- return _selector_run( 'cell', selector, run, settings, opts );
- };
-
-
-
-
- _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {
- // Argument shifting
- if ( $.isPlainObject( rowSelector ) ) {
- // Indexes
- if ( rowSelector.row === undefined ) {
- // Selector options in first parameter
- opts = rowSelector;
- rowSelector = null;
- }
- else {
- // Cell index objects in first parameter
- opts = columnSelector;
- columnSelector = null;
- }
- }
- if ( $.isPlainObject( columnSelector ) ) {
- opts = columnSelector;
- columnSelector = null;
- }
-
- // Cell selector
- if ( columnSelector === null || columnSelector === undefined ) {
- return this.iterator( 'table', function ( settings ) {
- return __cell_selector( settings, rowSelector, _selector_opts( opts ) );
- } );
- }
-
- // Row + column selector
- var columns = this.columns( columnSelector, opts );
- var rows = this.rows( rowSelector, opts );
- var a, i, ien, j, jen;
-
- var cells = this.iterator( 'table', function ( settings, idx ) {
- a = [];
-
- for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {
- for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {
- a.push( {
- row: rows[idx][i],
- column: columns[idx][j]
- } );
- }
- }
-
- return a;
- }, 1 );
-
- $.extend( cells.selector, {
- cols: columnSelector,
- rows: rowSelector,
- opts: opts
- } );
-
- return cells;
- } );
-
-
- _api_registerPlural( 'cells().nodes()', 'cell().node()', function () {
- return this.iterator( 'cell', function ( settings, row, column ) {
- var data = settings.aoData[ row ];
-
- return data && data.anCells ?
- data.anCells[ column ] :
- undefined;
- }, 1 );
- } );
-
-
- _api_register( 'cells().data()', function () {
- return this.iterator( 'cell', function ( settings, row, column ) {
- return _fnGetCellData( settings, row, column );
- }, 1 );
- } );
-
-
- _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {
- type = type === 'search' ? '_aFilterData' : '_aSortData';
-
- return this.iterator( 'cell', function ( settings, row, column ) {
- return settings.aoData[ row ][ type ][ column ];
- }, 1 );
- } );
-
-
- _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {
- return this.iterator( 'cell', function ( settings, row, column ) {
- return _fnGetCellData( settings, row, column, type );
- }, 1 );
- } );
-
-
- _api_registerPlural( 'cells().indexes()', 'cell().index()', function () {
- return this.iterator( 'cell', function ( settings, row, column ) {
- return {
- row: row,
- column: column,
- columnVisible: _fnColumnIndexToVisible( settings, column )
- };
- }, 1 );
- } );
-
-
- _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {
- return this.iterator( 'cell', function ( settings, row, column ) {
- _fnInvalidate( settings, row, src, column );
- } );
- } );
-
-
-
- _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {
- return _selector_first( this.cells( rowSelector, columnSelector, opts ) );
- } );
-
-
- _api_register( 'cell().data()', function ( data ) {
- var ctx = this.context;
- var cell = this[0];
-
- if ( data === undefined ) {
- // Get
- return ctx.length && cell.length ?
- _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) :
- undefined;
- }
-
- // Set
- _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
- _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );
-
- return this;
- } );
-
-
-
- /**
- * Get current ordering (sorting) that has been applied to the table.
- *
- * @returns {array} 2D array containing the sorting information for the first
- * table in the current context. Each element in the parent array represents
- * a column being sorted upon (i.e. multi-sorting with two columns would have
- * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is
- * the column index that the sorting condition applies to, the second is the
- * direction of the sort (`desc` or `asc`) and, optionally, the third is the
- * index of the sorting order from the `column.sorting` initialisation array.
- *//**
- * Set the ordering for the table.
- *
- * @param {integer} order Column index to sort upon.
- * @param {string} direction Direction of the sort to be applied (`asc` or `desc`)
- * @returns {DataTables.Api} this
- *//**
- * Set the ordering for the table.
- *
- * @param {array} order 1D array of sorting information to be applied.
- * @param {array} [...] Optional additional sorting conditions
- * @returns {DataTables.Api} this
- *//**
- * Set the ordering for the table.
- *
- * @param {array} order 2D array of sorting information to be applied.
- * @returns {DataTables.Api} this
- */
- _api_register( 'order()', function ( order, dir ) {
- var ctx = this.context;
-
- if ( order === undefined ) {
- // get
- return ctx.length !== 0 ?
- ctx[0].aaSorting :
- undefined;
- }
-
- // set
- if ( typeof order === 'number' ) {
- // Simple column / direction passed in
- order = [ [ order, dir ] ];
- }
- else if ( order.length && ! $.isArray( order[0] ) ) {
- // Arguments passed in (list of 1D arrays)
- order = Array.prototype.slice.call( arguments );
- }
- // otherwise a 2D array was passed in
-
- return this.iterator( 'table', function ( settings ) {
- settings.aaSorting = order.slice();
- } );
- } );
-
-
- /**
- * Attach a sort listener to an element for a given column
- *
- * @param {node|jQuery|string} node Identifier for the element(s) to attach the
- * listener to. This can take the form of a single DOM node, a jQuery
- * collection of nodes or a jQuery selector which will identify the node(s).
- * @param {integer} column the column that a click on this node will sort on
- * @param {function} [callback] callback function when sort is run
- * @returns {DataTables.Api} this
- */
- _api_register( 'order.listener()', function ( node, column, callback ) {
- return this.iterator( 'table', function ( settings ) {
- _fnSortAttachListener( settings, node, column, callback );
- } );
- } );
-
-
- _api_register( 'order.fixed()', function ( set ) {
- if ( ! set ) {
- var ctx = this.context;
- var fixed = ctx.length ?
- ctx[0].aaSortingFixed :
- undefined;
-
- return $.isArray( fixed ) ?
- { pre: fixed } :
- fixed;
- }
-
- return this.iterator( 'table', function ( settings ) {
- settings.aaSortingFixed = $.extend( true, {}, set );
- } );
- } );
-
-
- // Order by the selected column(s)
- _api_register( [
- 'columns().order()',
- 'column().order()'
- ], function ( dir ) {
- var that = this;
-
- return this.iterator( 'table', function ( settings, i ) {
- var sort = [];
-
- $.each( that[i], function (j, col) {
- sort.push( [ col, dir ] );
- } );
-
- settings.aaSorting = sort;
- } );
- } );
-
-
-
- _api_register( 'search()', function ( input, regex, smart, caseInsen ) {
- var ctx = this.context;
-
- if ( input === undefined ) {
- // get
- return ctx.length !== 0 ?
- ctx[0].oPreviousSearch.sSearch :
- undefined;
- }
-
- // set
- return this.iterator( 'table', function ( settings ) {
- if ( ! settings.oFeatures.bFilter ) {
- return;
- }
-
- _fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, {
- "sSearch": input+"",
- "bRegex": regex === null ? false : regex,
- "bSmart": smart === null ? true : smart,
- "bCaseInsensitive": caseInsen === null ? true : caseInsen
- } ), 1 );
- } );
- } );
-
-
- _api_registerPlural(
- 'columns().search()',
- 'column().search()',
- function ( input, regex, smart, caseInsen ) {
- return this.iterator( 'column', function ( settings, column ) {
- var preSearch = settings.aoPreSearchCols;
-
- if ( input === undefined ) {
- // get
- return preSearch[ column ].sSearch;
- }
-
- // set
- if ( ! settings.oFeatures.bFilter ) {
- return;
- }
-
- $.extend( preSearch[ column ], {
- "sSearch": input+"",
- "bRegex": regex === null ? false : regex,
- "bSmart": smart === null ? true : smart,
- "bCaseInsensitive": caseInsen === null ? true : caseInsen
- } );
-
- _fnFilterComplete( settings, settings.oPreviousSearch, 1 );
- } );
- }
- );
-
- /*
- * State API methods
- */
-
- _api_register( 'state()', function () {
- return this.context.length ?
- this.context[0].oSavedState :
- null;
- } );
-
-
- _api_register( 'state.clear()', function () {
- return this.iterator( 'table', function ( settings ) {
- // Save an empty object
- settings.fnStateSaveCallback.call( settings.oInstance, settings, {} );
- } );
- } );
-
-
- _api_register( 'state.loaded()', function () {
- return this.context.length ?
- this.context[0].oLoadedState :
- null;
- } );
-
-
- _api_register( 'state.save()', function () {
- return this.iterator( 'table', function ( settings ) {
- _fnSaveState( settings );
- } );
- } );
-
-
-
- /**
- * Provide a common method for plug-ins to check the version of DataTables being
- * used, in order to ensure compatibility.
- *
- * @param {string} version Version string to check for, in the format "X.Y.Z".
- * Note that the formats "X" and "X.Y" are also acceptable.
- * @returns {boolean} true if this version of DataTables is greater or equal to
- * the required version, or false if this version of DataTales is not
- * suitable
- * @static
- * @dtopt API-Static
- *
- * @example
- * alert( $.fn.dataTable.versionCheck( '1.9.0' ) );
- */
- DataTable.versionCheck = DataTable.fnVersionCheck = function( version )
- {
- var aThis = DataTable.version.split('.');
- var aThat = version.split('.');
- var iThis, iThat;
-
- for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) {
- iThis = parseInt( aThis[i], 10 ) || 0;
- iThat = parseInt( aThat[i], 10 ) || 0;
-
- // Parts are the same, keep comparing
- if (iThis === iThat) {
- continue;
- }
-
- // Parts are different, return immediately
- return iThis > iThat;
- }
-
- return true;
- };
-
-
- /**
- * Check if a `<table>` node is a DataTable table already or not.
- *
- * @param {node|jquery|string} table Table node, jQuery object or jQuery
- * selector for the table to test. Note that if more than more than one
- * table is passed on, only the first will be checked
- * @returns {boolean} true the table given is a DataTable, or false otherwise
- * @static
- * @dtopt API-Static
- *
- * @example
- * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) {
- * $('#example').dataTable();
- * }
- */
- DataTable.isDataTable = DataTable.fnIsDataTable = function ( table )
- {
- var t = $(table).get(0);
- var is = false;
-
- if ( table instanceof DataTable.Api ) {
- return true;
- }
-
- $.each( DataTable.settings, function (i, o) {
- var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null;
- var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null;
-
- if ( o.nTable === t || head === t || foot === t ) {
- is = true;
- }
- } );
-
- return is;
- };
-
-
- /**
- * Get all DataTable tables that have been initialised - optionally you can
- * select to get only currently visible tables.
- *
- * @param {boolean} [visible=false] Flag to indicate if you want all (default)
- * or visible tables only.
- * @returns {array} Array of `table` nodes (not DataTable instances) which are
- * DataTables
- * @static
- * @dtopt API-Static
- *
- * @example
- * $.each( $.fn.dataTable.tables(true), function () {
- * $(table).DataTable().columns.adjust();
- * } );
- */
- DataTable.tables = DataTable.fnTables = function ( visible )
- {
- var api = false;
-
- if ( $.isPlainObject( visible ) ) {
- api = visible.api;
- visible = visible.visible;
- }
-
- var a = $.map( DataTable.settings, function (o) {
- if ( !visible || (visible && $(o.nTable).is(':visible')) ) {
- return o.nTable;
- }
- } );
-
- return api ?
- new _Api( a ) :
- a;
- };
-
-
- /**
- * Convert from camel case parameters to Hungarian notation. This is made public
- * for the extensions to provide the same ability as DataTables core to accept
- * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase
- * parameters.
- *
- * @param {object} src The model object which holds all parameters that can be
- * mapped.
- * @param {object} user The object to convert from camel case to Hungarian.
- * @param {boolean} force When set to `true`, properties which already have a
- * Hungarian value in the `user` object will be overwritten. Otherwise they
- * won't be.
- */
- DataTable.camelToHungarian = _fnCamelToHungarian;
-
-
-
- /**
- *
- */
- _api_register( '$()', function ( selector, opts ) {
- var
- rows = this.rows( opts ).nodes(), // Get all rows
- jqRows = $(rows);
-
- return $( [].concat(
- jqRows.filter( selector ).toArray(),
- jqRows.find( selector ).toArray()
- ) );
- } );
-
-
- // jQuery functions to operate on the tables
- $.each( [ 'on', 'one', 'off' ], function (i, key) {
- _api_register( key+'()', function ( /* event, handler */ ) {
- var args = Array.prototype.slice.call(arguments);
-
- // Add the `dt` namespace automatically if it isn't already present
- args[0] = $.map( args[0].split( /\s/ ), function ( e ) {
- return ! e.match(/\.dt\b/) ?
- e+'.dt' :
- e;
- } ).join( ' ' );
-
- var inst = $( this.tables().nodes() );
- inst[key].apply( inst, args );
- return this;
- } );
- } );
-
-
- _api_register( 'clear()', function () {
- return this.iterator( 'table', function ( settings ) {
- _fnClearTable( settings );
- } );
- } );
-
-
- _api_register( 'settings()', function () {
- return new _Api( this.context, this.context );
- } );
-
-
- _api_register( 'init()', function () {
- var ctx = this.context;
- return ctx.length ? ctx[0].oInit : null;
- } );
-
-
- _api_register( 'data()', function () {
- return this.iterator( 'table', function ( settings ) {
- return _pluck( settings.aoData, '_aData' );
- } ).flatten();
- } );
-
-
- _api_register( 'destroy()', function ( remove ) {
- remove = remove || false;
-
- return this.iterator( 'table', function ( settings ) {
- var orig = settings.nTableWrapper.parentNode;
- var classes = settings.oClasses;
- var table = settings.nTable;
- var tbody = settings.nTBody;
- var thead = settings.nTHead;
- var tfoot = settings.nTFoot;
- var jqTable = $(table);
- var jqTbody = $(tbody);
- var jqWrapper = $(settings.nTableWrapper);
- var rows = $.map( settings.aoData, function (r) { return r.nTr; } );
- var i, ien;
-
- // Flag to note that the table is currently being destroyed - no action
- // should be taken
- settings.bDestroying = true;
-
- // Fire off the destroy callbacks for plug-ins etc
- _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] );
-
- // If not being removed from the document, make all columns visible
- if ( ! remove ) {
- new _Api( settings ).columns().visible( true );
- }
-
- // Blitz all `DT` namespaced events (these are internal events, the
- // lowercase, `dt` events are user subscribed and they are responsible
- // for removing them
- jqWrapper.off('.DT').find(':not(tbody *)').off('.DT');
- $(window).off('.DT-'+settings.sInstance);
-
- // When scrolling we had to break the table up - restore it
- if ( table != thead.parentNode ) {
- jqTable.children('thead').detach();
- jqTable.append( thead );
- }
-
- if ( tfoot && table != tfoot.parentNode ) {
- jqTable.children('tfoot').detach();
- jqTable.append( tfoot );
- }
-
- settings.aaSorting = [];
- settings.aaSortingFixed = [];
- _fnSortingClasses( settings );
-
- $( rows ).removeClass( settings.asStripeClasses.join(' ') );
-
- $('th, td', thead).removeClass( classes.sSortable+' '+
- classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone
- );
-
- // Add the TR elements back into the table in their original order
- jqTbody.children().detach();
- jqTbody.append( rows );
-
- // Remove the DataTables generated nodes, events and classes
- var removedMethod = remove ? 'remove' : 'detach';
- jqTable[ removedMethod ]();
- jqWrapper[ removedMethod ]();
-
- // If we need to reattach the table to the document
- if ( ! remove && orig ) {
- // insertBefore acts like appendChild if !arg[1]
- orig.insertBefore( table, settings.nTableReinsertBefore );
-
- // Restore the width of the original table - was read from the style property,
- // so we can restore directly to that
- jqTable
- .css( 'width', settings.sDestroyWidth )
- .removeClass( classes.sTable );
-
- // If the were originally stripe classes - then we add them back here.
- // Note this is not fool proof (for example if not all rows had stripe
- // classes - but it's a good effort without getting carried away
- ien = settings.asDestroyStripes.length;
-
- if ( ien ) {
- jqTbody.children().each( function (i) {
- $(this).addClass( settings.asDestroyStripes[i % ien] );
- } );
- }
- }
-
- /* Remove the settings object from the settings array */
- var idx = $.inArray( settings, DataTable.settings );
- if ( idx !== -1 ) {
- DataTable.settings.splice( idx, 1 );
- }
- } );
- } );
-
-
- // Add the `every()` method for rows, columns and cells in a compact form
- $.each( [ 'column', 'row', 'cell' ], function ( i, type ) {
- _api_register( type+'s().every()', function ( fn ) {
- var opts = this.selector.opts;
- var api = this;
-
- return this.iterator( type, function ( settings, arg1, arg2, arg3, arg4 ) {
- // Rows and columns:
- // arg1 - index
- // arg2 - table counter
- // arg3 - loop counter
- // arg4 - undefined
- // Cells:
- // arg1 - row index
- // arg2 - column index
- // arg3 - table counter
- // arg4 - loop counter
- fn.call(
- api[ type ](
- arg1,
- type==='cell' ? arg2 : opts,
- type==='cell' ? opts : undefined
- ),
- arg1, arg2, arg3, arg4
- );
- } );
- } );
- } );
-
-
- // i18n method for extensions to be able to use the language object from the
- // DataTable
- _api_register( 'i18n()', function ( token, def, plural ) {
- var ctx = this.context[0];
- var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage );
-
- if ( resolved === undefined ) {
- resolved = def;
- }
-
- if ( plural !== undefined && $.isPlainObject( resolved ) ) {
- resolved = resolved[ plural ] !== undefined ?
- resolved[ plural ] :
- resolved._;
- }
-
- return resolved.replace( '%d', plural ); // nb: plural might be undefined,
- } );
- /**
- * Version string for plug-ins to check compatibility. Allowed format is
- * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used
- * only for non-release builds. See http://semver.org/ for more information.
- * @member
- * @type string
- * @default Version number
- */
- DataTable.version = "1.10.16";
- /**
- * Private data store, containing all of the settings objects that are
- * created for the tables on a given page.
- *
- * Note that the `DataTable.settings` object is aliased to
- * `jQuery.fn.dataTableExt` through which it may be accessed and
- * manipulated, or `jQuery.fn.dataTable.settings`.
- * @member
- * @type array
- * @default []
- * @private
- */
- DataTable.settings = [];
- /**
- * Object models container, for the various models that DataTables has
- * available to it. These models define the objects that are used to hold
- * the active state and configuration of the table.
- * @namespace
- */
- DataTable.models = {};
-
-
-
- /**
- * Template object for the way in which DataTables holds information about
- * search information for the global filter and individual column filters.
- * @namespace
- */
- DataTable.models.oSearch = {
- /**
- * Flag to indicate if the filtering should be case insensitive or not
- * @type boolean
- * @default true
- */
- "bCaseInsensitive": true,
-
- /**
- * Applied search term
- * @type string
- * @default <i>Empty string</i>
- */
- "sSearch": "",
-
- /**
- * Flag to indicate if the search term should be interpreted as a
- * regular expression (true) or not (false) and therefore and special
- * regex characters escaped.
- * @type boolean
- * @default false
- */
- "bRegex": false,
-
- /**
- * Flag to indicate if DataTables is to use its smart filtering or not.
- * @type boolean
- * @default true
- */
- "bSmart": true
- };
-
-
-
-
- /**
- * Template object for the way in which DataTables holds information about
- * each individual row. This is the object format used for the settings
- * aoData array.
- * @namespace
- */
- DataTable.models.oRow = {
- /**
- * TR element for the row
- * @type node
- * @default null
- */
- "nTr": null,
-
- /**
- * Array of TD elements for each row. This is null until the row has been
- * created.
- * @type array nodes
- * @default []
- */
- "anCells": null,
-
- /**
- * Data object from the original data source for the row. This is either
- * an array if using the traditional form of DataTables, or an object if
- * using mData options. The exact type will depend on the passed in
- * data from the data source, or will be an array if using DOM a data
- * source.
- * @type array|object
- * @default []
- */
- "_aData": [],
-
- /**
- * Sorting data cache - this array is ostensibly the same length as the
- * number of columns (although each index is generated only as it is
- * needed), and holds the data that is used for sorting each column in the
- * row. We do this cache generation at the start of the sort in order that
- * the formatting of the sort data need be done only once for each cell
- * per sort. This array should not be read from or written to by anything
- * other than the master sorting methods.
- * @type array
- * @default null
- * @private
- */
- "_aSortData": null,
-
- /**
- * Per cell filtering data cache. As per the sort data cache, used to
- * increase the performance of the filtering in DataTables
- * @type array
- * @default null
- * @private
- */
- "_aFilterData": null,
-
- /**
- * Filtering data cache. This is the same as the cell filtering cache, but
- * in this case a string rather than an array. This is easily computed with
- * a join on `_aFilterData`, but is provided as a cache so the join isn't
- * needed on every search (memory traded for performance)
- * @type array
- * @default null
- * @private
- */
- "_sFilterRow": null,
-
- /**
- * Cache of the class name that DataTables has applied to the row, so we
- * can quickly look at this variable rather than needing to do a DOM check
- * on className for the nTr property.
- * @type string
- * @default <i>Empty string</i>
- * @private
- */
- "_sRowStripe": "",
-
- /**
- * Denote if the original data source was from the DOM, or the data source
- * object. This is used for invalidating data, so DataTables can
- * automatically read data from the original source, unless uninstructed
- * otherwise.
- * @type string
- * @default null
- * @private
- */
- "src": null,
-
- /**
- * Index in the aoData array. This saves an indexOf lookup when we have the
- * object, but want to know the index
- * @type integer
- * @default -1
- * @private
- */
- "idx": -1
- };
-
-
- /**
- * Template object for the column information object in DataTables. This object
- * is held in the settings aoColumns array and contains all the information that
- * DataTables needs about each individual column.
- *
- * Note that this object is related to {@link DataTable.defaults.column}
- * but this one is the internal data store for DataTables's cache of columns.
- * It should NOT be manipulated outside of DataTables. Any configuration should
- * be done through the initialisation options.
- * @namespace
- */
- DataTable.models.oColumn = {
- /**
- * Column index. This could be worked out on-the-fly with $.inArray, but it
- * is faster to just hold it as a variable
- * @type integer
- * @default null
- */
- "idx": null,
-
- /**
- * A list of the columns that sorting should occur on when this column
- * is sorted. That this property is an array allows multi-column sorting
- * to be defined for a column (for example first name / last name columns
- * would benefit from this). The values are integers pointing to the
- * columns to be sorted on (typically it will be a single integer pointing
- * at itself, but that doesn't need to be the case).
- * @type array
- */
- "aDataSort": null,
-
- /**
- * Define the sorting directions that are applied to the column, in sequence
- * as the column is repeatedly sorted upon - i.e. the first value is used
- * as the sorting direction when the column if first sorted (clicked on).
- * Sort it again (click again) and it will move on to the next index.
- * Repeat until loop.
- * @type array
- */
- "asSorting": null,
-
- /**
- * Flag to indicate if the column is searchable, and thus should be included
- * in the filtering or not.
- * @type boolean
- */
- "bSearchable": null,
-
- /**
- * Flag to indicate if the column is sortable or not.
- * @type boolean
- */
- "bSortable": null,
-
- /**
- * Flag to indicate if the column is currently visible in the table or not
- * @type boolean
- */
- "bVisible": null,
-
- /**
- * Store for manual type assignment using the `column.type` option. This
- * is held in store so we can manipulate the column's `sType` property.
- * @type string
- * @default null
- * @private
- */
- "_sManualType": null,
-
- /**
- * Flag to indicate if HTML5 data attributes should be used as the data
- * source for filtering or sorting. True is either are.
- * @type boolean
- * @default false
- * @private
- */
- "_bAttrSrc": false,
-
- /**
- * Developer definable function that is called whenever a cell is created (Ajax source,
- * etc) or processed for input (DOM source). This can be used as a compliment to mRender
- * allowing you to modify the DOM element (add background colour for example) when the
- * element is available.
- * @type function
- * @param {element} nTd The TD node that has been created
- * @param {*} sData The Data for the cell
- * @param {array|object} oData The data for the whole row
- * @param {int} iRow The row index for the aoData data store
- * @default null
- */
- "fnCreatedCell": null,
-
- /**
- * Function to get data from a cell in a column. You should <b>never</b>
- * access data directly through _aData internally in DataTables - always use
- * the method attached to this property. It allows mData to function as
- * required. This function is automatically assigned by the column
- * initialisation method
- * @type function
- * @param {array|object} oData The data array/object for the array
- * (i.e. aoData[]._aData)
- * @param {string} sSpecific The specific data type you want to get -
- * 'display', 'type' 'filter' 'sort'
- * @returns {*} The data for the cell from the given row's data
- * @default null
- */
- "fnGetData": null,
-
- /**
- * Function to set data for a cell in the column. You should <b>never</b>
- * set the data directly to _aData internally in DataTables - always use
- * this method. It allows mData to function as required. This function
- * is automatically assigned by the column initialisation method
- * @type function
- * @param {array|object} oData The data array/object for the array
- * (i.e. aoData[]._aData)
- * @param {*} sValue Value to set
- * @default null
- */
- "fnSetData": null,
-
- /**
- * Property to read the value for the cells in the column from the data
- * source array / object. If null, then the default content is used, if a
- * function is given then the return from the function is used.
- * @type function|int|string|null
- * @default null
- */
- "mData": null,
-
- /**
- * Partner property to mData which is used (only when defined) to get
- * the data - i.e. it is basically the same as mData, but without the
- * 'set' option, and also the data fed to it is the result from mData.
- * This is the rendering method to match the data method of mData.
- * @type function|int|string|null
- * @default null
- */
- "mRender": null,
-
- /**
- * Unique header TH/TD element for this column - this is what the sorting
- * listener is attached to (if sorting is enabled.)
- * @type node
- * @default null
- */
- "nTh": null,
-
- /**
- * Unique footer TH/TD element for this column (if there is one). Not used
- * in DataTables as such, but can be used for plug-ins to reference the
- * footer for each column.
- * @type node
- * @default null
- */
- "nTf": null,
-
- /**
- * The class to apply to all TD elements in the table's TBODY for the column
- * @type string
- * @default null
- */
- "sClass": null,
-
- /**
- * When DataTables calculates the column widths to assign to each column,
- * it finds the longest string in each column and then constructs a
- * temporary table and reads the widths from that. The problem with this
- * is that "mmm" is much wider then "iiii", but the latter is a longer
- * string - thus the calculation can go wrong (doing it properly and putting
- * it into an DOM object and measuring that is horribly(!) slow). Thus as
- * a "work around" we provide this option. It will append its value to the
- * text that is found to be the longest string for the column - i.e. padding.
- * @type string
- */
- "sContentPadding": null,
-
- /**
- * Allows a default value to be given for a column's data, and will be used
- * whenever a null data source is encountered (this can be because mData
- * is set to null, or because the data source itself is null).
- * @type string
- * @default null
- */
- "sDefaultContent": null,
-
- /**
- * Name for the column, allowing reference to the column by name as well as
- * by index (needs a lookup to work by name).
- * @type string
- */
- "sName": null,
-
- /**
- * Custom sorting data type - defines which of the available plug-ins in
- * afnSortData the custom sorting will use - if any is defined.
- * @type string
- * @default std
- */
- "sSortDataType": 'std',
-
- /**
- * Class to be applied to the header element when sorting on this column
- * @type string
- * @default null
- */
- "sSortingClass": null,
-
- /**
- * Class to be applied to the header element when sorting on this column -
- * when jQuery UI theming is used.
- * @type string
- * @default null
- */
- "sSortingClassJUI": null,
-
- /**
- * Title of the column - what is seen in the TH element (nTh).
- * @type string
- */
- "sTitle": null,
-
- /**
- * Column sorting and filtering type
- * @type string
- * @default null
- */
- "sType": null,
-
- /**
- * Width of the column
- * @type string
- * @default null
- */
- "sWidth": null,
-
- /**
- * Width of the column when it was first "encountered"
- * @type string
- * @default null
- */
- "sWidthOrig": null
- };
-
-
- /*
- * Developer note: The properties of the object below are given in Hungarian
- * notation, that was used as the interface for DataTables prior to v1.10, however
- * from v1.10 onwards the primary interface is camel case. In order to avoid
- * breaking backwards compatibility utterly with this change, the Hungarian
- * version is still, internally the primary interface, but is is not documented
- * - hence the @name tags in each doc comment. This allows a Javascript function
- * to create a map from Hungarian notation to camel case (going the other direction
- * would require each property to be listed, which would at around 3K to the size
- * of DataTables, while this method is about a 0.5K hit.
- *
- * Ultimately this does pave the way for Hungarian notation to be dropped
- * completely, but that is a massive amount of work and will break current
- * installs (therefore is on-hold until v2).
- */
-
- /**
- * Initialisation options that can be given to DataTables at initialisation
- * time.
- * @namespace
- */
- DataTable.defaults = {
- /**
- * An array of data to use for the table, passed in at initialisation which
- * will be used in preference to any data which is already in the DOM. This is
- * particularly useful for constructing tables purely in Javascript, for
- * example with a custom Ajax call.
- * @type array
- * @default null
- *
- * @dtopt Option
- * @name DataTable.defaults.data
- *
- * @example
- * // Using a 2D array data source
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "data": [
- * ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'],
- * ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'],
- * ],
- * "columns": [
- * { "title": "Engine" },
- * { "title": "Browser" },
- * { "title": "Platform" },
- * { "title": "Version" },
- * { "title": "Grade" }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using an array of objects as a data source (`data`)
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "data": [
- * {
- * "engine": "Trident",
- * "browser": "Internet Explorer 4.0",
- * "platform": "Win 95+",
- * "version": 4,
- * "grade": "X"
- * },
- * {
- * "engine": "Trident",
- * "browser": "Internet Explorer 5.0",
- * "platform": "Win 95+",
- * "version": 5,
- * "grade": "C"
- * }
- * ],
- * "columns": [
- * { "title": "Engine", "data": "engine" },
- * { "title": "Browser", "data": "browser" },
- * { "title": "Platform", "data": "platform" },
- * { "title": "Version", "data": "version" },
- * { "title": "Grade", "data": "grade" }
- * ]
- * } );
- * } );
- */
- "aaData": null,
-
-
- /**
- * If ordering is enabled, then DataTables will perform a first pass sort on
- * initialisation. You can define which column(s) the sort is performed
- * upon, and the sorting direction, with this variable. The `sorting` array
- * should contain an array for each column to be sorted initially containing
- * the column's index and a direction string ('asc' or 'desc').
- * @type array
- * @default [[0,'asc']]
- *
- * @dtopt Option
- * @name DataTable.defaults.order
- *
- * @example
- * // Sort by 3rd column first, and then 4th column
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "order": [[2,'asc'], [3,'desc']]
- * } );
- * } );
- *
- * // No initial sorting
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "order": []
- * } );
- * } );
- */
- "aaSorting": [[0,'asc']],
-
-
- /**
- * This parameter is basically identical to the `sorting` parameter, but
- * cannot be overridden by user interaction with the table. What this means
- * is that you could have a column (visible or hidden) which the sorting
- * will always be forced on first - any sorting after that (from the user)
- * will then be performed as required. This can be useful for grouping rows
- * together.
- * @type array
- * @default null
- *
- * @dtopt Option
- * @name DataTable.defaults.orderFixed
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "orderFixed": [[0,'asc']]
- * } );
- * } )
- */
- "aaSortingFixed": [],
-
-
- /**
- * DataTables can be instructed to load data to display in the table from a
- * Ajax source. This option defines how that Ajax call is made and where to.
- *
- * The `ajax` property has three different modes of operation, depending on
- * how it is defined. These are:
- *
- * * `string` - Set the URL from where the data should be loaded from.
- * * `object` - Define properties for `jQuery.ajax`.
- * * `function` - Custom data get function
- *
- * `string`
- * --------
- *
- * As a string, the `ajax` property simply defines the URL from which
- * DataTables will load data.
- *
- * `object`
- * --------
- *
- * As an object, the parameters in the object are passed to
- * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control
- * of the Ajax request. DataTables has a number of default parameters which
- * you can override using this option. Please refer to the jQuery
- * documentation for a full description of the options available, although
- * the following parameters provide additional options in DataTables or
- * require special consideration:
- *
- * * `data` - As with jQuery, `data` can be provided as an object, but it
- * can also be used as a function to manipulate the data DataTables sends
- * to the server. The function takes a single parameter, an object of
- * parameters with the values that DataTables has readied for sending. An
- * object may be returned which will be merged into the DataTables
- * defaults, or you can add the items to the object that was passed in and
- * not return anything from the function. This supersedes `fnServerParams`
- * from DataTables 1.9-.
- *
- * * `dataSrc` - By default DataTables will look for the property `data` (or
- * `aaData` for compatibility with DataTables 1.9-) when obtaining data
- * from an Ajax source or for server-side processing - this parameter
- * allows that property to be changed. You can use Javascript dotted
- * object notation to get a data source for multiple levels of nesting, or
- * it my be used as a function. As a function it takes a single parameter,
- * the JSON returned from the server, which can be manipulated as
- * required, with the returned value being that used by DataTables as the
- * data source for the table. This supersedes `sAjaxDataProp` from
- * DataTables 1.9-.
- *
- * * `success` - Should not be overridden it is used internally in
- * DataTables. To manipulate / transform the data returned by the server
- * use `ajax.dataSrc`, or use `ajax` as a function (see below).
- *
- * `function`
- * ----------
- *
- * As a function, making the Ajax call is left up to yourself allowing
- * complete control of the Ajax request. Indeed, if desired, a method other
- * than Ajax could be used to obtain the required data, such as Web storage
- * or an AIR database.
- *
- * The function is given four parameters and no return is required. The
- * parameters are:
- *
- * 1. _object_ - Data to send to the server
- * 2. _function_ - Callback function that must be executed when the required
- * data has been obtained. That data should be passed into the callback
- * as the only parameter
- * 3. _object_ - DataTables settings object for the table
- *
- * Note that this supersedes `fnServerData` from DataTables 1.9-.
- *
- * @type string|object|function
- * @default null
- *
- * @dtopt Option
- * @name DataTable.defaults.ajax
- * @since 1.10.0
- *
- * @example
- * // Get JSON data from a file via Ajax.
- * // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default).
- * $('#example').dataTable( {
- * "ajax": "data.json"
- * } );
- *
- * @example
- * // Get JSON data from a file via Ajax, using `dataSrc` to change
- * // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`)
- * $('#example').dataTable( {
- * "ajax": {
- * "url": "data.json",
- * "dataSrc": "tableData"
- * }
- * } );
- *
- * @example
- * // Get JSON data from a file via Ajax, using `dataSrc` to read data
- * // from a plain array rather than an array in an object
- * $('#example').dataTable( {
- * "ajax": {
- * "url": "data.json",
- * "dataSrc": ""
- * }
- * } );
- *
- * @example
- * // Manipulate the data returned from the server - add a link to data
- * // (note this can, should, be done using `render` for the column - this
- * // is just a simple example of how the data can be manipulated).
- * $('#example').dataTable( {
- * "ajax": {
- * "url": "data.json",
- * "dataSrc": function ( json ) {
- * for ( var i=0, ien=json.length ; i<ien ; i++ ) {
- * json[i][0] = '<a href="/message/'+json[i][0]+'>View message</a>';
- * }
- * return json;
- * }
- * }
- * } );
- *
- * @example
- * // Add data to the request
- * $('#example').dataTable( {
- * "ajax": {
- * "url": "data.json",
- * "data": function ( d ) {
- * return {
- * "extra_search": $('#extra').val()
- * };
- * }
- * }
- * } );
- *
- * @example
- * // Send request as POST
- * $('#example').dataTable( {
- * "ajax": {
- * "url": "data.json",
- * "type": "POST"
- * }
- * } );
- *
- * @example
- * // Get the data from localStorage (could interface with a form for
- * // adding, editing and removing rows).
- * $('#example').dataTable( {
- * "ajax": function (data, callback, settings) {
- * callback(
- * JSON.parse( localStorage.getItem('dataTablesData') )
- * );
- * }
- * } );
- */
- "ajax": null,
-
-
- /**
- * This parameter allows you to readily specify the entries in the length drop
- * down menu that DataTables shows when pagination is enabled. It can be
- * either a 1D array of options which will be used for both the displayed
- * option and the value, or a 2D array which will use the array in the first
- * position as the value, and the array in the second position as the
- * displayed options (useful for language strings such as 'All').
- *
- * Note that the `pageLength` property will be automatically set to the
- * first value given in this array, unless `pageLength` is also provided.
- * @type array
- * @default [ 10, 25, 50, 100 ]
- *
- * @dtopt Option
- * @name DataTable.defaults.lengthMenu
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]]
- * } );
- * } );
- */
- "aLengthMenu": [ 10, 25, 50, 100 ],
-
-
- /**
- * The `columns` option in the initialisation parameter allows you to define
- * details about the way individual columns behave. For a full list of
- * column options that can be set, please see
- * {@link DataTable.defaults.column}. Note that if you use `columns` to
- * define your columns, you must have an entry in the array for every single
- * column that you have in your table (these can be null if you don't which
- * to specify any options).
- * @member
- *
- * @name DataTable.defaults.column
- */
- "aoColumns": null,
-
- /**
- * Very similar to `columns`, `columnDefs` allows you to target a specific
- * column, multiple columns, or all columns, using the `targets` property of
- * each object in the array. This allows great flexibility when creating
- * tables, as the `columnDefs` arrays can be of any length, targeting the
- * columns you specifically want. `columnDefs` may use any of the column
- * options available: {@link DataTable.defaults.column}, but it _must_
- * have `targets` defined in each object in the array. Values in the `targets`
- * array may be:
- * <ul>
- * <li>a string - class name will be matched on the TH for the column</li>
- * <li>0 or a positive integer - column index counting from the left</li>
- * <li>a negative integer - column index counting from the right</li>
- * <li>the string "_all" - all columns (i.e. assign a default)</li>
- * </ul>
- * @member
- *
- * @name DataTable.defaults.columnDefs
- */
- "aoColumnDefs": null,
-
-
- /**
- * Basically the same as `search`, this parameter defines the individual column
- * filtering state at initialisation time. The array must be of the same size
- * as the number of columns, and each element be an object with the parameters
- * `search` and `escapeRegex` (the latter is optional). 'null' is also
- * accepted and the default will be used.
- * @type array
- * @default []
- *
- * @dtopt Option
- * @name DataTable.defaults.searchCols
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "searchCols": [
- * null,
- * { "search": "My filter" },
- * null,
- * { "search": "^[0-9]", "escapeRegex": false }
- * ]
- * } );
- * } )
- */
- "aoSearchCols": [],
-
-
- /**
- * An array of CSS classes that should be applied to displayed rows. This
- * array may be of any length, and DataTables will apply each class
- * sequentially, looping when required.
- * @type array
- * @default null <i>Will take the values determined by the `oClasses.stripe*`
- * options</i>
- *
- * @dtopt Option
- * @name DataTable.defaults.stripeClasses
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stripeClasses": [ 'strip1', 'strip2', 'strip3' ]
- * } );
- * } )
- */
- "asStripeClasses": null,
-
-
- /**
- * Enable or disable automatic column width calculation. This can be disabled
- * as an optimisation (it takes some time to calculate the widths) if the
- * tables widths are passed in using `columns`.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.autoWidth
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "autoWidth": false
- * } );
- * } );
- */
- "bAutoWidth": true,
-
-
- /**
- * Deferred rendering can provide DataTables with a huge speed boost when you
- * are using an Ajax or JS data source for the table. This option, when set to
- * true, will cause DataTables to defer the creation of the table elements for
- * each row until they are needed for a draw - saving a significant amount of
- * time.
- * @type boolean
- * @default false
- *
- * @dtopt Features
- * @name DataTable.defaults.deferRender
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "ajax": "sources/arrays.txt",
- * "deferRender": true
- * } );
- * } );
- */
- "bDeferRender": false,
-
-
- /**
- * Replace a DataTable which matches the given selector and replace it with
- * one which has the properties of the new initialisation object passed. If no
- * table matches the selector, then the new DataTable will be constructed as
- * per normal.
- * @type boolean
- * @default false
- *
- * @dtopt Options
- * @name DataTable.defaults.destroy
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "srollY": "200px",
- * "paginate": false
- * } );
- *
- * // Some time later....
- * $('#example').dataTable( {
- * "filter": false,
- * "destroy": true
- * } );
- * } );
- */
- "bDestroy": false,
-
-
- /**
- * Enable or disable filtering of data. Filtering in DataTables is "smart" in
- * that it allows the end user to input multiple words (space separated) and
- * will match a row containing those words, even if not in the order that was
- * specified (this allow matching across multiple columns). Note that if you
- * wish to use filtering in DataTables this must remain 'true' - to remove the
- * default filtering input box and retain filtering abilities, please use
- * {@link DataTable.defaults.dom}.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.searching
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "searching": false
- * } );
- * } );
- */
- "bFilter": true,
-
-
- /**
- * Enable or disable the table information display. This shows information
- * about the data that is currently visible on the page, including information
- * about filtered data if that action is being performed.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.info
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "info": false
- * } );
- * } );
- */
- "bInfo": true,
-
-
- /**
- * Allows the end user to select the size of a formatted page from a select
- * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`).
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.lengthChange
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "lengthChange": false
- * } );
- * } );
- */
- "bLengthChange": true,
-
-
- /**
- * Enable or disable pagination.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.paging
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "paging": false
- * } );
- * } );
- */
- "bPaginate": true,
-
-
- /**
- * Enable or disable the display of a 'processing' indicator when the table is
- * being processed (e.g. a sort). This is particularly useful for tables with
- * large amounts of data where it can take a noticeable amount of time to sort
- * the entries.
- * @type boolean
- * @default false
- *
- * @dtopt Features
- * @name DataTable.defaults.processing
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "processing": true
- * } );
- * } );
- */
- "bProcessing": false,
-
-
- /**
- * Retrieve the DataTables object for the given selector. Note that if the
- * table has already been initialised, this parameter will cause DataTables
- * to simply return the object that has already been set up - it will not take
- * account of any changes you might have made to the initialisation object
- * passed to DataTables (setting this parameter to true is an acknowledgement
- * that you understand this). `destroy` can be used to reinitialise a table if
- * you need.
- * @type boolean
- * @default false
- *
- * @dtopt Options
- * @name DataTable.defaults.retrieve
- *
- * @example
- * $(document).ready( function() {
- * initTable();
- * tableActions();
- * } );
- *
- * function initTable ()
- * {
- * return $('#example').dataTable( {
- * "scrollY": "200px",
- * "paginate": false,
- * "retrieve": true
- * } );
- * }
- *
- * function tableActions ()
- * {
- * var table = initTable();
- * // perform API operations with oTable
- * }
- */
- "bRetrieve": false,
-
-
- /**
- * When vertical (y) scrolling is enabled, DataTables will force the height of
- * the table's viewport to the given height at all times (useful for layout).
- * However, this can look odd when filtering data down to a small data set,
- * and the footer is left "floating" further down. This parameter (when
- * enabled) will cause DataTables to collapse the table's viewport down when
- * the result set will fit within the given Y height.
- * @type boolean
- * @default false
- *
- * @dtopt Options
- * @name DataTable.defaults.scrollCollapse
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "scrollY": "200",
- * "scrollCollapse": true
- * } );
- * } );
- */
- "bScrollCollapse": false,
-
-
- /**
- * Configure DataTables to use server-side processing. Note that the
- * `ajax` parameter must also be given in order to give DataTables a
- * source to obtain the required data for each draw.
- * @type boolean
- * @default false
- *
- * @dtopt Features
- * @dtopt Server-side
- * @name DataTable.defaults.serverSide
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "serverSide": true,
- * "ajax": "xhr.php"
- * } );
- * } );
- */
- "bServerSide": false,
-
-
- /**
- * Enable or disable sorting of columns. Sorting of individual columns can be
- * disabled by the `sortable` option for each column.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.ordering
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "ordering": false
- * } );
- * } );
- */
- "bSort": true,
-
-
- /**
- * Enable or display DataTables' ability to sort multiple columns at the
- * same time (activated by shift-click by the user).
- * @type boolean
- * @default true
- *
- * @dtopt Options
- * @name DataTable.defaults.orderMulti
- *
- * @example
- * // Disable multiple column sorting ability
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "orderMulti": false
- * } );
- * } );
- */
- "bSortMulti": true,
-
-
- /**
- * Allows control over whether DataTables should use the top (true) unique
- * cell that is found for a single column, or the bottom (false - default).
- * This is useful when using complex headers.
- * @type boolean
- * @default false
- *
- * @dtopt Options
- * @name DataTable.defaults.orderCellsTop
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "orderCellsTop": true
- * } );
- * } );
- */
- "bSortCellsTop": false,
-
-
- /**
- * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
- * `sorting\_3` to the columns which are currently being sorted on. This is
- * presented as a feature switch as it can increase processing time (while
- * classes are removed and added) so for large data sets you might want to
- * turn this off.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.orderClasses
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "orderClasses": false
- * } );
- * } );
- */
- "bSortClasses": true,
-
-
- /**
- * Enable or disable state saving. When enabled HTML5 `localStorage` will be
- * used to save table display information such as pagination information,
- * display length, filtering and sorting. As such when the end user reloads
- * the page the display display will match what thy had previously set up.
- *
- * Due to the use of `localStorage` the default state saving is not supported
- * in IE6 or 7. If state saving is required in those browsers, use
- * `stateSaveCallback` to provide a storage solution such as cookies.
- * @type boolean
- * @default false
- *
- * @dtopt Features
- * @name DataTable.defaults.stateSave
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "stateSave": true
- * } );
- * } );
- */
- "bStateSave": false,
-
-
- /**
- * This function is called when a TR element is created (and all TD child
- * elements have been inserted), or registered if using a DOM source, allowing
- * manipulation of the TR element (adding classes etc).
- * @type function
- * @param {node} row "TR" element for the current row
- * @param {array} data Raw data array for this row
- * @param {int} dataIndex The index of this row in the internal aoData array
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.createdRow
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "createdRow": function( row, data, dataIndex ) {
- * // Bold the grade for all 'A' grade browsers
- * if ( data[4] == "A" )
- * {
- * $('td:eq(4)', row).html( '<b>A</b>' );
- * }
- * }
- * } );
- * } );
- */
- "fnCreatedRow": null,
-
-
- /**
- * This function is called on every 'draw' event, and allows you to
- * dynamically modify any aspect you want about the created DOM.
- * @type function
- * @param {object} settings DataTables settings object
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.drawCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "drawCallback": function( settings ) {
- * alert( 'DataTables has redrawn the table' );
- * }
- * } );
- * } );
- */
- "fnDrawCallback": null,
-
-
- /**
- * Identical to fnHeaderCallback() but for the table footer this function
- * allows you to modify the table footer on every 'draw' event.
- * @type function
- * @param {node} foot "TR" element for the footer
- * @param {array} data Full table data (as derived from the original HTML)
- * @param {int} start Index for the current display starting point in the
- * display array
- * @param {int} end Index for the current display ending point in the
- * display array
- * @param {array int} display Index array to translate the visual position
- * to the full data array
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.footerCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "footerCallback": function( tfoot, data, start, end, display ) {
- * tfoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+start;
- * }
- * } );
- * } )
- */
- "fnFooterCallback": null,
-
-
- /**
- * When rendering large numbers in the information element for the table
- * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers
- * to have a comma separator for the 'thousands' units (e.g. 1 million is
- * rendered as "1,000,000") to help readability for the end user. This
- * function will override the default method DataTables uses.
- * @type function
- * @member
- * @param {int} toFormat number to be formatted
- * @returns {string} formatted string for DataTables to show the number
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.formatNumber
- *
- * @example
- * // Format a number using a single quote for the separator (note that
- * // this can also be done with the language.thousands option)
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "formatNumber": function ( toFormat ) {
- * return toFormat.toString().replace(
- * /\B(?=(\d{3})+(?!\d))/g, "'"
- * );
- * };
- * } );
- * } );
- */
- "fnFormatNumber": function ( toFormat ) {
- return toFormat.toString().replace(
- /\B(?=(\d{3})+(?!\d))/g,
- this.oLanguage.sThousands
- );
- },
-
-
- /**
- * This function is called on every 'draw' event, and allows you to
- * dynamically modify the header row. This can be used to calculate and
- * display useful information about the table.
- * @type function
- * @param {node} head "TR" element for the header
- * @param {array} data Full table data (as derived from the original HTML)
- * @param {int} start Index for the current display starting point in the
- * display array
- * @param {int} end Index for the current display ending point in the
- * display array
- * @param {array int} display Index array to translate the visual position
- * to the full data array
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.headerCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "fheaderCallback": function( head, data, start, end, display ) {
- * head.getElementsByTagName('th')[0].innerHTML = "Displaying "+(end-start)+" records";
- * }
- * } );
- * } )
- */
- "fnHeaderCallback": null,
-
-
- /**
- * The information element can be used to convey information about the current
- * state of the table. Although the internationalisation options presented by
- * DataTables are quite capable of dealing with most customisations, there may
- * be times where you wish to customise the string further. This callback
- * allows you to do exactly that.
- * @type function
- * @param {object} oSettings DataTables settings object
- * @param {int} start Starting position in data for the draw
- * @param {int} end End position in data for the draw
- * @param {int} max Total number of rows in the table (regardless of
- * filtering)
- * @param {int} total Total number of rows in the data set, after filtering
- * @param {string} pre The string that DataTables has formatted using it's
- * own rules
- * @returns {string} The string to be displayed in the information element.
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.infoCallback
- *
- * @example
- * $('#example').dataTable( {
- * "infoCallback": function( settings, start, end, max, total, pre ) {
- * return start +" to "+ end;
- * }
- * } );
- */
- "fnInfoCallback": null,
-
-
- /**
- * Called when the table has been initialised. Normally DataTables will
- * initialise sequentially and there will be no need for this function,
- * however, this does not hold true when using external language information
- * since that is obtained using an async XHR call.
- * @type function
- * @param {object} settings DataTables settings object
- * @param {object} json The JSON object request from the server - only
- * present if client-side Ajax sourced data is used
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.initComplete
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "initComplete": function(settings, json) {
- * alert( 'DataTables has finished its initialisation.' );
- * }
- * } );
- * } )
- */
- "fnInitComplete": null,
-
-
- /**
- * Called at the very start of each table draw and can be used to cancel the
- * draw by returning false, any other return (including undefined) results in
- * the full draw occurring).
- * @type function
- * @param {object} settings DataTables settings object
- * @returns {boolean} False will cancel the draw, anything else (including no
- * return) will allow it to complete.
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.preDrawCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "preDrawCallback": function( settings ) {
- * if ( $('#test').val() == 1 ) {
- * return false;
- * }
- * }
- * } );
- * } );
- */
- "fnPreDrawCallback": null,
-
-
- /**
- * This function allows you to 'post process' each row after it have been
- * generated for each table draw, but before it is rendered on screen. This
- * function might be used for setting the row class name etc.
- * @type function
- * @param {node} row "TR" element for the current row
- * @param {array} data Raw data array for this row
- * @param {int} displayIndex The display index for the current table draw
- * @param {int} displayIndexFull The index of the data in the full list of
- * rows (after filtering)
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.rowCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "rowCallback": function( row, data, displayIndex, displayIndexFull ) {
- * // Bold the grade for all 'A' grade browsers
- * if ( data[4] == "A" ) {
- * $('td:eq(4)', row).html( '<b>A</b>' );
- * }
- * }
- * } );
- * } );
- */
- "fnRowCallback": null,
-
-
- /**
- * __Deprecated__ The functionality provided by this parameter has now been
- * superseded by that provided through `ajax`, which should be used instead.
- *
- * This parameter allows you to override the default function which obtains
- * the data from the server so something more suitable for your application.
- * For example you could use POST data, or pull information from a Gears or
- * AIR database.
- * @type function
- * @member
- * @param {string} source HTTP source to obtain the data from (`ajax`)
- * @param {array} data A key/value pair object containing the data to send
- * to the server
- * @param {function} callback to be called on completion of the data get
- * process that will draw the data on the page.
- * @param {object} settings DataTables settings object
- *
- * @dtopt Callbacks
- * @dtopt Server-side
- * @name DataTable.defaults.serverData
- *
- * @deprecated 1.10. Please use `ajax` for this functionality now.
- */
- "fnServerData": null,
-
-
- /**
- * __Deprecated__ The functionality provided by this parameter has now been
- * superseded by that provided through `ajax`, which should be used instead.
- *
- * It is often useful to send extra data to the server when making an Ajax
- * request - for example custom filtering information, and this callback
- * function makes it trivial to send extra information to the server. The
- * passed in parameter is the data set that has been constructed by
- * DataTables, and you can add to this or modify it as you require.
- * @type function
- * @param {array} data Data array (array of objects which are name/value
- * pairs) that has been constructed by DataTables and will be sent to the
- * server. In the case of Ajax sourced data with server-side processing
- * this will be an empty array, for server-side processing there will be a
- * significant number of parameters!
- * @returns {undefined} Ensure that you modify the data array passed in,
- * as this is passed by reference.
- *
- * @dtopt Callbacks
- * @dtopt Server-side
- * @name DataTable.defaults.serverParams
- *
- * @deprecated 1.10. Please use `ajax` for this functionality now.
- */
- "fnServerParams": null,
-
-
- /**
- * Load the table state. With this function you can define from where, and how, the
- * state of a table is loaded. By default DataTables will load from `localStorage`
- * but you might wish to use a server-side database or cookies.
- * @type function
- * @member
- * @param {object} settings DataTables settings object
- * @param {object} callback Callback that can be executed when done. It
- * should be passed the loaded state object.
- * @return {object} The DataTables state object to be loaded
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.stateLoadCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateLoadCallback": function (settings, callback) {
- * $.ajax( {
- * "url": "/state_load",
- * "dataType": "json",
- * "success": function (json) {
- * callback( json );
- * }
- * } );
- * }
- * } );
- * } );
- */
- "fnStateLoadCallback": function ( settings ) {
- try {
- return JSON.parse(
- (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
- 'DataTables_'+settings.sInstance+'_'+location.pathname
- )
- );
- } catch (e) {}
- },
-
-
- /**
- * Callback which allows modification of the saved state prior to loading that state.
- * This callback is called when the table is loading state from the stored data, but
- * prior to the settings object being modified by the saved state. Note that for
- * plug-in authors, you should use the `stateLoadParams` event to load parameters for
- * a plug-in.
- * @type function
- * @param {object} settings DataTables settings object
- * @param {object} data The state object that is to be loaded
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.stateLoadParams
- *
- * @example
- * // Remove a saved filter, so filtering is never loaded
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateLoadParams": function (settings, data) {
- * data.oSearch.sSearch = "";
- * }
- * } );
- * } );
- *
- * @example
- * // Disallow state loading by returning false
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateLoadParams": function (settings, data) {
- * return false;
- * }
- * } );
- * } );
- */
- "fnStateLoadParams": null,
-
-
- /**
- * Callback that is called when the state has been loaded from the state saving method
- * and the DataTables settings object has been modified as a result of the loaded state.
- * @type function
- * @param {object} settings DataTables settings object
- * @param {object} data The state object that was loaded
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.stateLoaded
- *
- * @example
- * // Show an alert with the filtering value that was saved
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateLoaded": function (settings, data) {
- * alert( 'Saved filter was: '+data.oSearch.sSearch );
- * }
- * } );
- * } );
- */
- "fnStateLoaded": null,
-
-
- /**
- * Save the table state. This function allows you to define where and how the state
- * information for the table is stored By default DataTables will use `localStorage`
- * but you might wish to use a server-side database or cookies.
- * @type function
- * @member
- * @param {object} settings DataTables settings object
- * @param {object} data The state object to be saved
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.stateSaveCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateSaveCallback": function (settings, data) {
- * // Send an Ajax request to the server with the state object
- * $.ajax( {
- * "url": "/state_save",
- * "data": data,
- * "dataType": "json",
- * "method": "POST"
- * "success": function () {}
- * } );
- * }
- * } );
- * } );
- */
- "fnStateSaveCallback": function ( settings, data ) {
- try {
- (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
- 'DataTables_'+settings.sInstance+'_'+location.pathname,
- JSON.stringify( data )
- );
- } catch (e) {}
- },
-
-
- /**
- * Callback which allows modification of the state to be saved. Called when the table
- * has changed state a new state save is required. This method allows modification of
- * the state saving object prior to actually doing the save, including addition or
- * other state properties or modification. Note that for plug-in authors, you should
- * use the `stateSaveParams` event to save parameters for a plug-in.
- * @type function
- * @param {object} settings DataTables settings object
- * @param {object} data The state object to be saved
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.stateSaveParams
- *
- * @example
- * // Remove a saved filter, so filtering is never saved
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateSaveParams": function (settings, data) {
- * data.oSearch.sSearch = "";
- * }
- * } );
- * } );
- */
- "fnStateSaveParams": null,
-
-
- /**
- * Duration for which the saved state information is considered valid. After this period
- * has elapsed the state will be returned to the default.
- * Value is given in seconds.
- * @type int
- * @default 7200 <i>(2 hours)</i>
- *
- * @dtopt Options
- * @name DataTable.defaults.stateDuration
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateDuration": 60*60*24; // 1 day
- * } );
- * } )
- */
- "iStateDuration": 7200,
-
-
- /**
- * When enabled DataTables will not make a request to the server for the first
- * page draw - rather it will use the data already on the page (no sorting etc
- * will be applied to it), thus saving on an XHR at load time. `deferLoading`
- * is used to indicate that deferred loading is required, but it is also used
- * to tell DataTables how many records there are in the full table (allowing
- * the information element and pagination to be displayed correctly). In the case
- * where a filtering is applied to the table on initial load, this can be
- * indicated by giving the parameter as an array, where the first element is
- * the number of records available after filtering and the second element is the
- * number of records without filtering (allowing the table information element
- * to be shown correctly).
- * @type int | array
- * @default null
- *
- * @dtopt Options
- * @name DataTable.defaults.deferLoading
- *
- * @example
- * // 57 records available in the table, no filtering applied
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "serverSide": true,
- * "ajax": "scripts/server_processing.php",
- * "deferLoading": 57
- * } );
- * } );
- *
- * @example
- * // 57 records after filtering, 100 without filtering (an initial filter applied)
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "serverSide": true,
- * "ajax": "scripts/server_processing.php",
- * "deferLoading": [ 57, 100 ],
- * "search": {
- * "search": "my_filter"
- * }
- * } );
- * } );
- */
- "iDeferLoading": null,
-
-
- /**
- * Number of rows to display on a single page when using pagination. If
- * feature enabled (`lengthChange`) then the end user will be able to override
- * this to a custom setting using a pop-up menu.
- * @type int
- * @default 10
- *
- * @dtopt Options
- * @name DataTable.defaults.pageLength
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "pageLength": 50
- * } );
- * } )
- */
- "iDisplayLength": 10,
-
-
- /**
- * Define the starting point for data display when using DataTables with
- * pagination. Note that this parameter is the number of records, rather than
- * the page number, so if you have 10 records per page and want to start on
- * the third page, it should be "20".
- * @type int
- * @default 0
- *
- * @dtopt Options
- * @name DataTable.defaults.displayStart
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "displayStart": 20
- * } );
- * } )
- */
- "iDisplayStart": 0,
-
-
- /**
- * By default DataTables allows keyboard navigation of the table (sorting, paging,
- * and filtering) by adding a `tabindex` attribute to the required elements. This
- * allows you to tab through the controls and press the enter key to activate them.
- * The tabindex is default 0, meaning that the tab follows the flow of the document.
- * You can overrule this using this parameter if you wish. Use a value of -1 to
- * disable built-in keyboard navigation.
- * @type int
- * @default 0
- *
- * @dtopt Options
- * @name DataTable.defaults.tabIndex
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "tabIndex": 1
- * } );
- * } );
- */
- "iTabIndex": 0,
-
-
- /**
- * Classes that DataTables assigns to the various components and features
- * that it adds to the HTML table. This allows classes to be configured
- * during initialisation in addition to through the static
- * {@link DataTable.ext.oStdClasses} object).
- * @namespace
- * @name DataTable.defaults.classes
- */
- "oClasses": {},
-
-
- /**
- * All strings that DataTables uses in the user interface that it creates
- * are defined in this object, allowing you to modified them individually or
- * completely replace them all as required.
- * @namespace
- * @name DataTable.defaults.language
- */
- "oLanguage": {
- /**
- * Strings that are used for WAI-ARIA labels and controls only (these are not
- * actually visible on the page, but will be read by screenreaders, and thus
- * must be internationalised as well).
- * @namespace
- * @name DataTable.defaults.language.aria
- */
- "oAria": {
- /**
- * ARIA label that is added to the table headers when the column may be
- * sorted ascending by activing the column (click or return when focused).
- * Note that the column header is prefixed to this string.
- * @type string
- * @default : activate to sort column ascending
- *
- * @dtopt Language
- * @name DataTable.defaults.language.aria.sortAscending
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "aria": {
- * "sortAscending": " - click/return to sort ascending"
- * }
- * }
- * } );
- * } );
- */
- "sSortAscending": ": activate to sort column ascending",
-
- /**
- * ARIA label that is added to the table headers when the column may be
- * sorted descending by activing the column (click or return when focused).
- * Note that the column header is prefixed to this string.
- * @type string
- * @default : activate to sort column ascending
- *
- * @dtopt Language
- * @name DataTable.defaults.language.aria.sortDescending
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "aria": {
- * "sortDescending": " - click/return to sort descending"
- * }
- * }
- * } );
- * } );
- */
- "sSortDescending": ": activate to sort column descending"
- },
-
- /**
- * Pagination string used by DataTables for the built-in pagination
- * control types.
- * @namespace
- * @name DataTable.defaults.language.paginate
- */
- "oPaginate": {
- /**
- * Text to use when using the 'full_numbers' type of pagination for the
- * button to take the user to the first page.
- * @type string
- * @default First
- *
- * @dtopt Language
- * @name DataTable.defaults.language.paginate.first
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "paginate": {
- * "first": "First page"
- * }
- * }
- * } );
- * } );
- */
- "sFirst": "First",
-
-
- /**
- * Text to use when using the 'full_numbers' type of pagination for the
- * button to take the user to the last page.
- * @type string
- * @default Last
- *
- * @dtopt Language
- * @name DataTable.defaults.language.paginate.last
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "paginate": {
- * "last": "Last page"
- * }
- * }
- * } );
- * } );
- */
- "sLast": "Last",
-
-
- /**
- * Text to use for the 'next' pagination button (to take the user to the
- * next page).
- * @type string
- * @default Next
- *
- * @dtopt Language
- * @name DataTable.defaults.language.paginate.next
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "paginate": {
- * "next": "Next page"
- * }
- * }
- * } );
- * } );
- */
- "sNext": "Next",
-
-
- /**
- * Text to use for the 'previous' pagination button (to take the user to
- * the previous page).
- * @type string
- * @default Previous
- *
- * @dtopt Language
- * @name DataTable.defaults.language.paginate.previous
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "paginate": {
- * "previous": "Previous page"
- * }
- * }
- * } );
- * } );
- */
- "sPrevious": "Previous"
- },
-
- /**
- * This string is shown in preference to `zeroRecords` when the table is
- * empty of data (regardless of filtering). Note that this is an optional
- * parameter - if it is not given, the value of `zeroRecords` will be used
- * instead (either the default or given value).
- * @type string
- * @default No data available in table
- *
- * @dtopt Language
- * @name DataTable.defaults.language.emptyTable
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "emptyTable": "No data available in table"
- * }
- * } );
- * } );
- */
- "sEmptyTable": "No data available in table",
-
-
- /**
- * This string gives information to the end user about the information
- * that is current on display on the page. The following tokens can be
- * used in the string and will be dynamically replaced as the table
- * display updates. This tokens can be placed anywhere in the string, or
- * removed as needed by the language requires:
- *
- * * `\_START\_` - Display index of the first record on the current page
- * * `\_END\_` - Display index of the last record on the current page
- * * `\_TOTAL\_` - Number of records in the table after filtering
- * * `\_MAX\_` - Number of records in the table without filtering
- * * `\_PAGE\_` - Current page number
- * * `\_PAGES\_` - Total number of pages of data in the table
- *
- * @type string
- * @default Showing _START_ to _END_ of _TOTAL_ entries
- *
- * @dtopt Language
- * @name DataTable.defaults.language.info
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "info": "Showing page _PAGE_ of _PAGES_"
- * }
- * } );
- * } );
- */
- "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
-
-
- /**
- * Display information string for when the table is empty. Typically the
- * format of this string should match `info`.
- * @type string
- * @default Showing 0 to 0 of 0 entries
- *
- * @dtopt Language
- * @name DataTable.defaults.language.infoEmpty
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "infoEmpty": "No entries to show"
- * }
- * } );
- * } );
- */
- "sInfoEmpty": "Showing 0 to 0 of 0 entries",
-
-
- /**
- * When a user filters the information in a table, this string is appended
- * to the information (`info`) to give an idea of how strong the filtering
- * is. The variable _MAX_ is dynamically updated.
- * @type string
- * @default (filtered from _MAX_ total entries)
- *
- * @dtopt Language
- * @name DataTable.defaults.language.infoFiltered
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "infoFiltered": " - filtering from _MAX_ records"
- * }
- * } );
- * } );
- */
- "sInfoFiltered": "(filtered from _MAX_ total entries)",
-
-
- /**
- * If can be useful to append extra information to the info string at times,
- * and this variable does exactly that. This information will be appended to
- * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are
- * being used) at all times.
- * @type string
- * @default <i>Empty string</i>
- *
- * @dtopt Language
- * @name DataTable.defaults.language.infoPostFix
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "infoPostFix": "All records shown are derived from real information."
- * }
- * } );
- * } );
- */
- "sInfoPostFix": "",
-
-
- /**
- * This decimal place operator is a little different from the other
- * language options since DataTables doesn't output floating point
- * numbers, so it won't ever use this for display of a number. Rather,
- * what this parameter does is modify the sort methods of the table so
- * that numbers which are in a format which has a character other than
- * a period (`.`) as a decimal place will be sorted numerically.
- *
- * Note that numbers with different decimal places cannot be shown in
- * the same table and still be sortable, the table must be consistent.
- * However, multiple different tables on the page can use different
- * decimal place characters.
- * @type string
- * @default
- *
- * @dtopt Language
- * @name DataTable.defaults.language.decimal
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "decimal": ","
- * "thousands": "."
- * }
- * } );
- * } );
- */
- "sDecimal": "",
-
-
- /**
- * DataTables has a build in number formatter (`formatNumber`) which is
- * used to format large numbers that are used in the table information.
- * By default a comma is used, but this can be trivially changed to any
- * character you wish with this parameter.
- * @type string
- * @default ,
- *
- * @dtopt Language
- * @name DataTable.defaults.language.thousands
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "thousands": "'"
- * }
- * } );
- * } );
- */
- "sThousands": ",",
-
-
- /**
- * Detail the action that will be taken when the drop down menu for the
- * pagination length option is changed. The '_MENU_' variable is replaced
- * with a default select list of 10, 25, 50 and 100, and can be replaced
- * with a custom select box if required.
- * @type string
- * @default Show _MENU_ entries
- *
- * @dtopt Language
- * @name DataTable.defaults.language.lengthMenu
- *
- * @example
- * // Language change only
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "lengthMenu": "Display _MENU_ records"
- * }
- * } );
- * } );
- *
- * @example
- * // Language and options change
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "lengthMenu": 'Display <select>'+
- * '<option value="10">10</option>'+
- * '<option value="20">20</option>'+
- * '<option value="30">30</option>'+
- * '<option value="40">40</option>'+
- * '<option value="50">50</option>'+
- * '<option value="-1">All</option>'+
- * '</select> records'
- * }
- * } );
- * } );
- */
- "sLengthMenu": "Show _MENU_ entries",
-
-
- /**
- * When using Ajax sourced data and during the first draw when DataTables is
- * gathering the data, this message is shown in an empty row in the table to
- * indicate to the end user the the data is being loaded. Note that this
- * parameter is not used when loading data by server-side processing, just
- * Ajax sourced data with client-side processing.
- * @type string
- * @default Loading...
- *
- * @dtopt Language
- * @name DataTable.defaults.language.loadingRecords
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "loadingRecords": "Please wait - loading..."
- * }
- * } );
- * } );
- */
- "sLoadingRecords": "Loading...",
-
-
- /**
- * Text which is displayed when the table is processing a user action
- * (usually a sort command or similar).
- * @type string
- * @default Processing...
- *
- * @dtopt Language
- * @name DataTable.defaults.language.processing
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "processing": "DataTables is currently busy"
- * }
- * } );
- * } );
- */
- "sProcessing": "Processing...",
-
-
- /**
- * Details the actions that will be taken when the user types into the
- * filtering input text box. The variable "_INPUT_", if used in the string,
- * is replaced with the HTML text box for the filtering input allowing
- * control over where it appears in the string. If "_INPUT_" is not given
- * then the input box is appended to the string automatically.
- * @type string
- * @default Search:
- *
- * @dtopt Language
- * @name DataTable.defaults.language.search
- *
- * @example
- * // Input text box will be appended at the end automatically
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "search": "Filter records:"
- * }
- * } );
- * } );
- *
- * @example
- * // Specify where the filter should appear
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "search": "Apply filter _INPUT_ to table"
- * }
- * } );
- * } );
- */
- "sSearch": "Search:",
-
-
- /**
- * Assign a `placeholder` attribute to the search `input` element
- * @type string
- * @default
- *
- * @dtopt Language
- * @name DataTable.defaults.language.searchPlaceholder
- */
- "sSearchPlaceholder": "",
-
-
- /**
- * All of the language information can be stored in a file on the
- * server-side, which DataTables will look up if this parameter is passed.
- * It must store the URL of the language file, which is in a JSON format,
- * and the object has the same properties as the oLanguage object in the
- * initialiser object (i.e. the above parameters). Please refer to one of
- * the example language files to see how this works in action.
- * @type string
- * @default <i>Empty string - i.e. disabled</i>
- *
- * @dtopt Language
- * @name DataTable.defaults.language.url
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "url": "http://www.sprymedia.co.uk/dataTables/lang.txt"
- * }
- * } );
- * } );
- */
- "sUrl": "",
-
-
- /**
- * Text shown inside the table records when the is no information to be
- * displayed after filtering. `emptyTable` is shown when there is simply no
- * information in the table at all (regardless of filtering).
- * @type string
- * @default No matching records found
- *
- * @dtopt Language
- * @name DataTable.defaults.language.zeroRecords
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "zeroRecords": "No records to display"
- * }
- * } );
- * } );
- */
- "sZeroRecords": "No matching records found"
- },
-
-
- /**
- * This parameter allows you to have define the global filtering state at
- * initialisation time. As an object the `search` parameter must be
- * defined, but all other parameters are optional. When `regex` is true,
- * the search string will be treated as a regular expression, when false
- * (default) it will be treated as a straight string. When `smart`
- * DataTables will use it's smart filtering methods (to word match at
- * any point in the data), when false this will not be done.
- * @namespace
- * @extends DataTable.models.oSearch
- *
- * @dtopt Options
- * @name DataTable.defaults.search
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "search": {"search": "Initial search"}
- * } );
- * } )
- */
- "oSearch": $.extend( {}, DataTable.models.oSearch ),
-
-
- /**
- * __Deprecated__ The functionality provided by this parameter has now been
- * superseded by that provided through `ajax`, which should be used instead.
- *
- * By default DataTables will look for the property `data` (or `aaData` for
- * compatibility with DataTables 1.9-) when obtaining data from an Ajax
- * source or for server-side processing - this parameter allows that
- * property to be changed. You can use Javascript dotted object notation to
- * get a data source for multiple levels of nesting.
- * @type string
- * @default data
- *
- * @dtopt Options
- * @dtopt Server-side
- * @name DataTable.defaults.ajaxDataProp
- *
- * @deprecated 1.10. Please use `ajax` for this functionality now.
- */
- "sAjaxDataProp": "data",
-
-
- /**
- * __Deprecated__ The functionality provided by this parameter has now been
- * superseded by that provided through `ajax`, which should be used instead.
- *
- * You can instruct DataTables to load data from an external
- * source using this parameter (use aData if you want to pass data in you
- * already have). Simply provide a url a JSON object can be obtained from.
- * @type string
- * @default null
- *
- * @dtopt Options
- * @dtopt Server-side
- * @name DataTable.defaults.ajaxSource
- *
- * @deprecated 1.10. Please use `ajax` for this functionality now.
- */
- "sAjaxSource": null,
-
-
- /**
- * This initialisation variable allows you to specify exactly where in the
- * DOM you want DataTables to inject the various controls it adds to the page
- * (for example you might want the pagination controls at the top of the
- * table). DIV elements (with or without a custom class) can also be added to
- * aid styling. The follow syntax is used:
- * <ul>
- * <li>The following options are allowed:
- * <ul>
- * <li>'l' - Length changing</li>
- * <li>'f' - Filtering input</li>
- * <li>'t' - The table!</li>
- * <li>'i' - Information</li>
- * <li>'p' - Pagination</li>
- * <li>'r' - pRocessing</li>
- * </ul>
- * </li>
- * <li>The following constants are allowed:
- * <ul>
- * <li>'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li>
- * <li>'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li>
- * </ul>
- * </li>
- * <li>The following syntax is expected:
- * <ul>
- * <li>'<' and '>' - div elements</li>
- * <li>'<"class" and '>' - div with a class</li>
- * <li>'<"#id" and '>' - div with an ID</li>
- * </ul>
- * </li>
- * <li>Examples:
- * <ul>
- * <li>'<"wrapper"flipt>'</li>
- * <li>'<lf<t>ip>'</li>
- * </ul>
- * </li>
- * </ul>
- * @type string
- * @default lfrtip <i>(when `jQueryUI` is false)</i> <b>or</b>
- * <"H"lfr>t<"F"ip> <i>(when `jQueryUI` is true)</i>
- *
- * @dtopt Options
- * @name DataTable.defaults.dom
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "dom": '<"top"i>rt<"bottom"flp><"clear">'
- * } );
- * } );
- */
- "sDom": "lfrtip",
-
-
- /**
- * Search delay option. This will throttle full table searches that use the
- * DataTables provided search input element (it does not effect calls to
- * `dt-api search()`, providing a delay before the search is made.
- * @type integer
- * @default 0
- *
- * @dtopt Options
- * @name DataTable.defaults.searchDelay
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "searchDelay": 200
- * } );
- * } )
- */
- "searchDelay": null,
-
-
- /**
- * DataTables features six different built-in options for the buttons to
- * display for pagination control:
- *
- * * `numbers` - Page number buttons only
- * * `simple` - 'Previous' and 'Next' buttons only
- * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
- * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons
- * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
- * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers
- *
- * Further methods can be added using {@link DataTable.ext.oPagination}.
- * @type string
- * @default simple_numbers
- *
- * @dtopt Options
- * @name DataTable.defaults.pagingType
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "pagingType": "full_numbers"
- * } );
- * } )
- */
- "sPaginationType": "simple_numbers",
-
-
- /**
- * Enable horizontal scrolling. When a table is too wide to fit into a
- * certain layout, or you have a large number of columns in the table, you
- * can enable x-scrolling to show the table in a viewport, which can be
- * scrolled. This property can be `true` which will allow the table to
- * scroll horizontally when needed, or any CSS unit, or a number (in which
- * case it will be treated as a pixel measurement). Setting as simply `true`
- * is recommended.
- * @type boolean|string
- * @default <i>blank string - i.e. disabled</i>
- *
- * @dtopt Features
- * @name DataTable.defaults.scrollX
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "scrollX": true,
- * "scrollCollapse": true
- * } );
- * } );
- */
- "sScrollX": "",
-
-
- /**
- * This property can be used to force a DataTable to use more width than it
- * might otherwise do when x-scrolling is enabled. For example if you have a
- * table which requires to be well spaced, this parameter is useful for
- * "over-sizing" the table, and thus forcing scrolling. This property can by
- * any CSS unit, or a number (in which case it will be treated as a pixel
- * measurement).
- * @type string
- * @default <i>blank string - i.e. disabled</i>
- *
- * @dtopt Options
- * @name DataTable.defaults.scrollXInner
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "scrollX": "100%",
- * "scrollXInner": "110%"
- * } );
- * } );
- */
- "sScrollXInner": "",
-
-
- /**
- * Enable vertical scrolling. Vertical scrolling will constrain the DataTable
- * to the given height, and enable scrolling for any data which overflows the
- * current viewport. This can be used as an alternative to paging to display
- * a lot of data in a small area (although paging and scrolling can both be
- * enabled at the same time). This property can be any CSS unit, or a number
- * (in which case it will be treated as a pixel measurement).
- * @type string
- * @default <i>blank string - i.e. disabled</i>
- *
- * @dtopt Features
- * @name DataTable.defaults.scrollY
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "scrollY": "200px",
- * "paginate": false
- * } );
- * } );
- */
- "sScrollY": "",
-
-
- /**
- * __Deprecated__ The functionality provided by this parameter has now been
- * superseded by that provided through `ajax`, which should be used instead.
- *
- * Set the HTTP method that is used to make the Ajax call for server-side
- * processing or Ajax sourced data.
- * @type string
- * @default GET
- *
- * @dtopt Options
- * @dtopt Server-side
- * @name DataTable.defaults.serverMethod
- *
- * @deprecated 1.10. Please use `ajax` for this functionality now.
- */
- "sServerMethod": "GET",
-
-
- /**
- * DataTables makes use of renderers when displaying HTML elements for
- * a table. These renderers can be added or modified by plug-ins to
- * generate suitable mark-up for a site. For example the Bootstrap
- * integration plug-in for DataTables uses a paging button renderer to
- * display pagination buttons in the mark-up required by Bootstrap.
- *
- * For further information about the renderers available see
- * DataTable.ext.renderer
- * @type string|object
- * @default null
- *
- * @name DataTable.defaults.renderer
- *
- */
- "renderer": null,
-
-
- /**
- * Set the data property name that DataTables should use to get a row's id
- * to set as the `id` property in the node.
- * @type string
- * @default DT_RowId
- *
- * @name DataTable.defaults.rowId
- */
- "rowId": "DT_RowId"
- };
-
- _fnHungarianMap( DataTable.defaults );
-
-
-
- /*
- * Developer note - See note in model.defaults.js about the use of Hungarian
- * notation and camel case.
- */
-
- /**
- * Column options that can be given to DataTables at initialisation time.
- * @namespace
- */
- DataTable.defaults.column = {
- /**
- * Define which column(s) an order will occur on for this column. This
- * allows a column's ordering to take multiple columns into account when
- * doing a sort or use the data from a different column. For example first
- * name / last name columns make sense to do a multi-column sort over the
- * two columns.
- * @type array|int
- * @default null <i>Takes the value of the column index automatically</i>
- *
- * @name DataTable.defaults.column.orderData
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "orderData": [ 0, 1 ], "targets": [ 0 ] },
- * { "orderData": [ 1, 0 ], "targets": [ 1 ] },
- * { "orderData": 2, "targets": [ 2 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "orderData": [ 0, 1 ] },
- * { "orderData": [ 1, 0 ] },
- * { "orderData": 2 },
- * null,
- * null
- * ]
- * } );
- * } );
- */
- "aDataSort": null,
- "iDataSort": -1,
-
-
- /**
- * You can control the default ordering direction, and even alter the
- * behaviour of the sort handler (i.e. only allow ascending ordering etc)
- * using this parameter.
- * @type array
- * @default [ 'asc', 'desc' ]
- *
- * @name DataTable.defaults.column.orderSequence
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "orderSequence": [ "asc" ], "targets": [ 1 ] },
- * { "orderSequence": [ "desc", "asc", "asc" ], "targets": [ 2 ] },
- * { "orderSequence": [ "desc" ], "targets": [ 3 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * null,
- * { "orderSequence": [ "asc" ] },
- * { "orderSequence": [ "desc", "asc", "asc" ] },
- * { "orderSequence": [ "desc" ] },
- * null
- * ]
- * } );
- * } );
- */
- "asSorting": [ 'asc', 'desc' ],
-
-
- /**
- * Enable or disable filtering on the data in this column.
- * @type boolean
- * @default true
- *
- * @name DataTable.defaults.column.searchable
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "searchable": false, "targets": [ 0 ] }
- * ] } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "searchable": false },
- * null,
- * null,
- * null,
- * null
- * ] } );
- * } );
- */
- "bSearchable": true,
-
-
- /**
- * Enable or disable ordering on this column.
- * @type boolean
- * @default true
- *
- * @name DataTable.defaults.column.orderable
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "orderable": false, "targets": [ 0 ] }
- * ] } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "orderable": false },
- * null,
- * null,
- * null,
- * null
- * ] } );
- * } );
- */
- "bSortable": true,
-
-
- /**
- * Enable or disable the display of this column.
- * @type boolean
- * @default true
- *
- * @name DataTable.defaults.column.visible
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "visible": false, "targets": [ 0 ] }
- * ] } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "visible": false },
- * null,
- * null,
- * null,
- * null
- * ] } );
- * } );
- */
- "bVisible": true,
-
-
- /**
- * Developer definable function that is called whenever a cell is created (Ajax source,
- * etc) or processed for input (DOM source). This can be used as a compliment to mRender
- * allowing you to modify the DOM element (add background colour for example) when the
- * element is available.
- * @type function
- * @param {element} td The TD node that has been created
- * @param {*} cellData The Data for the cell
- * @param {array|object} rowData The data for the whole row
- * @param {int} row The row index for the aoData data store
- * @param {int} col The column index for aoColumns
- *
- * @name DataTable.defaults.column.createdCell
- * @dtopt Columns
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [3],
- * "createdCell": function (td, cellData, rowData, row, col) {
- * if ( cellData == "1.7" ) {
- * $(td).css('color', 'blue')
- * }
- * }
- * } ]
- * });
- * } );
- */
- "fnCreatedCell": null,
-
-
- /**
- * This parameter has been replaced by `data` in DataTables to ensure naming
- * consistency. `dataProp` can still be used, as there is backwards
- * compatibility in DataTables for this option, but it is strongly
- * recommended that you use `data` in preference to `dataProp`.
- * @name DataTable.defaults.column.dataProp
- */
-
-
- /**
- * This property can be used to read data from any data source property,
- * including deeply nested objects / properties. `data` can be given in a
- * number of different ways which effect its behaviour:
- *
- * * `integer` - treated as an array index for the data source. This is the
- * default that DataTables uses (incrementally increased for each column).
- * * `string` - read an object property from the data source. There are
- * three 'special' options that can be used in the string to alter how
- * DataTables reads the data from the source object:
- * * `.` - Dotted Javascript notation. Just as you use a `.` in
- * Javascript to read from nested objects, so to can the options
- * specified in `data`. For example: `browser.version` or
- * `browser.name`. If your object parameter name contains a period, use
- * `\\` to escape it - i.e. `first\\.name`.
- * * `[]` - Array notation. DataTables can automatically combine data
- * from and array source, joining the data with the characters provided
- * between the two brackets. For example: `name[, ]` would provide a
- * comma-space separated list from the source array. If no characters
- * are provided between the brackets, the original array source is
- * returned.
- * * `()` - Function notation. Adding `()` to the end of a parameter will
- * execute a function of the name given. For example: `browser()` for a
- * simple function on the data source, `browser.version()` for a
- * function in a nested property or even `browser().version` to get an
- * object property if the function called returns an object. Note that
- * function notation is recommended for use in `render` rather than
- * `data` as it is much simpler to use as a renderer.
- * * `null` - use the original data source for the row rather than plucking
- * data directly from it. This action has effects on two other
- * initialisation options:
- * * `defaultContent` - When null is given as the `data` option and
- * `defaultContent` is specified for the column, the value defined by
- * `defaultContent` will be used for the cell.
- * * `render` - When null is used for the `data` option and the `render`
- * option is specified for the column, the whole data source for the
- * row is used for the renderer.
- * * `function` - the function given will be executed whenever DataTables
- * needs to set or get the data for a cell in the column. The function
- * takes three parameters:
- * * Parameters:
- * * `{array|object}` The data source for the row
- * * `{string}` The type call data requested - this will be 'set' when
- * setting data or 'filter', 'display', 'type', 'sort' or undefined
- * when gathering data. Note that when `undefined` is given for the
- * type DataTables expects to get the raw data for the object back<
- * * `{*}` Data to set when the second parameter is 'set'.
- * * Return:
- * * The return value from the function is not required when 'set' is
- * the type of call, but otherwise the return is what will be used
- * for the data requested.
- *
- * Note that `data` is a getter and setter option. If you just require
- * formatting of data for output, you will likely want to use `render` which
- * is simply a getter and thus simpler to use.
- *
- * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The
- * name change reflects the flexibility of this property and is consistent
- * with the naming of mRender. If 'mDataProp' is given, then it will still
- * be used by DataTables, as it automatically maps the old name to the new
- * if required.
- *
- * @type string|int|function|null
- * @default null <i>Use automatically calculated column index</i>
- *
- * @name DataTable.defaults.column.data
- * @dtopt Columns
- *
- * @example
- * // Read table data from objects
- * // JSON structure for each row:
- * // {
- * // "engine": {value},
- * // "browser": {value},
- * // "platform": {value},
- * // "version": {value},
- * // "grade": {value}
- * // }
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "ajaxSource": "sources/objects.txt",
- * "columns": [
- * { "data": "engine" },
- * { "data": "browser" },
- * { "data": "platform" },
- * { "data": "version" },
- * { "data": "grade" }
- * ]
- * } );
- * } );
- *
- * @example
- * // Read information from deeply nested objects
- * // JSON structure for each row:
- * // {
- * // "engine": {value},
- * // "browser": {value},
- * // "platform": {
- * // "inner": {value}
- * // },
- * // "details": [
- * // {value}, {value}
- * // ]
- * // }
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "ajaxSource": "sources/deep.txt",
- * "columns": [
- * { "data": "engine" },
- * { "data": "browser" },
- * { "data": "platform.inner" },
- * { "data": "platform.details.0" },
- * { "data": "platform.details.1" }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `data` as a function to provide different information for
- * // sorting, filtering and display. In this case, currency (price)
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "data": function ( source, type, val ) {
- * if (type === 'set') {
- * source.price = val;
- * // Store the computed dislay and filter values for efficiency
- * source.price_display = val=="" ? "" : "$"+numberFormat(val);
- * source.price_filter = val=="" ? "" : "$"+numberFormat(val)+" "+val;
- * return;
- * }
- * else if (type === 'display') {
- * return source.price_display;
- * }
- * else if (type === 'filter') {
- * return source.price_filter;
- * }
- * // 'sort', 'type' and undefined all just use the integer
- * return source.price;
- * }
- * } ]
- * } );
- * } );
- *
- * @example
- * // Using default content
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "data": null,
- * "defaultContent": "Click to edit"
- * } ]
- * } );
- * } );
- *
- * @example
- * // Using array notation - outputting a list from an array
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "data": "name[, ]"
- * } ]
- * } );
- * } );
- *
- */
- "mData": null,
-
-
- /**
- * This property is the rendering partner to `data` and it is suggested that
- * when you want to manipulate data for display (including filtering,
- * sorting etc) without altering the underlying data for the table, use this
- * property. `render` can be considered to be the the read only companion to
- * `data` which is read / write (then as such more complex). Like `data`
- * this option can be given in a number of different ways to effect its
- * behaviour:
- *
- * * `integer` - treated as an array index for the data source. This is the
- * default that DataTables uses (incrementally increased for each column).
- * * `string` - read an object property from the data source. There are
- * three 'special' options that can be used in the string to alter how
- * DataTables reads the data from the source object:
- * * `.` - Dotted Javascript notation. Just as you use a `.` in
- * Javascript to read from nested objects, so to can the options
- * specified in `data`. For example: `browser.version` or
- * `browser.name`. If your object parameter name contains a period, use
- * `\\` to escape it - i.e. `first\\.name`.
- * * `[]` - Array notation. DataTables can automatically combine data
- * from and array source, joining the data with the characters provided
- * between the two brackets. For example: `name[, ]` would provide a
- * comma-space separated list from the source array. If no characters
- * are provided between the brackets, the original array source is
- * returned.
- * * `()` - Function notation. Adding `()` to the end of a parameter will
- * execute a function of the name given. For example: `browser()` for a
- * simple function on the data source, `browser.version()` for a
- * function in a nested property or even `browser().version` to get an
- * object property if the function called returns an object.
- * * `object` - use different data for the different data types requested by
- * DataTables ('filter', 'display', 'type' or 'sort'). The property names
- * of the object is the data type the property refers to and the value can
- * defined using an integer, string or function using the same rules as
- * `render` normally does. Note that an `_` option _must_ be specified.
- * This is the default value to use if you haven't specified a value for
- * the data type requested by DataTables.
- * * `function` - the function given will be executed whenever DataTables
- * needs to set or get the data for a cell in the column. The function
- * takes three parameters:
- * * Parameters:
- * * {array|object} The data source for the row (based on `data`)
- * * {string} The type call data requested - this will be 'filter',
- * 'display', 'type' or 'sort'.
- * * {array|object} The full data source for the row (not based on
- * `data`)
- * * Return:
- * * The return value from the function is what will be used for the
- * data requested.
- *
- * @type string|int|function|object|null
- * @default null Use the data source value.
- *
- * @name DataTable.defaults.column.render
- * @dtopt Columns
- *
- * @example
- * // Create a comma separated list from an array of objects
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "ajaxSource": "sources/deep.txt",
- * "columns": [
- * { "data": "engine" },
- * { "data": "browser" },
- * {
- * "data": "platform",
- * "render": "[, ].name"
- * }
- * ]
- * } );
- * } );
- *
- * @example
- * // Execute a function to obtain data
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "data": null, // Use the full data source object for the renderer's source
- * "render": "browserName()"
- * } ]
- * } );
- * } );
- *
- * @example
- * // As an object, extracting different data for the different types
- * // This would be used with a data source such as:
- * // { "phone": 5552368, "phone_filter": "5552368 555-2368", "phone_display": "555-2368" }
- * // Here the `phone` integer is used for sorting and type detection, while `phone_filter`
- * // (which has both forms) is used for filtering for if a user inputs either format, while
- * // the formatted phone number is the one that is shown in the table.
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "data": null, // Use the full data source object for the renderer's source
- * "render": {
- * "_": "phone",
- * "filter": "phone_filter",
- * "display": "phone_display"
- * }
- * } ]
- * } );
- * } );
- *
- * @example
- * // Use as a function to create a link from the data source
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "data": "download_link",
- * "render": function ( data, type, full ) {
- * return '<a href="'+data+'">Download</a>';
- * }
- * } ]
- * } );
- * } );
- */
- "mRender": null,
-
-
- /**
- * Change the cell type created for the column - either TD cells or TH cells. This
- * can be useful as TH cells have semantic meaning in the table body, allowing them
- * to act as a header for a row (you may wish to add scope='row' to the TH elements).
- * @type string
- * @default td
- *
- * @name DataTable.defaults.column.cellType
- * @dtopt Columns
- *
- * @example
- * // Make the first column use TH cells
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "cellType": "th"
- * } ]
- * } );
- * } );
- */
- "sCellType": "td",
-
-
- /**
- * Class to give to each cell in this column.
- * @type string
- * @default <i>Empty string</i>
- *
- * @name DataTable.defaults.column.class
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "class": "my_class", "targets": [ 0 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "class": "my_class" },
- * null,
- * null,
- * null,
- * null
- * ]
- * } );
- * } );
- */
- "sClass": "",
-
- /**
- * When DataTables calculates the column widths to assign to each column,
- * it finds the longest string in each column and then constructs a
- * temporary table and reads the widths from that. The problem with this
- * is that "mmm" is much wider then "iiii", but the latter is a longer
- * string - thus the calculation can go wrong (doing it properly and putting
- * it into an DOM object and measuring that is horribly(!) slow). Thus as
- * a "work around" we provide this option. It will append its value to the
- * text that is found to be the longest string for the column - i.e. padding.
- * Generally you shouldn't need this!
- * @type string
- * @default <i>Empty string<i>
- *
- * @name DataTable.defaults.column.contentPadding
- * @dtopt Columns
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * null,
- * null,
- * null,
- * {
- * "contentPadding": "mmm"
- * }
- * ]
- * } );
- * } );
- */
- "sContentPadding": "",
-
-
- /**
- * Allows a default value to be given for a column's data, and will be used
- * whenever a null data source is encountered (this can be because `data`
- * is set to null, or because the data source itself is null).
- * @type string
- * @default null
- *
- * @name DataTable.defaults.column.defaultContent
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * {
- * "data": null,
- * "defaultContent": "Edit",
- * "targets": [ -1 ]
- * }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * null,
- * null,
- * null,
- * {
- * "data": null,
- * "defaultContent": "Edit"
- * }
- * ]
- * } );
- * } );
- */
- "sDefaultContent": null,
-
-
- /**
- * This parameter is only used in DataTables' server-side processing. It can
- * be exceptionally useful to know what columns are being displayed on the
- * client side, and to map these to database fields. When defined, the names
- * also allow DataTables to reorder information from the server if it comes
- * back in an unexpected order (i.e. if you switch your columns around on the
- * client-side, your server-side code does not also need updating).
- * @type string
- * @default <i>Empty string</i>
- *
- * @name DataTable.defaults.column.name
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "name": "engine", "targets": [ 0 ] },
- * { "name": "browser", "targets": [ 1 ] },
- * { "name": "platform", "targets": [ 2 ] },
- * { "name": "version", "targets": [ 3 ] },
- * { "name": "grade", "targets": [ 4 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "name": "engine" },
- * { "name": "browser" },
- * { "name": "platform" },
- * { "name": "version" },
- * { "name": "grade" }
- * ]
- * } );
- * } );
- */
- "sName": "",
-
-
- /**
- * Defines a data source type for the ordering which can be used to read
- * real-time information from the table (updating the internally cached
- * version) prior to ordering. This allows ordering to occur on user
- * editable elements such as form inputs.
- * @type string
- * @default std
- *
- * @name DataTable.defaults.column.orderDataType
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "orderDataType": "dom-text", "targets": [ 2, 3 ] },
- * { "type": "numeric", "targets": [ 3 ] },
- * { "orderDataType": "dom-select", "targets": [ 4 ] },
- * { "orderDataType": "dom-checkbox", "targets": [ 5 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * null,
- * null,
- * { "orderDataType": "dom-text" },
- * { "orderDataType": "dom-text", "type": "numeric" },
- * { "orderDataType": "dom-select" },
- * { "orderDataType": "dom-checkbox" }
- * ]
- * } );
- * } );
- */
- "sSortDataType": "std",
-
-
- /**
- * The title of this column.
- * @type string
- * @default null <i>Derived from the 'TH' value for this column in the
- * original HTML table.</i>
- *
- * @name DataTable.defaults.column.title
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "title": "My column title", "targets": [ 0 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "title": "My column title" },
- * null,
- * null,
- * null,
- * null
- * ]
- * } );
- * } );
- */
- "sTitle": null,
-
-
- /**
- * The type allows you to specify how the data for this column will be
- * ordered. Four types (string, numeric, date and html (which will strip
- * HTML tags before ordering)) are currently available. Note that only date
- * formats understood by Javascript's Date() object will be accepted as type
- * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string',
- * 'numeric', 'date' or 'html' (by default). Further types can be adding
- * through plug-ins.
- * @type string
- * @default null <i>Auto-detected from raw data</i>
- *
- * @name DataTable.defaults.column.type
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "type": "html", "targets": [ 0 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "type": "html" },
- * null,
- * null,
- * null,
- * null
- * ]
- * } );
- * } );
- */
- "sType": null,
-
-
- /**
- * Defining the width of the column, this parameter may take any CSS value
- * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not
- * been given a specific width through this interface ensuring that the table
- * remains readable.
- * @type string
- * @default null <i>Automatic</i>
- *
- * @name DataTable.defaults.column.width
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "width": "20%", "targets": [ 0 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "width": "20%" },
- * null,
- * null,
- * null,
- * null
- * ]
- * } );
- * } );
- */
- "sWidth": null
- };
-
- _fnHungarianMap( DataTable.defaults.column );
-
-
-
- /**
- * DataTables settings object - this holds all the information needed for a
- * given table, including configuration, data and current application of the
- * table options. DataTables does not have a single instance for each DataTable
- * with the settings attached to that instance, but rather instances of the
- * DataTable "class" are created on-the-fly as needed (typically by a
- * $().dataTable() call) and the settings object is then applied to that
- * instance.
- *
- * Note that this object is related to {@link DataTable.defaults} but this
- * one is the internal data store for DataTables's cache of columns. It should
- * NOT be manipulated outside of DataTables. Any configuration should be done
- * through the initialisation options.
- * @namespace
- * @todo Really should attach the settings object to individual instances so we
- * don't need to create new instances on each $().dataTable() call (if the
- * table already exists). It would also save passing oSettings around and
- * into every single function. However, this is a very significant
- * architecture change for DataTables and will almost certainly break
- * backwards compatibility with older installations. This is something that
- * will be done in 2.0.
- */
- DataTable.models.oSettings = {
- /**
- * Primary features of DataTables and their enablement state.
- * @namespace
- */
- "oFeatures": {
-
- /**
- * Flag to say if DataTables should automatically try to calculate the
- * optimum table and columns widths (true) or not (false).
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bAutoWidth": null,
-
- /**
- * Delay the creation of TR and TD elements until they are actually
- * needed by a driven page draw. This can give a significant speed
- * increase for Ajax source and Javascript source data, but makes no
- * difference at all fro DOM and server-side processing tables.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bDeferRender": null,
-
- /**
- * Enable filtering on the table or not. Note that if this is disabled
- * then there is no filtering at all on the table, including fnFilter.
- * To just remove the filtering input use sDom and remove the 'f' option.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bFilter": null,
-
- /**
- * Table information element (the 'Showing x of y records' div) enable
- * flag.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bInfo": null,
-
- /**
- * Present a user control allowing the end user to change the page size
- * when pagination is enabled.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bLengthChange": null,
-
- /**
- * Pagination enabled or not. Note that if this is disabled then length
- * changing must also be disabled.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bPaginate": null,
-
- /**
- * Processing indicator enable flag whenever DataTables is enacting a
- * user request - typically an Ajax request for server-side processing.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bProcessing": null,
-
- /**
- * Server-side processing enabled flag - when enabled DataTables will
- * get all data from the server for every draw - there is no filtering,
- * sorting or paging done on the client-side.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bServerSide": null,
-
- /**
- * Sorting enablement flag.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bSort": null,
-
- /**
- * Multi-column sorting
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bSortMulti": null,
-
- /**
- * Apply a class to the columns which are being sorted to provide a
- * visual highlight or not. This can slow things down when enabled since
- * there is a lot of DOM interaction.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bSortClasses": null,
-
- /**
- * State saving enablement flag.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bStateSave": null
- },
-
-
- /**
- * Scrolling settings for a table.
- * @namespace
- */
- "oScroll": {
- /**
- * When the table is shorter in height than sScrollY, collapse the
- * table container down to the height of the table (when true).
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bCollapse": null,
-
- /**
- * Width of the scrollbar for the web-browser's platform. Calculated
- * during table initialisation.
- * @type int
- * @default 0
- */
- "iBarWidth": 0,
-
- /**
- * Viewport width for horizontal scrolling. Horizontal scrolling is
- * disabled if an empty string.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- */
- "sX": null,
-
- /**
- * Width to expand the table to when using x-scrolling. Typically you
- * should not need to use this.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- * @deprecated
- */
- "sXInner": null,
-
- /**
- * Viewport height for vertical scrolling. Vertical scrolling is disabled
- * if an empty string.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- */
- "sY": null
- },
-
- /**
- * Language information for the table.
- * @namespace
- * @extends DataTable.defaults.oLanguage
- */
- "oLanguage": {
- /**
- * Information callback function. See
- * {@link DataTable.defaults.fnInfoCallback}
- * @type function
- * @default null
- */
- "fnInfoCallback": null
- },
-
- /**
- * Browser support parameters
- * @namespace
- */
- "oBrowser": {
- /**
- * Indicate if the browser incorrectly calculates width:100% inside a
- * scrolling element (IE6/7)
- * @type boolean
- * @default false
- */
- "bScrollOversize": false,
-
- /**
- * Determine if the vertical scrollbar is on the right or left of the
- * scrolling container - needed for rtl language layout, although not
- * all browsers move the scrollbar (Safari).
- * @type boolean
- * @default false
- */
- "bScrollbarLeft": false,
-
- /**
- * Flag for if `getBoundingClientRect` is fully supported or not
- * @type boolean
- * @default false
- */
- "bBounding": false,
-
- /**
- * Browser scrollbar width
- * @type integer
- * @default 0
- */
- "barWidth": 0
- },
-
-
- "ajax": null,
-
-
- /**
- * Array referencing the nodes which are used for the features. The
- * parameters of this object match what is allowed by sDom - i.e.
- * <ul>
- * <li>'l' - Length changing</li>
- * <li>'f' - Filtering input</li>
- * <li>'t' - The table!</li>
- * <li>'i' - Information</li>
- * <li>'p' - Pagination</li>
- * <li>'r' - pRocessing</li>
- * </ul>
- * @type array
- * @default []
- */
- "aanFeatures": [],
-
- /**
- * Store data information - see {@link DataTable.models.oRow} for detailed
- * information.
- * @type array
- * @default []
- */
- "aoData": [],
-
- /**
- * Array of indexes which are in the current display (after filtering etc)
- * @type array
- * @default []
- */
- "aiDisplay": [],
-
- /**
- * Array of indexes for display - no filtering
- * @type array
- * @default []
- */
- "aiDisplayMaster": [],
-
- /**
- * Map of row ids to data indexes
- * @type object
- * @default {}
- */
- "aIds": {},
-
- /**
- * Store information about each column that is in use
- * @type array
- * @default []
- */
- "aoColumns": [],
-
- /**
- * Store information about the table's header
- * @type array
- * @default []
- */
- "aoHeader": [],
-
- /**
- * Store information about the table's footer
- * @type array
- * @default []
- */
- "aoFooter": [],
-
- /**
- * Store the applied global search information in case we want to force a
- * research or compare the old search to a new one.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @namespace
- * @extends DataTable.models.oSearch
- */
- "oPreviousSearch": {},
-
- /**
- * Store the applied search for each column - see
- * {@link DataTable.models.oSearch} for the format that is used for the
- * filtering information for each column.
- * @type array
- * @default []
- */
- "aoPreSearchCols": [],
-
- /**
- * Sorting that is applied to the table. Note that the inner arrays are
- * used in the following manner:
- * <ul>
- * <li>Index 0 - column number</li>
- * <li>Index 1 - current sorting direction</li>
- * </ul>
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type array
- * @todo These inner arrays should really be objects
- */
- "aaSorting": null,
-
- /**
- * Sorting that is always applied to the table (i.e. prefixed in front of
- * aaSorting).
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type array
- * @default []
- */
- "aaSortingFixed": [],
-
- /**
- * Classes to use for the striping of a table.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type array
- * @default []
- */
- "asStripeClasses": null,
-
- /**
- * If restoring a table - we should restore its striping classes as well
- * @type array
- * @default []
- */
- "asDestroyStripes": [],
-
- /**
- * If restoring a table - we should restore its width
- * @type int
- * @default 0
- */
- "sDestroyWidth": 0,
-
- /**
- * Callback functions array for every time a row is inserted (i.e. on a draw).
- * @type array
- * @default []
- */
- "aoRowCallback": [],
-
- /**
- * Callback functions for the header on each draw.
- * @type array
- * @default []
- */
- "aoHeaderCallback": [],
-
- /**
- * Callback function for the footer on each draw.
- * @type array
- * @default []
- */
- "aoFooterCallback": [],
-
- /**
- * Array of callback functions for draw callback functions
- * @type array
- * @default []
- */
- "aoDrawCallback": [],
-
- /**
- * Array of callback functions for row created function
- * @type array
- * @default []
- */
- "aoRowCreatedCallback": [],
-
- /**
- * Callback functions for just before the table is redrawn. A return of
- * false will be used to cancel the draw.
- * @type array
- * @default []
- */
- "aoPreDrawCallback": [],
-
- /**
- * Callback functions for when the table has been initialised.
- * @type array
- * @default []
- */
- "aoInitComplete": [],
-
-
- /**
- * Callbacks for modifying the settings to be stored for state saving, prior to
- * saving state.
- * @type array
- * @default []
- */
- "aoStateSaveParams": [],
-
- /**
- * Callbacks for modifying the settings that have been stored for state saving
- * prior to using the stored values to restore the state.
- * @type array
- * @default []
- */
- "aoStateLoadParams": [],
-
- /**
- * Callbacks for operating on the settings object once the saved state has been
- * loaded
- * @type array
- * @default []
- */
- "aoStateLoaded": [],
-
- /**
- * Cache the table ID for quick access
- * @type string
- * @default <i>Empty string</i>
- */
- "sTableId": "",
-
- /**
- * The TABLE node for the main table
- * @type node
- * @default null
- */
- "nTable": null,
-
- /**
- * Permanent ref to the thead element
- * @type node
- * @default null
- */
- "nTHead": null,
-
- /**
- * Permanent ref to the tfoot element - if it exists
- * @type node
- * @default null
- */
- "nTFoot": null,
-
- /**
- * Permanent ref to the tbody element
- * @type node
- * @default null
- */
- "nTBody": null,
-
- /**
- * Cache the wrapper node (contains all DataTables controlled elements)
- * @type node
- * @default null
- */
- "nTableWrapper": null,
-
- /**
- * Indicate if when using server-side processing the loading of data
- * should be deferred until the second draw.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- * @default false
- */
- "bDeferLoading": false,
-
- /**
- * Indicate if all required information has been read in
- * @type boolean
- * @default false
- */
- "bInitialised": false,
-
- /**
- * Information about open rows. Each object in the array has the parameters
- * 'nTr' and 'nParent'
- * @type array
- * @default []
- */
- "aoOpenRows": [],
-
- /**
- * Dictate the positioning of DataTables' control elements - see
- * {@link DataTable.model.oInit.sDom}.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- * @default null
- */
- "sDom": null,
-
- /**
- * Search delay (in mS)
- * @type integer
- * @default null
- */
- "searchDelay": null,
-
- /**
- * Which type of pagination should be used.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- * @default two_button
- */
- "sPaginationType": "two_button",
-
- /**
- * The state duration (for `stateSave`) in seconds.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type int
- * @default 0
- */
- "iStateDuration": 0,
-
- /**
- * Array of callback functions for state saving. Each array element is an
- * object with the following parameters:
- * <ul>
- * <li>function:fn - function to call. Takes two parameters, oSettings
- * and the JSON string to save that has been thus far created. Returns
- * a JSON string to be inserted into a json object
- * (i.e. '"param": [ 0, 1, 2]')</li>
- * <li>string:sName - name of callback</li>
- * </ul>
- * @type array
- * @default []
- */
- "aoStateSave": [],
-
- /**
- * Array of callback functions for state loading. Each array element is an
- * object with the following parameters:
- * <ul>
- * <li>function:fn - function to call. Takes two parameters, oSettings
- * and the object stored. May return false to cancel state loading</li>
- * <li>string:sName - name of callback</li>
- * </ul>
- * @type array
- * @default []
- */
- "aoStateLoad": [],
-
- /**
- * State that was saved. Useful for back reference
- * @type object
- * @default null
- */
- "oSavedState": null,
-
- /**
- * State that was loaded. Useful for back reference
- * @type object
- * @default null
- */
- "oLoadedState": null,
-
- /**
- * Source url for AJAX data for the table.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- * @default null
- */
- "sAjaxSource": null,
-
- /**
- * Property from a given object from which to read the table data from. This
- * can be an empty string (when not server-side processing), in which case
- * it is assumed an an array is given directly.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- */
- "sAjaxDataProp": null,
-
- /**
- * Note if draw should be blocked while getting data
- * @type boolean
- * @default true
- */
- "bAjaxDataGet": true,
-
- /**
- * The last jQuery XHR object that was used for server-side data gathering.
- * This can be used for working with the XHR information in one of the
- * callbacks
- * @type object
- * @default null
- */
- "jqXHR": null,
-
- /**
- * JSON returned from the server in the last Ajax request
- * @type object
- * @default undefined
- */
- "json": undefined,
-
- /**
- * Data submitted as part of the last Ajax request
- * @type object
- * @default undefined
- */
- "oAjaxData": undefined,
-
- /**
- * Function to get the server-side data.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type function
- */
- "fnServerData": null,
-
- /**
- * Functions which are called prior to sending an Ajax request so extra
- * parameters can easily be sent to the server
- * @type array
- * @default []
- */
- "aoServerParams": [],
-
- /**
- * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
- * required).
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- */
- "sServerMethod": null,
-
- /**
- * Format numbers for display.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type function
- */
- "fnFormatNumber": null,
-
- /**
- * List of options that can be used for the user selectable length menu.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type array
- * @default []
- */
- "aLengthMenu": null,
-
- /**
- * Counter for the draws that the table does. Also used as a tracker for
- * server-side processing
- * @type int
- * @default 0
- */
- "iDraw": 0,
-
- /**
- * Indicate if a redraw is being done - useful for Ajax
- * @type boolean
- * @default false
- */
- "bDrawing": false,
-
- /**
- * Draw index (iDraw) of the last error when parsing the returned data
- * @type int
- * @default -1
- */
- "iDrawError": -1,
-
- /**
- * Paging display length
- * @type int
- * @default 10
- */
- "_iDisplayLength": 10,
-
- /**
- * Paging start point - aiDisplay index
- * @type int
- * @default 0
- */
- "_iDisplayStart": 0,
-
- /**
- * Server-side processing - number of records in the result set
- * (i.e. before filtering), Use fnRecordsTotal rather than
- * this property to get the value of the number of records, regardless of
- * the server-side processing setting.
- * @type int
- * @default 0
- * @private
- */
- "_iRecordsTotal": 0,
-
- /**
- * Server-side processing - number of records in the current display set
- * (i.e. after filtering). Use fnRecordsDisplay rather than
- * this property to get the value of the number of records, regardless of
- * the server-side processing setting.
- * @type boolean
- * @default 0
- * @private
- */
- "_iRecordsDisplay": 0,
-
- /**
- * The classes to use for the table
- * @type object
- * @default {}
- */
- "oClasses": {},
-
- /**
- * Flag attached to the settings object so you can check in the draw
- * callback if filtering has been done in the draw. Deprecated in favour of
- * events.
- * @type boolean
- * @default false
- * @deprecated
- */
- "bFiltered": false,
-
- /**
- * Flag attached to the settings object so you can check in the draw
- * callback if sorting has been done in the draw. Deprecated in favour of
- * events.
- * @type boolean
- * @default false
- * @deprecated
- */
- "bSorted": false,
-
- /**
- * Indicate that if multiple rows are in the header and there is more than
- * one unique cell per column, if the top one (true) or bottom one (false)
- * should be used for sorting / title by DataTables.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bSortCellsTop": null,
-
- /**
- * Initialisation object that is used for the table
- * @type object
- * @default null
- */
- "oInit": null,
-
- /**
- * Destroy callback functions - for plug-ins to attach themselves to the
- * destroy so they can clean up markup and events.
- * @type array
- * @default []
- */
- "aoDestroyCallback": [],
-
-
- /**
- * Get the number of records in the current record set, before filtering
- * @type function
- */
- "fnRecordsTotal": function ()
- {
- return _fnDataSource( this ) == 'ssp' ?
- this._iRecordsTotal * 1 :
- this.aiDisplayMaster.length;
- },
-
- /**
- * Get the number of records in the current record set, after filtering
- * @type function
- */
- "fnRecordsDisplay": function ()
- {
- return _fnDataSource( this ) == 'ssp' ?
- this._iRecordsDisplay * 1 :
- this.aiDisplay.length;
- },
-
- /**
- * Get the display end point - aiDisplay index
- * @type function
- */
- "fnDisplayEnd": function ()
- {
- var
- len = this._iDisplayLength,
- start = this._iDisplayStart,
- calc = start + len,
- records = this.aiDisplay.length,
- features = this.oFeatures,
- paginate = features.bPaginate;
-
- if ( features.bServerSide ) {
- return paginate === false || len === -1 ?
- start + records :
- Math.min( start+len, this._iRecordsDisplay );
- }
- else {
- return ! paginate || calc>records || len===-1 ?
- records :
- calc;
- }
- },
-
- /**
- * The DataTables object for this table
- * @type object
- * @default null
- */
- "oInstance": null,
-
- /**
- * Unique identifier for each instance of the DataTables object. If there
- * is an ID on the table node, then it takes that value, otherwise an
- * incrementing internal counter is used.
- * @type string
- * @default null
- */
- "sInstance": null,
-
- /**
- * tabindex attribute value that is added to DataTables control elements, allowing
- * keyboard navigation of the table and its controls.
- */
- "iTabIndex": 0,
-
- /**
- * DIV container for the footer scrolling table if scrolling
- */
- "nScrollHead": null,
-
- /**
- * DIV container for the footer scrolling table if scrolling
- */
- "nScrollFoot": null,
-
- /**
- * Last applied sort
- * @type array
- * @default []
- */
- "aLastSort": [],
-
- /**
- * Stored plug-in instances
- * @type object
- * @default {}
- */
- "oPlugins": {},
-
- /**
- * Function used to get a row's id from the row's data
- * @type function
- * @default null
- */
- "rowIdFn": null,
-
- /**
- * Data location where to store a row's id
- * @type string
- * @default null
- */
- "rowId": null
- };
- /**
- * Extension object for DataTables that is used to provide all extension
- * options.
- *
- * Note that the `DataTable.ext` object is available through
- * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is
- * also aliased to `jQuery.fn.dataTableExt` for historic reasons.
- * @namespace
- * @extends DataTable.models.ext
- */
-
-
- /**
- * DataTables extensions
- *
- * This namespace acts as a collection area for plug-ins that can be used to
- * extend DataTables capabilities. Indeed many of the build in methods
- * use this method to provide their own capabilities (sorting methods for
- * example).
- *
- * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy
- * reasons
- *
- * @namespace
- */
- DataTable.ext = _ext = {
- /**
- * Buttons. For use with the Buttons extension for DataTables. This is
- * defined here so other extensions can define buttons regardless of load
- * order. It is _not_ used by DataTables core.
- *
- * @type object
- * @default {}
- */
- buttons: {},
-
-
- /**
- * Element class names
- *
- * @type object
- * @default {}
- */
- classes: {},
-
-
- /**
- * DataTables build type (expanded by the download builder)
- *
- * @type string
- */
- build:"dt/dt-1.10.16/af-2.2.2/b-1.5.1/cr-1.4.1/fc-3.2.4/fh-3.1.3/kt-2.3.2/r-2.2.1/rr-1.2.3/sc-1.4.4/sl-1.2.5",
-
-
- /**
- * Error reporting.
- *
- * How should DataTables report an error. Can take the value 'alert',
- * 'throw', 'none' or a function.
- *
- * @type string|function
- * @default alert
- */
- errMode: "alert",
-
-
- /**
- * Feature plug-ins.
- *
- * This is an array of objects which describe the feature plug-ins that are
- * available to DataTables. These feature plug-ins are then available for
- * use through the `dom` initialisation option.
- *
- * Each feature plug-in is described by an object which must have the
- * following properties:
- *
- * * `fnInit` - function that is used to initialise the plug-in,
- * * `cFeature` - a character so the feature can be enabled by the `dom`
- * instillation option. This is case sensitive.
- *
- * The `fnInit` function has the following input parameters:
- *
- * 1. `{object}` DataTables settings object: see
- * {@link DataTable.models.oSettings}
- *
- * And the following return is expected:
- *
- * * {node|null} The element which contains your feature. Note that the
- * return may also be void if your plug-in does not require to inject any
- * DOM elements into DataTables control (`dom`) - for example this might
- * be useful when developing a plug-in which allows table control via
- * keyboard entry
- *
- * @type array
- *
- * @example
- * $.fn.dataTable.ext.features.push( {
- * "fnInit": function( oSettings ) {
- * return new TableTools( { "oDTSettings": oSettings } );
- * },
- * "cFeature": "T"
- * } );
- */
- feature: [],
-
-
- /**
- * Row searching.
- *
- * This method of searching is complimentary to the default type based
- * searching, and a lot more comprehensive as it allows you complete control
- * over the searching logic. Each element in this array is a function
- * (parameters described below) that is called for every row in the table,
- * and your logic decides if it should be included in the searching data set
- * or not.
- *
- * Searching functions have the following input parameters:
- *
- * 1. `{object}` DataTables settings object: see
- * {@link DataTable.models.oSettings}
- * 2. `{array|object}` Data for the row to be processed (same as the
- * original format that was passed in as the data source, or an array
- * from a DOM data source
- * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which
- * can be useful to retrieve the `TR` element if you need DOM interaction.
- *
- * And the following return is expected:
- *
- * * {boolean} Include the row in the searched result set (true) or not
- * (false)
- *
- * Note that as with the main search ability in DataTables, technically this
- * is "filtering", since it is subtractive. However, for consistency in
- * naming we call it searching here.
- *
- * @type array
- * @default []
- *
- * @example
- * // The following example shows custom search being applied to the
- * // fourth column (i.e. the data[3] index) based on two input values
- * // from the end-user, matching the data in a certain range.
- * $.fn.dataTable.ext.search.push(
- * function( settings, data, dataIndex ) {
- * var min = document.getElementById('min').value * 1;
- * var max = document.getElementById('max').value * 1;
- * var version = data[3] == "-" ? 0 : data[3]*1;
- *
- * if ( min == "" && max == "" ) {
- * return true;
- * }
- * else if ( min == "" && version < max ) {
- * return true;
- * }
- * else if ( min < version && "" == max ) {
- * return true;
- * }
- * else if ( min < version && version < max ) {
- * return true;
- * }
- * return false;
- * }
- * );
- */
- search: [],
-
-
- /**
- * Selector extensions
- *
- * The `selector` option can be used to extend the options available for the
- * selector modifier options (`selector-modifier` object data type) that
- * each of the three built in selector types offer (row, column and cell +
- * their plural counterparts). For example the Select extension uses this
- * mechanism to provide an option to select only rows, columns and cells
- * that have been marked as selected by the end user (`{selected: true}`),
- * which can be used in conjunction with the existing built in selector
- * options.
- *
- * Each property is an array to which functions can be pushed. The functions
- * take three attributes:
- *
- * * Settings object for the host table
- * * Options object (`selector-modifier` object type)
- * * Array of selected item indexes
- *
- * The return is an array of the resulting item indexes after the custom
- * selector has been applied.
- *
- * @type object
- */
- selector: {
- cell: [],
- column: [],
- row: []
- },
-
-
- /**
- * Internal functions, exposed for used in plug-ins.
- *
- * Please note that you should not need to use the internal methods for
- * anything other than a plug-in (and even then, try to avoid if possible).
- * The internal function may change between releases.
- *
- * @type object
- * @default {}
- */
- internal: {},
-
-
- /**
- * Legacy configuration options. Enable and disable legacy options that
- * are available in DataTables.
- *
- * @type object
- */
- legacy: {
- /**
- * Enable / disable DataTables 1.9 compatible server-side processing
- * requests
- *
- * @type boolean
- * @default null
- */
- ajax: null
- },
-
-
- /**
- * Pagination plug-in methods.
- *
- * Each entry in this object is a function and defines which buttons should
- * be shown by the pagination rendering method that is used for the table:
- * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the
- * buttons are displayed in the document, while the functions here tell it
- * what buttons to display. This is done by returning an array of button
- * descriptions (what each button will do).
- *
- * Pagination types (the four built in options and any additional plug-in
- * options defined here) can be used through the `paginationType`
- * initialisation parameter.
- *
- * The functions defined take two parameters:
- *
- * 1. `{int} page` The current page index
- * 2. `{int} pages` The number of pages in the table
- *
- * Each function is expected to return an array where each element of the
- * array can be one of:
- *
- * * `first` - Jump to first page when activated
- * * `last` - Jump to last page when activated
- * * `previous` - Show previous page when activated
- * * `next` - Show next page when activated
- * * `{int}` - Show page of the index given
- * * `{array}` - A nested array containing the above elements to add a
- * containing 'DIV' element (might be useful for styling).
- *
- * Note that DataTables v1.9- used this object slightly differently whereby
- * an object with two functions would be defined for each plug-in. That
- * ability is still supported by DataTables 1.10+ to provide backwards
- * compatibility, but this option of use is now decremented and no longer
- * documented in DataTables 1.10+.
- *
- * @type object
- * @default {}
- *
- * @example
- * // Show previous, next and current page buttons only
- * $.fn.dataTableExt.oPagination.current = function ( page, pages ) {
- * return [ 'previous', page, 'next' ];
- * };
- */
- pager: {},
-
-
- renderer: {
- pageButton: {},
- header: {}
- },
-
-
- /**
- * Ordering plug-ins - custom data source
- *
- * The extension options for ordering of data available here is complimentary
- * to the default type based ordering that DataTables typically uses. It
- * allows much greater control over the the data that is being used to
- * order a column, but is necessarily therefore more complex.
- *
- * This type of ordering is useful if you want to do ordering based on data
- * live from the DOM (for example the contents of an 'input' element) rather
- * than just the static string that DataTables knows of.
- *
- * The way these plug-ins work is that you create an array of the values you
- * wish to be ordering for the column in question and then return that
- * array. The data in the array much be in the index order of the rows in
- * the table (not the currently ordering order!). Which order data gathering
- * function is run here depends on the `dt-init columns.orderDataType`
- * parameter that is used for the column (if any).
- *
- * The functions defined take two parameters:
- *
- * 1. `{object}` DataTables settings object: see
- * {@link DataTable.models.oSettings}
- * 2. `{int}` Target column index
- *
- * Each function is expected to return an array:
- *
- * * `{array}` Data for the column to be ordering upon
- *
- * @type array
- *
- * @example
- * // Ordering using `input` node values
- * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col )
- * {
- * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) {
- * return $('input', td).val();
- * } );
- * }
- */
- order: {},
-
-
- /**
- * Type based plug-ins.
- *
- * Each column in DataTables has a type assigned to it, either by automatic
- * detection or by direct assignment using the `type` option for the column.
- * The type of a column will effect how it is ordering and search (plug-ins
- * can also make use of the column type if required).
- *
- * @namespace
- */
- type: {
- /**
- * Type detection functions.
- *
- * The functions defined in this object are used to automatically detect
- * a column's type, making initialisation of DataTables super easy, even
- * when complex data is in the table.
- *
- * The functions defined take two parameters:
- *
- * 1. `{*}` Data from the column cell to be analysed
- * 2. `{settings}` DataTables settings object. This can be used to
- * perform context specific type detection - for example detection
- * based on language settings such as using a comma for a decimal
- * place. Generally speaking the options from the settings will not
- * be required
- *
- * Each function is expected to return:
- *
- * * `{string|null}` Data type detected, or null if unknown (and thus
- * pass it on to the other type detection functions.
- *
- * @type array
- *
- * @example
- * // Currency type detection plug-in:
- * $.fn.dataTable.ext.type.detect.push(
- * function ( data, settings ) {
- * // Check the numeric part
- * if ( ! $.isNumeric( data.substring(1) ) ) {
- * return null;
- * }
- *
- * // Check prefixed by currency
- * if ( data.charAt(0) == '$' || data.charAt(0) == '£' ) {
- * return 'currency';
- * }
- * return null;
- * }
- * );
- */
- detect: [],
-
-
- /**
- * Type based search formatting.
- *
- * The type based searching functions can be used to pre-format the
- * data to be search on. For example, it can be used to strip HTML
- * tags or to de-format telephone numbers for numeric only searching.
- *
- * Note that is a search is not defined for a column of a given type,
- * no search formatting will be performed.
- *
- * Pre-processing of searching data plug-ins - When you assign the sType
- * for a column (or have it automatically detected for you by DataTables
- * or a type detection plug-in), you will typically be using this for
- * custom sorting, but it can also be used to provide custom searching
- * by allowing you to pre-processing the data and returning the data in
- * the format that should be searched upon. This is done by adding
- * functions this object with a parameter name which matches the sType
- * for that target column. This is the corollary of <i>afnSortData</i>
- * for searching data.
- *
- * The functions defined take a single parameter:
- *
- * 1. `{*}` Data from the column cell to be prepared for searching
- *
- * Each function is expected to return:
- *
- * * `{string|null}` Formatted string that will be used for the searching.
- *
- * @type object
- * @default {}
- *
- * @example
- * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) {
- * return d.replace(/\n/g," ").replace( /<.*?>/g, "" );
- * }
- */
- search: {},
-
-
- /**
- * Type based ordering.
- *
- * The column type tells DataTables what ordering to apply to the table
- * when a column is sorted upon. The order for each type that is defined,
- * is defined by the functions available in this object.
- *
- * Each ordering option can be described by three properties added to
- * this object:
- *
- * * `{type}-pre` - Pre-formatting function
- * * `{type}-asc` - Ascending order function
- * * `{type}-desc` - Descending order function
- *
- * All three can be used together, only `{type}-pre` or only
- * `{type}-asc` and `{type}-desc` together. It is generally recommended
- * that only `{type}-pre` is used, as this provides the optimal
- * implementation in terms of speed, although the others are provided
- * for compatibility with existing Javascript sort functions.
- *
- * `{type}-pre`: Functions defined take a single parameter:
- *
- * 1. `{*}` Data from the column cell to be prepared for ordering
- *
- * And return:
- *
- * * `{*}` Data to be sorted upon
- *
- * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort
- * functions, taking two parameters:
- *
- * 1. `{*}` Data to compare to the second parameter
- * 2. `{*}` Data to compare to the first parameter
- *
- * And returning:
- *
- * * `{*}` Ordering match: <0 if first parameter should be sorted lower
- * than the second parameter, ===0 if the two parameters are equal and
- * >0 if the first parameter should be sorted height than the second
- * parameter.
- *
- * @type object
- * @default {}
- *
- * @example
- * // Numeric ordering of formatted numbers with a pre-formatter
- * $.extend( $.fn.dataTable.ext.type.order, {
- * "string-pre": function(x) {
- * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" );
- * return parseFloat( a );
- * }
- * } );
- *
- * @example
- * // Case-sensitive string ordering, with no pre-formatting method
- * $.extend( $.fn.dataTable.ext.order, {
- * "string-case-asc": function(x,y) {
- * return ((x < y) ? -1 : ((x > y) ? 1 : 0));
- * },
- * "string-case-desc": function(x,y) {
- * return ((x < y) ? 1 : ((x > y) ? -1 : 0));
- * }
- * } );
- */
- order: {}
- },
-
- /**
- * Unique DataTables instance counter
- *
- * @type int
- * @private
- */
- _unique: 0,
-
-
- //
- // Depreciated
- // The following properties are retained for backwards compatiblity only.
- // The should not be used in new projects and will be removed in a future
- // version
- //
-
- /**
- * Version check function.
- * @type function
- * @depreciated Since 1.10
- */
- fnVersionCheck: DataTable.fnVersionCheck,
-
-
- /**
- * Index for what 'this' index API functions should use
- * @type int
- * @deprecated Since v1.10
- */
- iApiIndex: 0,
-
-
- /**
- * jQuery UI class container
- * @type object
- * @deprecated Since v1.10
- */
- oJUIClasses: {},
-
-
- /**
- * Software version
- * @type string
- * @deprecated Since v1.10
- */
- sVersion: DataTable.version
- };
-
-
- //
- // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
- //
- $.extend( _ext, {
- afnFiltering: _ext.search,
- aTypes: _ext.type.detect,
- ofnSearch: _ext.type.search,
- oSort: _ext.type.order,
- afnSortData: _ext.order,
- aoFeatures: _ext.feature,
- oApi: _ext.internal,
- oStdClasses: _ext.classes,
- oPagination: _ext.pager
- } );
-
-
- $.extend( DataTable.ext.classes, {
- "sTable": "dataTable",
- "sNoFooter": "no-footer",
-
- /* Paging buttons */
- "sPageButton": "paginate_button",
- "sPageButtonActive": "current",
- "sPageButtonDisabled": "disabled",
-
- /* Striping classes */
- "sStripeOdd": "odd",
- "sStripeEven": "even",
-
- /* Empty row */
- "sRowEmpty": "dataTables_empty",
-
- /* Features */
- "sWrapper": "dataTables_wrapper",
- "sFilter": "dataTables_filter",
- "sInfo": "dataTables_info",
- "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
- "sLength": "dataTables_length",
- "sProcessing": "dataTables_processing",
-
- /* Sorting */
- "sSortAsc": "sorting_asc",
- "sSortDesc": "sorting_desc",
- "sSortable": "sorting", /* Sortable in both directions */
- "sSortableAsc": "sorting_asc_disabled",
- "sSortableDesc": "sorting_desc_disabled",
- "sSortableNone": "sorting_disabled",
- "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
-
- /* Filtering */
- "sFilterInput": "",
-
- /* Page length */
- "sLengthSelect": "",
-
- /* Scrolling */
- "sScrollWrapper": "dataTables_scroll",
- "sScrollHead": "dataTables_scrollHead",
- "sScrollHeadInner": "dataTables_scrollHeadInner",
- "sScrollBody": "dataTables_scrollBody",
- "sScrollFoot": "dataTables_scrollFoot",
- "sScrollFootInner": "dataTables_scrollFootInner",
-
- /* Misc */
- "sHeaderTH": "",
- "sFooterTH": "",
-
- // Deprecated
- "sSortJUIAsc": "",
- "sSortJUIDesc": "",
- "sSortJUI": "",
- "sSortJUIAscAllowed": "",
- "sSortJUIDescAllowed": "",
- "sSortJUIWrapper": "",
- "sSortIcon": "",
- "sJUIHeader": "",
- "sJUIFooter": ""
- } );
-
-
- var extPagination = DataTable.ext.pager;
-
- function _numbers ( page, pages ) {
- var
- numbers = [],
- buttons = extPagination.numbers_length,
- half = Math.floor( buttons / 2 ),
- i = 1;
-
- if ( pages <= buttons ) {
- numbers = _range( 0, pages );
- }
- else if ( page <= half ) {
- numbers = _range( 0, buttons-2 );
- numbers.push( 'ellipsis' );
- numbers.push( pages-1 );
- }
- else if ( page >= pages - 1 - half ) {
- numbers = _range( pages-(buttons-2), pages );
- numbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6
- numbers.splice( 0, 0, 0 );
- }
- else {
- numbers = _range( page-half+2, page+half-1 );
- numbers.push( 'ellipsis' );
- numbers.push( pages-1 );
- numbers.splice( 0, 0, 'ellipsis' );
- numbers.splice( 0, 0, 0 );
- }
-
- numbers.DT_el = 'span';
- return numbers;
- }
-
-
- $.extend( extPagination, {
- simple: function ( page, pages ) {
- return [ 'previous', 'next' ];
- },
-
- full: function ( page, pages ) {
- return [ 'first', 'previous', 'next', 'last' ];
- },
-
- numbers: function ( page, pages ) {
- return [ _numbers(page, pages) ];
- },
-
- simple_numbers: function ( page, pages ) {
- return [ 'previous', _numbers(page, pages), 'next' ];
- },
-
- full_numbers: function ( page, pages ) {
- return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ];
- },
-
- first_last_numbers: function (page, pages) {
- return ['first', _numbers(page, pages), 'last'];
- },
-
- // For testing and plug-ins to use
- _numbers: _numbers,
-
- // Number of number buttons (including ellipsis) to show. _Must be odd!_
- numbers_length: 7
- } );
-
-
- $.extend( true, DataTable.ext.renderer, {
- pageButton: {
- _: function ( settings, host, idx, buttons, page, pages ) {
- var classes = settings.oClasses;
- var lang = settings.oLanguage.oPaginate;
- var aria = settings.oLanguage.oAria.paginate || {};
- var btnDisplay, btnClass, counter=0;
-
- var attach = function( container, buttons ) {
- var i, ien, node, button;
- var clickHandler = function ( e ) {
- _fnPageChange( settings, e.data.action, true );
- };
-
- for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
- button = buttons[i];
-
- if ( $.isArray( button ) ) {
- var inner = $( '<'+(button.DT_el || 'div')+'/>' )
- .appendTo( container );
- attach( inner, button );
- }
- else {
- btnDisplay = null;
- btnClass = '';
-
- switch ( button ) {
- case 'ellipsis':
- container.append('<span class="ellipsis">…</span>');
- break;
-
- case 'first':
- btnDisplay = lang.sFirst;
- btnClass = button + (page > 0 ?
- '' : ' '+classes.sPageButtonDisabled);
- break;
-
- case 'previous':
- btnDisplay = lang.sPrevious;
- btnClass = button + (page > 0 ?
- '' : ' '+classes.sPageButtonDisabled);
- break;
-
- case 'next':
- btnDisplay = lang.sNext;
- btnClass = button + (page < pages-1 ?
- '' : ' '+classes.sPageButtonDisabled);
- break;
-
- case 'last':
- btnDisplay = lang.sLast;
- btnClass = button + (page < pages-1 ?
- '' : ' '+classes.sPageButtonDisabled);
- break;
-
- default:
- btnDisplay = button + 1;
- btnClass = page === button ?
- classes.sPageButtonActive : '';
- break;
- }
-
- if ( btnDisplay !== null ) {
- node = $('<a>', {
- 'class': classes.sPageButton+' '+btnClass,
- 'aria-controls': settings.sTableId,
- 'aria-label': aria[ button ],
- 'data-dt-idx': counter,
- 'tabindex': settings.iTabIndex,
- 'id': idx === 0 && typeof button === 'string' ?
- settings.sTableId +'_'+ button :
- null
- } )
- .html( btnDisplay )
- .appendTo( container );
-
- _fnBindAction(
- node, {action: button}, clickHandler
- );
-
- counter++;
- }
- }
- }
- };
-
- // IE9 throws an 'unknown error' if document.activeElement is used
- // inside an iframe or frame. Try / catch the error. Not good for
- // accessibility, but neither are frames.
- var activeEl;
-
- try {
- // Because this approach is destroying and recreating the paging
- // elements, focus is lost on the select button which is bad for
- // accessibility. So we want to restore focus once the draw has
- // completed
- activeEl = $(host).find(document.activeElement).data('dt-idx');
- }
- catch (e) {}
-
- attach( $(host).empty(), buttons );
-
- if ( activeEl !== undefined ) {
- $(host).find( '[data-dt-idx='+activeEl+']' ).focus();
- }
- }
- }
- } );
-
-
-
- // Built in type detection. See model.ext.aTypes for information about
- // what is required from this methods.
- $.extend( DataTable.ext.type.detect, [
- // Plain numbers - first since V8 detects some plain numbers as dates
- // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...).
- function ( d, settings )
- {
- var decimal = settings.oLanguage.sDecimal;
- return _isNumber( d, decimal ) ? 'num'+decimal : null;
- },
-
- // Dates (only those recognised by the browser's Date.parse)
- function ( d, settings )
- {
- // V8 tries _very_ hard to make a string passed into `Date.parse()`
- // valid, so we need to use a regex to restrict date formats. Use a
- // plug-in for anything other than ISO8601 style strings
- if ( d && !(d instanceof Date) && ! _re_date.test(d) ) {
- return null;
- }
- var parsed = Date.parse(d);
- return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
- },
-
- // Formatted numbers
- function ( d, settings )
- {
- var decimal = settings.oLanguage.sDecimal;
- return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
- },
-
- // HTML numeric
- function ( d, settings )
- {
- var decimal = settings.oLanguage.sDecimal;
- return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
- },
-
- // HTML numeric, formatted
- function ( d, settings )
- {
- var decimal = settings.oLanguage.sDecimal;
- return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
- },
-
- // HTML (this is strict checking - there must be html)
- function ( d, settings )
- {
- return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
- 'html' : null;
- }
- ] );
-
-
-
- // Filter formatting functions. See model.ext.ofnSearch for information about
- // what is required from these methods.
- //
- // Note that additional search methods are added for the html numbers and
- // html formatted numbers by `_addNumericSort()` when we know what the decimal
- // place is
-
-
- $.extend( DataTable.ext.type.search, {
- html: function ( data ) {
- return _empty(data) ?
- data :
- typeof data === 'string' ?
- data
- .replace( _re_new_lines, " " )
- .replace( _re_html, "" ) :
- '';
- },
-
- string: function ( data ) {
- return _empty(data) ?
- data :
- typeof data === 'string' ?
- data.replace( _re_new_lines, " " ) :
- data;
- }
- } );
-
-
-
- var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
- if ( d !== 0 && (!d || d === '-') ) {
- return -Infinity;
- }
-
- // If a decimal place other than `.` is used, it needs to be given to the
- // function so we can detect it and replace with a `.` which is the only
- // decimal place Javascript recognises - it is not locale aware.
- if ( decimalPlace ) {
- d = _numToDecimal( d, decimalPlace );
- }
-
- if ( d.replace ) {
- if ( re1 ) {
- d = d.replace( re1, '' );
- }
-
- if ( re2 ) {
- d = d.replace( re2, '' );
- }
- }
-
- return d * 1;
- };
-
-
- // Add the numeric 'deformatting' functions for sorting and search. This is done
- // in a function to provide an easy ability for the language options to add
- // additional methods if a non-period decimal place is used.
- function _addNumericSort ( decimalPlace ) {
- $.each(
- {
- // Plain numbers
- "num": function ( d ) {
- return __numericReplace( d, decimalPlace );
- },
-
- // Formatted numbers
- "num-fmt": function ( d ) {
- return __numericReplace( d, decimalPlace, _re_formatted_numeric );
- },
-
- // HTML numeric
- "html-num": function ( d ) {
- return __numericReplace( d, decimalPlace, _re_html );
- },
-
- // HTML numeric, formatted
- "html-num-fmt": function ( d ) {
- return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );
- }
- },
- function ( key, fn ) {
- // Add the ordering method
- _ext.type.order[ key+decimalPlace+'-pre' ] = fn;
-
- // For HTML types add a search formatter that will strip the HTML
- if ( key.match(/^html\-/) ) {
- _ext.type.search[ key+decimalPlace ] = _ext.type.search.html;
- }
- }
- );
- }
-
-
- // Default sort methods
- $.extend( _ext.type.order, {
- // Dates
- "date-pre": function ( d ) {
- return Date.parse( d ) || -Infinity;
- },
-
- // html
- "html-pre": function ( a ) {
- return _empty(a) ?
- '' :
- a.replace ?
- a.replace( /<.*?>/g, "" ).toLowerCase() :
- a+'';
- },
-
- // string
- "string-pre": function ( a ) {
- // This is a little complex, but faster than always calling toString,
- // http://jsperf.com/tostring-v-check
- return _empty(a) ?
- '' :
- typeof a === 'string' ?
- a.toLowerCase() :
- ! a.toString ?
- '' :
- a.toString();
- },
-
- // string-asc and -desc are retained only for compatibility with the old
- // sort methods
- "string-asc": function ( x, y ) {
- return ((x < y) ? -1 : ((x > y) ? 1 : 0));
- },
-
- "string-desc": function ( x, y ) {
- return ((x < y) ? 1 : ((x > y) ? -1 : 0));
- }
- } );
-
-
- // Numeric sorting types - order doesn't matter here
- _addNumericSort( '' );
-
-
- $.extend( true, DataTable.ext.renderer, {
- header: {
- _: function ( settings, cell, column, classes ) {
- // No additional mark-up required
- // Attach a sort listener to update on sort - note that using the
- // `DT` namespace will allow the event to be removed automatically
- // on destroy, while the `dt` namespaced event is the one we are
- // listening for
- $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
- if ( settings !== ctx ) { // need to check this this is the host
- return; // table, not a nested one
- }
-
- var colIdx = column.idx;
-
- cell
- .removeClass(
- column.sSortingClass +' '+
- classes.sSortAsc +' '+
- classes.sSortDesc
- )
- .addClass( columns[ colIdx ] == 'asc' ?
- classes.sSortAsc : columns[ colIdx ] == 'desc' ?
- classes.sSortDesc :
- column.sSortingClass
- );
- } );
- },
-
- jqueryui: function ( settings, cell, column, classes ) {
- $('<div/>')
- .addClass( classes.sSortJUIWrapper )
- .append( cell.contents() )
- .append( $('<span/>')
- .addClass( classes.sSortIcon+' '+column.sSortingClassJUI )
- )
- .appendTo( cell );
-
- // Attach a sort listener to update on sort
- $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
- if ( settings !== ctx ) {
- return;
- }
-
- var colIdx = column.idx;
-
- cell
- .removeClass( classes.sSortAsc +" "+classes.sSortDesc )
- .addClass( columns[ colIdx ] == 'asc' ?
- classes.sSortAsc : columns[ colIdx ] == 'desc' ?
- classes.sSortDesc :
- column.sSortingClass
- );
-
- cell
- .find( 'span.'+classes.sSortIcon )
- .removeClass(
- classes.sSortJUIAsc +" "+
- classes.sSortJUIDesc +" "+
- classes.sSortJUI +" "+
- classes.sSortJUIAscAllowed +" "+
- classes.sSortJUIDescAllowed
- )
- .addClass( columns[ colIdx ] == 'asc' ?
- classes.sSortJUIAsc : columns[ colIdx ] == 'desc' ?
- classes.sSortJUIDesc :
- column.sSortingClassJUI
- );
- } );
- }
- }
- } );
-
- /*
- * Public helper functions. These aren't used internally by DataTables, or
- * called by any of the options passed into DataTables, but they can be used
- * externally by developers working with DataTables. They are helper functions
- * to make working with DataTables a little bit easier.
- */
-
- var __htmlEscapeEntities = function ( d ) {
- return typeof d === 'string' ?
- d.replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"') :
- d;
- };
-
- /**
- * Helpers for `columns.render`.
- *
- * The options defined here can be used with the `columns.render` initialisation
- * option to provide a display renderer. The following functions are defined:
- *
- * * `number` - Will format numeric data (defined by `columns.data`) for
- * display, retaining the original unformatted data for sorting and filtering.
- * It takes 5 parameters:
- * * `string` - Thousands grouping separator
- * * `string` - Decimal point indicator
- * * `integer` - Number of decimal points to show
- * * `string` (optional) - Prefix.
- * * `string` (optional) - Postfix (/suffix).
- * * `text` - Escape HTML to help prevent XSS attacks. It has no optional
- * parameters.
- *
- * @example
- * // Column definition using the number renderer
- * {
- * data: "salary",
- * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' )
- * }
- *
- * @namespace
- */
- DataTable.render = {
- number: function ( thousands, decimal, precision, prefix, postfix ) {
- return {
- display: function ( d ) {
- if ( typeof d !== 'number' && typeof d !== 'string' ) {
- return d;
- }
-
- var negative = d < 0 ? '-' : '';
- var flo = parseFloat( d );
-
- // If NaN then there isn't much formatting that we can do - just
- // return immediately, escaping any HTML (this was supposed to
- // be a number after all)
- if ( isNaN( flo ) ) {
- return __htmlEscapeEntities( d );
- }
-
- flo = flo.toFixed( precision );
- d = Math.abs( flo );
-
- var intPart = parseInt( d, 10 );
- var floatPart = precision ?
- decimal+(d - intPart).toFixed( precision ).substring( 2 ):
- '';
-
- return negative + (prefix||'') +
- intPart.toString().replace(
- /\B(?=(\d{3})+(?!\d))/g, thousands
- ) +
- floatPart +
- (postfix||'');
- }
- };
- },
-
- text: function () {
- return {
- display: __htmlEscapeEntities
- };
- }
- };
-
-
- /*
- * This is really a good bit rubbish this method of exposing the internal methods
- * publicly... - To be fixed in 2.0 using methods on the prototype
- */
-
-
- /**
- * Create a wrapper function for exporting an internal functions to an external API.
- * @param {string} fn API function name
- * @returns {function} wrapped function
- * @memberof DataTable#internal
- */
- function _fnExternApiFunc (fn)
- {
- return function() {
- var args = [_fnSettingsFromNode( this[DataTable.ext.iApiIndex] )].concat(
- Array.prototype.slice.call(arguments)
- );
- return DataTable.ext.internal[fn].apply( this, args );
- };
- }
-
-
- /**
- * Reference to internal functions for use by plug-in developers. Note that
- * these methods are references to internal functions and are considered to be
- * private. If you use these methods, be aware that they are liable to change
- * between versions.
- * @namespace
- */
- $.extend( DataTable.ext.internal, {
- _fnExternApiFunc: _fnExternApiFunc,
- _fnBuildAjax: _fnBuildAjax,
- _fnAjaxUpdate: _fnAjaxUpdate,
- _fnAjaxParameters: _fnAjaxParameters,
- _fnAjaxUpdateDraw: _fnAjaxUpdateDraw,
- _fnAjaxDataSrc: _fnAjaxDataSrc,
- _fnAddColumn: _fnAddColumn,
- _fnColumnOptions: _fnColumnOptions,
- _fnAdjustColumnSizing: _fnAdjustColumnSizing,
- _fnVisibleToColumnIndex: _fnVisibleToColumnIndex,
- _fnColumnIndexToVisible: _fnColumnIndexToVisible,
- _fnVisbleColumns: _fnVisbleColumns,
- _fnGetColumns: _fnGetColumns,
- _fnColumnTypes: _fnColumnTypes,
- _fnApplyColumnDefs: _fnApplyColumnDefs,
- _fnHungarianMap: _fnHungarianMap,
- _fnCamelToHungarian: _fnCamelToHungarian,
- _fnLanguageCompat: _fnLanguageCompat,
- _fnBrowserDetect: _fnBrowserDetect,
- _fnAddData: _fnAddData,
- _fnAddTr: _fnAddTr,
- _fnNodeToDataIndex: _fnNodeToDataIndex,
- _fnNodeToColumnIndex: _fnNodeToColumnIndex,
- _fnGetCellData: _fnGetCellData,
- _fnSetCellData: _fnSetCellData,
- _fnSplitObjNotation: _fnSplitObjNotation,
- _fnGetObjectDataFn: _fnGetObjectDataFn,
- _fnSetObjectDataFn: _fnSetObjectDataFn,
- _fnGetDataMaster: _fnGetDataMaster,
- _fnClearTable: _fnClearTable,
- _fnDeleteIndex: _fnDeleteIndex,
- _fnInvalidate: _fnInvalidate,
- _fnGetRowElements: _fnGetRowElements,
- _fnCreateTr: _fnCreateTr,
- _fnBuildHead: _fnBuildHead,
- _fnDrawHead: _fnDrawHead,
- _fnDraw: _fnDraw,
- _fnReDraw: _fnReDraw,
- _fnAddOptionsHtml: _fnAddOptionsHtml,
- _fnDetectHeader: _fnDetectHeader,
- _fnGetUniqueThs: _fnGetUniqueThs,
- _fnFeatureHtmlFilter: _fnFeatureHtmlFilter,
- _fnFilterComplete: _fnFilterComplete,
- _fnFilterCustom: _fnFilterCustom,
- _fnFilterColumn: _fnFilterColumn,
- _fnFilter: _fnFilter,
- _fnFilterCreateSearch: _fnFilterCreateSearch,
- _fnEscapeRegex: _fnEscapeRegex,
- _fnFilterData: _fnFilterData,
- _fnFeatureHtmlInfo: _fnFeatureHtmlInfo,
- _fnUpdateInfo: _fnUpdateInfo,
- _fnInfoMacros: _fnInfoMacros,
- _fnInitialise: _fnInitialise,
- _fnInitComplete: _fnInitComplete,
- _fnLengthChange: _fnLengthChange,
- _fnFeatureHtmlLength: _fnFeatureHtmlLength,
- _fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate,
- _fnPageChange: _fnPageChange,
- _fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing,
- _fnProcessingDisplay: _fnProcessingDisplay,
- _fnFeatureHtmlTable: _fnFeatureHtmlTable,
- _fnScrollDraw: _fnScrollDraw,
- _fnApplyToChildren: _fnApplyToChildren,
- _fnCalculateColumnWidths: _fnCalculateColumnWidths,
- _fnThrottle: _fnThrottle,
- _fnConvertToWidth: _fnConvertToWidth,
- _fnGetWidestNode: _fnGetWidestNode,
- _fnGetMaxLenString: _fnGetMaxLenString,
- _fnStringToCss: _fnStringToCss,
- _fnSortFlatten: _fnSortFlatten,
- _fnSort: _fnSort,
- _fnSortAria: _fnSortAria,
- _fnSortListener: _fnSortListener,
- _fnSortAttachListener: _fnSortAttachListener,
- _fnSortingClasses: _fnSortingClasses,
- _fnSortData: _fnSortData,
- _fnSaveState: _fnSaveState,
- _fnLoadState: _fnLoadState,
- _fnSettingsFromNode: _fnSettingsFromNode,
- _fnLog: _fnLog,
- _fnMap: _fnMap,
- _fnBindAction: _fnBindAction,
- _fnCallbackReg: _fnCallbackReg,
- _fnCallbackFire: _fnCallbackFire,
- _fnLengthOverflow: _fnLengthOverflow,
- _fnRenderer: _fnRenderer,
- _fnDataSource: _fnDataSource,
- _fnRowAttributes: _fnRowAttributes,
- _fnCalculateEnd: function () {} // Used by a lot of plug-ins, but redundant
- // in 1.10, so this dead-end function is
- // added to prevent errors
- } );
-
- // jQuery access
- $.fn.dataTable = DataTable;
- // Provide access to the host jQuery object (circular reference)
- DataTable.$ = $;
- // Legacy aliases
- $.fn.dataTableSettings = DataTable.settings;
- $.fn.dataTableExt = DataTable.ext;
- // With a capital `D` we return a DataTables API instance rather than a
- // jQuery object
- $.fn.DataTable = function ( opts ) {
- return $(this).dataTable( opts ).api();
- };
- // All properties that are available to $.fn.dataTable should also be
- // available on $.fn.DataTable
- $.each( DataTable, function ( prop, val ) {
- $.fn.DataTable[ prop ] = val;
- } );
- // Information about events fired by DataTables - for documentation.
- /**
- * Draw event, fired whenever the table is redrawn on the page, at the same
- * point as fnDrawCallback. This may be useful for binding events or
- * performing calculations when the table is altered at all.
- * @name DataTable#draw.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
- */
- /**
- * Search event, fired when the searching applied to the table (using the
- * built-in global search, or column filters) is altered.
- * @name DataTable#search.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
- */
- /**
- * Page change event, fired when the paging of the table is altered.
- * @name DataTable#page.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
- */
- /**
- * Order event, fired when the ordering applied to the table is altered.
- * @name DataTable#order.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
- */
- /**
- * DataTables initialisation complete event, fired when the table is fully
- * drawn, including Ajax data loaded, if Ajax data is required.
- * @name DataTable#init.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} oSettings DataTables settings object
- * @param {object} json The JSON object request from the server - only
- * present if client-side Ajax sourced data is used</li></ol>
- */
- /**
- * State save event, fired when the table has changed state a new state save
- * is required. This event allows modification of the state saving object
- * prior to actually doing the save, including addition or other state
- * properties (for plug-ins) or modification of a DataTables core property.
- * @name DataTable#stateSaveParams.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} oSettings DataTables settings object
- * @param {object} json The state information to be saved
- */
- /**
- * State load event, fired when the table is loading state from the stored
- * data, but prior to the settings object being modified by the saved state
- * - allowing modification of the saved state is required or loading of
- * state for a plug-in.
- * @name DataTable#stateLoadParams.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} oSettings DataTables settings object
- * @param {object} json The saved state information
- */
- /**
- * State loaded event, fired when state has been loaded from stored data and
- * the settings object has been modified by the loaded data.
- * @name DataTable#stateLoaded.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} oSettings DataTables settings object
- * @param {object} json The saved state information
- */
- /**
- * Processing event, fired when DataTables is doing some kind of processing
- * (be it, order, searcg or anything else). It can be used to indicate to
- * the end user that there is something happening, or that something has
- * finished.
- * @name DataTable#processing.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} oSettings DataTables settings object
- * @param {boolean} bShow Flag for if DataTables is doing processing or not
- */
- /**
- * Ajax (XHR) event, fired whenever an Ajax request is completed from a
- * request to made to the server for new data. This event is called before
- * DataTables processed the returned data, so it can also be used to pre-
- * process the data returned from the server, if needed.
- *
- * Note that this trigger is called in `fnServerData`, if you override
- * `fnServerData` and which to use this event, you need to trigger it in you
- * success function.
- * @name DataTable#xhr.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
- * @param {object} json JSON returned from the server
- *
- * @example
- * // Use a custom property returned from the server in another DOM element
- * $('#table').dataTable().on('xhr.dt', function (e, settings, json) {
- * $('#status').html( json.status );
- * } );
- *
- * @example
- * // Pre-process the data returned from the server
- * $('#table').dataTable().on('xhr.dt', function (e, settings, json) {
- * for ( var i=0, ien=json.aaData.length ; i<ien ; i++ ) {
- * json.aaData[i].sum = json.aaData[i].one + json.aaData[i].two;
- * }
- * // Note no return - manipulate the data directly in the JSON object.
- * } );
- */
- /**
- * Destroy event, fired when the DataTable is destroyed by calling fnDestroy
- * or passing the bDestroy:true parameter in the initialisation object. This
- * can be used to remove bound events, added DOM nodes, etc.
- * @name DataTable#destroy.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
- */
- /**
- * Page length change event, fired when number of records to show on each
- * page (the length) is changed.
- * @name DataTable#length.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
- * @param {integer} len New length
- */
- /**
- * Column sizing has changed.
- * @name DataTable#column-sizing.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
- */
- /**
- * Column visibility has changed.
- * @name DataTable#column-visibility.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
- * @param {int} column Column index
- * @param {bool} vis `false` if column now hidden, or `true` if visible
- */
- return $.fn.dataTable;
- }));
- /*! AutoFill 2.2.2
- * ©2008-2017 SpryMedia Ltd - datatables.net/license
- */
- /**
- * @summary AutoFill
- * @description Add Excel like click and drag auto-fill options to DataTables
- * @version 2.2.2
- * @file dataTables.autoFill.js
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
- * @contact www.sprymedia.co.uk/contact
- * @copyright Copyright 2010-2017 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license/mit
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- var _instance = 0;
- /**
- * AutoFill provides Excel like auto-fill features for a DataTable
- *
- * @class AutoFill
- * @constructor
- * @param {object} oTD DataTables settings object
- * @param {object} oConfig Configuration object for AutoFill
- */
- var AutoFill = function( dt, opts )
- {
- if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) {
- throw( "Warning: AutoFill requires DataTables 1.10.8 or greater");
- }
- // User and defaults configuration object
- this.c = $.extend( true, {},
- DataTable.defaults.autoFill,
- AutoFill.defaults,
- opts
- );
- /**
- * @namespace Settings object which contains customisable information for AutoFill instance
- */
- this.s = {
- /** @type {DataTable.Api} DataTables' API instance */
- dt: new DataTable.Api( dt ),
- /** @type {String} Unique namespace for events attached to the document */
- namespace: '.autoFill'+(_instance++),
- /** @type {Object} Cached dimension information for use in the mouse move event handler */
- scroll: {},
- /** @type {integer} Interval object used for smooth scrolling */
- scrollInterval: null,
- handle: {
- height: 0,
- width: 0
- },
- /**
- * Enabled setting
- * @type {Boolean}
- */
- enabled: false
- };
- /**
- * @namespace Common and useful DOM elements for the class instance
- */
- this.dom = {
- /** @type {jQuery} AutoFill handle */
- handle: $('<div class="dt-autofill-handle"/>'),
- /**
- * @type {Object} Selected cells outline - Need to use 4 elements,
- * otherwise the mouse over if you back into the selected rectangle
- * will be over that element, rather than the cells!
- */
- select: {
- top: $('<div class="dt-autofill-select top"/>'),
- right: $('<div class="dt-autofill-select right"/>'),
- bottom: $('<div class="dt-autofill-select bottom"/>'),
- left: $('<div class="dt-autofill-select left"/>')
- },
- /** @type {jQuery} Fill type chooser background */
- background: $('<div class="dt-autofill-background"/>'),
- /** @type {jQuery} Fill type chooser */
- list: $('<div class="dt-autofill-list">'+this.s.dt.i18n('autoFill.info', '')+'<ul/></div>'),
- /** @type {jQuery} DataTables scrolling container */
- dtScroll: null,
- /** @type {jQuery} Offset parent element */
- offsetParent: null
- };
- /* Constructor logic */
- this._constructor();
- };
- $.extend( AutoFill.prototype, {
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Public methods (exposed via the DataTables API below)
- */
- enabled: function ()
- {
- return this.s.enabled;
- },
- enable: function ( flag )
- {
- var that = this;
- if ( flag === false ) {
- return this.disable();
- }
- this.s.enabled = true;
- this._focusListener();
- this.dom.handle.on( 'mousedown', function (e) {
- that._mousedown( e );
- return false;
- } );
- return this;
- },
- disable: function ()
- {
- this.s.enabled = false;
- this._focusListenerRemove();
- return this;
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constructor
- */
- /**
- * Initialise the RowReorder instance
- *
- * @private
- */
- _constructor: function ()
- {
- var that = this;
- var dt = this.s.dt;
- var dtScroll = $('div.dataTables_scrollBody', this.s.dt.table().container());
- // Make the instance accessible to the API
- dt.settings()[0].autoFill = this;
- if ( dtScroll.length ) {
- this.dom.dtScroll = dtScroll;
- // Need to scroll container to be the offset parent
- if ( dtScroll.css('position') === 'static' ) {
- dtScroll.css( 'position', 'relative' );
- }
- }
- if ( this.c.enable !== false ) {
- this.enable();
- }
- dt.on( 'destroy.autoFill', function () {
- that._focusListenerRemove();
- } );
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Private methods
- */
- /**
- * Display the AutoFill drag handle by appending it to a table cell. This
- * is the opposite of the _detach method.
- *
- * @param {node} node TD/TH cell to insert the handle into
- * @private
- */
- _attach: function ( node )
- {
- var dt = this.s.dt;
- var idx = dt.cell( node ).index();
- var handle = this.dom.handle;
- var handleDim = this.s.handle;
- if ( ! idx || dt.columns( this.c.columns ).indexes().indexOf( idx.column ) === -1 ) {
- this._detach();
- return;
- }
- if ( ! this.dom.offsetParent ) {
- // We attach to the table's offset parent
- this.dom.offsetParent = $( dt.table().node() ).offsetParent();
- }
- if ( ! handleDim.height || ! handleDim.width ) {
- // Append to document so we can get its size. Not expecting it to
- // change during the life time of the page
- handle.appendTo( 'body' );
- handleDim.height = handle.outerHeight();
- handleDim.width = handle.outerWidth();
- }
- // Might need to go through multiple offset parents
- var offset = this._getPosition( node, this.dom.offsetParent );
- this.dom.attachedTo = node;
- handle
- .css( {
- top: offset.top + node.offsetHeight - handleDim.height,
- left: offset.left + node.offsetWidth - handleDim.width
- } )
- .appendTo( this.dom.offsetParent );
- },
- /**
- * Determine can the fill type should be. This can be automatic, or ask the
- * end user.
- *
- * @param {array} cells Information about the selected cells from the key
- * up function
- * @private
- */
- _actionSelector: function ( cells )
- {
- var that = this;
- var dt = this.s.dt;
- var actions = AutoFill.actions;
- var available = [];
- // "Ask" each plug-in if it wants to handle this data
- $.each( actions, function ( key, action ) {
- if ( action.available( dt, cells ) ) {
- available.push( key );
- }
- } );
- if ( available.length === 1 && this.c.alwaysAsk === false ) {
- // Only one action available - enact it immediately
- var result = actions[ available[0] ].execute( dt, cells );
- this._update( result, cells );
- }
- else {
- // Multiple actions available - ask the end user what they want to do
- var list = this.dom.list.children('ul').empty();
- // Add a cancel option
- available.push( 'cancel' );
- $.each( available, function ( i, name ) {
- list.append( $('<li/>')
- .append(
- '<div class="dt-autofill-question">'+
- actions[ name ].option( dt, cells )+
- '<div>'
- )
- .append( $('<div class="dt-autofill-button">' )
- .append( $('<button class="'+AutoFill.classes.btn+'">'+dt.i18n('autoFill.button', '>')+'</button>')
- .on( 'click', function () {
- var result = actions[ name ].execute(
- dt, cells, $(this).closest('li')
- );
- that._update( result, cells );
- that.dom.background.remove();
- that.dom.list.remove();
- } )
- )
- )
- );
- } );
- this.dom.background.appendTo( 'body' );
- this.dom.list.appendTo( 'body' );
- this.dom.list.css( 'margin-top', this.dom.list.outerHeight()/2 * -1 );
- }
- },
- /**
- * Remove the AutoFill handle from the document
- *
- * @private
- */
- _detach: function ()
- {
- this.dom.attachedTo = null;
- this.dom.handle.detach();
- },
- /**
- * Draw the selection outline by calculating the range between the start
- * and end cells, then placing the highlighting elements to draw a rectangle
- *
- * @param {node} target End cell
- * @param {object} e Originating event
- * @private
- */
- _drawSelection: function ( target, e )
- {
- // Calculate boundary for start cell to this one
- var dt = this.s.dt;
- var start = this.s.start;
- var startCell = $(this.dom.start);
- var endCell = $(target);
- var end = {
- row: dt.rows( { page: 'current' } ).nodes().indexOf( endCell.parent()[0] ),
- column: endCell.index()
- };
- var colIndx = dt.column.index( 'toData', end.column );
- // Be sure that is a DataTables controlled cell
- if ( ! dt.cell( endCell ).any() ) {
- return;
- }
- // if target is not in the columns available - do nothing
- if ( dt.columns( this.c.columns ).indexes().indexOf( colIndx ) === -1 ) {
- return;
- }
- this.s.end = end;
- var top, bottom, left, right, height, width;
- top = start.row < end.row ? startCell : endCell;
- bottom = start.row < end.row ? endCell : startCell;
- left = start.column < end.column ? startCell : endCell;
- right = start.column < end.column ? endCell : startCell;
- top = this._getPosition( top ).top;
- left = this._getPosition( left ).left;
- height = this._getPosition( bottom ).top + bottom.outerHeight() - top;
- width = this._getPosition( right ).left + right.outerWidth() - left;
- var select = this.dom.select;
- select.top.css( {
- top: top,
- left: left,
- width: width
- } );
- select.left.css( {
- top: top,
- left: left,
- height: height
- } );
- select.bottom.css( {
- top: top + height,
- left: left,
- width: width
- } );
- select.right.css( {
- top: top,
- left: left + width,
- height: height
- } );
- },
- /**
- * Use the Editor API to perform an update based on the new data for the
- * cells
- *
- * @param {array} cells Information about the selected cells from the key
- * up function
- * @private
- */
- _editor: function ( cells )
- {
- var dt = this.s.dt;
- var editor = this.c.editor;
- if ( ! editor ) {
- return;
- }
- // Build the object structure for Editor's multi-row editing
- var idValues = {};
- var nodes = [];
- var fields = editor.fields();
- for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
- for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
- var cell = cells[i][j];
- // Determine the field name for the cell being edited
- var col = dt.settings()[0].aoColumns[ cell.index.column ];
- var fieldName = col.editField;
- if ( fieldName === undefined ) {
- var dataSrc = col.mData;
- // dataSrc is the `field.data` property, but we need to set
- // using the field name, so we need to translate from the
- // data to the name
- for ( var k=0, ken=fields.length ; k<ken ; k++ ) {
- var field = editor.field( fields[k] );
- if ( field.dataSrc() === dataSrc ) {
- fieldName = field.name();
- break;
- }
- }
- }
- if ( ! fieldName ) {
- throw 'Could not automatically determine field data. '+
- 'Please see https://datatables.net/tn/11';
- }
- if ( ! idValues[ fieldName ] ) {
- idValues[ fieldName ] = {};
- }
- var id = dt.row( cell.index.row ).id();
- idValues[ fieldName ][ id ] = cell.set;
- // Keep a list of cells so we can activate the bubble editing
- // with them
- nodes.push( cell.index );
- }
- }
- // Perform the edit using bubble editing as it allows us to specify
- // the cells to be edited, rather than using full rows
- editor
- .bubble( nodes, false )
- .multiSet( idValues )
- .submit();
- },
- /**
- * Emit an event on the DataTable for listeners
- *
- * @param {string} name Event name
- * @param {array} args Event arguments
- * @private
- */
- _emitEvent: function ( name, args )
- {
- this.s.dt.iterator( 'table', function ( ctx, i ) {
- $(ctx.nTable).triggerHandler( name+'.dt', args );
- } );
- },
- /**
- * Attach suitable listeners (based on the configuration) that will attach
- * and detach the AutoFill handle in the document.
- *
- * @private
- */
- _focusListener: function ()
- {
- var that = this;
- var dt = this.s.dt;
- var namespace = this.s.namespace;
- var focus = this.c.focus !== null ?
- this.c.focus :
- dt.init().keys || dt.settings()[0].keytable ?
- 'focus' :
- 'hover';
- // All event listeners attached here are removed in the `destroy`
- // callback in the constructor
- if ( focus === 'focus' ) {
- dt
- .on( 'key-focus.autoFill', function ( e, dt, cell ) {
- that._attach( cell.node() );
- } )
- .on( 'key-blur.autoFill', function ( e, dt, cell ) {
- that._detach();
- } );
- }
- else if ( focus === 'click' ) {
- $(dt.table().body()).on( 'click'+namespace, 'td, th', function (e) {
- that._attach( this );
- } );
- $(document.body).on( 'click'+namespace, function (e) {
- if ( ! $(e.target).parents().filter( dt.table().body() ).length ) {
- that._detach();
- }
- } );
- }
- else {
- $(dt.table().body())
- .on( 'mouseenter'+namespace, 'td, th', function (e) {
- that._attach( this );
- } )
- .on( 'mouseleave'+namespace, function (e) {
- if ( $(e.relatedTarget).hasClass('dt-autofill-handle') ) {
- return;
- }
- that._detach();
- } );
- }
- },
- _focusListenerRemove: function ()
- {
- var dt = this.s.dt;
- dt.off( '.autoFill' );
- $(dt.table().body()).off( this.s.namespace );
- $(document.body).off( this.s.namespace );
- },
- /**
- * Get the position of a node, relative to another, including any scrolling
- * offsets.
- * @param {Node} node Node to get the position of
- * @param {jQuery} targetParent Node to use as the parent
- * @return {object} Offset calculation
- * @private
- */
- _getPosition: function ( node, targetParent )
- {
- var
- currNode = $(node),
- currOffsetParent,
- position,
- top = 0,
- left = 0;
- if ( ! targetParent ) {
- targetParent = $( this.s.dt.table().node() ).offsetParent();
- }
- do {
- position = currNode.position();
- currOffsetParent = currNode.offsetParent();
- top += position.top + currOffsetParent.scrollTop();
- left += position.left + currOffsetParent.scrollLeft();
- // Emergency fall back. Shouldn't happen, but just in case!
- if ( currNode.get(0).nodeName.toLowerCase() === 'body' ) {
- break;
- }
- currNode = currOffsetParent; // for next loop
- }
- while ( currOffsetParent.get(0) !== targetParent.get(0) )
- return {
- top: top,
- left: left
- };
- },
- /**
- * Start mouse drag - selects the start cell
- *
- * @param {object} e Mouse down event
- * @private
- */
- _mousedown: function ( e )
- {
- var that = this;
- var dt = this.s.dt;
- this.dom.start = this.dom.attachedTo;
- this.s.start = {
- row: dt.rows( { page: 'current' } ).nodes().indexOf( $(this.dom.start).parent()[0] ),
- column: $(this.dom.start).index()
- };
- $(document.body)
- .on( 'mousemove.autoFill', function (e) {
- that._mousemove( e );
- } )
- .on( 'mouseup.autoFill', function (e) {
- that._mouseup( e );
- } );
- var select = this.dom.select;
- var offsetParent = $( dt.table().node() ).offsetParent();
- select.top.appendTo( offsetParent );
- select.left.appendTo( offsetParent );
- select.right.appendTo( offsetParent );
- select.bottom.appendTo( offsetParent );
- this._drawSelection( this.dom.start, e );
- this.dom.handle.css( 'display', 'none' );
- // Cache scrolling information so mouse move doesn't need to read.
- // This assumes that the window and DT scroller will not change size
- // during an AutoFill drag, which I think is a fair assumption
- var scrollWrapper = this.dom.dtScroll;
- this.s.scroll = {
- windowHeight: $(window).height(),
- windowWidth: $(window).width(),
- dtTop: scrollWrapper ? scrollWrapper.offset().top : null,
- dtLeft: scrollWrapper ? scrollWrapper.offset().left : null,
- dtHeight: scrollWrapper ? scrollWrapper.outerHeight() : null,
- dtWidth: scrollWrapper ? scrollWrapper.outerWidth() : null
- };
- },
- /**
- * Mouse drag - selects the end cell and update the selection display for
- * the end user
- *
- * @param {object} e Mouse move event
- * @private
- */
- _mousemove: function ( e )
- {
- var that = this;
- var dt = this.s.dt;
- var name = e.target.nodeName.toLowerCase();
- if ( name !== 'td' && name !== 'th' ) {
- return;
- }
- this._drawSelection( e.target, e );
- this._shiftScroll( e );
- },
- /**
- * End mouse drag - perform the update actions
- *
- * @param {object} e Mouse up event
- * @private
- */
- _mouseup: function ( e )
- {
- $(document.body).off( '.autoFill' );
- var dt = this.s.dt;
- var select = this.dom.select;
- select.top.remove();
- select.left.remove();
- select.right.remove();
- select.bottom.remove();
- this.dom.handle.css( 'display', 'block' );
- // Display complete - now do something useful with the selection!
- var start = this.s.start;
- var end = this.s.end;
- // Haven't selected multiple cells, so nothing to do
- if ( start.row === end.row && start.column === end.column ) {
- return;
- }
- // Build a matrix representation of the selected rows
- var rows = this._range( start.row, end.row );
- var columns = this._range( start.column, end.column );
- var selected = [];
- var dtSettings = dt.settings()[0];
- var dtColumns = dtSettings.aoColumns;
- // Can't use Array.prototype.map as IE8 doesn't support it
- // Can't use $.map as jQuery flattens 2D arrays
- // Need to use a good old fashioned for loop
- for ( var rowIdx=0 ; rowIdx<rows.length ; rowIdx++ ) {
- selected.push(
- $.map( columns, function (column) {
- var cell = dt.cell( ':eq('+rows[rowIdx]+')', column+':visible', {page:'current'} );
- var data = cell.data();
- var cellIndex = cell.index();
- var editField = dtColumns[ cellIndex.column ].editField;
- if ( editField !== undefined ) {
- data = dtSettings.oApi._fnGetObjectDataFn( editField )( dt.row( cellIndex.row ).data() );
- }
- return {
- cell: cell,
- data: data,
- label: cell.data(),
- index: cellIndex
- };
- } )
- );
- }
- this._actionSelector( selected );
-
- // Stop shiftScroll
- clearInterval( this.s.scrollInterval );
- this.s.scrollInterval = null;
- },
- /**
- * Create an array with a range of numbers defined by the start and end
- * parameters passed in (inclusive!).
- *
- * @param {integer} start Start
- * @param {integer} end End
- * @private
- */
- _range: function ( start, end )
- {
- var out = [];
- var i;
- if ( start <= end ) {
- for ( i=start ; i<=end ; i++ ) {
- out.push( i );
- }
- }
- else {
- for ( i=start ; i>=end ; i-- ) {
- out.push( i );
- }
- }
- return out;
- },
- /**
- * Move the window and DataTables scrolling during a drag to scroll new
- * content into view. This is done by proximity to the edge of the scrolling
- * container of the mouse - for example near the top edge of the window
- * should scroll up. This is a little complicated as there are two elements
- * that can be scrolled - the window and the DataTables scrolling view port
- * (if scrollX and / or scrollY is enabled).
- *
- * @param {object} e Mouse move event object
- * @private
- */
- _shiftScroll: function ( e )
- {
- var that = this;
- var dt = this.s.dt;
- var scroll = this.s.scroll;
- var runInterval = false;
- var scrollSpeed = 5;
- var buffer = 65;
- var
- windowY = e.pageY - document.body.scrollTop,
- windowX = e.pageX - document.body.scrollLeft,
- windowVert, windowHoriz,
- dtVert, dtHoriz;
- // Window calculations - based on the mouse position in the window,
- // regardless of scrolling
- if ( windowY < buffer ) {
- windowVert = scrollSpeed * -1;
- }
- else if ( windowY > scroll.windowHeight - buffer ) {
- windowVert = scrollSpeed;
- }
- if ( windowX < buffer ) {
- windowHoriz = scrollSpeed * -1;
- }
- else if ( windowX > scroll.windowWidth - buffer ) {
- windowHoriz = scrollSpeed;
- }
- // DataTables scrolling calculations - based on the table's position in
- // the document and the mouse position on the page
- if ( scroll.dtTop !== null && e.pageY < scroll.dtTop + buffer ) {
- dtVert = scrollSpeed * -1;
- }
- else if ( scroll.dtTop !== null && e.pageY > scroll.dtTop + scroll.dtHeight - buffer ) {
- dtVert = scrollSpeed;
- }
- if ( scroll.dtLeft !== null && e.pageX < scroll.dtLeft + buffer ) {
- dtHoriz = scrollSpeed * -1;
- }
- else if ( scroll.dtLeft !== null && e.pageX > scroll.dtLeft + scroll.dtWidth - buffer ) {
- dtHoriz = scrollSpeed;
- }
- // This is where it gets interesting. We want to continue scrolling
- // without requiring a mouse move, so we need an interval to be
- // triggered. The interval should continue until it is no longer needed,
- // but it must also use the latest scroll commands (for example consider
- // that the mouse might move from scrolling up to scrolling left, all
- // with the same interval running. We use the `scroll` object to "pass"
- // this information to the interval. Can't use local variables as they
- // wouldn't be the ones that are used by an already existing interval!
- if ( windowVert || windowHoriz || dtVert || dtHoriz ) {
- scroll.windowVert = windowVert;
- scroll.windowHoriz = windowHoriz;
- scroll.dtVert = dtVert;
- scroll.dtHoriz = dtHoriz;
- runInterval = true;
- }
- else if ( this.s.scrollInterval ) {
- // Don't need to scroll - remove any existing timer
- clearInterval( this.s.scrollInterval );
- this.s.scrollInterval = null;
- }
- // If we need to run the interval to scroll and there is no existing
- // interval (if there is an existing one, it will continue to run)
- if ( ! this.s.scrollInterval && runInterval ) {
- this.s.scrollInterval = setInterval( function () {
- // Don't need to worry about setting scroll <0 or beyond the
- // scroll bound as the browser will just reject that.
- if ( scroll.windowVert ) {
- document.body.scrollTop += scroll.windowVert;
- }
- if ( scroll.windowHoriz ) {
- document.body.scrollLeft += scroll.windowHoriz;
- }
- // DataTables scrolling
- if ( scroll.dtVert || scroll.dtHoriz ) {
- var scroller = that.dom.dtScroll[0];
- if ( scroll.dtVert ) {
- scroller.scrollTop += scroll.dtVert;
- }
- if ( scroll.dtHoriz ) {
- scroller.scrollLeft += scroll.dtHoriz;
- }
- }
- }, 20 );
- }
- },
- /**
- * Update the DataTable after the user has selected what they want to do
- *
- * @param {false|undefined} result Return from the `execute` method - can
- * be false internally to do nothing. This is not documented for plug-ins
- * and is used only by the cancel option.
- * @param {array} cells Information about the selected cells from the key
- * up function, argumented with the set values
- * @private
- */
- _update: function ( result, cells )
- {
- // Do nothing on `false` return from an execute function
- if ( result === false ) {
- return;
- }
- var dt = this.s.dt;
- var cell;
- // Potentially allow modifications to the cells matrix
- this._emitEvent( 'preAutoFill', [ dt, cells ] );
- this._editor( cells );
- // Automatic updates are not performed if `update` is null and the
- // `editor` parameter is passed in - the reason being that Editor will
- // update the data once submitted
- var update = this.c.update !== null ?
- this.c.update :
- this.c.editor ?
- false :
- true;
- if ( update ) {
- for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
- for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
- cell = cells[i][j];
- cell.cell.data( cell.set );
- }
- }
- dt.draw(false);
- }
- this._emitEvent( 'autoFill', [ dt, cells ] );
- }
- } );
- /**
- * AutoFill actions. The options here determine how AutoFill will fill the data
- * in the table when the user has selected a range of cells. Please see the
- * documentation on the DataTables site for full details on how to create plug-
- * ins.
- *
- * @type {Object}
- */
- AutoFill.actions = {
- increment: {
- available: function ( dt, cells ) {
- return $.isNumeric( cells[0][0].label );
- },
- option: function ( dt, cells ) {
- return dt.i18n(
- 'autoFill.increment',
- 'Increment / decrement each cell by: <input type="number" value="1">'
- );
- },
- execute: function ( dt, cells, node ) {
- var value = cells[0][0].data * 1;
- var increment = $('input', node).val() * 1;
- for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
- for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
- cells[i][j].set = value;
- value += increment;
- }
- }
- }
- },
- fill: {
- available: function ( dt, cells ) {
- return true;
- },
- option: function ( dt, cells ) {
- return dt.i18n('autoFill.fill', 'Fill all cells with <i>'+cells[0][0].label+'</i>' );
- },
- execute: function ( dt, cells, node ) {
- var value = cells[0][0].data;
- for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
- for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
- cells[i][j].set = value;
- }
- }
- }
- },
- fillHorizontal: {
- available: function ( dt, cells ) {
- return cells.length > 1 && cells[0].length > 1;
- },
- option: function ( dt, cells ) {
- return dt.i18n('autoFill.fillHorizontal', 'Fill cells horizontally' );
- },
- execute: function ( dt, cells, node ) {
- for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
- for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
- cells[i][j].set = cells[i][0].data;
- }
- }
- }
- },
- fillVertical: {
- available: function ( dt, cells ) {
- return cells.length > 1 && cells[0].length > 1;
- },
- option: function ( dt, cells ) {
- return dt.i18n('autoFill.fillVertical', 'Fill cells vertically' );
- },
- execute: function ( dt, cells, node ) {
- for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
- for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
- cells[i][j].set = cells[0][j].data;
- }
- }
- }
- },
- // Special type that does not make itself available, but is added
- // automatically by AutoFill if a multi-choice list is shown. This allows
- // sensible code reuse
- cancel: {
- available: function () {
- return false;
- },
- option: function ( dt ) {
- return dt.i18n('autoFill.cancel', 'Cancel' );
- },
- execute: function () {
- return false;
- }
- }
- };
- /**
- * AutoFill version
- *
- * @static
- * @type String
- */
- AutoFill.version = '2.2.2';
- /**
- * AutoFill defaults
- *
- * @namespace
- */
- AutoFill.defaults = {
- /** @type {Boolean} Ask user what they want to do, even for a single option */
- alwaysAsk: false,
- /** @type {string|null} What will trigger a focus */
- focus: null, // focus, click, hover
- /** @type {column-selector} Columns to provide auto fill for */
- columns: '', // all
- /** @type {Boolean} Enable AutoFill on load */
- enable: true,
- /** @type {boolean|null} Update the cells after a drag */
- update: null, // false is editor given, true otherwise
- /** @type {DataTable.Editor} Editor instance for automatic submission */
- editor: null
- };
- /**
- * Classes used by AutoFill that are configurable
- *
- * @namespace
- */
- AutoFill.classes = {
- /** @type {String} Class used by the selection button */
- btn: 'btn'
- };
- /*
- * API
- */
- var Api = $.fn.dataTable.Api;
- // Doesn't do anything - Not documented
- Api.register( 'autoFill()', function () {
- return this;
- } );
- Api.register( 'autoFill().enabled()', function () {
- var ctx = this.context[0];
- return ctx.autoFill ?
- ctx.autoFill.enabled() :
- false;
- } );
- Api.register( 'autoFill().enable()', function ( flag ) {
- return this.iterator( 'table', function ( ctx ) {
- if ( ctx.autoFill ) {
- ctx.autoFill.enable( flag );
- }
- } );
- } );
- Api.register( 'autoFill().disable()', function () {
- return this.iterator( 'table', function ( ctx ) {
- if ( ctx.autoFill ) {
- ctx.autoFill.disable();
- }
- } );
- } );
- // Attach a listener to the document which listens for DataTables initialisation
- // events so we can automatically initialise
- $(document).on( 'preInit.dt.autofill', function (e, settings, json) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- var init = settings.oInit.autoFill;
- var defaults = DataTable.defaults.autoFill;
- if ( init || defaults ) {
- var opts = $.extend( {}, init, defaults );
- if ( init !== false ) {
- new AutoFill( settings, opts );
- }
- }
- } );
- // Alias for access
- DataTable.AutoFill = AutoFill;
- DataTable.AutoFill = AutoFill;
- return AutoFill;
- }));
- /*! Buttons for DataTables 1.5.1
- * ©2016-2017 SpryMedia Ltd - datatables.net/license
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- // Used for namespacing events added to the document by each instance, so they
- // can be removed on destroy
- var _instCounter = 0;
- // Button namespacing counter for namespacing events on individual buttons
- var _buttonCounter = 0;
- var _dtButtons = DataTable.ext.buttons;
- /**
- * [Buttons description]
- * @param {[type]}
- * @param {[type]}
- */
- var Buttons = function( dt, config )
- {
- // If there is no config set it to an empty object
- if ( typeof( config ) === 'undefined' ) {
- config = {};
- }
-
- // Allow a boolean true for defaults
- if ( config === true ) {
- config = {};
- }
- // For easy configuration of buttons an array can be given
- if ( $.isArray( config ) ) {
- config = { buttons: config };
- }
- this.c = $.extend( true, {}, Buttons.defaults, config );
- // Don't want a deep copy for the buttons
- if ( config.buttons ) {
- this.c.buttons = config.buttons;
- }
- this.s = {
- dt: new DataTable.Api( dt ),
- buttons: [],
- listenKeys: '',
- namespace: 'dtb'+(_instCounter++)
- };
- this.dom = {
- container: $('<'+this.c.dom.container.tag+'/>')
- .addClass( this.c.dom.container.className )
- };
- this._constructor();
- };
- $.extend( Buttons.prototype, {
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Public methods
- */
- /**
- * Get the action of a button
- * @param {int|string} Button index
- * @return {function}
- *//**
- * Set the action of a button
- * @param {node} node Button element
- * @param {function} action Function to set
- * @return {Buttons} Self for chaining
- */
- action: function ( node, action )
- {
- var button = this._nodeToButton( node );
- if ( action === undefined ) {
- return button.conf.action;
- }
- button.conf.action = action;
- return this;
- },
- /**
- * Add an active class to the button to make to look active or get current
- * active state.
- * @param {node} node Button element
- * @param {boolean} [flag] Enable / disable flag
- * @return {Buttons} Self for chaining or boolean for getter
- */
- active: function ( node, flag ) {
- var button = this._nodeToButton( node );
- var klass = this.c.dom.button.active;
- var jqNode = $(button.node);
- if ( flag === undefined ) {
- return jqNode.hasClass( klass );
- }
- jqNode.toggleClass( klass, flag === undefined ? true : flag );
- return this;
- },
- /**
- * Add a new button
- * @param {object} config Button configuration object, base string name or function
- * @param {int|string} [idx] Button index for where to insert the button
- * @return {Buttons} Self for chaining
- */
- add: function ( config, idx )
- {
- var buttons = this.s.buttons;
- if ( typeof idx === 'string' ) {
- var split = idx.split('-');
- var base = this.s;
- for ( var i=0, ien=split.length-1 ; i<ien ; i++ ) {
- base = base.buttons[ split[i]*1 ];
- }
- buttons = base.buttons;
- idx = split[ split.length-1 ]*1;
- }
- this._expandButton( buttons, config, false, idx );
- this._draw();
- return this;
- },
- /**
- * Get the container node for the buttons
- * @return {jQuery} Buttons node
- */
- container: function ()
- {
- return this.dom.container;
- },
- /**
- * Disable a button
- * @param {node} node Button node
- * @return {Buttons} Self for chaining
- */
- disable: function ( node ) {
- var button = this._nodeToButton( node );
- $(button.node).addClass( this.c.dom.button.disabled );
- return this;
- },
- /**
- * Destroy the instance, cleaning up event handlers and removing DOM
- * elements
- * @return {Buttons} Self for chaining
- */
- destroy: function ()
- {
- // Key event listener
- $('body').off( 'keyup.'+this.s.namespace );
- // Individual button destroy (so they can remove their own events if
- // needed). Take a copy as the array is modified by `remove`
- var buttons = this.s.buttons.slice();
- var i, ien;
-
- for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
- this.remove( buttons[i].node );
- }
- // Container
- this.dom.container.remove();
- // Remove from the settings object collection
- var buttonInsts = this.s.dt.settings()[0];
- for ( i=0, ien=buttonInsts.length ; i<ien ; i++ ) {
- if ( buttonInsts.inst === this ) {
- buttonInsts.splice( i, 1 );
- break;
- }
- }
- return this;
- },
- /**
- * Enable / disable a button
- * @param {node} node Button node
- * @param {boolean} [flag=true] Enable / disable flag
- * @return {Buttons} Self for chaining
- */
- enable: function ( node, flag )
- {
- if ( flag === false ) {
- return this.disable( node );
- }
- var button = this._nodeToButton( node );
- $(button.node).removeClass( this.c.dom.button.disabled );
- return this;
- },
- /**
- * Get the instance name for the button set selector
- * @return {string} Instance name
- */
- name: function ()
- {
- return this.c.name;
- },
- /**
- * Get a button's node
- * @param {node} node Button node
- * @return {jQuery} Button element
- */
- node: function ( node )
- {
- var button = this._nodeToButton( node );
- return $(button.node);
- },
- /**
- * Set / get a processing class on the selected button
- * @param {boolean} flag true to add, false to remove, undefined to get
- * @return {boolean|Buttons} Getter value or this if a setter.
- */
- processing: function ( node, flag )
- {
- var button = this._nodeToButton( node );
- if ( flag === undefined ) {
- return $(button.node).hasClass( 'processing' );
- }
- $(button.node).toggleClass( 'processing', flag );
- return this;
- },
- /**
- * Remove a button.
- * @param {node} node Button node
- * @return {Buttons} Self for chaining
- */
- remove: function ( node )
- {
- var button = this._nodeToButton( node );
- var host = this._nodeToHost( node );
- var dt = this.s.dt;
- // Remove any child buttons first
- if ( button.buttons.length ) {
- for ( var i=button.buttons.length-1 ; i>=0 ; i-- ) {
- this.remove( button.buttons[i].node );
- }
- }
- // Allow the button to remove event handlers, etc
- if ( button.conf.destroy ) {
- button.conf.destroy.call( dt.button(node), dt, $(node), button.conf );
- }
- this._removeKey( button.conf );
- $(button.node).remove();
- var idx = $.inArray( button, host );
- host.splice( idx, 1 );
- return this;
- },
- /**
- * Get the text for a button
- * @param {int|string} node Button index
- * @return {string} Button text
- *//**
- * Set the text for a button
- * @param {int|string|function} node Button index
- * @param {string} label Text
- * @return {Buttons} Self for chaining
- */
- text: function ( node, label )
- {
- var button = this._nodeToButton( node );
- var buttonLiner = this.c.dom.collection.buttonLiner;
- var linerTag = button.inCollection && buttonLiner && buttonLiner.tag ?
- buttonLiner.tag :
- this.c.dom.buttonLiner.tag;
- var dt = this.s.dt;
- var jqNode = $(button.node);
- var text = function ( opt ) {
- return typeof opt === 'function' ?
- opt( dt, jqNode, button.conf ) :
- opt;
- };
- if ( label === undefined ) {
- return text( button.conf.text );
- }
- button.conf.text = label;
- if ( linerTag ) {
- jqNode.children( linerTag ).html( text(label) );
- }
- else {
- jqNode.html( text(label) );
- }
- return this;
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constructor
- */
- /**
- * Buttons constructor
- * @private
- */
- _constructor: function ()
- {
- var that = this;
- var dt = this.s.dt;
- var dtSettings = dt.settings()[0];
- var buttons = this.c.buttons;
- if ( ! dtSettings._buttons ) {
- dtSettings._buttons = [];
- }
- dtSettings._buttons.push( {
- inst: this,
- name: this.c.name
- } );
- for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
- this.add( buttons[i] );
- }
- dt.on( 'destroy', function () {
- that.destroy();
- } );
- // Global key event binding to listen for button keys
- $('body').on( 'keyup.'+this.s.namespace, function ( e ) {
- if ( ! document.activeElement || document.activeElement === document.body ) {
- // SUse a string of characters for fast lookup of if we need to
- // handle this
- var character = String.fromCharCode(e.keyCode).toLowerCase();
- if ( that.s.listenKeys.toLowerCase().indexOf( character ) !== -1 ) {
- that._keypress( character, e );
- }
- }
- } );
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Private methods
- */
- /**
- * Add a new button to the key press listener
- * @param {object} conf Resolved button configuration object
- * @private
- */
- _addKey: function ( conf )
- {
- if ( conf.key ) {
- this.s.listenKeys += $.isPlainObject( conf.key ) ?
- conf.key.key :
- conf.key;
- }
- },
- /**
- * Insert the buttons into the container. Call without parameters!
- * @param {node} [container] Recursive only - Insert point
- * @param {array} [buttons] Recursive only - Buttons array
- * @private
- */
- _draw: function ( container, buttons )
- {
- if ( ! container ) {
- container = this.dom.container;
- buttons = this.s.buttons;
- }
- container.children().detach();
- for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
- container.append( buttons[i].inserter );
- container.append( ' ' );
- if ( buttons[i].buttons && buttons[i].buttons.length ) {
- this._draw( buttons[i].collection, buttons[i].buttons );
- }
- }
- },
- /**
- * Create buttons from an array of buttons
- * @param {array} attachTo Buttons array to attach to
- * @param {object} button Button definition
- * @param {boolean} inCollection true if the button is in a collection
- * @private
- */
- _expandButton: function ( attachTo, button, inCollection, attachPoint )
- {
- var dt = this.s.dt;
- var buttonCounter = 0;
- var buttons = ! $.isArray( button ) ?
- [ button ] :
- button;
- for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
- var conf = this._resolveExtends( buttons[i] );
- if ( ! conf ) {
- continue;
- }
- // If the configuration is an array, then expand the buttons at this
- // point
- if ( $.isArray( conf ) ) {
- this._expandButton( attachTo, conf, inCollection, attachPoint );
- continue;
- }
- var built = this._buildButton( conf, inCollection );
- if ( ! built ) {
- continue;
- }
- if ( attachPoint !== undefined ) {
- attachTo.splice( attachPoint, 0, built );
- attachPoint++;
- }
- else {
- attachTo.push( built );
- }
- if ( built.conf.buttons ) {
- var collectionDom = this.c.dom.collection;
- built.collection = $('<'+collectionDom.tag+'/>')
- .addClass( collectionDom.className )
- .attr( 'role', 'menu') ;
- built.conf._collection = built.collection;
- this._expandButton( built.buttons, built.conf.buttons, true, attachPoint );
- }
- // init call is made here, rather than buildButton as it needs to
- // be selectable, and for that it needs to be in the buttons array
- if ( conf.init ) {
- conf.init.call( dt.button( built.node ), dt, $(built.node), conf );
- }
- buttonCounter++;
- }
- },
- /**
- * Create an individual button
- * @param {object} config Resolved button configuration
- * @param {boolean} inCollection `true` if a collection button
- * @return {jQuery} Created button node (jQuery)
- * @private
- */
- _buildButton: function ( config, inCollection )
- {
- var buttonDom = this.c.dom.button;
- var linerDom = this.c.dom.buttonLiner;
- var collectionDom = this.c.dom.collection;
- var dt = this.s.dt;
- var text = function ( opt ) {
- return typeof opt === 'function' ?
- opt( dt, button, config ) :
- opt;
- };
- if ( inCollection && collectionDom.button ) {
- buttonDom = collectionDom.button;
- }
- if ( inCollection && collectionDom.buttonLiner ) {
- linerDom = collectionDom.buttonLiner;
- }
- // Make sure that the button is available based on whatever requirements
- // it has. For example, Flash buttons require Flash
- if ( config.available && ! config.available( dt, config ) ) {
- return false;
- }
- var action = function ( e, dt, button, config ) {
- config.action.call( dt.button( button ), e, dt, button, config );
- $(dt.table().node()).triggerHandler( 'buttons-action.dt', [
- dt.button( button ), dt, button, config
- ] );
- };
- var button = $('<'+buttonDom.tag+'/>')
- .addClass( buttonDom.className )
- .attr( 'tabindex', this.s.dt.settings()[0].iTabIndex )
- .attr( 'aria-controls', this.s.dt.table().node().id )
- .on( 'click.dtb', function (e) {
- e.preventDefault();
- if ( ! button.hasClass( buttonDom.disabled ) && config.action ) {
- action( e, dt, button, config );
- }
- button.blur();
- } )
- .on( 'keyup.dtb', function (e) {
- if ( e.keyCode === 13 ) {
- if ( ! button.hasClass( buttonDom.disabled ) && config.action ) {
- action( e, dt, button, config );
- }
- }
- } );
- // Make `a` tags act like a link
- if ( buttonDom.tag.toLowerCase() === 'a' ) {
- button.attr( 'href', '#' );
- }
- if ( linerDom.tag ) {
- var liner = $('<'+linerDom.tag+'/>')
- .html( text( config.text ) )
- .addClass( linerDom.className );
- if ( linerDom.tag.toLowerCase() === 'a' ) {
- liner.attr( 'href', '#' );
- }
- button.append( liner );
- }
- else {
- button.html( text( config.text ) );
- }
- if ( config.enabled === false ) {
- button.addClass( buttonDom.disabled );
- }
- if ( config.className ) {
- button.addClass( config.className );
- }
- if ( config.titleAttr ) {
- button.attr( 'title', text( config.titleAttr ) );
- }
- if ( config.attr ) {
- button.attr( config.attr );
- }
- if ( ! config.namespace ) {
- config.namespace = '.dt-button-'+(_buttonCounter++);
- }
- var buttonContainer = this.c.dom.buttonContainer;
- var inserter;
- if ( buttonContainer && buttonContainer.tag ) {
- inserter = $('<'+buttonContainer.tag+'/>')
- .addClass( buttonContainer.className )
- .append( button );
- }
- else {
- inserter = button;
- }
- this._addKey( config );
- return {
- conf: config,
- node: button.get(0),
- inserter: inserter,
- buttons: [],
- inCollection: inCollection,
- collection: null
- };
- },
- /**
- * Get the button object from a node (recursive)
- * @param {node} node Button node
- * @param {array} [buttons] Button array, uses base if not defined
- * @return {object} Button object
- * @private
- */
- _nodeToButton: function ( node, buttons )
- {
- if ( ! buttons ) {
- buttons = this.s.buttons;
- }
- for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
- if ( buttons[i].node === node ) {
- return buttons[i];
- }
- if ( buttons[i].buttons.length ) {
- var ret = this._nodeToButton( node, buttons[i].buttons );
- if ( ret ) {
- return ret;
- }
- }
- }
- },
- /**
- * Get container array for a button from a button node (recursive)
- * @param {node} node Button node
- * @param {array} [buttons] Button array, uses base if not defined
- * @return {array} Button's host array
- * @private
- */
- _nodeToHost: function ( node, buttons )
- {
- if ( ! buttons ) {
- buttons = this.s.buttons;
- }
- for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
- if ( buttons[i].node === node ) {
- return buttons;
- }
- if ( buttons[i].buttons.length ) {
- var ret = this._nodeToHost( node, buttons[i].buttons );
- if ( ret ) {
- return ret;
- }
- }
- }
- },
- /**
- * Handle a key press - determine if any button's key configured matches
- * what was typed and trigger the action if so.
- * @param {string} character The character pressed
- * @param {object} e Key event that triggered this call
- * @private
- */
- _keypress: function ( character, e )
- {
- // Check if this button press already activated on another instance of Buttons
- if ( e._buttonsHandled ) {
- return;
- }
- var run = function ( conf, node ) {
- if ( ! conf.key ) {
- return;
- }
- if ( conf.key === character ) {
- e._buttonsHandled = true;
- $(node).click();
- }
- else if ( $.isPlainObject( conf.key ) ) {
- if ( conf.key.key !== character ) {
- return;
- }
- if ( conf.key.shiftKey && ! e.shiftKey ) {
- return;
- }
- if ( conf.key.altKey && ! e.altKey ) {
- return;
- }
- if ( conf.key.ctrlKey && ! e.ctrlKey ) {
- return;
- }
- if ( conf.key.metaKey && ! e.metaKey ) {
- return;
- }
- // Made it this far - it is good
- e._buttonsHandled = true;
- $(node).click();
- }
- };
- var recurse = function ( a ) {
- for ( var i=0, ien=a.length ; i<ien ; i++ ) {
- run( a[i].conf, a[i].node );
- if ( a[i].buttons.length ) {
- recurse( a[i].buttons );
- }
- }
- };
- recurse( this.s.buttons );
- },
- /**
- * Remove a key from the key listener for this instance (to be used when a
- * button is removed)
- * @param {object} conf Button configuration
- * @private
- */
- _removeKey: function ( conf )
- {
- if ( conf.key ) {
- var character = $.isPlainObject( conf.key ) ?
- conf.key.key :
- conf.key;
- // Remove only one character, as multiple buttons could have the
- // same listening key
- var a = this.s.listenKeys.split('');
- var idx = $.inArray( character, a );
- a.splice( idx, 1 );
- this.s.listenKeys = a.join('');
- }
- },
- /**
- * Resolve a button configuration
- * @param {string|function|object} conf Button config to resolve
- * @return {object} Button configuration
- * @private
- */
- _resolveExtends: function ( conf )
- {
- var dt = this.s.dt;
- var i, ien;
- var toConfObject = function ( base ) {
- var loop = 0;
- // Loop until we have resolved to a button configuration, or an
- // array of button configurations (which will be iterated
- // separately)
- while ( ! $.isPlainObject(base) && ! $.isArray(base) ) {
- if ( base === undefined ) {
- return;
- }
- if ( typeof base === 'function' ) {
- base = base( dt, conf );
- if ( ! base ) {
- return false;
- }
- }
- else if ( typeof base === 'string' ) {
- if ( ! _dtButtons[ base ] ) {
- throw 'Unknown button type: '+base;
- }
- base = _dtButtons[ base ];
- }
- loop++;
- if ( loop > 30 ) {
- // Protect against misconfiguration killing the browser
- throw 'Buttons: Too many iterations';
- }
- }
- return $.isArray( base ) ?
- base :
- $.extend( {}, base );
- };
- conf = toConfObject( conf );
- while ( conf && conf.extend ) {
- // Use `toConfObject` in case the button definition being extended
- // is itself a string or a function
- if ( ! _dtButtons[ conf.extend ] ) {
- throw 'Cannot extend unknown button type: '+conf.extend;
- }
- var objArray = toConfObject( _dtButtons[ conf.extend ] );
- if ( $.isArray( objArray ) ) {
- return objArray;
- }
- else if ( ! objArray ) {
- // This is a little brutal as it might be possible to have a
- // valid button without the extend, but if there is no extend
- // then the host button would be acting in an undefined state
- return false;
- }
- // Stash the current class name
- var originalClassName = objArray.className;
- conf = $.extend( {}, objArray, conf );
- // The extend will have overwritten the original class name if the
- // `conf` object also assigned a class, but we want to concatenate
- // them so they are list that is combined from all extended buttons
- if ( originalClassName && conf.className !== originalClassName ) {
- conf.className = originalClassName+' '+conf.className;
- }
- // Buttons to be added to a collection -gives the ability to define
- // if buttons should be added to the start or end of a collection
- var postfixButtons = conf.postfixButtons;
- if ( postfixButtons ) {
- if ( ! conf.buttons ) {
- conf.buttons = [];
- }
- for ( i=0, ien=postfixButtons.length ; i<ien ; i++ ) {
- conf.buttons.push( postfixButtons[i] );
- }
- conf.postfixButtons = null;
- }
- var prefixButtons = conf.prefixButtons;
- if ( prefixButtons ) {
- if ( ! conf.buttons ) {
- conf.buttons = [];
- }
- for ( i=0, ien=prefixButtons.length ; i<ien ; i++ ) {
- conf.buttons.splice( i, 0, prefixButtons[i] );
- }
- conf.prefixButtons = null;
- }
- // Although we want the `conf` object to overwrite almost all of
- // the properties of the object being extended, the `extend`
- // property should come from the object being extended
- conf.extend = objArray.extend;
- }
- return conf;
- }
- } );
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Statics
- */
- /**
- * Show / hide a background layer behind a collection
- * @param {boolean} Flag to indicate if the background should be shown or
- * hidden
- * @param {string} Class to assign to the background
- * @static
- */
- Buttons.background = function ( show, className, fade ) {
- if ( fade === undefined ) {
- fade = 400;
- }
- if ( show ) {
- $('<div/>')
- .addClass( className )
- .css( 'display', 'none' )
- .appendTo( 'body' )
- .fadeIn( fade );
- }
- else {
- $('body > div.'+className)
- .fadeOut( fade, function () {
- $(this)
- .removeClass( className )
- .remove();
- } );
- }
- };
- /**
- * Instance selector - select Buttons instances based on an instance selector
- * value from the buttons assigned to a DataTable. This is only useful if
- * multiple instances are attached to a DataTable.
- * @param {string|int|array} Instance selector - see `instance-selector`
- * documentation on the DataTables site
- * @param {array} Button instance array that was attached to the DataTables
- * settings object
- * @return {array} Buttons instances
- * @static
- */
- Buttons.instanceSelector = function ( group, buttons )
- {
- if ( ! group ) {
- return $.map( buttons, function ( v ) {
- return v.inst;
- } );
- }
- var ret = [];
- var names = $.map( buttons, function ( v ) {
- return v.name;
- } );
- // Flatten the group selector into an array of single options
- var process = function ( input ) {
- if ( $.isArray( input ) ) {
- for ( var i=0, ien=input.length ; i<ien ; i++ ) {
- process( input[i] );
- }
- return;
- }
- if ( typeof input === 'string' ) {
- if ( input.indexOf( ',' ) !== -1 ) {
- // String selector, list of names
- process( input.split(',') );
- }
- else {
- // String selector individual name
- var idx = $.inArray( $.trim(input), names );
- if ( idx !== -1 ) {
- ret.push( buttons[ idx ].inst );
- }
- }
- }
- else if ( typeof input === 'number' ) {
- // Index selector
- ret.push( buttons[ input ].inst );
- }
- };
-
- process( group );
- return ret;
- };
- /**
- * Button selector - select one or more buttons from a selector input so some
- * operation can be performed on them.
- * @param {array} Button instances array that the selector should operate on
- * @param {string|int|node|jQuery|array} Button selector - see
- * `button-selector` documentation on the DataTables site
- * @return {array} Array of objects containing `inst` and `idx` properties of
- * the selected buttons so you know which instance each button belongs to.
- * @static
- */
- Buttons.buttonSelector = function ( insts, selector )
- {
- var ret = [];
- var nodeBuilder = function ( a, buttons, baseIdx ) {
- var button;
- var idx;
- for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
- button = buttons[i];
- if ( button ) {
- idx = baseIdx !== undefined ?
- baseIdx+i :
- i+'';
- a.push( {
- node: button.node,
- name: button.conf.name,
- idx: idx
- } );
- if ( button.buttons ) {
- nodeBuilder( a, button.buttons, idx+'-' );
- }
- }
- }
- };
- var run = function ( selector, inst ) {
- var i, ien;
- var buttons = [];
- nodeBuilder( buttons, inst.s.buttons );
- var nodes = $.map( buttons, function (v) {
- return v.node;
- } );
- if ( $.isArray( selector ) || selector instanceof $ ) {
- for ( i=0, ien=selector.length ; i<ien ; i++ ) {
- run( selector[i], inst );
- }
- return;
- }
- if ( selector === null || selector === undefined || selector === '*' ) {
- // Select all
- for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
- ret.push( {
- inst: inst,
- node: buttons[i].node
- } );
- }
- }
- else if ( typeof selector === 'number' ) {
- // Main button index selector
- ret.push( {
- inst: inst,
- node: inst.s.buttons[ selector ].node
- } );
- }
- else if ( typeof selector === 'string' ) {
- if ( selector.indexOf( ',' ) !== -1 ) {
- // Split
- var a = selector.split(',');
- for ( i=0, ien=a.length ; i<ien ; i++ ) {
- run( $.trim(a[i]), inst );
- }
- }
- else if ( selector.match( /^\d+(\-\d+)*$/ ) ) {
- // Sub-button index selector
- var indexes = $.map( buttons, function (v) {
- return v.idx;
- } );
- ret.push( {
- inst: inst,
- node: buttons[ $.inArray( selector, indexes ) ].node
- } );
- }
- else if ( selector.indexOf( ':name' ) !== -1 ) {
- // Button name selector
- var name = selector.replace( ':name', '' );
- for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
- if ( buttons[i].name === name ) {
- ret.push( {
- inst: inst,
- node: buttons[i].node
- } );
- }
- }
- }
- else {
- // jQuery selector on the nodes
- $( nodes ).filter( selector ).each( function () {
- ret.push( {
- inst: inst,
- node: this
- } );
- } );
- }
- }
- else if ( typeof selector === 'object' && selector.nodeName ) {
- // Node selector
- var idx = $.inArray( selector, nodes );
- if ( idx !== -1 ) {
- ret.push( {
- inst: inst,
- node: nodes[ idx ]
- } );
- }
- }
- };
- for ( var i=0, ien=insts.length ; i<ien ; i++ ) {
- var inst = insts[i];
- run( selector, inst );
- }
- return ret;
- };
- /**
- * Buttons defaults. For full documentation, please refer to the docs/option
- * directory or the DataTables site.
- * @type {Object}
- * @static
- */
- Buttons.defaults = {
- buttons: [ 'copy', 'excel', 'csv', 'pdf', 'print' ],
- name: 'main',
- tabIndex: 0,
- dom: {
- container: {
- tag: 'div',
- className: 'dt-buttons'
- },
- collection: {
- tag: 'div',
- className: 'dt-button-collection'
- },
- button: {
- tag: 'button',
- className: 'dt-button',
- active: 'active',
- disabled: 'disabled'
- },
- buttonLiner: {
- tag: 'span',
- className: ''
- }
- }
- };
- /**
- * Version information
- * @type {string}
- * @static
- */
- Buttons.version = '1.5.1';
- $.extend( _dtButtons, {
- collection: {
- text: function ( dt ) {
- return dt.i18n( 'buttons.collection', 'Collection' );
- },
- className: 'buttons-collection',
- action: function ( e, dt, button, config ) {
- var host = button;
- var collectionParent = $(button).parents('div.dt-button-collection');
- var hostPosition = host.position();
- var tableContainer = $( dt.table().container() );
- var multiLevel = false;
- var insertPoint = host;
- // Remove any old collection
- if ( collectionParent.length ) {
- multiLevel = $('.dt-button-collection').position();
- insertPoint = collectionParent;
- $('body').trigger( 'click.dtb-collection' );
- }
- config._collection
- .addClass( config.collectionLayout )
- .css( 'display', 'none' )
- .insertAfter( insertPoint )
- .fadeIn( config.fade );
-
- var position = config._collection.css( 'position' );
- if ( multiLevel && position === 'absolute' ) {
- config._collection.css( {
- top: multiLevel.top,
- left: multiLevel.left
- } );
- }
- else if ( position === 'absolute' ) {
- config._collection.css( {
- top: hostPosition.top + host.outerHeight(),
- left: hostPosition.left
- } );
- // calculate overflow when positioned beneath
- var tableBottom = tableContainer.offset().top + tableContainer.height();
- var listBottom = hostPosition.top + host.outerHeight() + config._collection.outerHeight();
- var bottomOverflow = listBottom - tableBottom;
-
- // calculate overflow when positioned above
- var listTop = hostPosition.top - config._collection.outerHeight();
- var tableTop = tableContainer.offset().top;
- var topOverflow = tableTop - listTop;
-
- // if bottom overflow is larger, move to the top because it fits better
- if (bottomOverflow > topOverflow) {
- config._collection.css( 'top', hostPosition.top - config._collection.outerHeight() - 5);
- }
- var listRight = hostPosition.left + config._collection.outerWidth();
- var tableRight = tableContainer.offset().left + tableContainer.width();
- if ( listRight > tableRight ) {
- config._collection.css( 'left', hostPosition.left - ( listRight - tableRight ) );
- }
- }
- else {
- // Fix position - centre on screen
- var top = config._collection.height() / 2;
- if ( top > $(window).height() / 2 ) {
- top = $(window).height() / 2;
- }
- config._collection.css( 'marginTop', top*-1 );
- }
- if ( config.background ) {
- Buttons.background( true, config.backgroundClassName, config.fade );
- }
- // Need to break the 'thread' for the collection button being
- // activated by a click - it would also trigger this event
- setTimeout( function () {
- // This is bonkers, but if we don't have a click listener on the
- // background element, iOS Safari will ignore the body click
- // listener below. An empty function here is all that is
- // required to make it work...
- $('div.dt-button-background').on( 'click.dtb-collection', function () {} );
- $('body').on( 'click.dtb-collection', function (e) {
- // andSelf is deprecated in jQ1.8, but we want 1.7 compat
- var back = $.fn.addBack ? 'addBack' : 'andSelf';
- if ( ! $(e.target).parents()[back]().filter( config._collection ).length ) {
- config._collection
- .fadeOut( config.fade, function () {
- config._collection.detach();
- } );
- $('div.dt-button-background').off( 'click.dtb-collection' );
- Buttons.background( false, config.backgroundClassName, config.fade );
- $('body').off( 'click.dtb-collection' );
- dt.off( 'buttons-action.b-internal' );
- }
- } );
- }, 10 );
- if ( config.autoClose ) {
- dt.on( 'buttons-action.b-internal', function () {
- $('div.dt-button-background').click();
- } );
- }
- },
- background: true,
- collectionLayout: '',
- backgroundClassName: 'dt-button-background',
- autoClose: false,
- fade: 400,
- attr: {
- 'aria-haspopup': true
- }
- },
- copy: function ( dt, conf ) {
- if ( _dtButtons.copyHtml5 ) {
- return 'copyHtml5';
- }
- if ( _dtButtons.copyFlash && _dtButtons.copyFlash.available( dt, conf ) ) {
- return 'copyFlash';
- }
- },
- csv: function ( dt, conf ) {
- // Common option that will use the HTML5 or Flash export buttons
- if ( _dtButtons.csvHtml5 && _dtButtons.csvHtml5.available( dt, conf ) ) {
- return 'csvHtml5';
- }
- if ( _dtButtons.csvFlash && _dtButtons.csvFlash.available( dt, conf ) ) {
- return 'csvFlash';
- }
- },
- excel: function ( dt, conf ) {
- // Common option that will use the HTML5 or Flash export buttons
- if ( _dtButtons.excelHtml5 && _dtButtons.excelHtml5.available( dt, conf ) ) {
- return 'excelHtml5';
- }
- if ( _dtButtons.excelFlash && _dtButtons.excelFlash.available( dt, conf ) ) {
- return 'excelFlash';
- }
- },
- pdf: function ( dt, conf ) {
- // Common option that will use the HTML5 or Flash export buttons
- if ( _dtButtons.pdfHtml5 && _dtButtons.pdfHtml5.available( dt, conf ) ) {
- return 'pdfHtml5';
- }
- if ( _dtButtons.pdfFlash && _dtButtons.pdfFlash.available( dt, conf ) ) {
- return 'pdfFlash';
- }
- },
- pageLength: function ( dt ) {
- var lengthMenu = dt.settings()[0].aLengthMenu;
- var vals = $.isArray( lengthMenu[0] ) ? lengthMenu[0] : lengthMenu;
- var lang = $.isArray( lengthMenu[0] ) ? lengthMenu[1] : lengthMenu;
- var text = function ( dt ) {
- return dt.i18n( 'buttons.pageLength', {
- "-1": 'Show all rows',
- _: 'Show %d rows'
- }, dt.page.len() );
- };
- return {
- extend: 'collection',
- text: text,
- className: 'buttons-page-length',
- autoClose: true,
- buttons: $.map( vals, function ( val, i ) {
- return {
- text: lang[i],
- className: 'button-page-length',
- action: function ( e, dt ) {
- dt.page.len( val ).draw();
- },
- init: function ( dt, node, conf ) {
- var that = this;
- var fn = function () {
- that.active( dt.page.len() === val );
- };
- dt.on( 'length.dt'+conf.namespace, fn );
- fn();
- },
- destroy: function ( dt, node, conf ) {
- dt.off( 'length.dt'+conf.namespace );
- }
- };
- } ),
- init: function ( dt, node, conf ) {
- var that = this;
- dt.on( 'length.dt'+conf.namespace, function () {
- that.text( text( dt ) );
- } );
- },
- destroy: function ( dt, node, conf ) {
- dt.off( 'length.dt'+conf.namespace );
- }
- };
- }
- } );
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables API
- *
- * For complete documentation, please refer to the docs/api directory or the
- * DataTables site
- */
- // Buttons group and individual button selector
- DataTable.Api.register( 'buttons()', function ( group, selector ) {
- // Argument shifting
- if ( selector === undefined ) {
- selector = group;
- group = undefined;
- }
- this.selector.buttonGroup = group;
- var res = this.iterator( true, 'table', function ( ctx ) {
- if ( ctx._buttons ) {
- return Buttons.buttonSelector(
- Buttons.instanceSelector( group, ctx._buttons ),
- selector
- );
- }
- }, true );
- res._groupSelector = group;
- return res;
- } );
- // Individual button selector
- DataTable.Api.register( 'button()', function ( group, selector ) {
- // just run buttons() and truncate
- var buttons = this.buttons( group, selector );
- if ( buttons.length > 1 ) {
- buttons.splice( 1, buttons.length );
- }
- return buttons;
- } );
- // Active buttons
- DataTable.Api.registerPlural( 'buttons().active()', 'button().active()', function ( flag ) {
- if ( flag === undefined ) {
- return this.map( function ( set ) {
- return set.inst.active( set.node );
- } );
- }
- return this.each( function ( set ) {
- set.inst.active( set.node, flag );
- } );
- } );
- // Get / set button action
- DataTable.Api.registerPlural( 'buttons().action()', 'button().action()', function ( action ) {
- if ( action === undefined ) {
- return this.map( function ( set ) {
- return set.inst.action( set.node );
- } );
- }
- return this.each( function ( set ) {
- set.inst.action( set.node, action );
- } );
- } );
- // Enable / disable buttons
- DataTable.Api.register( ['buttons().enable()', 'button().enable()'], function ( flag ) {
- return this.each( function ( set ) {
- set.inst.enable( set.node, flag );
- } );
- } );
- // Disable buttons
- DataTable.Api.register( ['buttons().disable()', 'button().disable()'], function () {
- return this.each( function ( set ) {
- set.inst.disable( set.node );
- } );
- } );
- // Get button nodes
- DataTable.Api.registerPlural( 'buttons().nodes()', 'button().node()', function () {
- var jq = $();
- // jQuery will automatically reduce duplicates to a single entry
- $( this.each( function ( set ) {
- jq = jq.add( set.inst.node( set.node ) );
- } ) );
- return jq;
- } );
- // Get / set button processing state
- DataTable.Api.registerPlural( 'buttons().processing()', 'button().processing()', function ( flag ) {
- if ( flag === undefined ) {
- return this.map( function ( set ) {
- return set.inst.processing( set.node );
- } );
- }
- return this.each( function ( set ) {
- set.inst.processing( set.node, flag );
- } );
- } );
- // Get / set button text (i.e. the button labels)
- DataTable.Api.registerPlural( 'buttons().text()', 'button().text()', function ( label ) {
- if ( label === undefined ) {
- return this.map( function ( set ) {
- return set.inst.text( set.node );
- } );
- }
- return this.each( function ( set ) {
- set.inst.text( set.node, label );
- } );
- } );
- // Trigger a button's action
- DataTable.Api.registerPlural( 'buttons().trigger()', 'button().trigger()', function () {
- return this.each( function ( set ) {
- set.inst.node( set.node ).trigger( 'click' );
- } );
- } );
- // Get the container elements
- DataTable.Api.registerPlural( 'buttons().containers()', 'buttons().container()', function () {
- var jq = $();
- var groupSelector = this._groupSelector;
- // We need to use the group selector directly, since if there are no buttons
- // the result set will be empty
- this.iterator( true, 'table', function ( ctx ) {
- if ( ctx._buttons ) {
- var insts = Buttons.instanceSelector( groupSelector, ctx._buttons );
- for ( var i=0, ien=insts.length ; i<ien ; i++ ) {
- jq = jq.add( insts[i].container() );
- }
- }
- } );
- return jq;
- } );
- // Add a new button
- DataTable.Api.register( 'button().add()', function ( idx, conf ) {
- var ctx = this.context;
- // Don't use `this` as it could be empty - select the instances directly
- if ( ctx.length ) {
- var inst = Buttons.instanceSelector( this._groupSelector, ctx[0]._buttons );
- if ( inst.length ) {
- inst[0].add( conf, idx );
- }
- }
- return this.button( this._groupSelector, idx );
- } );
- // Destroy the button sets selected
- DataTable.Api.register( 'buttons().destroy()', function () {
- this.pluck( 'inst' ).unique().each( function ( inst ) {
- inst.destroy();
- } );
- return this;
- } );
- // Remove a button
- DataTable.Api.registerPlural( 'buttons().remove()', 'buttons().remove()', function () {
- this.each( function ( set ) {
- set.inst.remove( set.node );
- } );
- return this;
- } );
- // Information box that can be used by buttons
- var _infoTimer;
- DataTable.Api.register( 'buttons.info()', function ( title, message, time ) {
- var that = this;
- if ( title === false ) {
- $('#datatables_buttons_info').fadeOut( function () {
- $(this).remove();
- } );
- clearTimeout( _infoTimer );
- _infoTimer = null;
- return this;
- }
- if ( _infoTimer ) {
- clearTimeout( _infoTimer );
- }
- if ( $('#datatables_buttons_info').length ) {
- $('#datatables_buttons_info').remove();
- }
- title = title ? '<h2>'+title+'</h2>' : '';
- $('<div id="datatables_buttons_info" class="dt-button-info"/>')
- .html( title )
- .append( $('<div/>')[ typeof message === 'string' ? 'html' : 'append' ]( message ) )
- .css( 'display', 'none' )
- .appendTo( 'body' )
- .fadeIn();
- if ( time !== undefined && time !== 0 ) {
- _infoTimer = setTimeout( function () {
- that.buttons.info( false );
- }, time );
- }
- return this;
- } );
- // Get data from the table for export - this is common to a number of plug-in
- // buttons so it is included in the Buttons core library
- DataTable.Api.register( 'buttons.exportData()', function ( options ) {
- if ( this.context.length ) {
- return _exportData( new DataTable.Api( this.context[0] ), options );
- }
- } );
- // Get information about the export that is common to many of the export data
- // types (DRY)
- DataTable.Api.register( 'buttons.exportInfo()', function ( conf ) {
- if ( ! conf ) {
- conf = {};
- }
- return {
- filename: _filename( conf ),
- title: _title( conf ),
- messageTop: _message(this, conf.message || conf.messageTop, 'top'),
- messageBottom: _message(this, conf.messageBottom, 'bottom')
- };
- } );
- /**
- * Get the file name for an exported file.
- *
- * @param {object} config Button configuration
- * @param {boolean} incExtension Include the file name extension
- */
- var _filename = function ( config )
- {
- // Backwards compatibility
- var filename = config.filename === '*' && config.title !== '*' && config.title !== undefined && config.title !== null && config.title !== '' ?
- config.title :
- config.filename;
- if ( typeof filename === 'function' ) {
- filename = filename();
- }
- if ( filename === undefined || filename === null ) {
- return null;
- }
- if ( filename.indexOf( '*' ) !== -1 ) {
- filename = $.trim( filename.replace( '*', $('head > title').text() ) );
- }
- // Strip characters which the OS will object to
- filename = filename.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, "");
- var extension = _stringOrFunction( config.extension );
- if ( ! extension ) {
- extension = '';
- }
- return filename + extension;
- };
- /**
- * Simply utility method to allow parameters to be given as a function
- *
- * @param {undefined|string|function} option Option
- * @return {null|string} Resolved value
- */
- var _stringOrFunction = function ( option )
- {
- if ( option === null || option === undefined ) {
- return null;
- }
- else if ( typeof option === 'function' ) {
- return option();
- }
- return option;
- };
- /**
- * Get the title for an exported file.
- *
- * @param {object} config Button configuration
- */
- var _title = function ( config )
- {
- var title = _stringOrFunction( config.title );
- return title === null ?
- null : title.indexOf( '*' ) !== -1 ?
- title.replace( '*', $('head > title').text() || 'Exported data' ) :
- title;
- };
- var _message = function ( dt, option, position )
- {
- var message = _stringOrFunction( option );
- if ( message === null ) {
- return null;
- }
- var caption = $('caption', dt.table().container()).eq(0);
- if ( message === '*' ) {
- var side = caption.css( 'caption-side' );
- if ( side !== position ) {
- return null;
- }
- return caption.length ?
- caption.text() :
- '';
- }
- return message;
- };
- var _exportTextarea = $('<textarea/>')[0];
- var _exportData = function ( dt, inOpts )
- {
- var config = $.extend( true, {}, {
- rows: null,
- columns: '',
- modifier: {
- search: 'applied',
- order: 'applied'
- },
- orthogonal: 'display',
- stripHtml: true,
- stripNewlines: true,
- decodeEntities: true,
- trim: true,
- format: {
- header: function ( d ) {
- return strip( d );
- },
- footer: function ( d ) {
- return strip( d );
- },
- body: function ( d ) {
- return strip( d );
- }
- }
- }, inOpts );
- var strip = function ( str ) {
- if ( typeof str !== 'string' ) {
- return str;
- }
- // Always remove script tags
- str = str.replace( /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '' );
- if ( config.stripHtml ) {
- str = str.replace( /<[^>]*>/g, '' );
- }
- if ( config.trim ) {
- str = str.replace( /^\s+|\s+$/g, '' );
- }
- if ( config.stripNewlines ) {
- str = str.replace( /\n/g, ' ' );
- }
- if ( config.decodeEntities ) {
- _exportTextarea.innerHTML = str;
- str = _exportTextarea.value;
- }
- return str;
- };
- var header = dt.columns( config.columns ).indexes().map( function (idx) {
- var el = dt.column( idx ).header();
- return config.format.header( el.innerHTML, idx, el );
- } ).toArray();
- var footer = dt.table().footer() ?
- dt.columns( config.columns ).indexes().map( function (idx) {
- var el = dt.column( idx ).footer();
- return config.format.footer( el ? el.innerHTML : '', idx, el );
- } ).toArray() :
- null;
-
- // If Select is available on this table, and any rows are selected, limit the export
- // to the selected rows. If no rows are selected, all rows will be exported. Specify
- // a `selected` modifier to control directly.
- var modifier = $.extend( {}, config.modifier );
- if ( dt.select && typeof dt.select.info === 'function' && modifier.selected === undefined ) {
- if ( dt.rows( config.rows, $.extend( { selected: true }, modifier ) ).any() ) {
- $.extend( modifier, { selected: true } )
- }
- }
- var rowIndexes = dt.rows( config.rows, modifier ).indexes().toArray();
- var selectedCells = dt.cells( rowIndexes, config.columns );
- var cells = selectedCells
- .render( config.orthogonal )
- .toArray();
- var cellNodes = selectedCells
- .nodes()
- .toArray();
- var columns = header.length;
- var rows = columns > 0 ? cells.length / columns : 0;
- var body = [ rows ];
- var cellCounter = 0;
- for ( var i=0, ien=rows ; i<ien ; i++ ) {
- var row = [ columns ];
- for ( var j=0 ; j<columns ; j++ ) {
- row[j] = config.format.body( cells[ cellCounter ], i, j, cellNodes[ cellCounter ] );
- cellCounter++;
- }
- body[i] = row;
- }
- return {
- header: header,
- footer: footer,
- body: body
- };
- };
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables interface
- */
- // Attach to DataTables objects for global access
- $.fn.dataTable.Buttons = Buttons;
- $.fn.DataTable.Buttons = Buttons;
- // DataTables creation - check if the buttons have been defined for this table,
- // they will have been if the `B` option was used in `dom`, otherwise we should
- // create the buttons instance here so they can be inserted into the document
- // using the API. Listen for `init` for compatibility with pre 1.10.10, but to
- // be removed in future.
- $(document).on( 'init.dt plugin-init.dt', function (e, settings) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- var opts = settings.oInit.buttons || DataTable.defaults.buttons;
- if ( opts && ! settings._buttons ) {
- new Buttons( settings, opts ).container();
- }
- } );
- // DataTables `dom` feature option
- DataTable.ext.feature.push( {
- fnInit: function( settings ) {
- var api = new DataTable.Api( settings );
- var opts = api.init().buttons || DataTable.defaults.buttons;
- return new Buttons( api, opts ).container();
- },
- cFeature: "B"
- } );
- return Buttons;
- }));
- /*! ColReorder 1.4.1
- * ©2010-2017 SpryMedia Ltd - datatables.net/license
- */
- /**
- * @summary ColReorder
- * @description Provide the ability to reorder columns in a DataTable
- * @version 1.4.1
- * @file dataTables.colReorder.js
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
- * @contact www.sprymedia.co.uk/contact
- * @copyright Copyright 2010-2017 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license/mit
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- /**
- * Switch the key value pairing of an index array to be value key (i.e. the old value is now the
- * key). For example consider [ 2, 0, 1 ] this would be returned as [ 1, 2, 0 ].
- * @method fnInvertKeyValues
- * @param array aIn Array to switch around
- * @returns array
- */
- function fnInvertKeyValues( aIn )
- {
- var aRet=[];
- for ( var i=0, iLen=aIn.length ; i<iLen ; i++ )
- {
- aRet[ aIn[i] ] = i;
- }
- return aRet;
- }
- /**
- * Modify an array by switching the position of two elements
- * @method fnArraySwitch
- * @param array aArray Array to consider, will be modified by reference (i.e. no return)
- * @param int iFrom From point
- * @param int iTo Insert point
- * @returns void
- */
- function fnArraySwitch( aArray, iFrom, iTo )
- {
- var mStore = aArray.splice( iFrom, 1 )[0];
- aArray.splice( iTo, 0, mStore );
- }
- /**
- * Switch the positions of nodes in a parent node (note this is specifically designed for
- * table rows). Note this function considers all element nodes under the parent!
- * @method fnDomSwitch
- * @param string sTag Tag to consider
- * @param int iFrom Element to move
- * @param int Point to element the element to (before this point), can be null for append
- * @returns void
- */
- function fnDomSwitch( nParent, iFrom, iTo )
- {
- var anTags = [];
- for ( var i=0, iLen=nParent.childNodes.length ; i<iLen ; i++ )
- {
- if ( nParent.childNodes[i].nodeType == 1 )
- {
- anTags.push( nParent.childNodes[i] );
- }
- }
- var nStore = anTags[ iFrom ];
- if ( iTo !== null )
- {
- nParent.insertBefore( nStore, anTags[iTo] );
- }
- else
- {
- nParent.appendChild( nStore );
- }
- }
- /**
- * Plug-in for DataTables which will reorder the internal column structure by taking the column
- * from one position (iFrom) and insert it into a given point (iTo).
- * @method $.fn.dataTableExt.oApi.fnColReorder
- * @param object oSettings DataTables settings object - automatically added by DataTables!
- * @param int iFrom Take the column to be repositioned from this point
- * @param int iTo and insert it into this point
- * @param bool drop Indicate if the reorder is the final one (i.e. a drop)
- * not a live reorder
- * @param bool invalidateRows speeds up processing if false passed
- * @returns void
- */
- $.fn.dataTableExt.oApi.fnColReorder = function ( oSettings, iFrom, iTo, drop, invalidateRows )
- {
- var i, iLen, j, jLen, jen, iCols=oSettings.aoColumns.length, nTrs, oCol;
- var attrMap = function ( obj, prop, mapping ) {
- if ( ! obj[ prop ] || typeof obj[ prop ] === 'function' ) {
- return;
- }
- var a = obj[ prop ].split('.');
- var num = a.shift();
- if ( isNaN( num*1 ) ) {
- return;
- }
- obj[ prop ] = mapping[ num*1 ]+'.'+a.join('.');
- };
- /* Sanity check in the input */
- if ( iFrom == iTo )
- {
- /* Pointless reorder */
- return;
- }
- if ( iFrom < 0 || iFrom >= iCols )
- {
- this.oApi._fnLog( oSettings, 1, "ColReorder 'from' index is out of bounds: "+iFrom );
- return;
- }
- if ( iTo < 0 || iTo >= iCols )
- {
- this.oApi._fnLog( oSettings, 1, "ColReorder 'to' index is out of bounds: "+iTo );
- return;
- }
- /*
- * Calculate the new column array index, so we have a mapping between the old and new
- */
- var aiMapping = [];
- for ( i=0, iLen=iCols ; i<iLen ; i++ )
- {
- aiMapping[i] = i;
- }
- fnArraySwitch( aiMapping, iFrom, iTo );
- var aiInvertMapping = fnInvertKeyValues( aiMapping );
- /*
- * Convert all internal indexing to the new column order indexes
- */
- /* Sorting */
- for ( i=0, iLen=oSettings.aaSorting.length ; i<iLen ; i++ )
- {
- oSettings.aaSorting[i][0] = aiInvertMapping[ oSettings.aaSorting[i][0] ];
- }
- /* Fixed sorting */
- if ( oSettings.aaSortingFixed !== null )
- {
- for ( i=0, iLen=oSettings.aaSortingFixed.length ; i<iLen ; i++ )
- {
- oSettings.aaSortingFixed[i][0] = aiInvertMapping[ oSettings.aaSortingFixed[i][0] ];
- }
- }
- /* Data column sorting (the column which the sort for a given column should take place on) */
- for ( i=0, iLen=iCols ; i<iLen ; i++ )
- {
- oCol = oSettings.aoColumns[i];
- for ( j=0, jLen=oCol.aDataSort.length ; j<jLen ; j++ )
- {
- oCol.aDataSort[j] = aiInvertMapping[ oCol.aDataSort[j] ];
- }
- // Update the column indexes
- oCol.idx = aiInvertMapping[ oCol.idx ];
- }
- // Update 1.10 optimised sort class removal variable
- $.each( oSettings.aLastSort, function (i, val) {
- oSettings.aLastSort[i].src = aiInvertMapping[ val.src ];
- } );
- /* Update the Get and Set functions for each column */
- for ( i=0, iLen=iCols ; i<iLen ; i++ )
- {
- oCol = oSettings.aoColumns[i];
- if ( typeof oCol.mData == 'number' ) {
- oCol.mData = aiInvertMapping[ oCol.mData ];
- }
- else if ( $.isPlainObject( oCol.mData ) ) {
- // HTML5 data sourced
- attrMap( oCol.mData, '_', aiInvertMapping );
- attrMap( oCol.mData, 'filter', aiInvertMapping );
- attrMap( oCol.mData, 'sort', aiInvertMapping );
- attrMap( oCol.mData, 'type', aiInvertMapping );
- }
- }
- /*
- * Move the DOM elements
- */
- if ( oSettings.aoColumns[iFrom].bVisible )
- {
- /* Calculate the current visible index and the point to insert the node before. The insert
- * before needs to take into account that there might not be an element to insert before,
- * in which case it will be null, and an appendChild should be used
- */
- var iVisibleIndex = this.oApi._fnColumnIndexToVisible( oSettings, iFrom );
- var iInsertBeforeIndex = null;
- i = iTo < iFrom ? iTo : iTo + 1;
- while ( iInsertBeforeIndex === null && i < iCols )
- {
- iInsertBeforeIndex = this.oApi._fnColumnIndexToVisible( oSettings, i );
- i++;
- }
- /* Header */
- nTrs = oSettings.nTHead.getElementsByTagName('tr');
- for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
- {
- fnDomSwitch( nTrs[i], iVisibleIndex, iInsertBeforeIndex );
- }
- /* Footer */
- if ( oSettings.nTFoot !== null )
- {
- nTrs = oSettings.nTFoot.getElementsByTagName('tr');
- for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
- {
- fnDomSwitch( nTrs[i], iVisibleIndex, iInsertBeforeIndex );
- }
- }
- /* Body */
- for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
- {
- if ( oSettings.aoData[i].nTr !== null )
- {
- fnDomSwitch( oSettings.aoData[i].nTr, iVisibleIndex, iInsertBeforeIndex );
- }
- }
- }
- /*
- * Move the internal array elements
- */
- /* Columns */
- fnArraySwitch( oSettings.aoColumns, iFrom, iTo );
- // regenerate the get / set functions
- for ( i=0, iLen=iCols ; i<iLen ; i++ ) {
- oSettings.oApi._fnColumnOptions( oSettings, i, {} );
- }
- /* Search columns */
- fnArraySwitch( oSettings.aoPreSearchCols, iFrom, iTo );
- /* Array array - internal data anodes cache */
- for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
- {
- var data = oSettings.aoData[i];
- var cells = data.anCells;
- if ( cells ) {
- fnArraySwitch( cells, iFrom, iTo );
- // Longer term, should this be moved into the DataTables' invalidate
- // methods?
- for ( j=0, jen=cells.length ; j<jen ; j++ ) {
- if ( cells[j] && cells[j]._DT_CellIndex ) {
- cells[j]._DT_CellIndex.column = j;
- }
- }
- }
- // For DOM sourced data, the invalidate will reread the cell into
- // the data array, but for data sources as an array, they need to
- // be flipped
- if ( data.src !== 'dom' && $.isArray( data._aData ) ) {
- fnArraySwitch( data._aData, iFrom, iTo );
- }
- }
- /* Reposition the header elements in the header layout array */
- for ( i=0, iLen=oSettings.aoHeader.length ; i<iLen ; i++ )
- {
- fnArraySwitch( oSettings.aoHeader[i], iFrom, iTo );
- }
- if ( oSettings.aoFooter !== null )
- {
- for ( i=0, iLen=oSettings.aoFooter.length ; i<iLen ; i++ )
- {
- fnArraySwitch( oSettings.aoFooter[i], iFrom, iTo );
- }
- }
- if ( invalidateRows || invalidateRows === undefined )
- {
- $.fn.dataTable.Api( oSettings ).rows().invalidate();
- }
- /*
- * Update DataTables' event handlers
- */
- /* Sort listener */
- for ( i=0, iLen=iCols ; i<iLen ; i++ )
- {
- $(oSettings.aoColumns[i].nTh).off('click.DT');
- this.oApi._fnSortAttachListener( oSettings, oSettings.aoColumns[i].nTh, i );
- }
- /* Fire an event so other plug-ins can update */
- $(oSettings.oInstance).trigger( 'column-reorder.dt', [ oSettings, {
- from: iFrom,
- to: iTo,
- mapping: aiInvertMapping,
- drop: drop,
- // Old style parameters for compatibility
- iFrom: iFrom,
- iTo: iTo,
- aiInvertMapping: aiInvertMapping
- } ] );
- };
- /**
- * ColReorder provides column visibility control for DataTables
- * @class ColReorder
- * @constructor
- * @param {object} dt DataTables settings object
- * @param {object} opts ColReorder options
- */
- var ColReorder = function( dt, opts )
- {
- var settings = new $.fn.dataTable.Api( dt ).settings()[0];
- // Ensure that we can't initialise on the same table twice
- if ( settings._colReorder ) {
- return settings._colReorder;
- }
- // Allow the options to be a boolean for defaults
- if ( opts === true ) {
- opts = {};
- }
- // Convert from camelCase to Hungarian, just as DataTables does
- var camelToHungarian = $.fn.dataTable.camelToHungarian;
- if ( camelToHungarian ) {
- camelToHungarian( ColReorder.defaults, ColReorder.defaults, true );
- camelToHungarian( ColReorder.defaults, opts || {} );
- }
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Public class variables
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * @namespace Settings object which contains customisable information for ColReorder instance
- */
- this.s = {
- /**
- * DataTables settings object
- * @property dt
- * @type Object
- * @default null
- */
- "dt": null,
- /**
- * Initialisation object used for this instance
- * @property init
- * @type object
- * @default {}
- */
- "init": $.extend( true, {}, ColReorder.defaults, opts ),
- /**
- * Number of columns to fix (not allow to be reordered)
- * @property fixed
- * @type int
- * @default 0
- */
- "fixed": 0,
- /**
- * Number of columns to fix counting from right (not allow to be reordered)
- * @property fixedRight
- * @type int
- * @default 0
- */
- "fixedRight": 0,
- /**
- * Callback function for once the reorder has been done
- * @property reorderCallback
- * @type function
- * @default null
- */
- "reorderCallback": null,
- /**
- * @namespace Information used for the mouse drag
- */
- "mouse": {
- "startX": -1,
- "startY": -1,
- "offsetX": -1,
- "offsetY": -1,
- "target": -1,
- "targetIndex": -1,
- "fromIndex": -1
- },
- /**
- * Information which is used for positioning the insert cusor and knowing where to do the
- * insert. Array of objects with the properties:
- * x: x-axis position
- * to: insert point
- * @property aoTargets
- * @type array
- * @default []
- */
- "aoTargets": []
- };
- /**
- * @namespace Common and useful DOM elements for the class instance
- */
- this.dom = {
- /**
- * Dragging element (the one the mouse is moving)
- * @property drag
- * @type element
- * @default null
- */
- "drag": null,
- /**
- * The insert cursor
- * @property pointer
- * @type element
- * @default null
- */
- "pointer": null
- };
- /* Constructor logic */
- this.s.dt = settings;
- this.s.dt._colReorder = this;
- this._fnConstruct();
- return this;
- };
- $.extend( ColReorder.prototype, {
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Public methods
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * Reset the column ordering to the original ordering that was detected on
- * start up.
- * @return {this} Returns `this` for chaining.
- *
- * @example
- * // DataTables initialisation with ColReorder
- * var table = $('#example').dataTable( {
- * "sDom": 'Rlfrtip'
- * } );
- *
- * // Add click event to a button to reset the ordering
- * $('#resetOrdering').click( function (e) {
- * e.preventDefault();
- * $.fn.dataTable.ColReorder( table ).fnReset();
- * } );
- */
- "fnReset": function ()
- {
- this._fnOrderColumns( this.fnOrder() );
- return this;
- },
- /**
- * `Deprecated` - Get the current order of the columns, as an array.
- * @return {array} Array of column identifiers
- * @deprecated `fnOrder` should be used in preference to this method.
- * `fnOrder` acts as a getter/setter.
- */
- "fnGetCurrentOrder": function ()
- {
- return this.fnOrder();
- },
- /**
- * Get the current order of the columns, as an array. Note that the values
- * given in the array are unique identifiers for each column. Currently
- * these are the original ordering of the columns that was detected on
- * start up, but this could potentially change in future.
- * @return {array} Array of column identifiers
- *
- * @example
- * // Get column ordering for the table
- * var order = $.fn.dataTable.ColReorder( dataTable ).fnOrder();
- *//**
- * Set the order of the columns, from the positions identified in the
- * ordering array given. Note that ColReorder takes a brute force approach
- * to reordering, so it is possible multiple reordering events will occur
- * before the final order is settled upon.
- * @param {array} [set] Array of column identifiers in the new order. Note
- * that every column must be included, uniquely, in this array.
- * @return {this} Returns `this` for chaining.
- *
- * @example
- * // Swap the first and second columns
- * $.fn.dataTable.ColReorder( dataTable ).fnOrder( [1, 0, 2, 3, 4] );
- *
- * @example
- * // Move the first column to the end for the table `#example`
- * var curr = $.fn.dataTable.ColReorder( '#example' ).fnOrder();
- * var first = curr.shift();
- * curr.push( first );
- * $.fn.dataTable.ColReorder( '#example' ).fnOrder( curr );
- *
- * @example
- * // Reverse the table's order
- * $.fn.dataTable.ColReorder( '#example' ).fnOrder(
- * $.fn.dataTable.ColReorder( '#example' ).fnOrder().reverse()
- * );
- */
- "fnOrder": function ( set, original )
- {
- var a = [], i, ien, j, jen;
- var columns = this.s.dt.aoColumns;
- if ( set === undefined ){
- for ( i=0, ien=columns.length ; i<ien ; i++ ) {
- a.push( columns[i]._ColReorder_iOrigCol );
- }
- return a;
- }
- // The order given is based on the original indexes, rather than the
- // existing ones, so we need to translate from the original to current
- // before then doing the order
- if ( original ) {
- var order = this.fnOrder();
- for ( i=0, ien=set.length ; i<ien ; i++ ) {
- a.push( $.inArray( set[i], order ) );
- }
- set = a;
- }
- this._fnOrderColumns( fnInvertKeyValues( set ) );
- return this;
- },
- /**
- * Convert from the original column index, to the original
- *
- * @param {int|array} idx Index(es) to convert
- * @param {string} dir Transpose direction - `fromOriginal` / `toCurrent`
- * or `'toOriginal` / `fromCurrent`
- * @return {int|array} Converted values
- */
- fnTranspose: function ( idx, dir )
- {
- if ( ! dir ) {
- dir = 'toCurrent';
- }
- var order = this.fnOrder();
- var columns = this.s.dt.aoColumns;
- if ( dir === 'toCurrent' ) {
- // Given an original index, want the current
- return ! $.isArray( idx ) ?
- $.inArray( idx, order ) :
- $.map( idx, function ( index ) {
- return $.inArray( index, order );
- } );
- }
- else {
- // Given a current index, want the original
- return ! $.isArray( idx ) ?
- columns[idx]._ColReorder_iOrigCol :
- $.map( idx, function ( index ) {
- return columns[index]._ColReorder_iOrigCol;
- } );
- }
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Private methods (they are of course public in JS, but recommended as private)
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * Constructor logic
- * @method _fnConstruct
- * @returns void
- * @private
- */
- "_fnConstruct": function ()
- {
- var that = this;
- var iLen = this.s.dt.aoColumns.length;
- var table = this.s.dt.nTable;
- var i;
- /* Columns discounted from reordering - counting left to right */
- if ( this.s.init.iFixedColumns )
- {
- this.s.fixed = this.s.init.iFixedColumns;
- }
- if ( this.s.init.iFixedColumnsLeft )
- {
- this.s.fixed = this.s.init.iFixedColumnsLeft;
- }
- /* Columns discounted from reordering - counting right to left */
- this.s.fixedRight = this.s.init.iFixedColumnsRight ?
- this.s.init.iFixedColumnsRight :
- 0;
- /* Drop callback initialisation option */
- if ( this.s.init.fnReorderCallback )
- {
- this.s.reorderCallback = this.s.init.fnReorderCallback;
- }
- /* Add event handlers for the drag and drop, and also mark the original column order */
- for ( i = 0; i < iLen; i++ )
- {
- if ( i > this.s.fixed-1 && i < iLen - this.s.fixedRight )
- {
- this._fnMouseListener( i, this.s.dt.aoColumns[i].nTh );
- }
- /* Mark the original column order for later reference */
- this.s.dt.aoColumns[i]._ColReorder_iOrigCol = i;
- }
- /* State saving */
- this.s.dt.oApi._fnCallbackReg( this.s.dt, 'aoStateSaveParams', function (oS, oData) {
- that._fnStateSave.call( that, oData );
- }, "ColReorder_State" );
- /* An initial column order has been specified */
- var aiOrder = null;
- if ( this.s.init.aiOrder )
- {
- aiOrder = this.s.init.aiOrder.slice();
- }
- /* State loading, overrides the column order given */
- if ( this.s.dt.oLoadedState && typeof this.s.dt.oLoadedState.ColReorder != 'undefined' &&
- this.s.dt.oLoadedState.ColReorder.length == this.s.dt.aoColumns.length )
- {
- aiOrder = this.s.dt.oLoadedState.ColReorder;
- }
- /* If we have an order to apply - do so */
- if ( aiOrder )
- {
- /* We might be called during or after the DataTables initialisation. If before, then we need
- * to wait until the draw is done, if after, then do what we need to do right away
- */
- if ( !that.s.dt._bInitComplete )
- {
- var bDone = false;
- $(table).on( 'draw.dt.colReorder', function () {
- if ( !that.s.dt._bInitComplete && !bDone )
- {
- bDone = true;
- var resort = fnInvertKeyValues( aiOrder );
- that._fnOrderColumns.call( that, resort );
- }
- } );
- }
- else
- {
- var resort = fnInvertKeyValues( aiOrder );
- that._fnOrderColumns.call( that, resort );
- }
- }
- else {
- this._fnSetColumnIndexes();
- }
- // Destroy clean up
- $(table).on( 'destroy.dt.colReorder', function () {
- $(table).off( 'destroy.dt.colReorder draw.dt.colReorder' );
- $(that.s.dt.nTHead).find( '*' ).off( '.ColReorder' );
- $.each( that.s.dt.aoColumns, function (i, column) {
- $(column.nTh).removeAttr('data-column-index');
- } );
- that.s.dt._colReorder = null;
- that.s = null;
- } );
- },
- /**
- * Set the column order from an array
- * @method _fnOrderColumns
- * @param array a An array of integers which dictate the column order that should be applied
- * @returns void
- * @private
- */
- "_fnOrderColumns": function ( a )
- {
- var changed = false;
- if ( a.length != this.s.dt.aoColumns.length )
- {
- this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "ColReorder - array reorder does not "+
- "match known number of columns. Skipping." );
- return;
- }
- for ( var i=0, iLen=a.length ; i<iLen ; i++ )
- {
- var currIndex = $.inArray( i, a );
- if ( i != currIndex )
- {
- /* Reorder our switching array */
- fnArraySwitch( a, currIndex, i );
- /* Do the column reorder in the table */
- this.s.dt.oInstance.fnColReorder( currIndex, i, true, false );
- changed = true;
- }
- }
- $.fn.dataTable.Api( this.s.dt ).rows().invalidate();
- this._fnSetColumnIndexes();
- // Has anything actually changed? If not, then nothing else to do
- if ( ! changed ) {
- return;
- }
- /* When scrolling we need to recalculate the column sizes to allow for the shift */
- if ( this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" )
- {
- this.s.dt.oInstance.fnAdjustColumnSizing( false );
- }
- /* Save the state */
- this.s.dt.oInstance.oApi._fnSaveState( this.s.dt );
- if ( this.s.reorderCallback !== null )
- {
- this.s.reorderCallback.call( this );
- }
- },
- /**
- * Because we change the indexes of columns in the table, relative to their starting point
- * we need to reorder the state columns to what they are at the starting point so we can
- * then rearrange them again on state load!
- * @method _fnStateSave
- * @param object oState DataTables state
- * @returns string JSON encoded cookie string for DataTables
- * @private
- */
- "_fnStateSave": function ( oState )
- {
- var i, iLen, aCopy, iOrigColumn;
- var oSettings = this.s.dt;
- var columns = oSettings.aoColumns;
- oState.ColReorder = [];
- /* Sorting */
- if ( oState.aaSorting ) {
- // 1.10.0-
- for ( i=0 ; i<oState.aaSorting.length ; i++ ) {
- oState.aaSorting[i][0] = columns[ oState.aaSorting[i][0] ]._ColReorder_iOrigCol;
- }
- var aSearchCopy = $.extend( true, [], oState.aoSearchCols );
- for ( i=0, iLen=columns.length ; i<iLen ; i++ )
- {
- iOrigColumn = columns[i]._ColReorder_iOrigCol;
- /* Column filter */
- oState.aoSearchCols[ iOrigColumn ] = aSearchCopy[i];
- /* Visibility */
- oState.abVisCols[ iOrigColumn ] = columns[i].bVisible;
- /* Column reordering */
- oState.ColReorder.push( iOrigColumn );
- }
- }
- else if ( oState.order ) {
- // 1.10.1+
- for ( i=0 ; i<oState.order.length ; i++ ) {
- oState.order[i][0] = columns[ oState.order[i][0] ]._ColReorder_iOrigCol;
- }
- var stateColumnsCopy = $.extend( true, [], oState.columns );
- for ( i=0, iLen=columns.length ; i<iLen ; i++ )
- {
- iOrigColumn = columns[i]._ColReorder_iOrigCol;
- /* Columns */
- oState.columns[ iOrigColumn ] = stateColumnsCopy[i];
- /* Column reordering */
- oState.ColReorder.push( iOrigColumn );
- }
- }
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Mouse drop and drag
- */
- /**
- * Add a mouse down listener to a particluar TH element
- * @method _fnMouseListener
- * @param int i Column index
- * @param element nTh TH element clicked on
- * @returns void
- * @private
- */
- "_fnMouseListener": function ( i, nTh )
- {
- var that = this;
- $(nTh)
- .on( 'mousedown.ColReorder', function (e) {
- that._fnMouseDown.call( that, e, nTh );
- } )
- .on( 'touchstart.ColReorder', function (e) {
- that._fnMouseDown.call( that, e, nTh );
- } );
- },
- /**
- * Mouse down on a TH element in the table header
- * @method _fnMouseDown
- * @param event e Mouse event
- * @param element nTh TH element to be dragged
- * @returns void
- * @private
- */
- "_fnMouseDown": function ( e, nTh )
- {
- var that = this;
- /* Store information about the mouse position */
- var target = $(e.target).closest('th, td');
- var offset = target.offset();
- var idx = parseInt( $(nTh).attr('data-column-index'), 10 );
- if ( idx === undefined ) {
- return;
- }
- this.s.mouse.startX = this._fnCursorPosition( e, 'pageX' );
- this.s.mouse.startY = this._fnCursorPosition( e, 'pageY' );
- this.s.mouse.offsetX = this._fnCursorPosition( e, 'pageX' ) - offset.left;
- this.s.mouse.offsetY = this._fnCursorPosition( e, 'pageY' ) - offset.top;
- this.s.mouse.target = this.s.dt.aoColumns[ idx ].nTh;//target[0];
- this.s.mouse.targetIndex = idx;
- this.s.mouse.fromIndex = idx;
- this._fnRegions();
- /* Add event handlers to the document */
- $(document)
- .on( 'mousemove.ColReorder touchmove.ColReorder', function (e) {
- that._fnMouseMove.call( that, e );
- } )
- .on( 'mouseup.ColReorder touchend.ColReorder', function (e) {
- that._fnMouseUp.call( that, e );
- } );
- },
- /**
- * Deal with a mouse move event while dragging a node
- * @method _fnMouseMove
- * @param event e Mouse event
- * @returns void
- * @private
- */
- "_fnMouseMove": function ( e )
- {
- var that = this;
- if ( this.dom.drag === null )
- {
- /* Only create the drag element if the mouse has moved a specific distance from the start
- * point - this allows the user to make small mouse movements when sorting and not have a
- * possibly confusing drag element showing up
- */
- if ( Math.pow(
- Math.pow(this._fnCursorPosition( e, 'pageX') - this.s.mouse.startX, 2) +
- Math.pow(this._fnCursorPosition( e, 'pageY') - this.s.mouse.startY, 2), 0.5 ) < 5 )
- {
- return;
- }
- this._fnCreateDragNode();
- }
- /* Position the element - we respect where in the element the click occured */
- this.dom.drag.css( {
- left: this._fnCursorPosition( e, 'pageX' ) - this.s.mouse.offsetX,
- top: this._fnCursorPosition( e, 'pageY' ) - this.s.mouse.offsetY
- } );
- /* Based on the current mouse position, calculate where the insert should go */
- var bSet = false;
- var lastToIndex = this.s.mouse.toIndex;
- for ( var i=1, iLen=this.s.aoTargets.length ; i<iLen ; i++ )
- {
- if ( this._fnCursorPosition(e, 'pageX') < this.s.aoTargets[i-1].x + ((this.s.aoTargets[i].x-this.s.aoTargets[i-1].x)/2) )
- {
- this.dom.pointer.css( 'left', this.s.aoTargets[i-1].x );
- this.s.mouse.toIndex = this.s.aoTargets[i-1].to;
- bSet = true;
- break;
- }
- }
- // The insert element wasn't positioned in the array (less than
- // operator), so we put it at the end
- if ( !bSet )
- {
- this.dom.pointer.css( 'left', this.s.aoTargets[this.s.aoTargets.length-1].x );
- this.s.mouse.toIndex = this.s.aoTargets[this.s.aoTargets.length-1].to;
- }
- // Perform reordering if realtime updating is on and the column has moved
- if ( this.s.init.bRealtime && lastToIndex !== this.s.mouse.toIndex ) {
- this.s.dt.oInstance.fnColReorder( this.s.mouse.fromIndex, this.s.mouse.toIndex, false );
- this.s.mouse.fromIndex = this.s.mouse.toIndex;
- this._fnRegions();
- }
- },
- /**
- * Finish off the mouse drag and insert the column where needed
- * @method _fnMouseUp
- * @param event e Mouse event
- * @returns void
- * @private
- */
- "_fnMouseUp": function ( e )
- {
- var that = this;
- $(document).off( '.ColReorder' );
- if ( this.dom.drag !== null )
- {
- /* Remove the guide elements */
- this.dom.drag.remove();
- this.dom.pointer.remove();
- this.dom.drag = null;
- this.dom.pointer = null;
- /* Actually do the reorder */
- this.s.dt.oInstance.fnColReorder( this.s.mouse.fromIndex, this.s.mouse.toIndex, true );
- this._fnSetColumnIndexes();
- /* When scrolling we need to recalculate the column sizes to allow for the shift */
- if ( this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" )
- {
- this.s.dt.oInstance.fnAdjustColumnSizing( false );
- }
- /* Save the state */
- this.s.dt.oInstance.oApi._fnSaveState( this.s.dt );
- if ( this.s.reorderCallback !== null )
- {
- this.s.reorderCallback.call( this );
- }
- }
- },
- /**
- * Calculate a cached array with the points of the column inserts, and the
- * 'to' points
- * @method _fnRegions
- * @returns void
- * @private
- */
- "_fnRegions": function ()
- {
- var aoColumns = this.s.dt.aoColumns;
- this.s.aoTargets.splice( 0, this.s.aoTargets.length );
- this.s.aoTargets.push( {
- "x": $(this.s.dt.nTable).offset().left,
- "to": 0
- } );
- var iToPoint = 0;
- var total = this.s.aoTargets[0].x;
- for ( var i=0, iLen=aoColumns.length ; i<iLen ; i++ )
- {
- /* For the column / header in question, we want it's position to remain the same if the
- * position is just to it's immediate left or right, so we only increment the counter for
- * other columns
- */
- if ( i != this.s.mouse.fromIndex )
- {
- iToPoint++;
- }
- if ( aoColumns[i].bVisible && aoColumns[i].nTh.style.display !=='none' )
- {
- total += $(aoColumns[i].nTh).outerWidth();
- this.s.aoTargets.push( {
- "x": total,
- "to": iToPoint
- } );
- }
- }
- /* Disallow columns for being reordered by drag and drop, counting right to left */
- if ( this.s.fixedRight !== 0 )
- {
- this.s.aoTargets.splice( this.s.aoTargets.length - this.s.fixedRight );
- }
- /* Disallow columns for being reordered by drag and drop, counting left to right */
- if ( this.s.fixed !== 0 )
- {
- this.s.aoTargets.splice( 0, this.s.fixed );
- }
- },
- /**
- * Copy the TH element that is being drags so the user has the idea that they are actually
- * moving it around the page.
- * @method _fnCreateDragNode
- * @returns void
- * @private
- */
- "_fnCreateDragNode": function ()
- {
- var scrolling = this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "";
- var origCell = this.s.dt.aoColumns[ this.s.mouse.targetIndex ].nTh;
- var origTr = origCell.parentNode;
- var origThead = origTr.parentNode;
- var origTable = origThead.parentNode;
- var cloneCell = $(origCell).clone();
- // This is a slightly odd combination of jQuery and DOM, but it is the
- // fastest and least resource intensive way I could think of cloning
- // the table with just a single header cell in it.
- this.dom.drag = $(origTable.cloneNode(false))
- .addClass( 'DTCR_clonedTable' )
- .append(
- $(origThead.cloneNode(false)).append(
- $(origTr.cloneNode(false)).append(
- cloneCell[0]
- )
- )
- )
- .css( {
- position: 'absolute',
- top: 0,
- left: 0,
- width: $(origCell).outerWidth(),
- height: $(origCell).outerHeight()
- } )
- .appendTo( 'body' );
- this.dom.pointer = $('<div></div>')
- .addClass( 'DTCR_pointer' )
- .css( {
- position: 'absolute',
- top: scrolling ?
- $('div.dataTables_scroll', this.s.dt.nTableWrapper).offset().top :
- $(this.s.dt.nTable).offset().top,
- height : scrolling ?
- $('div.dataTables_scroll', this.s.dt.nTableWrapper).height() :
- $(this.s.dt.nTable).height()
- } )
- .appendTo( 'body' );
- },
- /**
- * Add a data attribute to the column headers, so we know the index of
- * the row to be reordered. This allows fast detection of the index, and
- * for this plug-in to work with FixedHeader which clones the nodes.
- * @private
- */
- "_fnSetColumnIndexes": function ()
- {
- $.each( this.s.dt.aoColumns, function (i, column) {
- $(column.nTh).attr('data-column-index', i);
- } );
- },
- /**
- * Get cursor position regardless of mouse or touch input
- * @param {Event} e jQuery Event
- * @param {string} prop Property to get
- * @return {number} Value
- */
- _fnCursorPosition: function ( e, prop ) {
- if ( e.type.indexOf('touch') !== -1 ) {
- return e.originalEvent.touches[0][ prop ];
- }
- return e[ prop ];
- }
- } );
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Static parameters
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * ColReorder default settings for initialisation
- * @namespace
- * @static
- */
- ColReorder.defaults = {
- /**
- * Predefined ordering for the columns that will be applied automatically
- * on initialisation. If not specified then the order that the columns are
- * found to be in the HTML is the order used.
- * @type array
- * @default null
- * @static
- */
- aiOrder: null,
- /**
- * Redraw the table's column ordering as the end user draws the column
- * (`true`) or wait until the mouse is released (`false` - default). Note
- * that this will perform a redraw on each reordering, which involves an
- * Ajax request each time if you are using server-side processing in
- * DataTables.
- * @type boolean
- * @default false
- * @static
- */
- bRealtime: true,
- /**
- * Indicate how many columns should be fixed in position (counting from the
- * left). This will typically be 1 if used, but can be as high as you like.
- * @type int
- * @default 0
- * @static
- */
- iFixedColumnsLeft: 0,
- /**
- * As `iFixedColumnsRight` but counting from the right.
- * @type int
- * @default 0
- * @static
- */
- iFixedColumnsRight: 0,
- /**
- * Callback function that is fired when columns are reordered. The `column-
- * reorder` event is preferred over this callback
- * @type function():void
- * @default null
- * @static
- */
- fnReorderCallback: null
- };
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constants
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * ColReorder version
- * @constant version
- * @type String
- * @default As code
- */
- ColReorder.version = "1.4.1";
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables interfaces
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- // Expose
- $.fn.dataTable.ColReorder = ColReorder;
- $.fn.DataTable.ColReorder = ColReorder;
- // Register a new feature with DataTables
- if ( typeof $.fn.dataTable == "function" &&
- typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
- $.fn.dataTableExt.fnVersionCheck('1.10.8') )
- {
- $.fn.dataTableExt.aoFeatures.push( {
- "fnInit": function( settings ) {
- var table = settings.oInstance;
- if ( ! settings._colReorder ) {
- var dtInit = settings.oInit;
- var opts = dtInit.colReorder || dtInit.oColReorder || {};
- new ColReorder( settings, opts );
- }
- else {
- table.oApi._fnLog( settings, 1, "ColReorder attempted to initialise twice. Ignoring second" );
- }
- return null; /* No node for DataTables to insert */
- },
- "cFeature": "R",
- "sFeature": "ColReorder"
- } );
- }
- else {
- alert( "Warning: ColReorder requires DataTables 1.10.8 or greater - www.datatables.net/download");
- }
- // Attach a listener to the document which listens for DataTables initialisation
- // events so we can automatically initialise
- $(document).on( 'preInit.dt.colReorder', function (e, settings) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- var init = settings.oInit.colReorder;
- var defaults = DataTable.defaults.colReorder;
- if ( init || defaults ) {
- var opts = $.extend( {}, init, defaults );
- if ( init !== false ) {
- new ColReorder( settings, opts );
- }
- }
- } );
- // API augmentation
- $.fn.dataTable.Api.register( 'colReorder.reset()', function () {
- return this.iterator( 'table', function ( ctx ) {
- ctx._colReorder.fnReset();
- } );
- } );
- $.fn.dataTable.Api.register( 'colReorder.order()', function ( set, original ) {
- if ( set ) {
- return this.iterator( 'table', function ( ctx ) {
- ctx._colReorder.fnOrder( set, original );
- } );
- }
- return this.context.length ?
- this.context[0]._colReorder.fnOrder() :
- null;
- } );
- $.fn.dataTable.Api.register( 'colReorder.transpose()', function ( idx, dir ) {
- return this.context.length && this.context[0]._colReorder ?
- this.context[0]._colReorder.fnTranspose( idx, dir ) :
- idx;
- } );
- $.fn.dataTable.Api.register( 'colReorder.move()', function( from, to, drop, invalidateRows ) {
- if (this.context.length) {
- this.context[0]._colReorder.s.dt.oInstance.fnColReorder( from, to, drop, invalidateRows );
- }
- return this;
- } );
- return ColReorder;
- }));
- /*! FixedColumns 3.2.4
- * ©2010-2017 SpryMedia Ltd - datatables.net/license
- */
- /**
- * @summary FixedColumns
- * @description Freeze columns in place on a scrolling DataTable
- * @version 3.2.4
- * @file dataTables.fixedColumns.js
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
- * @contact www.sprymedia.co.uk/contact
- * @copyright Copyright 2010-2017 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license/mit
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- var _firefoxScroll;
- /**
- * When making use of DataTables' x-axis scrolling feature, you may wish to
- * fix the left most column in place. This plug-in for DataTables provides
- * exactly this option (note for non-scrolling tables, please use the
- * FixedHeader plug-in, which can fix headers and footers). Key
- * features include:
- *
- * * Freezes the left or right most columns to the side of the table
- * * Option to freeze two or more columns
- * * Full integration with DataTables' scrolling options
- * * Speed - FixedColumns is fast in its operation
- *
- * @class
- * @constructor
- * @global
- * @param {object} dt DataTables instance. With DataTables 1.10 this can also
- * be a jQuery collection, a jQuery selector, DataTables API instance or
- * settings object.
- * @param {object} [init={}] Configuration object for FixedColumns. Options are
- * defined by {@link FixedColumns.defaults}
- *
- * @requires jQuery 1.7+
- * @requires DataTables 1.8.0+
- *
- * @example
- * var table = $('#example').dataTable( {
- * "scrollX": "100%"
- * } );
- * new $.fn.dataTable.fixedColumns( table );
- */
- var FixedColumns = function ( dt, init ) {
- var that = this;
- /* Sanity check - you just know it will happen */
- if ( ! ( this instanceof FixedColumns ) ) {
- alert( "FixedColumns warning: FixedColumns must be initialised with the 'new' keyword." );
- return;
- }
- if ( init === undefined || init === true ) {
- init = {};
- }
- // Use the DataTables Hungarian notation mapping method, if it exists to
- // provide forwards compatibility for camel case variables
- var camelToHungarian = $.fn.dataTable.camelToHungarian;
- if ( camelToHungarian ) {
- camelToHungarian( FixedColumns.defaults, FixedColumns.defaults, true );
- camelToHungarian( FixedColumns.defaults, init );
- }
- // v1.10 allows the settings object to be got form a number of sources
- var dtSettings = new $.fn.dataTable.Api( dt ).settings()[0];
- /**
- * Settings object which contains customisable information for FixedColumns instance
- * @namespace
- * @extends FixedColumns.defaults
- * @private
- */
- this.s = {
- /**
- * DataTables settings objects
- * @type object
- * @default Obtained from DataTables instance
- */
- "dt": dtSettings,
- /**
- * Number of columns in the DataTable - stored for quick access
- * @type int
- * @default Obtained from DataTables instance
- */
- "iTableColumns": dtSettings.aoColumns.length,
- /**
- * Original outer widths of the columns as rendered by DataTables - used to calculate
- * the FixedColumns grid bounding box
- * @type array.<int>
- * @default []
- */
- "aiOuterWidths": [],
- /**
- * Original inner widths of the columns as rendered by DataTables - used to apply widths
- * to the columns
- * @type array.<int>
- * @default []
- */
- "aiInnerWidths": [],
- /**
- * Is the document layout right-to-left
- * @type boolean
- */
- rtl: $(dtSettings.nTable).css('direction') === 'rtl'
- };
- /**
- * DOM elements used by the class instance
- * @namespace
- * @private
- *
- */
- this.dom = {
- /**
- * DataTables scrolling element
- * @type node
- * @default null
- */
- "scroller": null,
- /**
- * DataTables header table
- * @type node
- * @default null
- */
- "header": null,
- /**
- * DataTables body table
- * @type node
- * @default null
- */
- "body": null,
- /**
- * DataTables footer table
- * @type node
- * @default null
- */
- "footer": null,
- /**
- * Display grid elements
- * @namespace
- */
- "grid": {
- /**
- * Grid wrapper. This is the container element for the 3x3 grid
- * @type node
- * @default null
- */
- "wrapper": null,
- /**
- * DataTables scrolling element. This element is the DataTables
- * component in the display grid (making up the main table - i.e.
- * not the fixed columns).
- * @type node
- * @default null
- */
- "dt": null,
- /**
- * Left fixed column grid components
- * @namespace
- */
- "left": {
- "wrapper": null,
- "head": null,
- "body": null,
- "foot": null
- },
- /**
- * Right fixed column grid components
- * @namespace
- */
- "right": {
- "wrapper": null,
- "head": null,
- "body": null,
- "foot": null
- }
- },
- /**
- * Cloned table nodes
- * @namespace
- */
- "clone": {
- /**
- * Left column cloned table nodes
- * @namespace
- */
- "left": {
- /**
- * Cloned header table
- * @type node
- * @default null
- */
- "header": null,
- /**
- * Cloned body table
- * @type node
- * @default null
- */
- "body": null,
- /**
- * Cloned footer table
- * @type node
- * @default null
- */
- "footer": null
- },
- /**
- * Right column cloned table nodes
- * @namespace
- */
- "right": {
- /**
- * Cloned header table
- * @type node
- * @default null
- */
- "header": null,
- /**
- * Cloned body table
- * @type node
- * @default null
- */
- "body": null,
- /**
- * Cloned footer table
- * @type node
- * @default null
- */
- "footer": null
- }
- }
- };
- if ( dtSettings._oFixedColumns ) {
- throw 'FixedColumns already initialised on this table';
- }
- /* Attach the instance to the DataTables instance so it can be accessed easily */
- dtSettings._oFixedColumns = this;
- /* Let's do it */
- if ( ! dtSettings._bInitComplete )
- {
- dtSettings.oApi._fnCallbackReg( dtSettings, 'aoInitComplete', function () {
- that._fnConstruct( init );
- }, 'FixedColumns' );
- }
- else
- {
- this._fnConstruct( init );
- }
- };
- $.extend( FixedColumns.prototype , {
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Public methods
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * Update the fixed columns - including headers and footers. Note that FixedColumns will
- * automatically update the display whenever the host DataTable redraws.
- * @returns {void}
- * @example
- * var table = $('#example').dataTable( {
- * "scrollX": "100%"
- * } );
- * var fc = new $.fn.dataTable.fixedColumns( table );
- *
- * // at some later point when the table has been manipulated....
- * fc.fnUpdate();
- */
- "fnUpdate": function ()
- {
- this._fnDraw( true );
- },
- /**
- * Recalculate the resizes of the 3x3 grid that FixedColumns uses for display of the table.
- * This is useful if you update the width of the table container. Note that FixedColumns will
- * perform this function automatically when the window.resize event is fired.
- * @returns {void}
- * @example
- * var table = $('#example').dataTable( {
- * "scrollX": "100%"
- * } );
- * var fc = new $.fn.dataTable.fixedColumns( table );
- *
- * // Resize the table container and then have FixedColumns adjust its layout....
- * $('#content').width( 1200 );
- * fc.fnRedrawLayout();
- */
- "fnRedrawLayout": function ()
- {
- this._fnColCalc();
- this._fnGridLayout();
- this.fnUpdate();
- },
- /**
- * Mark a row such that it's height should be recalculated when using 'semiauto' row
- * height matching. This function will have no effect when 'none' or 'auto' row height
- * matching is used.
- * @param {Node} nTr TR element that should have it's height recalculated
- * @returns {void}
- * @example
- * var table = $('#example').dataTable( {
- * "scrollX": "100%"
- * } );
- * var fc = new $.fn.dataTable.fixedColumns( table );
- *
- * // manipulate the table - mark the row as needing an update then update the table
- * // this allows the redraw performed by DataTables fnUpdate to recalculate the row
- * // height
- * fc.fnRecalculateHeight();
- * table.fnUpdate( $('#example tbody tr:eq(0)')[0], ["insert date", 1, 2, 3 ... ]);
- */
- "fnRecalculateHeight": function ( nTr )
- {
- delete nTr._DTTC_iHeight;
- nTr.style.height = 'auto';
- },
- /**
- * Set the height of a given row - provides cross browser compatibility
- * @param {Node} nTarget TR element that should have it's height recalculated
- * @param {int} iHeight Height in pixels to set
- * @returns {void}
- * @example
- * var table = $('#example').dataTable( {
- * "scrollX": "100%"
- * } );
- * var fc = new $.fn.dataTable.fixedColumns( table );
- *
- * // You may want to do this after manipulating a row in the fixed column
- * fc.fnSetRowHeight( $('#example tbody tr:eq(0)')[0], 50 );
- */
- "fnSetRowHeight": function ( nTarget, iHeight )
- {
- nTarget.style.height = iHeight+"px";
- },
- /**
- * Get data index information about a row or cell in the table body.
- * This function is functionally identical to fnGetPosition in DataTables,
- * taking the same parameter (TH, TD or TR node) and returning exactly the
- * the same information (data index information). THe difference between
- * the two is that this method takes into account the fixed columns in the
- * table, so you can pass in nodes from the master table, or the cloned
- * tables and get the index position for the data in the main table.
- * @param {node} node TR, TH or TD element to get the information about
- * @returns {int} If nNode is given as a TR, then a single index is
- * returned, or if given as a cell, an array of [row index, column index
- * (visible), column index (all)] is given.
- */
- "fnGetPosition": function ( node )
- {
- var idx;
- var inst = this.s.dt.oInstance;
- if ( ! $(node).parents('.DTFC_Cloned').length )
- {
- // Not in a cloned table
- return inst.fnGetPosition( node );
- }
- else
- {
- // Its in the cloned table, so need to look up position
- if ( node.nodeName.toLowerCase() === 'tr' ) {
- idx = $(node).index();
- return inst.fnGetPosition( $('tr', this.s.dt.nTBody)[ idx ] );
- }
- else
- {
- var colIdx = $(node).index();
- idx = $(node.parentNode).index();
- var row = inst.fnGetPosition( $('tr', this.s.dt.nTBody)[ idx ] );
- return [
- row,
- colIdx,
- inst.oApi._fnVisibleToColumnIndex( this.s.dt, colIdx )
- ];
- }
- }
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Private methods (they are of course public in JS, but recommended as private)
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * Initialisation for FixedColumns
- * @param {Object} oInit User settings for initialisation
- * @returns {void}
- * @private
- */
- "_fnConstruct": function ( oInit )
- {
- var i, iLen, iWidth,
- that = this;
- /* Sanity checking */
- if ( typeof this.s.dt.oInstance.fnVersionCheck != 'function' ||
- this.s.dt.oInstance.fnVersionCheck( '1.8.0' ) !== true )
- {
- alert( "FixedColumns "+FixedColumns.VERSION+" required DataTables 1.8.0 or later. "+
- "Please upgrade your DataTables installation" );
- return;
- }
- if ( this.s.dt.oScroll.sX === "" )
- {
- this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "FixedColumns is not needed (no "+
- "x-scrolling in DataTables enabled), so no action will be taken. Use 'FixedHeader' for "+
- "column fixing when scrolling is not enabled" );
- return;
- }
- /* Apply the settings from the user / defaults */
- this.s = $.extend( true, this.s, FixedColumns.defaults, oInit );
- /* Set up the DOM as we need it and cache nodes */
- var classes = this.s.dt.oClasses;
- this.dom.grid.dt = $(this.s.dt.nTable).parents('div.'+classes.sScrollWrapper)[0];
- this.dom.scroller = $('div.'+classes.sScrollBody, this.dom.grid.dt )[0];
- /* Set up the DOM that we want for the fixed column layout grid */
- this._fnColCalc();
- this._fnGridSetup();
- /* Event handlers */
- var mouseController;
- var mouseDown = false;
- // When the mouse is down (drag scroll) the mouse controller cannot
- // change, as the browser keeps the original element as the scrolling one
- $(this.s.dt.nTableWrapper).on( 'mousedown.DTFC', function (e) {
- if ( e.button === 0 ) {
- mouseDown = true;
- $(document).one( 'mouseup', function () {
- mouseDown = false;
- } );
- }
- } );
- // When the body is scrolled - scroll the left and right columns
- $(this.dom.scroller)
- .on( 'mouseover.DTFC touchstart.DTFC', function () {
- if ( ! mouseDown ) {
- mouseController = 'main';
- }
- } )
- .on( 'scroll.DTFC', function (e) {
- if ( ! mouseController && e.originalEvent ) {
- mouseController = 'main';
- }
- if ( mouseController === 'main' ) {
- if ( that.s.iLeftColumns > 0 ) {
- that.dom.grid.left.liner.scrollTop = that.dom.scroller.scrollTop;
- }
- if ( that.s.iRightColumns > 0 ) {
- that.dom.grid.right.liner.scrollTop = that.dom.scroller.scrollTop;
- }
- }
- } );
- var wheelType = 'onwheel' in document.createElement('div') ?
- 'wheel.DTFC' :
- 'mousewheel.DTFC';
- if ( that.s.iLeftColumns > 0 ) {
- // When scrolling the left column, scroll the body and right column
- $(that.dom.grid.left.liner)
- .on( 'mouseover.DTFC touchstart.DTFC', function () {
- if ( ! mouseDown ) {
- mouseController = 'left';
- }
- } )
- .on( 'scroll.DTFC', function ( e ) {
- if ( ! mouseController && e.originalEvent ) {
- mouseController = 'left';
- }
- if ( mouseController === 'left' ) {
- that.dom.scroller.scrollTop = that.dom.grid.left.liner.scrollTop;
- if ( that.s.iRightColumns > 0 ) {
- that.dom.grid.right.liner.scrollTop = that.dom.grid.left.liner.scrollTop;
- }
- }
- } )
- .on( wheelType, function(e) {
- // Pass horizontal scrolling through
- var xDelta = e.type === 'wheel' ?
- -e.originalEvent.deltaX :
- e.originalEvent.wheelDeltaX;
- that.dom.scroller.scrollLeft -= xDelta;
- } );
- }
- if ( that.s.iRightColumns > 0 ) {
- // When scrolling the right column, scroll the body and the left column
- $(that.dom.grid.right.liner)
- .on( 'mouseover.DTFC touchstart.DTFC', function () {
- if ( ! mouseDown ) {
- mouseController = 'right';
- }
- } )
- .on( 'scroll.DTFC', function ( e ) {
- if ( ! mouseController && e.originalEvent ) {
- mouseController = 'right';
- }
- if ( mouseController === 'right' ) {
- that.dom.scroller.scrollTop = that.dom.grid.right.liner.scrollTop;
- if ( that.s.iLeftColumns > 0 ) {
- that.dom.grid.left.liner.scrollTop = that.dom.grid.right.liner.scrollTop;
- }
- }
- } )
- .on( wheelType, function(e) {
- // Pass horizontal scrolling through
- var xDelta = e.type === 'wheel' ?
- -e.originalEvent.deltaX :
- e.originalEvent.wheelDeltaX;
- that.dom.scroller.scrollLeft -= xDelta;
- } );
- }
- $(window).on( 'resize.DTFC', function () {
- that._fnGridLayout.call( that );
- } );
- var bFirstDraw = true;
- var jqTable = $(this.s.dt.nTable);
- jqTable
- .on( 'draw.dt.DTFC', function () {
- that._fnColCalc();
- that._fnDraw.call( that, bFirstDraw );
- bFirstDraw = false;
- } )
- .on( 'column-sizing.dt.DTFC', function () {
- that._fnColCalc();
- that._fnGridLayout( that );
- } )
- .on( 'column-visibility.dt.DTFC', function ( e, settings, column, vis, recalc ) {
- if ( recalc === undefined || recalc ) {
- that._fnColCalc();
- that._fnGridLayout( that );
- that._fnDraw( true );
- }
- } )
- .on( 'select.dt.DTFC deselect.dt.DTFC', function ( e, dt, type, indexes ) {
- if ( e.namespace === 'dt' ) {
- that._fnDraw( false );
- }
- } )
- .on( 'destroy.dt.DTFC', function () {
- jqTable.off( '.DTFC' );
- $(that.dom.scroller).off( '.DTFC' );
- $(window).off( '.DTFC' );
- $(that.s.dt.nTableWrapper).off( '.DTFC' );
- $(that.dom.grid.left.liner).off( '.DTFC '+wheelType );
- $(that.dom.grid.left.wrapper).remove();
- $(that.dom.grid.right.liner).off( '.DTFC '+wheelType );
- $(that.dom.grid.right.wrapper).remove();
- } );
- /* Get things right to start with - note that due to adjusting the columns, there must be
- * another redraw of the main table. It doesn't need to be a full redraw however.
- */
- this._fnGridLayout();
- this.s.dt.oInstance.fnDraw(false);
- },
- /**
- * Calculate the column widths for the grid layout
- * @returns {void}
- * @private
- */
- "_fnColCalc": function ()
- {
- var that = this;
- var iLeftWidth = 0;
- var iRightWidth = 0;
- this.s.aiInnerWidths = [];
- this.s.aiOuterWidths = [];
- $.each( this.s.dt.aoColumns, function (i, col) {
- var th = $(col.nTh);
- var border;
- if ( ! th.filter(':visible').length ) {
- that.s.aiInnerWidths.push( 0 );
- that.s.aiOuterWidths.push( 0 );
- }
- else
- {
- // Inner width is used to assign widths to cells
- // Outer width is used to calculate the container
- var iWidth = th.outerWidth();
- // When working with the left most-cell, need to add on the
- // table's border to the outerWidth, since we need to take
- // account of it, but it isn't in any cell
- if ( that.s.aiOuterWidths.length === 0 ) {
- border = $(that.s.dt.nTable).css('border-left-width');
- iWidth += typeof border === 'string' && border.indexOf('px') === -1 ?
- 1 :
- parseInt( border, 10 );
- }
- // Likewise with the final column on the right
- if ( that.s.aiOuterWidths.length === that.s.dt.aoColumns.length-1 ) {
- border = $(that.s.dt.nTable).css('border-right-width');
- iWidth += typeof border === 'string' && border.indexOf('px') === -1 ?
- 1 :
- parseInt( border, 10 );
- }
- that.s.aiOuterWidths.push( iWidth );
- that.s.aiInnerWidths.push( th.width() );
- if ( i < that.s.iLeftColumns )
- {
- iLeftWidth += iWidth;
- }
- if ( that.s.iTableColumns-that.s.iRightColumns <= i )
- {
- iRightWidth += iWidth;
- }
- }
- } );
- this.s.iLeftWidth = iLeftWidth;
- this.s.iRightWidth = iRightWidth;
- },
- /**
- * Set up the DOM for the fixed column. The way the layout works is to create a 1x3 grid
- * for the left column, the DataTable (for which we just reuse the scrolling element DataTable
- * puts into the DOM) and the right column. In each of he two fixed column elements there is a
- * grouping wrapper element and then a head, body and footer wrapper. In each of these we then
- * place the cloned header, body or footer tables. This effectively gives as 3x3 grid structure.
- * @returns {void}
- * @private
- */
- "_fnGridSetup": function ()
- {
- var that = this;
- var oOverflow = this._fnDTOverflow();
- var block;
- this.dom.body = this.s.dt.nTable;
- this.dom.header = this.s.dt.nTHead.parentNode;
- this.dom.header.parentNode.parentNode.style.position = "relative";
- var nSWrapper =
- $('<div class="DTFC_ScrollWrapper" style="position:relative; clear:both;">'+
- '<div class="DTFC_LeftWrapper" style="position:absolute; top:0; left:0;" aria-hidden="true">'+
- '<div class="DTFC_LeftHeadWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
- '<div class="DTFC_LeftBodyWrapper" style="position:relative; top:0; left:0; overflow:hidden;">'+
- '<div class="DTFC_LeftBodyLiner" style="position:relative; top:0; left:0; overflow-y:scroll;"></div>'+
- '</div>'+
- '<div class="DTFC_LeftFootWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
- '</div>'+
- '<div class="DTFC_RightWrapper" style="position:absolute; top:0; right:0;" aria-hidden="true">'+
- '<div class="DTFC_RightHeadWrapper" style="position:relative; top:0; left:0;">'+
- '<div class="DTFC_RightHeadBlocker DTFC_Blocker" style="position:absolute; top:0; bottom:0;"></div>'+
- '</div>'+
- '<div class="DTFC_RightBodyWrapper" style="position:relative; top:0; left:0; overflow:hidden;">'+
- '<div class="DTFC_RightBodyLiner" style="position:relative; top:0; left:0; overflow-y:scroll;"></div>'+
- '</div>'+
- '<div class="DTFC_RightFootWrapper" style="position:relative; top:0; left:0;">'+
- '<div class="DTFC_RightFootBlocker DTFC_Blocker" style="position:absolute; top:0; bottom:0;"></div>'+
- '</div>'+
- '</div>'+
- '</div>')[0];
- var nLeft = nSWrapper.childNodes[0];
- var nRight = nSWrapper.childNodes[1];
- this.dom.grid.dt.parentNode.insertBefore( nSWrapper, this.dom.grid.dt );
- nSWrapper.appendChild( this.dom.grid.dt );
- this.dom.grid.wrapper = nSWrapper;
- if ( this.s.iLeftColumns > 0 )
- {
- this.dom.grid.left.wrapper = nLeft;
- this.dom.grid.left.head = nLeft.childNodes[0];
- this.dom.grid.left.body = nLeft.childNodes[1];
- this.dom.grid.left.liner = $('div.DTFC_LeftBodyLiner', nSWrapper)[0];
- nSWrapper.appendChild( nLeft );
- }
- if ( this.s.iRightColumns > 0 )
- {
- this.dom.grid.right.wrapper = nRight;
- this.dom.grid.right.head = nRight.childNodes[0];
- this.dom.grid.right.body = nRight.childNodes[1];
- this.dom.grid.right.liner = $('div.DTFC_RightBodyLiner', nSWrapper)[0];
- nRight.style.right = oOverflow.bar+"px";
- block = $('div.DTFC_RightHeadBlocker', nSWrapper)[0];
- block.style.width = oOverflow.bar+"px";
- block.style.right = -oOverflow.bar+"px";
- this.dom.grid.right.headBlock = block;
- block = $('div.DTFC_RightFootBlocker', nSWrapper)[0];
- block.style.width = oOverflow.bar+"px";
- block.style.right = -oOverflow.bar+"px";
- this.dom.grid.right.footBlock = block;
- nSWrapper.appendChild( nRight );
- }
- if ( this.s.dt.nTFoot )
- {
- this.dom.footer = this.s.dt.nTFoot.parentNode;
- if ( this.s.iLeftColumns > 0 )
- {
- this.dom.grid.left.foot = nLeft.childNodes[2];
- }
- if ( this.s.iRightColumns > 0 )
- {
- this.dom.grid.right.foot = nRight.childNodes[2];
- }
- }
- // RTL support - swap the position of the left and right columns (#48)
- if ( this.s.rtl ) {
- $('div.DTFC_RightHeadBlocker', nSWrapper).css( {
- left: -oOverflow.bar+'px',
- right: ''
- } );
- }
- },
- /**
- * Style and position the grid used for the FixedColumns layout
- * @returns {void}
- * @private
- */
- "_fnGridLayout": function ()
- {
- var that = this;
- var oGrid = this.dom.grid;
- var iWidth = $(oGrid.wrapper).width();
- var iBodyHeight = this.s.dt.nTable.parentNode.offsetHeight;
- var iFullHeight = this.s.dt.nTable.parentNode.parentNode.offsetHeight;
- var oOverflow = this._fnDTOverflow();
- var iLeftWidth = this.s.iLeftWidth;
- var iRightWidth = this.s.iRightWidth;
- var rtl = $(this.dom.body).css('direction') === 'rtl';
- var wrapper;
- var scrollbarAdjust = function ( node, width ) {
- if ( ! oOverflow.bar ) {
- // If there is no scrollbar (Macs) we need to hide the auto scrollbar
- node.style.width = (width+20)+"px";
- node.style.paddingRight = "20px";
- node.style.boxSizing = "border-box";
- }
- else if ( that._firefoxScrollError() ) {
- // See the above function for why this is required
- if ( $(node).height() > 34 ) {
- node.style.width = (width+oOverflow.bar)+"px";
- }
- }
- else {
- // Otherwise just overflow by the scrollbar
- node.style.width = (width+oOverflow.bar)+"px";
- }
- };
- // When x scrolling - don't paint the fixed columns over the x scrollbar
- if ( oOverflow.x )
- {
- iBodyHeight -= oOverflow.bar;
- }
- oGrid.wrapper.style.height = iFullHeight+"px";
- if ( this.s.iLeftColumns > 0 )
- {
- wrapper = oGrid.left.wrapper;
- wrapper.style.width = iLeftWidth+'px';
- wrapper.style.height = '1px';
- // Swap the position of the left and right columns for rtl (#48)
- // This is always up against the edge, scrollbar on the far side
- if ( rtl ) {
- wrapper.style.left = '';
- wrapper.style.right = 0;
- }
- else {
- wrapper.style.left = 0;
- wrapper.style.right = '';
- }
- oGrid.left.body.style.height = iBodyHeight+"px";
- if ( oGrid.left.foot ) {
- oGrid.left.foot.style.top = (oOverflow.x ? oOverflow.bar : 0)+"px"; // shift footer for scrollbar
- }
- scrollbarAdjust( oGrid.left.liner, iLeftWidth );
- oGrid.left.liner.style.height = iBodyHeight+"px";
- oGrid.left.liner.style.maxHeight = iBodyHeight+"px";
- }
- if ( this.s.iRightColumns > 0 )
- {
- wrapper = oGrid.right.wrapper;
- wrapper.style.width = iRightWidth+'px';
- wrapper.style.height = '1px';
- // Need to take account of the vertical scrollbar
- if ( this.s.rtl ) {
- wrapper.style.left = oOverflow.y ? oOverflow.bar+'px' : 0;
- wrapper.style.right = '';
- }
- else {
- wrapper.style.left = '';
- wrapper.style.right = oOverflow.y ? oOverflow.bar+'px' : 0;
- }
- oGrid.right.body.style.height = iBodyHeight+"px";
- if ( oGrid.right.foot ) {
- oGrid.right.foot.style.top = (oOverflow.x ? oOverflow.bar : 0)+"px";
- }
- scrollbarAdjust( oGrid.right.liner, iRightWidth );
- oGrid.right.liner.style.height = iBodyHeight+"px";
- oGrid.right.liner.style.maxHeight = iBodyHeight+"px";
- oGrid.right.headBlock.style.display = oOverflow.y ? 'block' : 'none';
- oGrid.right.footBlock.style.display = oOverflow.y ? 'block' : 'none';
- }
- },
- /**
- * Get information about the DataTable's scrolling state - specifically if the table is scrolling
- * on either the x or y axis, and also the scrollbar width.
- * @returns {object} Information about the DataTables scrolling state with the properties:
- * 'x', 'y' and 'bar'
- * @private
- */
- "_fnDTOverflow": function ()
- {
- var nTable = this.s.dt.nTable;
- var nTableScrollBody = nTable.parentNode;
- var out = {
- "x": false,
- "y": false,
- "bar": this.s.dt.oScroll.iBarWidth
- };
- if ( nTable.offsetWidth > nTableScrollBody.clientWidth )
- {
- out.x = true;
- }
- if ( nTable.offsetHeight > nTableScrollBody.clientHeight )
- {
- out.y = true;
- }
- return out;
- },
- /**
- * Clone and position the fixed columns
- * @returns {void}
- * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true)
- * @private
- */
- "_fnDraw": function ( bAll )
- {
- this._fnGridLayout();
- this._fnCloneLeft( bAll );
- this._fnCloneRight( bAll );
- /* Draw callback function */
- if ( this.s.fnDrawCallback !== null )
- {
- this.s.fnDrawCallback.call( this, this.dom.clone.left, this.dom.clone.right );
- }
- /* Event triggering */
- $(this).trigger( 'draw.dtfc', {
- "leftClone": this.dom.clone.left,
- "rightClone": this.dom.clone.right
- } );
- },
- /**
- * Clone the right columns
- * @returns {void}
- * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true)
- * @private
- */
- "_fnCloneRight": function ( bAll )
- {
- if ( this.s.iRightColumns <= 0 ) {
- return;
- }
- var that = this,
- i, jq,
- aiColumns = [];
- for ( i=this.s.iTableColumns-this.s.iRightColumns ; i<this.s.iTableColumns ; i++ ) {
- if ( this.s.dt.aoColumns[i].bVisible ) {
- aiColumns.push( i );
- }
- }
- this._fnClone( this.dom.clone.right, this.dom.grid.right, aiColumns, bAll );
- },
- /**
- * Clone the left columns
- * @returns {void}
- * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true)
- * @private
- */
- "_fnCloneLeft": function ( bAll )
- {
- if ( this.s.iLeftColumns <= 0 ) {
- return;
- }
- var that = this,
- i, jq,
- aiColumns = [];
- for ( i=0 ; i<this.s.iLeftColumns ; i++ ) {
- if ( this.s.dt.aoColumns[i].bVisible ) {
- aiColumns.push( i );
- }
- }
- this._fnClone( this.dom.clone.left, this.dom.grid.left, aiColumns, bAll );
- },
- /**
- * Make a copy of the layout object for a header or footer element from DataTables. Note that
- * this method will clone the nodes in the layout object.
- * @returns {Array} Copy of the layout array
- * @param {Object} aoOriginal Layout array from DataTables (aoHeader or aoFooter)
- * @param {Object} aiColumns Columns to copy
- * @param {boolean} events Copy cell events or not
- * @private
- */
- "_fnCopyLayout": function ( aoOriginal, aiColumns, events )
- {
- var aReturn = [];
- var aClones = [];
- var aCloned = [];
- for ( var i=0, iLen=aoOriginal.length ; i<iLen ; i++ )
- {
- var aRow = [];
- aRow.nTr = $(aoOriginal[i].nTr).clone(events, false)[0];
- for ( var j=0, jLen=this.s.iTableColumns ; j<jLen ; j++ )
- {
- if ( $.inArray( j, aiColumns ) === -1 )
- {
- continue;
- }
- var iCloned = $.inArray( aoOriginal[i][j].cell, aCloned );
- if ( iCloned === -1 )
- {
- var nClone = $(aoOriginal[i][j].cell).clone(events, false)[0];
- aClones.push( nClone );
- aCloned.push( aoOriginal[i][j].cell );
- aRow.push( {
- "cell": nClone,
- "unique": aoOriginal[i][j].unique
- } );
- }
- else
- {
- aRow.push( {
- "cell": aClones[ iCloned ],
- "unique": aoOriginal[i][j].unique
- } );
- }
- }
- aReturn.push( aRow );
- }
- return aReturn;
- },
- /**
- * Clone the DataTable nodes and place them in the DOM (sized correctly)
- * @returns {void}
- * @param {Object} oClone Object containing the header, footer and body cloned DOM elements
- * @param {Object} oGrid Grid object containing the display grid elements for the cloned
- * column (left or right)
- * @param {Array} aiColumns Column indexes which should be operated on from the DataTable
- * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true)
- * @private
- */
- "_fnClone": function ( oClone, oGrid, aiColumns, bAll )
- {
- var that = this,
- i, iLen, j, jLen, jq, nTarget, iColumn, nClone, iIndex, aoCloneLayout,
- jqCloneThead, aoFixedHeader,
- dt = this.s.dt;
- /*
- * Header
- */
- if ( bAll )
- {
- $(oClone.header).remove();
- oClone.header = $(this.dom.header).clone(true, false)[0];
- oClone.header.className += " DTFC_Cloned";
- oClone.header.style.width = "100%";
- oGrid.head.appendChild( oClone.header );
- /* Copy the DataTables layout cache for the header for our floating column */
- aoCloneLayout = this._fnCopyLayout( dt.aoHeader, aiColumns, true );
- jqCloneThead = $('>thead', oClone.header);
- jqCloneThead.empty();
- /* Add the created cloned TR elements to the table */
- for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
- {
- jqCloneThead[0].appendChild( aoCloneLayout[i].nTr );
- }
- /* Use the handy _fnDrawHead function in DataTables to do the rowspan/colspan
- * calculations for us
- */
- dt.oApi._fnDrawHead( dt, aoCloneLayout, true );
- }
- else
- {
- /* To ensure that we copy cell classes exactly, regardless of colspan, multiple rows
- * etc, we make a copy of the header from the DataTable again, but don't insert the
- * cloned cells, just copy the classes across. To get the matching layout for the
- * fixed component, we use the DataTables _fnDetectHeader method, allowing 1:1 mapping
- */
- aoCloneLayout = this._fnCopyLayout( dt.aoHeader, aiColumns, false );
- aoFixedHeader=[];
- dt.oApi._fnDetectHeader( aoFixedHeader, $('>thead', oClone.header)[0] );
- for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
- {
- for ( j=0, jLen=aoCloneLayout[i].length ; j<jLen ; j++ )
- {
- aoFixedHeader[i][j].cell.className = aoCloneLayout[i][j].cell.className;
- // If jQuery UI theming is used we need to copy those elements as well
- $('span.DataTables_sort_icon', aoFixedHeader[i][j].cell).each( function () {
- this.className = $('span.DataTables_sort_icon', aoCloneLayout[i][j].cell)[0].className;
- } );
- }
- }
- }
- this._fnEqualiseHeights( 'thead', this.dom.header, oClone.header );
- /*
- * Body
- */
- if ( this.s.sHeightMatch == 'auto' )
- {
- /* Remove any heights which have been applied already and let the browser figure it out */
- $('>tbody>tr', that.dom.body).css('height', 'auto');
- }
- if ( oClone.body !== null )
- {
- $(oClone.body).remove();
- oClone.body = null;
- }
- oClone.body = $(this.dom.body).clone(true)[0];
- oClone.body.className += " DTFC_Cloned";
- oClone.body.style.paddingBottom = dt.oScroll.iBarWidth+"px";
- oClone.body.style.marginBottom = (dt.oScroll.iBarWidth*2)+"px"; /* For IE */
- if ( oClone.body.getAttribute('id') !== null )
- {
- oClone.body.removeAttribute('id');
- }
- $('>thead>tr', oClone.body).empty();
- $('>tfoot', oClone.body).remove();
- var nBody = $('tbody', oClone.body)[0];
- $(nBody).empty();
- if ( dt.aiDisplay.length > 0 )
- {
- /* Copy the DataTables' header elements to force the column width in exactly the
- * same way that DataTables does it - have the header element, apply the width and
- * colapse it down
- */
- var nInnerThead = $('>thead>tr', oClone.body)[0];
- for ( iIndex=0 ; iIndex<aiColumns.length ; iIndex++ )
- {
- iColumn = aiColumns[iIndex];
- nClone = $(dt.aoColumns[iColumn].nTh).clone(true)[0];
- nClone.innerHTML = "";
- var oStyle = nClone.style;
- oStyle.paddingTop = "0";
- oStyle.paddingBottom = "0";
- oStyle.borderTopWidth = "0";
- oStyle.borderBottomWidth = "0";
- oStyle.height = 0;
- oStyle.width = that.s.aiInnerWidths[iColumn]+"px";
- nInnerThead.appendChild( nClone );
- }
- /* Add in the tbody elements, cloning form the master table */
- $('>tbody>tr', that.dom.body).each( function (z) {
- var i = that.s.dt.oFeatures.bServerSide===false ?
- that.s.dt.aiDisplay[ that.s.dt._iDisplayStart+z ] : z;
- var aTds = that.s.dt.aoData[ i ].anCells || $(this).children('td, th');
- var n = this.cloneNode(false);
- n.removeAttribute('id');
- n.setAttribute( 'data-dt-row', i );
- for ( iIndex=0 ; iIndex<aiColumns.length ; iIndex++ )
- {
- iColumn = aiColumns[iIndex];
- if ( aTds.length > 0 )
- {
- nClone = $( aTds[iColumn] ).clone(true, true)[0];
- nClone.removeAttribute( 'id' );
- nClone.setAttribute( 'data-dt-row', i );
- nClone.setAttribute( 'data-dt-column', dt.oApi._fnVisibleToColumnIndex( dt, iColumn ) );
- n.appendChild( nClone );
- }
- }
- nBody.appendChild( n );
- } );
- }
- else
- {
- $('>tbody>tr', that.dom.body).each( function (z) {
- nClone = this.cloneNode(true);
- nClone.className += ' DTFC_NoData';
- $('td', nClone).html('');
- nBody.appendChild( nClone );
- } );
- }
- oClone.body.style.width = "100%";
- oClone.body.style.margin = "0";
- oClone.body.style.padding = "0";
- // Interop with Scroller - need to use a height forcing element in the
- // scrolling area in the same way that Scroller does in the body scroll.
- if ( dt.oScroller !== undefined )
- {
- var scrollerForcer = dt.oScroller.dom.force;
- if ( ! oGrid.forcer ) {
- oGrid.forcer = scrollerForcer.cloneNode( true );
- oGrid.liner.appendChild( oGrid.forcer );
- }
- else {
- oGrid.forcer.style.height = scrollerForcer.style.height;
- }
- }
- oGrid.liner.appendChild( oClone.body );
- this._fnEqualiseHeights( 'tbody', that.dom.body, oClone.body );
- /*
- * Footer
- */
- if ( dt.nTFoot !== null )
- {
- if ( bAll )
- {
- if ( oClone.footer !== null )
- {
- oClone.footer.parentNode.removeChild( oClone.footer );
- }
- oClone.footer = $(this.dom.footer).clone(true, true)[0];
- oClone.footer.className += " DTFC_Cloned";
- oClone.footer.style.width = "100%";
- oGrid.foot.appendChild( oClone.footer );
- /* Copy the footer just like we do for the header */
- aoCloneLayout = this._fnCopyLayout( dt.aoFooter, aiColumns, true );
- var jqCloneTfoot = $('>tfoot', oClone.footer);
- jqCloneTfoot.empty();
- for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
- {
- jqCloneTfoot[0].appendChild( aoCloneLayout[i].nTr );
- }
- dt.oApi._fnDrawHead( dt, aoCloneLayout, true );
- }
- else
- {
- aoCloneLayout = this._fnCopyLayout( dt.aoFooter, aiColumns, false );
- var aoCurrFooter=[];
- dt.oApi._fnDetectHeader( aoCurrFooter, $('>tfoot', oClone.footer)[0] );
- for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
- {
- for ( j=0, jLen=aoCloneLayout[i].length ; j<jLen ; j++ )
- {
- aoCurrFooter[i][j].cell.className = aoCloneLayout[i][j].cell.className;
- }
- }
- }
- this._fnEqualiseHeights( 'tfoot', this.dom.footer, oClone.footer );
- }
- /* Equalise the column widths between the header footer and body - body get's priority */
- var anUnique = dt.oApi._fnGetUniqueThs( dt, $('>thead', oClone.header)[0] );
- $(anUnique).each( function (i) {
- iColumn = aiColumns[i];
- this.style.width = that.s.aiInnerWidths[iColumn]+"px";
- } );
- if ( that.s.dt.nTFoot !== null )
- {
- anUnique = dt.oApi._fnGetUniqueThs( dt, $('>tfoot', oClone.footer)[0] );
- $(anUnique).each( function (i) {
- iColumn = aiColumns[i];
- this.style.width = that.s.aiInnerWidths[iColumn]+"px";
- } );
- }
- },
- /**
- * From a given table node (THEAD etc), get a list of TR direct child elements
- * @param {Node} nIn Table element to search for TR elements (THEAD, TBODY or TFOOT element)
- * @returns {Array} List of TR elements found
- * @private
- */
- "_fnGetTrNodes": function ( nIn )
- {
- var aOut = [];
- for ( var i=0, iLen=nIn.childNodes.length ; i<iLen ; i++ )
- {
- if ( nIn.childNodes[i].nodeName.toUpperCase() == "TR" )
- {
- aOut.push( nIn.childNodes[i] );
- }
- }
- return aOut;
- },
- /**
- * Equalise the heights of the rows in a given table node in a cross browser way
- * @returns {void}
- * @param {String} nodeName Node type - thead, tbody or tfoot
- * @param {Node} original Original node to take the heights from
- * @param {Node} clone Copy the heights to
- * @private
- */
- "_fnEqualiseHeights": function ( nodeName, original, clone )
- {
- if ( this.s.sHeightMatch == 'none' && nodeName !== 'thead' && nodeName !== 'tfoot' )
- {
- return;
- }
- var that = this,
- i, iLen, iHeight, iHeight2, iHeightOriginal, iHeightClone,
- rootOriginal = original.getElementsByTagName(nodeName)[0],
- rootClone = clone.getElementsByTagName(nodeName)[0],
- jqBoxHack = $('>'+nodeName+'>tr:eq(0)', original).children(':first'),
- iBoxHack = jqBoxHack.outerHeight() - jqBoxHack.height(),
- anOriginal = this._fnGetTrNodes( rootOriginal ),
- anClone = this._fnGetTrNodes( rootClone ),
- heights = [];
- for ( i=0, iLen=anClone.length ; i<iLen ; i++ )
- {
- iHeightOriginal = anOriginal[i].offsetHeight;
- iHeightClone = anClone[i].offsetHeight;
- iHeight = iHeightClone > iHeightOriginal ? iHeightClone : iHeightOriginal;
- if ( this.s.sHeightMatch == 'semiauto' )
- {
- anOriginal[i]._DTTC_iHeight = iHeight;
- }
- heights.push( iHeight );
- }
- for ( i=0, iLen=anClone.length ; i<iLen ; i++ )
- {
- anClone[i].style.height = heights[i]+"px";
- anOriginal[i].style.height = heights[i]+"px";
- }
- },
- /**
- * Determine if the UA suffers from Firefox's overflow:scroll scrollbars
- * not being shown bug.
- *
- * Firefox doesn't draw scrollbars, even if it is told to using
- * overflow:scroll, if the div is less than 34px height. See bugs 292284 and
- * 781885. Using UA detection here since this is particularly hard to detect
- * using objects - its a straight up rendering error in Firefox.
- *
- * @return {boolean} True if Firefox error is present, false otherwise
- */
- _firefoxScrollError: function () {
- if ( _firefoxScroll === undefined ) {
- var test = $('<div/>')
- .css( {
- position: 'absolute',
- top: 0,
- left: 0,
- height: 10,
- width: 50,
- overflow: 'scroll'
- } )
- .appendTo( 'body' );
- // Make sure this doesn't apply on Macs with 0 width scrollbars
- _firefoxScroll = (
- test[0].clientWidth === test[0].offsetWidth && this._fnDTOverflow().bar !== 0
- );
- test.remove();
- }
- return _firefoxScroll;
- }
- } );
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Statics
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * FixedColumns default settings for initialisation
- * @name FixedColumns.defaults
- * @namespace
- * @static
- */
- FixedColumns.defaults = /** @lends FixedColumns.defaults */{
- /**
- * Number of left hand columns to fix in position
- * @type int
- * @default 1
- * @static
- * @example
- * var = $('#example').dataTable( {
- * "scrollX": "100%"
- * } );
- * new $.fn.dataTable.fixedColumns( table, {
- * "leftColumns": 2
- * } );
- */
- "iLeftColumns": 1,
- /**
- * Number of right hand columns to fix in position
- * @type int
- * @default 0
- * @static
- * @example
- * var table = $('#example').dataTable( {
- * "scrollX": "100%"
- * } );
- * new $.fn.dataTable.fixedColumns( table, {
- * "rightColumns": 1
- * } );
- */
- "iRightColumns": 0,
- /**
- * Draw callback function which is called when FixedColumns has redrawn the fixed assets
- * @type function(object, object):void
- * @default null
- * @static
- * @example
- * var table = $('#example').dataTable( {
- * "scrollX": "100%"
- * } );
- * new $.fn.dataTable.fixedColumns( table, {
- * "drawCallback": function () {
- * alert( "FixedColumns redraw" );
- * }
- * } );
- */
- "fnDrawCallback": null,
- /**
- * Height matching algorthim to use. This can be "none" which will result in no height
- * matching being applied by FixedColumns (height matching could be forced by CSS in this
- * case), "semiauto" whereby the height calculation will be performed once, and the result
- * cached to be used again (fnRecalculateHeight can be used to force recalculation), or
- * "auto" when height matching is performed on every draw (slowest but must accurate)
- * @type string
- * @default semiauto
- * @static
- * @example
- * var table = $('#example').dataTable( {
- * "scrollX": "100%"
- * } );
- * new $.fn.dataTable.fixedColumns( table, {
- * "heightMatch": "auto"
- * } );
- */
- "sHeightMatch": "semiauto"
- };
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constants
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * FixedColumns version
- * @name FixedColumns.version
- * @type String
- * @default See code
- * @static
- */
- FixedColumns.version = "3.2.4";
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables API integration
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- DataTable.Api.register( 'fixedColumns()', function () {
- return this;
- } );
- DataTable.Api.register( 'fixedColumns().update()', function () {
- return this.iterator( 'table', function ( ctx ) {
- if ( ctx._oFixedColumns ) {
- ctx._oFixedColumns.fnUpdate();
- }
- } );
- } );
- DataTable.Api.register( 'fixedColumns().relayout()', function () {
- return this.iterator( 'table', function ( ctx ) {
- if ( ctx._oFixedColumns ) {
- ctx._oFixedColumns.fnRedrawLayout();
- }
- } );
- } );
- DataTable.Api.register( 'rows().recalcHeight()', function () {
- return this.iterator( 'row', function ( ctx, idx ) {
- if ( ctx._oFixedColumns ) {
- ctx._oFixedColumns.fnRecalculateHeight( this.row(idx).node() );
- }
- } );
- } );
- DataTable.Api.register( 'fixedColumns().rowIndex()', function ( row ) {
- row = $(row);
- return row.parents('.DTFC_Cloned').length ?
- this.rows( { page: 'current' } ).indexes()[ row.index() ] :
- this.row( row ).index();
- } );
- DataTable.Api.register( 'fixedColumns().cellIndex()', function ( cell ) {
- cell = $(cell);
- if ( cell.parents('.DTFC_Cloned').length ) {
- var rowClonedIdx = cell.parent().index();
- var rowIdx = this.rows( { page: 'current' } ).indexes()[ rowClonedIdx ];
- var columnIdx;
- if ( cell.parents('.DTFC_LeftWrapper').length ) {
- columnIdx = cell.index();
- }
- else {
- var columns = this.columns().flatten().length;
- columnIdx = columns - this.context[0]._oFixedColumns.s.iRightColumns + cell.index();
- }
- return {
- row: rowIdx,
- column: this.column.index( 'toData', columnIdx ),
- columnVisible: columnIdx
- };
- }
- else {
- return this.cell( cell ).index();
- }
- } );
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Initialisation
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- // Attach a listener to the document which listens for DataTables initialisation
- // events so we can automatically initialise
- $(document).on( 'init.dt.fixedColumns', function (e, settings) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- var init = settings.oInit.fixedColumns;
- var defaults = DataTable.defaults.fixedColumns;
- if ( init || defaults ) {
- var opts = $.extend( {}, init, defaults );
- if ( init !== false ) {
- new FixedColumns( settings, opts );
- }
- }
- } );
- // Make FixedColumns accessible from the DataTables instance
- $.fn.dataTable.FixedColumns = FixedColumns;
- $.fn.DataTable.FixedColumns = FixedColumns;
- return FixedColumns;
- }));
- /*! FixedHeader 3.1.3
- * ©2009-2017 SpryMedia Ltd - datatables.net/license
- */
- /**
- * @summary FixedHeader
- * @description Fix a table's header or footer, so it is always visible while
- * scrolling
- * @version 3.1.3
- * @file dataTables.fixedHeader.js
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
- * @contact www.sprymedia.co.uk/contact
- * @copyright Copyright 2009-2017 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license/mit
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- var _instCounter = 0;
- var FixedHeader = function ( dt, config ) {
- // Sanity check - you just know it will happen
- if ( ! (this instanceof FixedHeader) ) {
- throw "FixedHeader must be initialised with the 'new' keyword.";
- }
- // Allow a boolean true for defaults
- if ( config === true ) {
- config = {};
- }
- dt = new DataTable.Api( dt );
- this.c = $.extend( true, {}, FixedHeader.defaults, config );
- this.s = {
- dt: dt,
- position: {
- theadTop: 0,
- tbodyTop: 0,
- tfootTop: 0,
- tfootBottom: 0,
- width: 0,
- left: 0,
- tfootHeight: 0,
- theadHeight: 0,
- windowHeight: $(window).height(),
- visible: true
- },
- headerMode: null,
- footerMode: null,
- autoWidth: dt.settings()[0].oFeatures.bAutoWidth,
- namespace: '.dtfc'+(_instCounter++),
- scrollLeft: {
- header: -1,
- footer: -1
- },
- enable: true
- };
- this.dom = {
- floatingHeader: null,
- thead: $(dt.table().header()),
- tbody: $(dt.table().body()),
- tfoot: $(dt.table().footer()),
- header: {
- host: null,
- floating: null,
- placeholder: null
- },
- footer: {
- host: null,
- floating: null,
- placeholder: null
- }
- };
- this.dom.header.host = this.dom.thead.parent();
- this.dom.footer.host = this.dom.tfoot.parent();
- var dtSettings = dt.settings()[0];
- if ( dtSettings._fixedHeader ) {
- throw "FixedHeader already initialised on table "+dtSettings.nTable.id;
- }
- dtSettings._fixedHeader = this;
- this._constructor();
- };
- /*
- * Variable: FixedHeader
- * Purpose: Prototype for FixedHeader
- * Scope: global
- */
- $.extend( FixedHeader.prototype, {
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * API methods
- */
-
- /**
- * Enable / disable the fixed elements
- *
- * @param {boolean} enable `true` to enable, `false` to disable
- */
- enable: function ( enable )
- {
- this.s.enable = enable;
- if ( this.c.header ) {
- this._modeChange( 'in-place', 'header', true );
- }
- if ( this.c.footer && this.dom.tfoot.length ) {
- this._modeChange( 'in-place', 'footer', true );
- }
- this.update();
- },
-
- /**
- * Set header offset
- *
- * @param {int} new value for headerOffset
- */
- headerOffset: function ( offset )
- {
- if ( offset !== undefined ) {
- this.c.headerOffset = offset;
- this.update();
- }
- return this.c.headerOffset;
- },
-
- /**
- * Set footer offset
- *
- * @param {int} new value for footerOffset
- */
- footerOffset: function ( offset )
- {
- if ( offset !== undefined ) {
- this.c.footerOffset = offset;
- this.update();
- }
- return this.c.footerOffset;
- },
-
- /**
- * Recalculate the position of the fixed elements and force them into place
- */
- update: function ()
- {
- this._positions();
- this._scroll( true );
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constructor
- */
-
- /**
- * FixedHeader constructor - adding the required event listeners and
- * simple initialisation
- *
- * @private
- */
- _constructor: function ()
- {
- var that = this;
- var dt = this.s.dt;
- $(window)
- .on( 'scroll'+this.s.namespace, function () {
- that._scroll();
- } )
- .on( 'resize'+this.s.namespace, function () {
- that.s.position.windowHeight = $(window).height();
- that.update();
- } );
- var autoHeader = $('.fh-fixedHeader');
- if ( ! this.c.headerOffset && autoHeader.length ) {
- this.c.headerOffset = autoHeader.outerHeight();
- }
- var autoFooter = $('.fh-fixedFooter');
- if ( ! this.c.footerOffset && autoFooter.length ) {
- this.c.footerOffset = autoFooter.outerHeight();
- }
- dt.on( 'column-reorder.dt.dtfc column-visibility.dt.dtfc draw.dt.dtfc column-sizing.dt.dtfc', function () {
- that.update();
- } );
- dt.on( 'destroy.dtfc', function () {
- dt.off( '.dtfc' );
- $(window).off( that.s.namespace );
- } );
- this._positions();
- this._scroll();
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Private methods
- */
- /**
- * Clone a fixed item to act as a place holder for the original element
- * which is moved into a clone of the table element, and moved around the
- * document to give the fixed effect.
- *
- * @param {string} item 'header' or 'footer'
- * @param {boolean} force Force the clone to happen, or allow automatic
- * decision (reuse existing if available)
- * @private
- */
- _clone: function ( item, force )
- {
- var dt = this.s.dt;
- var itemDom = this.dom[ item ];
- var itemElement = item === 'header' ?
- this.dom.thead :
- this.dom.tfoot;
- if ( ! force && itemDom.floating ) {
- // existing floating element - reuse it
- itemDom.floating.removeClass( 'fixedHeader-floating fixedHeader-locked' );
- }
- else {
- if ( itemDom.floating ) {
- itemDom.placeholder.remove();
- this._unsize( item );
- itemDom.floating.children().detach();
- itemDom.floating.remove();
- }
- itemDom.floating = $( dt.table().node().cloneNode( false ) )
- .css( 'table-layout', 'fixed' )
- .removeAttr( 'id' )
- .append( itemElement )
- .appendTo( 'body' );
- // Insert a fake thead/tfoot into the DataTable to stop it jumping around
- itemDom.placeholder = itemElement.clone( false )
- itemDom.placeholder
- .find( '*[id]' )
- .removeAttr( 'id' );
- itemDom.host.prepend( itemDom.placeholder );
- // Clone widths
- this._matchWidths( itemDom.placeholder, itemDom.floating );
- }
- },
- /**
- * Copy widths from the cells in one element to another. This is required
- * for the footer as the footer in the main table takes its sizes from the
- * header columns. That isn't present in the footer so to have it still
- * align correctly, the sizes need to be copied over. It is also required
- * for the header when auto width is not enabled
- *
- * @param {jQuery} from Copy widths from
- * @param {jQuery} to Copy widths to
- * @private
- */
- _matchWidths: function ( from, to ) {
- var get = function ( name ) {
- return $(name, from)
- .map( function () {
- return $(this).width();
- } ).toArray();
- };
- var set = function ( name, toWidths ) {
- $(name, to).each( function ( i ) {
- $(this).css( {
- width: toWidths[i],
- minWidth: toWidths[i]
- } );
- } );
- };
- var thWidths = get( 'th' );
- var tdWidths = get( 'td' );
- set( 'th', thWidths );
- set( 'td', tdWidths );
- },
- /**
- * Remove assigned widths from the cells in an element. This is required
- * when inserting the footer back into the main table so the size is defined
- * by the header columns and also when auto width is disabled in the
- * DataTable.
- *
- * @param {string} item The `header` or `footer`
- * @private
- */
- _unsize: function ( item ) {
- var el = this.dom[ item ].floating;
- if ( el && (item === 'footer' || (item === 'header' && ! this.s.autoWidth)) ) {
- $('th, td', el).css( {
- width: '',
- minWidth: ''
- } );
- }
- else if ( el && item === 'header' ) {
- $('th, td', el).css( 'min-width', '' );
- }
- },
- /**
- * Reposition the floating elements to take account of horizontal page
- * scroll
- *
- * @param {string} item The `header` or `footer`
- * @param {int} scrollLeft Document scrollLeft
- * @private
- */
- _horizontal: function ( item, scrollLeft )
- {
- var itemDom = this.dom[ item ];
- var position = this.s.position;
- var lastScrollLeft = this.s.scrollLeft;
- if ( itemDom.floating && lastScrollLeft[ item ] !== scrollLeft ) {
- itemDom.floating.css( 'left', position.left - scrollLeft );
- lastScrollLeft[ item ] = scrollLeft;
- }
- },
- /**
- * Change from one display mode to another. Each fixed item can be in one
- * of:
- *
- * * `in-place` - In the main DataTable
- * * `in` - Floating over the DataTable
- * * `below` - (Header only) Fixed to the bottom of the table body
- * * `above` - (Footer only) Fixed to the top of the table body
- *
- * @param {string} mode Mode that the item should be shown in
- * @param {string} item 'header' or 'footer'
- * @param {boolean} forceChange Force a redraw of the mode, even if already
- * in that mode.
- * @private
- */
- _modeChange: function ( mode, item, forceChange )
- {
- var dt = this.s.dt;
- var itemDom = this.dom[ item ];
- var position = this.s.position;
- // Record focus. Browser's will cause input elements to loose focus if
- // they are inserted else where in the doc
- var tablePart = this.dom[ item==='footer' ? 'tfoot' : 'thead' ];
- var focus = $.contains( tablePart[0], document.activeElement ) ?
- document.activeElement :
- null;
- if ( mode === 'in-place' ) {
- // Insert the header back into the table's real header
- if ( itemDom.placeholder ) {
- itemDom.placeholder.remove();
- itemDom.placeholder = null;
- }
- this._unsize( item );
- if ( item === 'header' ) {
- itemDom.host.prepend( this.dom.thead );
- }
- else {
- itemDom.host.append( this.dom.tfoot );
- }
- if ( itemDom.floating ) {
- itemDom.floating.remove();
- itemDom.floating = null;
- }
- }
- else if ( mode === 'in' ) {
- // Remove the header from the read header and insert into a fixed
- // positioned floating table clone
- this._clone( item, forceChange );
- itemDom.floating
- .addClass( 'fixedHeader-floating' )
- .css( item === 'header' ? 'top' : 'bottom', this.c[item+'Offset'] )
- .css( 'left', position.left+'px' )
- .css( 'width', position.width+'px' );
- if ( item === 'footer' ) {
- itemDom.floating.css( 'top', '' );
- }
- }
- else if ( mode === 'below' ) { // only used for the header
- // Fix the position of the floating header at base of the table body
- this._clone( item, forceChange );
- itemDom.floating
- .addClass( 'fixedHeader-locked' )
- .css( 'top', position.tfootTop - position.theadHeight )
- .css( 'left', position.left+'px' )
- .css( 'width', position.width+'px' );
- }
- else if ( mode === 'above' ) { // only used for the footer
- // Fix the position of the floating footer at top of the table body
- this._clone( item, forceChange );
- itemDom.floating
- .addClass( 'fixedHeader-locked' )
- .css( 'top', position.tbodyTop )
- .css( 'left', position.left+'px' )
- .css( 'width', position.width+'px' );
- }
- // Restore focus if it was lost
- if ( focus && focus !== document.activeElement ) {
- focus.focus();
- }
- this.s.scrollLeft.header = -1;
- this.s.scrollLeft.footer = -1;
- this.s[item+'Mode'] = mode;
- },
- /**
- * Cache the positional information that is required for the mode
- * calculations that FixedHeader performs.
- *
- * @private
- */
- _positions: function ()
- {
- var dt = this.s.dt;
- var table = dt.table();
- var position = this.s.position;
- var dom = this.dom;
- var tableNode = $(table.node());
- // Need to use the header and footer that are in the main table,
- // regardless of if they are clones, since they hold the positions we
- // want to measure from
- var thead = tableNode.children('thead');
- var tfoot = tableNode.children('tfoot');
- var tbody = dom.tbody;
- position.visible = tableNode.is(':visible');
- position.width = tableNode.outerWidth();
- position.left = tableNode.offset().left;
- position.theadTop = thead.offset().top;
- position.tbodyTop = tbody.offset().top;
- position.theadHeight = position.tbodyTop - position.theadTop;
- if ( tfoot.length ) {
- position.tfootTop = tfoot.offset().top;
- position.tfootBottom = position.tfootTop + tfoot.outerHeight();
- position.tfootHeight = position.tfootBottom - position.tfootTop;
- }
- else {
- position.tfootTop = position.tbodyTop + tbody.outerHeight();
- position.tfootBottom = position.tfootTop;
- position.tfootHeight = position.tfootTop;
- }
- },
- /**
- * Mode calculation - determine what mode the fixed items should be placed
- * into.
- *
- * @param {boolean} forceChange Force a redraw of the mode, even if already
- * in that mode.
- * @private
- */
- _scroll: function ( forceChange )
- {
- var windowTop = $(document).scrollTop();
- var windowLeft = $(document).scrollLeft();
- var position = this.s.position;
- var headerMode, footerMode;
- if ( ! this.s.enable ) {
- return;
- }
- if ( this.c.header ) {
- if ( ! position.visible || windowTop <= position.theadTop - this.c.headerOffset ) {
- headerMode = 'in-place';
- }
- else if ( windowTop <= position.tfootTop - position.theadHeight - this.c.headerOffset ) {
- headerMode = 'in';
- }
- else {
- headerMode = 'below';
- }
- if ( forceChange || headerMode !== this.s.headerMode ) {
- this._modeChange( headerMode, 'header', forceChange );
- }
- this._horizontal( 'header', windowLeft );
- }
- if ( this.c.footer && this.dom.tfoot.length ) {
- if ( ! position.visible || windowTop + position.windowHeight >= position.tfootBottom + this.c.footerOffset ) {
- footerMode = 'in-place';
- }
- else if ( position.windowHeight + windowTop > position.tbodyTop + position.tfootHeight + this.c.footerOffset ) {
- footerMode = 'in';
- }
- else {
- footerMode = 'above';
- }
- if ( forceChange || footerMode !== this.s.footerMode ) {
- this._modeChange( footerMode, 'footer', forceChange );
- }
- this._horizontal( 'footer', windowLeft );
- }
- }
- } );
- /**
- * Version
- * @type {String}
- * @static
- */
- FixedHeader.version = "3.1.3";
- /**
- * Defaults
- * @type {Object}
- * @static
- */
- FixedHeader.defaults = {
- header: true,
- footer: false,
- headerOffset: 0,
- footerOffset: 0
- };
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables interfaces
- */
- // Attach for constructor access
- $.fn.dataTable.FixedHeader = FixedHeader;
- $.fn.DataTable.FixedHeader = FixedHeader;
- // DataTables creation - check if the FixedHeader option has been defined on the
- // table and if so, initialise
- $(document).on( 'init.dt.dtfh', function (e, settings, json) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- var init = settings.oInit.fixedHeader;
- var defaults = DataTable.defaults.fixedHeader;
- if ( (init || defaults) && ! settings._fixedHeader ) {
- var opts = $.extend( {}, defaults, init );
- if ( init !== false ) {
- new FixedHeader( settings, opts );
- }
- }
- } );
- // DataTables API methods
- DataTable.Api.register( 'fixedHeader()', function () {} );
- DataTable.Api.register( 'fixedHeader.adjust()', function () {
- return this.iterator( 'table', function ( ctx ) {
- var fh = ctx._fixedHeader;
- if ( fh ) {
- fh.update();
- }
- } );
- } );
- DataTable.Api.register( 'fixedHeader.enable()', function ( flag ) {
- return this.iterator( 'table', function ( ctx ) {
- var fh = ctx._fixedHeader;
- flag = ( flag !== undefined ? flag : true );
- if ( fh && flag !== fh.s.enable ) {
- fh.enable( flag );
- }
- } );
- } );
- DataTable.Api.register( 'fixedHeader.disable()', function ( ) {
- return this.iterator( 'table', function ( ctx ) {
- var fh = ctx._fixedHeader;
- if ( fh && fh.s.enable ) {
- fh.enable( false );
- }
- } );
- } );
- $.each( ['header', 'footer'], function ( i, el ) {
- DataTable.Api.register( 'fixedHeader.'+el+'Offset()', function ( offset ) {
- var ctx = this.context;
- if ( offset === undefined ) {
- return ctx.length && ctx[0]._fixedHeader ?
- ctx[0]._fixedHeader[el +'Offset']() :
- undefined;
- }
- return this.iterator( 'table', function ( ctx ) {
- var fh = ctx._fixedHeader;
- if ( fh ) {
- fh[ el +'Offset' ]( offset );
- }
- } );
- } );
- } );
- return FixedHeader;
- }));
- /*! KeyTable 2.3.2
- * ©2009-2017 SpryMedia Ltd - datatables.net/license
- */
- /**
- * @summary KeyTable
- * @description Spreadsheet like keyboard navigation for DataTables
- * @version 2.3.2
- * @file dataTables.keyTable.js
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
- * @contact www.sprymedia.co.uk/contact
- * @copyright Copyright 2009-2017 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license/mit
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- var KeyTable = function ( dt, opts ) {
- // Sanity check that we are using DataTables 1.10 or newer
- if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) {
- throw 'KeyTable requires DataTables 1.10.8 or newer';
- }
- // User and defaults configuration object
- this.c = $.extend( true, {},
- DataTable.defaults.keyTable,
- KeyTable.defaults,
- opts
- );
- // Internal settings
- this.s = {
- /** @type {DataTable.Api} DataTables' API instance */
- dt: new DataTable.Api( dt ),
- enable: true,
- /** @type {bool} Flag for if a draw is triggered by focus */
- focusDraw: false,
- /** @type {bool} Flag to indicate when waiting for a draw to happen.
- * Will ignore key presses at this point
- */
- waitingForDraw: false,
- /** @type {object} Information about the last cell that was focused */
- lastFocus: null
- };
- // DOM items
- this.dom = {
- };
- // Check if row reorder has already been initialised on this table
- var settings = this.s.dt.settings()[0];
- var exisiting = settings.keytable;
- if ( exisiting ) {
- return exisiting;
- }
- settings.keytable = this;
- this._constructor();
- };
- $.extend( KeyTable.prototype, {
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * API methods for DataTables API interface
- */
- /**
- * Blur the table's cell focus
- */
- blur: function ()
- {
- this._blur();
- },
- /**
- * Enable cell focus for the table
- *
- * @param {string} state Can be `true`, `false` or `-string navigation-only`
- */
- enable: function ( state )
- {
- this.s.enable = state;
- },
- /**
- * Focus on a cell
- * @param {integer} row Row index
- * @param {integer} column Column index
- */
- focus: function ( row, column )
- {
- this._focus( this.s.dt.cell( row, column ) );
- },
- /**
- * Is the cell focused
- * @param {object} cell Cell index to check
- * @returns {boolean} true if focused, false otherwise
- */
- focused: function ( cell )
- {
- var lastFocus = this.s.lastFocus;
- if ( ! lastFocus ) {
- return false;
- }
- var lastIdx = this.s.lastFocus.cell.index();
- return cell.row === lastIdx.row && cell.column === lastIdx.column;
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constructor
- */
- /**
- * Initialise the KeyTable instance
- *
- * @private
- */
- _constructor: function ()
- {
- this._tabInput();
- var that = this;
- var dt = this.s.dt;
- var table = $( dt.table().node() );
- // Need to be able to calculate the cell positions relative to the table
- if ( table.css('position') === 'static' ) {
- table.css( 'position', 'relative' );
- }
- // Click to focus
- $( dt.table().body() ).on( 'click.keyTable', 'th, td', function (e) {
- if ( that.s.enable === false ) {
- return;
- }
- var cell = dt.cell( this );
- if ( ! cell.any() ) {
- return;
- }
- that._focus( cell, null, false, e );
- } );
- // Key events
- $( document ).on( 'keydown.keyTable', function (e) {
- that._key( e );
- } );
- // Click blur
- if ( this.c.blurable ) {
- $( document ).on( 'mousedown.keyTable', function ( e ) {
- // Click on the search input will blur focus
- if ( $(e.target).parents( '.dataTables_filter' ).length ) {
- that._blur();
- }
- // If the click was inside the DataTables container, don't blur
- if ( $(e.target).parents().filter( dt.table().container() ).length ) {
- return;
- }
- // Don't blur in Editor form
- if ( $(e.target).parents('div.DTE').length ) {
- return;
- }
- // Or an Editor date input
- if ( $(e.target).parents('div.editor-datetime').length ) {
- return;
- }
- //If the click was inside the fixed columns container, don't blur
- if ( $(e.target).parents().filter('.DTFC_Cloned').length ) {
- return;
- }
- that._blur();
- } );
- }
- if ( this.c.editor ) {
- var editor = this.c.editor;
- // Need to disable KeyTable when the main editor is shown
- editor.on( 'open.keyTableMain', function (e, mode, action) {
- if ( mode !== 'inline' && that.s.enable ) {
- that.enable( false );
- editor.one( 'close.keyTable', function () {
- that.enable( true );
- } );
- }
- } );
- if ( this.c.editOnFocus ) {
- dt.on( 'key-focus.keyTable key-refocus.keyTable', function ( e, dt, cell, orig ) {
- that._editor( null, orig );
- } );
- }
- // Activate Editor when a key is pressed (will be ignored, if
- // already active).
- dt.on( 'key.keyTable', function ( e, dt, key, cell, orig ) {
- that._editor( key, orig );
- } );
- }
- // Stave saving
- if ( dt.settings()[0].oFeatures.bStateSave ) {
- dt.on( 'stateSaveParams.keyTable', function (e, s, d) {
- d.keyTable = that.s.lastFocus ?
- that.s.lastFocus.cell.index() :
- null;
- } );
- }
- // Redraw - retain focus on the current cell
- dt.on( 'draw.keyTable', function (e) {
- if ( that.s.focusDraw ) {
- return;
- }
- var lastFocus = that.s.lastFocus;
- if ( lastFocus && lastFocus.node && $(lastFocus.node).closest('body') === document.body ) {
- var relative = that.s.lastFocus.relative;
- var info = dt.page.info();
- var row = relative.row + info.start;
- if ( info.recordsDisplay === 0 ) {
- return;
- }
- // Reverse if needed
- if ( row >= info.recordsDisplay ) {
- row = info.recordsDisplay - 1;
- }
- that._focus( row, relative.column, true, e );
- }
- } );
- dt.on( 'destroy.keyTable', function () {
- dt.off( '.keyTable' );
- $( dt.table().body() ).off( 'click.keyTable', 'th, td' );
- $( document.body )
- .off( 'keydown.keyTable' )
- .off( 'click.keyTable' );
- } );
- // Initial focus comes from state or options
- var state = dt.state.loaded();
- if ( state && state.keyTable ) {
- // Wait until init is done
- dt.one( 'init', function () {
- var cell = dt.cell( state.keyTable );
- // Ensure that the saved cell still exists
- if ( cell.any() ) {
- cell.focus();
- }
- } );
- }
- else if ( this.c.focus ) {
- dt.cell( this.c.focus ).focus();
- }
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Private methods
- */
- /**
- * Blur the control
- *
- * @private
- */
- _blur: function ()
- {
- if ( ! this.s.enable || ! this.s.lastFocus ) {
- return;
- }
- var cell = this.s.lastFocus.cell;
- $( cell.node() ).removeClass( this.c.className );
- this.s.lastFocus = null;
- this._updateFixedColumns(cell.index().column);
- this._emitEvent( 'key-blur', [ this.s.dt, cell ] );
- },
- /**
- * Copy text from the focused cell to clipboard
- *
- * @private
- */
- _clipboardCopy: function ()
- {
- var dt = this.s.dt;
- // If there is a cell focused, and there is no other text selected
- // allow the focused cell's text to be copied to clipboard
- if ( this.s.lastFocus && window.getSelection && !window.getSelection().toString() ) {
- var cell = this.s.lastFocus.cell;
- var text = cell.render('display');
- var hiddenDiv = $('<div/>')
- .css( {
- height: 1,
- width: 1,
- overflow: 'hidden',
- position: 'fixed',
- top: 0,
- left: 0
- } );
- var textarea = $('<textarea readonly/>')
- .val( text )
- .appendTo( hiddenDiv );
- try {
- hiddenDiv.appendTo( dt.table().container() );
- textarea[0].focus();
- textarea[0].select();
- document.execCommand( 'copy' );
- }
- catch (e) {}
- hiddenDiv.remove();
- }
- },
- /**
- * Get an array of the column indexes that KeyTable can operate on. This
- * is a merge of the user supplied columns and the visible columns.
- *
- * @private
- */
- _columns: function ()
- {
- var dt = this.s.dt;
- var user = dt.columns( this.c.columns ).indexes();
- var out = [];
- dt.columns( ':visible' ).every( function (i) {
- if ( user.indexOf( i ) !== -1 ) {
- out.push( i );
- }
- } );
- return out;
- },
- /**
- * Perform excel like navigation for Editor by triggering an edit on key
- * press
- *
- * @param {integer} key Key code for the pressed key
- * @param {object} orig Original event
- * @private
- */
- _editor: function ( key, orig )
- {
- var that = this;
- var dt = this.s.dt;
- var editor = this.c.editor;
- // Do nothing if there is already an inline edit in this cell
- if ( $('div.DTE', this.s.lastFocus.cell.node()).length ) {
- return;
- }
- // Don't activate inline editing when the shift key is pressed
- if ( key === 16 ) {
- return;
- }
- orig.stopPropagation();
- // Return key should do nothing - for textareas it would empty the
- // contents
- if ( key === 13 ) {
- orig.preventDefault();
- }
- editor
- .one( 'open.keyTable', function () {
- // Remove cancel open
- editor.off( 'cancelOpen.keyTable' );
- // Excel style - select all text
- if ( that.c.editAutoSelect ) {
- $('div.DTE_Field_InputControl input, div.DTE_Field_InputControl textarea').select();
- }
- // Reduce the keys the Keys listens for
- dt.keys.enable( that.c.editorKeys );
- // On blur of the navigation submit
- dt.one( 'key-blur.editor', function () {
- if ( editor.displayed() ) {
- editor.submit();
- }
- } );
- // Restore full key navigation on close
- editor.one( 'close', function () {
- dt.keys.enable( true );
- dt.off( 'key-blur.editor' );
- } );
- } )
- .one( 'cancelOpen.keyTable', function () {
- // `preOpen` can cancel the display of the form, so it
- // might be that the open event handler isn't needed
- editor.off( 'open.keyTable' );
- } )
- .inline( this.s.lastFocus.cell.index() );
- },
- /**
- * Emit an event on the DataTable for listeners
- *
- * @param {string} name Event name
- * @param {array} args Event arguments
- * @private
- */
- _emitEvent: function ( name, args )
- {
- this.s.dt.iterator( 'table', function ( ctx, i ) {
- $(ctx.nTable).triggerHandler( name, args );
- } );
- },
- /**
- * Focus on a particular cell, shifting the table's paging if required
- *
- * @param {DataTables.Api|integer} row Can be given as an API instance that
- * contains the cell to focus or as an integer. As the latter it is the
- * visible row index (from the whole data set) - NOT the data index
- * @param {integer} [column] Not required if a cell is given as the first
- * parameter. Otherwise this is the column data index for the cell to
- * focus on
- * @param {boolean} [shift=true] Should the viewport be moved to show cell
- * @private
- */
- _focus: function ( row, column, shift, originalEvent )
- {
- var that = this;
- var dt = this.s.dt;
- var pageInfo = dt.page.info();
- var lastFocus = this.s.lastFocus;
- if ( ! originalEvent) {
- originalEvent = null;
- }
- if ( ! this.s.enable ) {
- return;
- }
- if ( typeof row !== 'number' ) {
- // Convert the cell to a row and column
- var index = row.index();
- column = index.column;
- row = dt
- .rows( { filter: 'applied', order: 'applied' } )
- .indexes()
- .indexOf( index.row );
- // For server-side processing normalise the row by adding the start
- // point, since `rows().indexes()` includes only rows that are
- // available at the client-side
- if ( pageInfo.serverSide ) {
- row += pageInfo.start;
- }
- }
- // Is the row on the current page? If not, we need to redraw to show the
- // page
- if ( pageInfo.length !== -1 && (row < pageInfo.start || row >= pageInfo.start+pageInfo.length) ) {
- this.s.focusDraw = true;
- this.s.waitingForDraw = true;
- dt
- .one( 'draw', function () {
- that.s.focusDraw = false;
- that.s.waitingForDraw = false;
- that._focus( row, column, undefined, originalEvent );
- } )
- .page( Math.floor( row / pageInfo.length ) )
- .draw( false );
- return;
- }
- // In the available columns?
- if ( $.inArray( column, this._columns() ) === -1 ) {
- return;
- }
- // De-normalise the server-side processing row, so we select the row
- // in its displayed position
- if ( pageInfo.serverSide ) {
- row -= pageInfo.start;
- }
- // Get the cell from the current position - ignoring any cells which might
- // not have been rendered (therefore can't use `:eq()` selector).
- var cells = dt.cells( null, column, {search: 'applied', order: 'applied'} ).flatten();
- var cell = dt.cell( cells[ row ] );
- if ( lastFocus ) {
- // Don't trigger a refocus on the same cell
- if ( lastFocus.node === cell.node() ) {
- this._emitEvent( 'key-refocus', [ this.s.dt, cell, originalEvent || null ] );
- return;
- }
- // Otherwise blur the old focus
- this._blur();
- }
- var node = $( cell.node() );
- node.addClass( this.c.className );
- this._updateFixedColumns(column);
- // Shift viewpoint and page to make cell visible
- if ( shift === undefined || shift === true ) {
- this._scroll( $(window), $(document.body), node, 'offset' );
- var bodyParent = dt.table().body().parentNode;
- if ( bodyParent !== dt.table().header().parentNode ) {
- var parent = $(bodyParent.parentNode);
- this._scroll( parent, parent, node, 'position' );
- }
- }
- // Event and finish
- this.s.lastFocus = {
- cell: cell,
- node: cell.node(),
- relative: {
- row: dt.rows( { page: 'current' } ).indexes().indexOf( cell.index().row ),
- column: cell.index().column
- }
- };
- this._emitEvent( 'key-focus', [ this.s.dt, cell, originalEvent || null ] );
- dt.state.save();
- },
- /**
- * Handle key press
- *
- * @param {object} e Event
- * @private
- */
- _key: function ( e )
- {
- // If we are waiting for a draw to happen from another key event, then
- // do nothing for this new key press.
- if ( this.s.waitingForDraw ) {
- e.preventDefault();
- return;
- }
- var enable = this.s.enable;
- var navEnable = enable === true || enable === 'navigation-only';
- if ( ! enable ) {
- return;
- }
- if ( e.ctrlKey && e.keyCode === 67 ) { // c
- this._clipboardCopy();
- return;
- }
- if ( e.keyCode === 0 || e.ctrlKey || e.metaKey || e.altKey ) {
- return;
- }
- // If not focused, then there is no key action to take
- var lastFocus = this.s.lastFocus;
- if ( ! lastFocus ) {
- return;
- }
- var that = this;
- var dt = this.s.dt;
- // If we are not listening for this key, do nothing
- if ( this.c.keys && $.inArray( e.keyCode, this.c.keys ) === -1 ) {
- return;
- }
- switch( e.keyCode ) {
- case 9: // tab
- // `enable` can be tab-only
- this._shift( e, e.shiftKey ? 'left' : 'right', true );
- break;
- case 27: // esc
- if ( this.s.blurable && enable === true ) {
- this._blur();
- }
- break;
- case 33: // page up (previous page)
- case 34: // page down (next page)
- if ( navEnable ) {
- e.preventDefault();
- dt
- .page( e.keyCode === 33 ? 'previous' : 'next' )
- .draw( false );
- }
- break;
- case 35: // end (end of current page)
- case 36: // home (start of current page)
- if ( navEnable ) {
- e.preventDefault();
- var indexes = dt.cells( {page: 'current'} ).indexes();
- var colIndexes = this._columns();
- this._focus( dt.cell(
- indexes[ e.keyCode === 35 ? indexes.length-1 : colIndexes[0] ]
- ), null, true, e );
- }
- break;
- case 37: // left arrow
- if ( navEnable ) {
- this._shift( e, 'left' );
- }
- break;
- case 38: // up arrow
- if ( navEnable ) {
- this._shift( e, 'up' );
- }
- break;
- case 39: // right arrow
- if ( navEnable ) {
- this._shift( e, 'right' );
- }
- break;
- case 40: // down arrow
- if ( navEnable ) {
- this._shift( e, 'down' );
- }
- break;
- default:
- // Everything else - pass through only when fully enabled
- if ( enable === true ) {
- this._emitEvent( 'key', [ dt, e.keyCode, this.s.lastFocus.cell, e ] );
- }
- break;
- }
- },
- /**
- * Scroll a container to make a cell visible in it. This can be used for
- * both DataTables scrolling and native window scrolling.
- *
- * @param {jQuery} container Scrolling container
- * @param {jQuery} scroller Item being scrolled
- * @param {jQuery} cell Cell in the scroller
- * @param {string} posOff `position` or `offset` - which to use for the
- * calculation. `offset` for the document, otherwise `position`
- * @private
- */
- _scroll: function ( container, scroller, cell, posOff )
- {
- var offset = cell[posOff]();
- var height = cell.outerHeight();
- var width = cell.outerWidth();
- var scrollTop = scroller.scrollTop();
- var scrollLeft = scroller.scrollLeft();
- var containerHeight = container.height();
- var containerWidth = container.width();
- // If Scroller is being used, the table can be `position: absolute` and that
- // needs to be taken account of in the offset. If no Scroller, this will be 0
- if ( posOff === 'position' ) {
- offset.top += parseInt( cell.closest('table').css('top'), 10 );
- }
- // Top correction
- if ( offset.top < scrollTop ) {
- scroller.scrollTop( offset.top );
- }
- // Left correction
- if ( offset.left < scrollLeft ) {
- scroller.scrollLeft( offset.left );
- }
- // Bottom correction
- if ( offset.top + height > scrollTop + containerHeight && height < containerHeight ) {
- scroller.scrollTop( offset.top + height - containerHeight );
- }
- // Right correction
- if ( offset.left + width > scrollLeft + containerWidth && width < containerWidth ) {
- scroller.scrollLeft( offset.left + width - containerWidth );
- }
- },
- /**
- * Calculate a single offset movement in the table - up, down, left and
- * right and then perform the focus if possible
- *
- * @param {object} e Event object
- * @param {string} direction Movement direction
- * @param {boolean} keyBlurable `true` if the key press can result in the
- * table being blurred. This is so arrow keys won't blur the table, but
- * tab will.
- * @private
- */
- _shift: function ( e, direction, keyBlurable )
- {
- var that = this;
- var dt = this.s.dt;
- var pageInfo = dt.page.info();
- var rows = pageInfo.recordsDisplay;
- var currentCell = this.s.lastFocus.cell;
- var columns = this._columns();
- if ( ! currentCell ) {
- return;
- }
- var currRow = dt
- .rows( { filter: 'applied', order: 'applied' } )
- .indexes()
- .indexOf( currentCell.index().row );
- // When server-side processing, `rows().indexes()` only gives the rows
- // that are available at the client-side, so we need to normalise the
- // row's current position by the display start point
- if ( pageInfo.serverSide ) {
- currRow += pageInfo.start;
- }
- var currCol = dt
- .columns( columns )
- .indexes()
- .indexOf( currentCell.index().column );
- var
- row = currRow,
- column = columns[ currCol ]; // row is the display, column is an index
- if ( direction === 'right' ) {
- if ( currCol >= columns.length - 1 ) {
- row++;
- column = columns[0];
- }
- else {
- column = columns[ currCol+1 ];
- }
- }
- else if ( direction === 'left' ) {
- if ( currCol === 0 ) {
- row--;
- column = columns[ columns.length - 1 ];
- }
- else {
- column = columns[ currCol-1 ];
- }
- }
- else if ( direction === 'up' ) {
- row--;
- }
- else if ( direction === 'down' ) {
- row++;
- }
- if ( row >= 0 && row < rows && $.inArray( column, columns ) !== -1
- ) {
- e.preventDefault();
- this._focus( row, column, true, e );
- }
- else if ( ! keyBlurable || ! this.c.blurable ) {
- // No new focus, but if the table isn't blurable, then don't loose
- // focus
- e.preventDefault();
- }
- else {
- this._blur();
- }
- },
- /**
- * Create a hidden input element that can receive focus on behalf of the
- * table
- *
- * @private
- */
- _tabInput: function ()
- {
- var that = this;
- var dt = this.s.dt;
- var tabIndex = this.c.tabIndex !== null ?
- this.c.tabIndex :
- dt.settings()[0].iTabIndex;
- if ( tabIndex == -1 ) {
- return;
- }
- var div = $('<div><input type="text" tabindex="'+tabIndex+'"/></div>')
- .css( {
- position: 'absolute',
- height: 1,
- width: 0,
- overflow: 'hidden'
- } )
- .insertBefore( dt.table().node() );
- div.children().on( 'focus', function (e) {
- if ( dt.cell(':eq(0)', {page: 'current'}).any() ) {
- that._focus( dt.cell(':eq(0)', '0:visible', {page: 'current'}), null, true, e );
- }
- } );
- },
- /**
- * Update fixed columns if they are enabled and if the cell we are
- * focusing is inside a fixed column
- * @param {integer} column Index of the column being changed
- * @private
- */
- _updateFixedColumns: function( column )
- {
- var dt = this.s.dt;
- var settings = dt.settings()[0];
- if ( settings._oFixedColumns ) {
- var leftCols = settings._oFixedColumns.s.iLeftColumns;
- var rightCols = settings.aoColumns.length - settings._oFixedColumns.s.iRightColumns;
- if (column < leftCols || column >= rightCols) {
- dt.fixedColumns().update();
- }
- }
- }
- } );
- /**
- * KeyTable default settings for initialisation
- *
- * @namespace
- * @name KeyTable.defaults
- * @static
- */
- KeyTable.defaults = {
- /**
- * Can focus be removed from the table
- * @type {Boolean}
- */
- blurable: true,
- /**
- * Class to give to the focused cell
- * @type {String}
- */
- className: 'focus',
- /**
- * Columns that can be focused. This is automatically merged with the
- * visible columns as only visible columns can gain focus.
- * @type {String}
- */
- columns: '', // all
- /**
- * Editor instance to automatically perform Excel like navigation
- * @type {Editor}
- */
- editor: null,
- /**
- * Option that defines what KeyTable's behaviour will be when used with
- * Editor's inline editing. Can be `navigation-only` or `tab-only`.
- * @type {String}
- */
- editorKeys: 'navigation-only',
- /**
- * Set if Editor should automatically select the text in the input
- * @type {Boolean}
- */
- editAutoSelect: true,
- /**
- * Control if editing should be activated immediately upon focus
- * @type {Boolean}
- */
- editOnFocus: false,
- /**
- * Select a cell to automatically select on start up. `null` for no
- * automatic selection
- * @type {cell-selector}
- */
- focus: null,
- /**
- * Array of keys to listen for
- * @type {null|array}
- */
- keys: null,
- /**
- * Tab index for where the table should sit in the document's tab flow
- * @type {integer|null}
- */
- tabIndex: null
- };
- KeyTable.version = "2.3.2";
- $.fn.dataTable.KeyTable = KeyTable;
- $.fn.DataTable.KeyTable = KeyTable;
- DataTable.Api.register( 'cell.blur()', function () {
- return this.iterator( 'table', function (ctx) {
- if ( ctx.keytable ) {
- ctx.keytable.blur();
- }
- } );
- } );
- DataTable.Api.register( 'cell().focus()', function () {
- return this.iterator( 'cell', function (ctx, row, column) {
- if ( ctx.keytable ) {
- ctx.keytable.focus( row, column );
- }
- } );
- } );
- DataTable.Api.register( 'keys.disable()', function () {
- return this.iterator( 'table', function (ctx) {
- if ( ctx.keytable ) {
- ctx.keytable.enable( false );
- }
- } );
- } );
- DataTable.Api.register( 'keys.enable()', function ( opts ) {
- return this.iterator( 'table', function (ctx) {
- if ( ctx.keytable ) {
- ctx.keytable.enable( opts === undefined ? true : opts );
- }
- } );
- } );
- // Cell selector
- DataTable.ext.selector.cell.push( function ( settings, opts, cells ) {
- var focused = opts.focused;
- var kt = settings.keytable;
- var out = [];
- if ( ! kt || focused === undefined ) {
- return cells;
- }
- for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
- if ( (focused === true && kt.focused( cells[i] ) ) ||
- (focused === false && ! kt.focused( cells[i] ) )
- ) {
- out.push( cells[i] );
- }
- }
- return out;
- } );
- // Attach a listener to the document which listens for DataTables initialisation
- // events so we can automatically initialise
- $(document).on( 'preInit.dt.dtk', function (e, settings, json) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- var init = settings.oInit.keys;
- var defaults = DataTable.defaults.keys;
- if ( init || defaults ) {
- var opts = $.extend( {}, defaults, init );
- if ( init !== false ) {
- new KeyTable( settings, opts );
- }
- }
- } );
- return KeyTable;
- }));
- /*! Responsive 2.2.1
- * 2014-2017 SpryMedia Ltd - datatables.net/license
- */
- /**
- * @summary Responsive
- * @description Responsive tables plug-in for DataTables
- * @version 2.2.1
- * @file dataTables.responsive.js
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
- * @contact www.sprymedia.co.uk/contact
- * @copyright Copyright 2014-2017 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license/mit
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- /**
- * Responsive is a plug-in for the DataTables library that makes use of
- * DataTables' ability to change the visibility of columns, changing the
- * visibility of columns so the displayed columns fit into the table container.
- * The end result is that complex tables will be dynamically adjusted to fit
- * into the viewport, be it on a desktop, tablet or mobile browser.
- *
- * Responsive for DataTables has two modes of operation, which can used
- * individually or combined:
- *
- * * Class name based control - columns assigned class names that match the
- * breakpoint logic can be shown / hidden as required for each breakpoint.
- * * Automatic control - columns are automatically hidden when there is no
- * room left to display them. Columns removed from the right.
- *
- * In additional to column visibility control, Responsive also has built into
- * options to use DataTables' child row display to show / hide the information
- * from the table that has been hidden. There are also two modes of operation
- * for this child row display:
- *
- * * Inline - when the control element that the user can use to show / hide
- * child rows is displayed inside the first column of the table.
- * * Column - where a whole column is dedicated to be the show / hide control.
- *
- * Initialisation of Responsive is performed by:
- *
- * * Adding the class `responsive` or `dt-responsive` to the table. In this case
- * Responsive will automatically be initialised with the default configuration
- * options when the DataTable is created.
- * * Using the `responsive` option in the DataTables configuration options. This
- * can also be used to specify the configuration options, or simply set to
- * `true` to use the defaults.
- *
- * @class
- * @param {object} settings DataTables settings object for the host table
- * @param {object} [opts] Configuration options
- * @requires jQuery 1.7+
- * @requires DataTables 1.10.3+
- *
- * @example
- * $('#example').DataTable( {
- * responsive: true
- * } );
- * } );
- */
- var Responsive = function ( settings, opts ) {
- // Sanity check that we are using DataTables 1.10 or newer
- if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.10' ) ) {
- throw 'DataTables Responsive requires DataTables 1.10.10 or newer';
- }
- this.s = {
- dt: new DataTable.Api( settings ),
- columns: [],
- current: []
- };
- // Check if responsive has already been initialised on this table
- if ( this.s.dt.settings()[0].responsive ) {
- return;
- }
- // details is an object, but for simplicity the user can give it as a string
- // or a boolean
- if ( opts && typeof opts.details === 'string' ) {
- opts.details = { type: opts.details };
- }
- else if ( opts && opts.details === false ) {
- opts.details = { type: false };
- }
- else if ( opts && opts.details === true ) {
- opts.details = { type: 'inline' };
- }
- this.c = $.extend( true, {}, Responsive.defaults, DataTable.defaults.responsive, opts );
- settings.responsive = this;
- this._constructor();
- };
- $.extend( Responsive.prototype, {
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constructor
- */
- /**
- * Initialise the Responsive instance
- *
- * @private
- */
- _constructor: function ()
- {
- var that = this;
- var dt = this.s.dt;
- var dtPrivateSettings = dt.settings()[0];
- var oldWindowWidth = $(window).width();
- dt.settings()[0]._responsive = this;
- // Use DataTables' throttle function to avoid processor thrashing on
- // resize
- $(window).on( 'resize.dtr orientationchange.dtr', DataTable.util.throttle( function () {
- // iOS has a bug whereby resize can fire when only scrolling
- // See: http://stackoverflow.com/questions/8898412
- var width = $(window).width();
- if ( width !== oldWindowWidth ) {
- that._resize();
- oldWindowWidth = width;
- }
- } ) );
- // DataTables doesn't currently trigger an event when a row is added, so
- // we need to hook into its private API to enforce the hidden rows when
- // new data is added
- dtPrivateSettings.oApi._fnCallbackReg( dtPrivateSettings, 'aoRowCreatedCallback', function (tr, data, idx) {
- if ( $.inArray( false, that.s.current ) !== -1 ) {
- $('>td, >th', tr).each( function ( i ) {
- var idx = dt.column.index( 'toData', i );
- if ( that.s.current[idx] === false ) {
- $(this).css('display', 'none');
- }
- } );
- }
- } );
- // Destroy event handler
- dt.on( 'destroy.dtr', function () {
- dt.off( '.dtr' );
- $( dt.table().body() ).off( '.dtr' );
- $(window).off( 'resize.dtr orientationchange.dtr' );
- // Restore the columns that we've hidden
- $.each( that.s.current, function ( i, val ) {
- if ( val === false ) {
- that._setColumnVis( i, true );
- }
- } );
- } );
- // Reorder the breakpoints array here in case they have been added out
- // of order
- this.c.breakpoints.sort( function (a, b) {
- return a.width < b.width ? 1 :
- a.width > b.width ? -1 : 0;
- } );
- this._classLogic();
- this._resizeAuto();
- // Details handler
- var details = this.c.details;
- if ( details.type !== false ) {
- that._detailsInit();
- // DataTables will trigger this event on every column it shows and
- // hides individually
- dt.on( 'column-visibility.dtr', function (e, ctx, col, vis, recalc) {
- if ( recalc ) {
- that._classLogic();
- that._resizeAuto();
- that._resize();
- }
- } );
- // Redraw the details box on each draw which will happen if the data
- // has changed. This is used until DataTables implements a native
- // `updated` event for rows
- dt.on( 'draw.dtr', function () {
- that._redrawChildren();
- } );
- $(dt.table().node()).addClass( 'dtr-'+details.type );
- }
- dt.on( 'column-reorder.dtr', function (e, settings, details) {
- that._classLogic();
- that._resizeAuto();
- that._resize();
- } );
- // Change in column sizes means we need to calc
- dt.on( 'column-sizing.dtr', function () {
- that._resizeAuto();
- that._resize();
- });
- // On Ajax reload we want to reopen any child rows which are displayed
- // by responsive
- dt.on( 'preXhr.dtr', function () {
- var rowIds = [];
- dt.rows().every( function () {
- if ( this.child.isShown() ) {
- rowIds.push( this.id(true) );
- }
- } );
- dt.one( 'draw.dtr', function () {
- that._resizeAuto();
- that._resize();
- dt.rows( rowIds ).every( function () {
- that._detailsDisplay( this, false );
- } );
- } );
- });
- dt.on( 'init.dtr', function (e, settings, details) {
- that._resizeAuto();
- that._resize();
- // If columns were hidden, then DataTables needs to adjust the
- // column sizing
- if ( $.inArray( false, that.s.current ) ) {
- dt.columns.adjust();
- }
- } );
- // First pass - draw the table for the current viewport size
- this._resize();
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Private methods
- */
- /**
- * Calculate the visibility for the columns in a table for a given
- * breakpoint. The result is pre-determined based on the class logic if
- * class names are used to control all columns, but the width of the table
- * is also used if there are columns which are to be automatically shown
- * and hidden.
- *
- * @param {string} breakpoint Breakpoint name to use for the calculation
- * @return {array} Array of boolean values initiating the visibility of each
- * column.
- * @private
- */
- _columnsVisiblity: function ( breakpoint )
- {
- var dt = this.s.dt;
- var columns = this.s.columns;
- var i, ien;
- // Create an array that defines the column ordering based first on the
- // column's priority, and secondly the column index. This allows the
- // columns to be removed from the right if the priority matches
- var order = columns
- .map( function ( col, idx ) {
- return {
- columnIdx: idx,
- priority: col.priority
- };
- } )
- .sort( function ( a, b ) {
- if ( a.priority !== b.priority ) {
- return a.priority - b.priority;
- }
- return a.columnIdx - b.columnIdx;
- } );
- // Class logic - determine which columns are in this breakpoint based
- // on the classes. If no class control (i.e. `auto`) then `-` is used
- // to indicate this to the rest of the function
- var display = $.map( columns, function ( col ) {
- return col.auto && col.minWidth === null ?
- false :
- col.auto === true ?
- '-' :
- $.inArray( breakpoint, col.includeIn ) !== -1;
- } );
- // Auto column control - first pass: how much width is taken by the
- // ones that must be included from the non-auto columns
- var requiredWidth = 0;
- for ( i=0, ien=display.length ; i<ien ; i++ ) {
- if ( display[i] === true ) {
- requiredWidth += columns[i].minWidth;
- }
- }
- // Second pass, use up any remaining width for other columns. For
- // scrolling tables we need to subtract the width of the scrollbar. It
- // may not be requires which makes this sub-optimal, but it would
- // require another full redraw to make complete use of those extra few
- // pixels
- var scrolling = dt.settings()[0].oScroll;
- var bar = scrolling.sY || scrolling.sX ? scrolling.iBarWidth : 0;
- var widthAvailable = dt.table().container().offsetWidth - bar;
- var usedWidth = widthAvailable - requiredWidth;
- // Control column needs to always be included. This makes it sub-
- // optimal in terms of using the available with, but to stop layout
- // thrashing or overflow. Also we need to account for the control column
- // width first so we know how much width is available for the other
- // columns, since the control column might not be the first one shown
- for ( i=0, ien=display.length ; i<ien ; i++ ) {
- if ( columns[i].control ) {
- usedWidth -= columns[i].minWidth;
- }
- }
- // Allow columns to be shown (counting by priority and then right to
- // left) until we run out of room
- var empty = false;
- for ( i=0, ien=order.length ; i<ien ; i++ ) {
- var colIdx = order[i].columnIdx;
- if ( display[colIdx] === '-' && ! columns[colIdx].control && columns[colIdx].minWidth ) {
- // Once we've found a column that won't fit we don't let any
- // others display either, or columns might disappear in the
- // middle of the table
- if ( empty || usedWidth - columns[colIdx].minWidth < 0 ) {
- empty = true;
- display[colIdx] = false;
- }
- else {
- display[colIdx] = true;
- }
- usedWidth -= columns[colIdx].minWidth;
- }
- }
- // Determine if the 'control' column should be shown (if there is one).
- // This is the case when there is a hidden column (that is not the
- // control column). The two loops look inefficient here, but they are
- // trivial and will fly through. We need to know the outcome from the
- // first , before the action in the second can be taken
- var showControl = false;
- for ( i=0, ien=columns.length ; i<ien ; i++ ) {
- if ( ! columns[i].control && ! columns[i].never && ! display[i] ) {
- showControl = true;
- break;
- }
- }
- for ( i=0, ien=columns.length ; i<ien ; i++ ) {
- if ( columns[i].control ) {
- display[i] = showControl;
- }
- }
- // Finally we need to make sure that there is at least one column that
- // is visible
- if ( $.inArray( true, display ) === -1 ) {
- display[0] = true;
- }
- return display;
- },
- /**
- * Create the internal `columns` array with information about the columns
- * for the table. This includes determining which breakpoints the column
- * will appear in, based upon class names in the column, which makes up the
- * vast majority of this method.
- *
- * @private
- */
- _classLogic: function ()
- {
- var that = this;
- var calc = {};
- var breakpoints = this.c.breakpoints;
- var dt = this.s.dt;
- var columns = dt.columns().eq(0).map( function (i) {
- var column = this.column(i);
- var className = column.header().className;
- var priority = dt.settings()[0].aoColumns[i].responsivePriority;
- if ( priority === undefined ) {
- var dataPriority = $(column.header()).data('priority');
- priority = dataPriority !== undefined ?
- dataPriority * 1 :
- 10000;
- }
- return {
- className: className,
- includeIn: [],
- auto: false,
- control: false,
- never: className.match(/\bnever\b/) ? true : false,
- priority: priority
- };
- } );
- // Simply add a breakpoint to `includeIn` array, ensuring that there are
- // no duplicates
- var add = function ( colIdx, name ) {
- var includeIn = columns[ colIdx ].includeIn;
- if ( $.inArray( name, includeIn ) === -1 ) {
- includeIn.push( name );
- }
- };
- var column = function ( colIdx, name, operator, matched ) {
- var size, i, ien;
- if ( ! operator ) {
- columns[ colIdx ].includeIn.push( name );
- }
- else if ( operator === 'max-' ) {
- // Add this breakpoint and all smaller
- size = that._find( name ).width;
- for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
- if ( breakpoints[i].width <= size ) {
- add( colIdx, breakpoints[i].name );
- }
- }
- }
- else if ( operator === 'min-' ) {
- // Add this breakpoint and all larger
- size = that._find( name ).width;
- for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
- if ( breakpoints[i].width >= size ) {
- add( colIdx, breakpoints[i].name );
- }
- }
- }
- else if ( operator === 'not-' ) {
- // Add all but this breakpoint
- for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
- if ( breakpoints[i].name.indexOf( matched ) === -1 ) {
- add( colIdx, breakpoints[i].name );
- }
- }
- }
- };
- // Loop over each column and determine if it has a responsive control
- // class
- columns.each( function ( col, i ) {
- var classNames = col.className.split(' ');
- var hasClass = false;
- // Split the class name up so multiple rules can be applied if needed
- for ( var k=0, ken=classNames.length ; k<ken ; k++ ) {
- var className = $.trim( classNames[k] );
- if ( className === 'all' ) {
- // Include in all
- hasClass = true;
- col.includeIn = $.map( breakpoints, function (a) {
- return a.name;
- } );
- return;
- }
- else if ( className === 'none' || col.never ) {
- // Include in none (default) and no auto
- hasClass = true;
- return;
- }
- else if ( className === 'control' ) {
- // Special column that is only visible, when one of the other
- // columns is hidden. This is used for the details control
- hasClass = true;
- col.control = true;
- return;
- }
- $.each( breakpoints, function ( j, breakpoint ) {
- // Does this column have a class that matches this breakpoint?
- var brokenPoint = breakpoint.name.split('-');
- var re = new RegExp( '(min\\-|max\\-|not\\-)?('+brokenPoint[0]+')(\\-[_a-zA-Z0-9])?' );
- var match = className.match( re );
- if ( match ) {
- hasClass = true;
- if ( match[2] === brokenPoint[0] && match[3] === '-'+brokenPoint[1] ) {
- // Class name matches breakpoint name fully
- column( i, breakpoint.name, match[1], match[2]+match[3] );
- }
- else if ( match[2] === brokenPoint[0] && ! match[3] ) {
- // Class name matched primary breakpoint name with no qualifier
- column( i, breakpoint.name, match[1], match[2] );
- }
- }
- } );
- }
- // If there was no control class, then automatic sizing is used
- if ( ! hasClass ) {
- col.auto = true;
- }
- } );
- this.s.columns = columns;
- },
- /**
- * Show the details for the child row
- *
- * @param {DataTables.Api} row API instance for the row
- * @param {boolean} update Update flag
- * @private
- */
- _detailsDisplay: function ( row, update )
- {
- var that = this;
- var dt = this.s.dt;
- var details = this.c.details;
- if ( details && details.type !== false ) {
- var res = details.display( row, update, function () {
- return details.renderer(
- dt, row[0], that._detailsObj(row[0])
- );
- } );
- if ( res === true || res === false ) {
- $(dt.table().node()).triggerHandler( 'responsive-display.dt', [dt, row, res, update] );
- }
- }
- },
- /**
- * Initialisation for the details handler
- *
- * @private
- */
- _detailsInit: function ()
- {
- var that = this;
- var dt = this.s.dt;
- var details = this.c.details;
- // The inline type always uses the first child as the target
- if ( details.type === 'inline' ) {
- details.target = 'td:first-child, th:first-child';
- }
- // Keyboard accessibility
- dt.on( 'draw.dtr', function () {
- that._tabIndexes();
- } );
- that._tabIndexes(); // Initial draw has already happened
- $( dt.table().body() ).on( 'keyup.dtr', 'td, th', function (e) {
- if ( e.keyCode === 13 && $(this).data('dtr-keyboard') ) {
- $(this).click();
- }
- } );
- // type.target can be a string jQuery selector or a column index
- var target = details.target;
- var selector = typeof target === 'string' ? target : 'td, th';
- // Click handler to show / hide the details rows when they are available
- $( dt.table().body() )
- .on( 'click.dtr mousedown.dtr mouseup.dtr', selector, function (e) {
- // If the table is not collapsed (i.e. there is no hidden columns)
- // then take no action
- if ( ! $(dt.table().node()).hasClass('collapsed' ) ) {
- return;
- }
- // Check that the row is actually a DataTable's controlled node
- if ( $.inArray( $(this).closest('tr').get(0), dt.rows().nodes().toArray() ) === -1 ) {
- return;
- }
- // For column index, we determine if we should act or not in the
- // handler - otherwise it is already okay
- if ( typeof target === 'number' ) {
- var targetIdx = target < 0 ?
- dt.columns().eq(0).length + target :
- target;
- if ( dt.cell( this ).index().column !== targetIdx ) {
- return;
- }
- }
- // $().closest() includes itself in its check
- var row = dt.row( $(this).closest('tr') );
- // Check event type to do an action
- if ( e.type === 'click' ) {
- // The renderer is given as a function so the caller can execute it
- // only when they need (i.e. if hiding there is no point is running
- // the renderer)
- that._detailsDisplay( row, false );
- }
- else if ( e.type === 'mousedown' ) {
- // For mouse users, prevent the focus ring from showing
- $(this).css('outline', 'none');
- }
- else if ( e.type === 'mouseup' ) {
- // And then re-allow at the end of the click
- $(this).blur().css('outline', '');
- }
- } );
- },
- /**
- * Get the details to pass to a renderer for a row
- * @param {int} rowIdx Row index
- * @private
- */
- _detailsObj: function ( rowIdx )
- {
- var that = this;
- var dt = this.s.dt;
- return $.map( this.s.columns, function( col, i ) {
- // Never and control columns should not be passed to the renderer
- if ( col.never || col.control ) {
- return;
- }
- return {
- title: dt.settings()[0].aoColumns[ i ].sTitle,
- data: dt.cell( rowIdx, i ).render( that.c.orthogonal ),
- hidden: dt.column( i ).visible() && !that.s.current[ i ],
- columnIndex: i,
- rowIndex: rowIdx
- };
- } );
- },
- /**
- * Find a breakpoint object from a name
- *
- * @param {string} name Breakpoint name to find
- * @return {object} Breakpoint description object
- * @private
- */
- _find: function ( name )
- {
- var breakpoints = this.c.breakpoints;
- for ( var i=0, ien=breakpoints.length ; i<ien ; i++ ) {
- if ( breakpoints[i].name === name ) {
- return breakpoints[i];
- }
- }
- },
- /**
- * Re-create the contents of the child rows as the display has changed in
- * some way.
- *
- * @private
- */
- _redrawChildren: function ()
- {
- var that = this;
- var dt = this.s.dt;
- dt.rows( {page: 'current'} ).iterator( 'row', function ( settings, idx ) {
- var row = dt.row( idx );
- that._detailsDisplay( dt.row( idx ), true );
- } );
- },
- /**
- * Alter the table display for a resized viewport. This involves first
- * determining what breakpoint the window currently is in, getting the
- * column visibilities to apply and then setting them.
- *
- * @private
- */
- _resize: function ()
- {
- var that = this;
- var dt = this.s.dt;
- var width = $(window).width();
- var breakpoints = this.c.breakpoints;
- var breakpoint = breakpoints[0].name;
- var columns = this.s.columns;
- var i, ien;
- var oldVis = this.s.current.slice();
- // Determine what breakpoint we are currently at
- for ( i=breakpoints.length-1 ; i>=0 ; i-- ) {
- if ( width <= breakpoints[i].width ) {
- breakpoint = breakpoints[i].name;
- break;
- }
- }
-
- // Show the columns for that break point
- var columnsVis = this._columnsVisiblity( breakpoint );
- this.s.current = columnsVis;
- // Set the class before the column visibility is changed so event
- // listeners know what the state is. Need to determine if there are
- // any columns that are not visible but can be shown
- var collapsedClass = false;
- for ( i=0, ien=columns.length ; i<ien ; i++ ) {
- if ( columnsVis[i] === false && ! columns[i].never && ! columns[i].control ) {
- collapsedClass = true;
- break;
- }
- }
- $( dt.table().node() ).toggleClass( 'collapsed', collapsedClass );
- var changed = false;
- var visible = 0;
- dt.columns().eq(0).each( function ( colIdx, i ) {
- if ( columnsVis[i] === true ) {
- visible++;
- }
- if ( columnsVis[i] !== oldVis[i] ) {
- changed = true;
- that._setColumnVis( colIdx, columnsVis[i] );
- }
- } );
- if ( changed ) {
- this._redrawChildren();
- // Inform listeners of the change
- $(dt.table().node()).trigger( 'responsive-resize.dt', [dt, this.s.current] );
- // If no records, update the "No records" display element
- if ( dt.page.info().recordsDisplay === 0 ) {
- $('td', dt.table().body()).eq(0).attr('colspan', visible);
- }
- }
- },
- /**
- * Determine the width of each column in the table so the auto column hiding
- * has that information to work with. This method is never going to be 100%
- * perfect since column widths can change slightly per page, but without
- * seriously compromising performance this is quite effective.
- *
- * @private
- */
- _resizeAuto: function ()
- {
- var dt = this.s.dt;
- var columns = this.s.columns;
- // Are we allowed to do auto sizing?
- if ( ! this.c.auto ) {
- return;
- }
- // Are there any columns that actually need auto-sizing, or do they all
- // have classes defined
- if ( $.inArray( true, $.map( columns, function (c) { return c.auto; } ) ) === -1 ) {
- return;
- }
- // Need to restore all children. They will be reinstated by a re-render
- if ( ! $.isEmptyObject( _childNodeStore ) ) {
- $.each( _childNodeStore, function ( key ) {
- var idx = key.split('-');
- _childNodesRestore( dt, idx[0]*1, idx[1]*1 );
- } );
- }
- // Clone the table with the current data in it
- var tableWidth = dt.table().node().offsetWidth;
- var columnWidths = dt.columns;
- var clonedTable = dt.table().node().cloneNode( false );
- var clonedHeader = $( dt.table().header().cloneNode( false ) ).appendTo( clonedTable );
- var clonedBody = $( dt.table().body() ).clone( false, false ).empty().appendTo( clonedTable ); // use jQuery because of IE8
- // Header
- var headerCells = dt.columns()
- .header()
- .filter( function (idx) {
- return dt.column(idx).visible();
- } )
- .to$()
- .clone( false )
- .css( 'display', 'table-cell' )
- .css( 'min-width', 0 );
- // Body rows - we don't need to take account of DataTables' column
- // visibility since we implement our own here (hence the `display` set)
- $(clonedBody)
- .append( $(dt.rows( { page: 'current' } ).nodes()).clone( false ) )
- .find( 'th, td' ).css( 'display', '' );
- // Footer
- var footer = dt.table().footer();
- if ( footer ) {
- var clonedFooter = $( footer.cloneNode( false ) ).appendTo( clonedTable );
- var footerCells = dt.columns()
- .footer()
- .filter( function (idx) {
- return dt.column(idx).visible();
- } )
- .to$()
- .clone( false )
- .css( 'display', 'table-cell' );
- $('<tr/>')
- .append( footerCells )
- .appendTo( clonedFooter );
- }
- $('<tr/>')
- .append( headerCells )
- .appendTo( clonedHeader );
- // In the inline case extra padding is applied to the first column to
- // give space for the show / hide icon. We need to use this in the
- // calculation
- if ( this.c.details.type === 'inline' ) {
- $(clonedTable).addClass( 'dtr-inline collapsed' );
- }
-
- // It is unsafe to insert elements with the same name into the DOM
- // multiple times. For example, cloning and inserting a checked radio
- // clears the chcecked state of the original radio.
- $( clonedTable ).find( '[name]' ).removeAttr( 'name' );
-
- var inserted = $('<div/>')
- .css( {
- width: 1,
- height: 1,
- overflow: 'hidden',
- clear: 'both'
- } )
- .append( clonedTable );
- inserted.insertBefore( dt.table().node() );
- // The cloned header now contains the smallest that each column can be
- headerCells.each( function (i) {
- var idx = dt.column.index( 'fromVisible', i );
- columns[ idx ].minWidth = this.offsetWidth || 0;
- } );
- inserted.remove();
- },
- /**
- * Set a column's visibility.
- *
- * We don't use DataTables' column visibility controls in order to ensure
- * that column visibility can Responsive can no-exist. Since only IE8+ is
- * supported (and all evergreen browsers of course) the control of the
- * display attribute works well.
- *
- * @param {integer} col Column index
- * @param {boolean} showHide Show or hide (true or false)
- * @private
- */
- _setColumnVis: function ( col, showHide )
- {
- var dt = this.s.dt;
- var display = showHide ? '' : 'none'; // empty string will remove the attr
- $( dt.column( col ).header() ).css( 'display', display );
- $( dt.column( col ).footer() ).css( 'display', display );
- dt.column( col ).nodes().to$().css( 'display', display );
- // If the are child nodes stored, we might need to reinsert them
- if ( ! $.isEmptyObject( _childNodeStore ) ) {
- dt.cells( null, col ).indexes().each( function (idx) {
- _childNodesRestore( dt, idx.row, idx.column );
- } );
- }
- },
- /**
- * Update the cell tab indexes for keyboard accessibility. This is called on
- * every table draw - that is potentially inefficient, but also the least
- * complex option given that column visibility can change on the fly. Its a
- * shame user-focus was removed from CSS 3 UI, as it would have solved this
- * issue with a single CSS statement.
- *
- * @private
- */
- _tabIndexes: function ()
- {
- var dt = this.s.dt;
- var cells = dt.cells( { page: 'current' } ).nodes().to$();
- var ctx = dt.settings()[0];
- var target = this.c.details.target;
- cells.filter( '[data-dtr-keyboard]' ).removeData( '[data-dtr-keyboard]' );
- var selector = typeof target === 'number' ?
- ':eq('+target+')' :
- target;
- // This is a bit of a hack - we need to limit the selected nodes to just
- // those of this table
- if ( selector === 'td:first-child, th:first-child' ) {
- selector = '>td:first-child, >th:first-child';
- }
- $( selector, dt.rows( { page: 'current' } ).nodes() )
- .attr( 'tabIndex', ctx.iTabIndex )
- .data( 'dtr-keyboard', 1 );
- }
- } );
- /**
- * List of default breakpoints. Each item in the array is an object with two
- * properties:
- *
- * * `name` - the breakpoint name.
- * * `width` - the breakpoint width
- *
- * @name Responsive.breakpoints
- * @static
- */
- Responsive.breakpoints = [
- { name: 'desktop', width: Infinity },
- { name: 'tablet-l', width: 1024 },
- { name: 'tablet-p', width: 768 },
- { name: 'mobile-l', width: 480 },
- { name: 'mobile-p', width: 320 }
- ];
- /**
- * Display methods - functions which define how the hidden data should be shown
- * in the table.
- *
- * @namespace
- * @name Responsive.defaults
- * @static
- */
- Responsive.display = {
- childRow: function ( row, update, render ) {
- if ( update ) {
- if ( $(row.node()).hasClass('parent') ) {
- row.child( render(), 'child' ).show();
- return true;
- }
- }
- else {
- if ( ! row.child.isShown() ) {
- row.child( render(), 'child' ).show();
- $( row.node() ).addClass( 'parent' );
- return true;
- }
- else {
- row.child( false );
- $( row.node() ).removeClass( 'parent' );
- return false;
- }
- }
- },
- childRowImmediate: function ( row, update, render ) {
- if ( (! update && row.child.isShown()) || ! row.responsive.hasHidden() ) {
- // User interaction and the row is show, or nothing to show
- row.child( false );
- $( row.node() ).removeClass( 'parent' );
- return false;
- }
- else {
- // Display
- row.child( render(), 'child' ).show();
- $( row.node() ).addClass( 'parent' );
- return true;
- }
- },
- // This is a wrapper so the modal options for Bootstrap and jQuery UI can
- // have options passed into them. This specific one doesn't need to be a
- // function but it is for consistency in the `modal` name
- modal: function ( options ) {
- return function ( row, update, render ) {
- if ( ! update ) {
- // Show a modal
- var close = function () {
- modal.remove(); // will tidy events for us
- $(document).off( 'keypress.dtr' );
- };
- var modal = $('<div class="dtr-modal"/>')
- .append( $('<div class="dtr-modal-display"/>')
- .append( $('<div class="dtr-modal-content"/>')
- .append( render() )
- )
- .append( $('<div class="dtr-modal-close">×</div>' )
- .click( function () {
- close();
- } )
- )
- )
- .append( $('<div class="dtr-modal-background"/>')
- .click( function () {
- close();
- } )
- )
- .appendTo( 'body' );
- $(document).on( 'keyup.dtr', function (e) {
- if ( e.keyCode === 27 ) {
- e.stopPropagation();
- close();
- }
- } );
- }
- else {
- $('div.dtr-modal-content')
- .empty()
- .append( render() );
- }
- if ( options && options.header ) {
- $('div.dtr-modal-content').prepend(
- '<h2>'+options.header( row )+'</h2>'
- );
- }
- };
- }
- };
- var _childNodeStore = {};
- function _childNodes( dt, row, col ) {
- var name = row+'-'+col;
- if ( _childNodeStore[ name ] ) {
- return _childNodeStore[ name ];
- }
- // https://jsperf.com/childnodes-array-slice-vs-loop
- var nodes = [];
- var children = dt.cell( row, col ).node().childNodes;
- for ( var i=0, ien=children.length ; i<ien ; i++ ) {
- nodes.push( children[i] );
- }
- _childNodeStore[ name ] = nodes;
- return nodes;
- }
- function _childNodesRestore( dt, row, col ) {
- var name = row+'-'+col;
- if ( ! _childNodeStore[ name ] ) {
- return;
- }
- var node = dt.cell( row, col ).node();
- var store = _childNodeStore[ name ];
- var parent = store[0].parentNode;
- var parentChildren = parent.childNodes;
- var a = [];
- for ( var i=0, ien=parentChildren.length ; i<ien ; i++ ) {
- a.push( parentChildren[i] );
- }
- for ( var j=0, jen=a.length ; j<jen ; j++ ) {
- node.appendChild( a[j] );
- }
- _childNodeStore[ name ] = undefined;
- }
- /**
- * Display methods - functions which define how the hidden data should be shown
- * in the table.
- *
- * @namespace
- * @name Responsive.defaults
- * @static
- */
- Responsive.renderer = {
- listHiddenNodes: function () {
- return function ( api, rowIdx, columns ) {
- var ul = $('<ul data-dtr-index="'+rowIdx+'" class="dtr-details"/>');
- var found = false;
- var data = $.each( columns, function ( i, col ) {
- if ( col.hidden ) {
- $(
- '<li data-dtr-index="'+col.columnIndex+'" data-dt-row="'+col.rowIndex+'" data-dt-column="'+col.columnIndex+'">'+
- '<span class="dtr-title">'+
- col.title+
- '</span> '+
- '</li>'
- )
- .append( $('<span class="dtr-data"/>').append( _childNodes( api, col.rowIndex, col.columnIndex ) ) )// api.cell( col.rowIndex, col.columnIndex ).node().childNodes ) )
- .appendTo( ul );
- found = true;
- }
- } );
- return found ?
- ul :
- false;
- };
- },
- listHidden: function () {
- return function ( api, rowIdx, columns ) {
- var data = $.map( columns, function ( col ) {
- return col.hidden ?
- '<li data-dtr-index="'+col.columnIndex+'" data-dt-row="'+col.rowIndex+'" data-dt-column="'+col.columnIndex+'">'+
- '<span class="dtr-title">'+
- col.title+
- '</span> '+
- '<span class="dtr-data">'+
- col.data+
- '</span>'+
- '</li>' :
- '';
- } ).join('');
- return data ?
- $('<ul data-dtr-index="'+rowIdx+'" class="dtr-details"/>').append( data ) :
- false;
- }
- },
- tableAll: function ( options ) {
- options = $.extend( {
- tableClass: ''
- }, options );
- return function ( api, rowIdx, columns ) {
- var data = $.map( columns, function ( col ) {
- return '<tr data-dt-row="'+col.rowIndex+'" data-dt-column="'+col.columnIndex+'">'+
- '<td>'+col.title+':'+'</td> '+
- '<td>'+col.data+'</td>'+
- '</tr>';
- } ).join('');
- return $('<table class="'+options.tableClass+' dtr-details" width="100%"/>').append( data );
- }
- }
- };
- /**
- * Responsive default settings for initialisation
- *
- * @namespace
- * @name Responsive.defaults
- * @static
- */
- Responsive.defaults = {
- /**
- * List of breakpoints for the instance. Note that this means that each
- * instance can have its own breakpoints. Additionally, the breakpoints
- * cannot be changed once an instance has been creased.
- *
- * @type {Array}
- * @default Takes the value of `Responsive.breakpoints`
- */
- breakpoints: Responsive.breakpoints,
- /**
- * Enable / disable auto hiding calculations. It can help to increase
- * performance slightly if you disable this option, but all columns would
- * need to have breakpoint classes assigned to them
- *
- * @type {Boolean}
- * @default `true`
- */
- auto: true,
- /**
- * Details control. If given as a string value, the `type` property of the
- * default object is set to that value, and the defaults used for the rest
- * of the object - this is for ease of implementation.
- *
- * The object consists of the following properties:
- *
- * * `display` - A function that is used to show and hide the hidden details
- * * `renderer` - function that is called for display of the child row data.
- * The default function will show the data from the hidden columns
- * * `target` - Used as the selector for what objects to attach the child
- * open / close to
- * * `type` - `false` to disable the details display, `inline` or `column`
- * for the two control types
- *
- * @type {Object|string}
- */
- details: {
- display: Responsive.display.childRow,
- renderer: Responsive.renderer.listHidden(),
- target: 0,
- type: 'inline'
- },
- /**
- * Orthogonal data request option. This is used to define the data type
- * requested when Responsive gets the data to show in the child row.
- *
- * @type {String}
- */
- orthogonal: 'display'
- };
- /*
- * API
- */
- var Api = $.fn.dataTable.Api;
- // Doesn't do anything - work around for a bug in DT... Not documented
- Api.register( 'responsive()', function () {
- return this;
- } );
- Api.register( 'responsive.index()', function ( li ) {
- li = $(li);
- return {
- column: li.data('dtr-index'),
- row: li.parent().data('dtr-index')
- };
- } );
- Api.register( 'responsive.rebuild()', function () {
- return this.iterator( 'table', function ( ctx ) {
- if ( ctx._responsive ) {
- ctx._responsive._classLogic();
- }
- } );
- } );
- Api.register( 'responsive.recalc()', function () {
- return this.iterator( 'table', function ( ctx ) {
- if ( ctx._responsive ) {
- ctx._responsive._resizeAuto();
- ctx._responsive._resize();
- }
- } );
- } );
- Api.register( 'responsive.hasHidden()', function () {
- var ctx = this.context[0];
- return ctx._responsive ?
- $.inArray( false, ctx._responsive.s.current ) !== -1 :
- false;
- } );
- Api.registerPlural( 'columns().responsiveHidden()', 'column().responsiveHidden()', function () {
- return this.iterator( 'column', function ( settings, column ) {
- return settings._responsive ?
- settings._responsive.s.current[ column ] :
- false;
- }, 1 );
- } );
- /**
- * Version information
- *
- * @name Responsive.version
- * @static
- */
- Responsive.version = '2.2.1';
- $.fn.dataTable.Responsive = Responsive;
- $.fn.DataTable.Responsive = Responsive;
- // Attach a listener to the document which listens for DataTables initialisation
- // events so we can automatically initialise
- $(document).on( 'preInit.dt.dtr', function (e, settings, json) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- if ( $(settings.nTable).hasClass( 'responsive' ) ||
- $(settings.nTable).hasClass( 'dt-responsive' ) ||
- settings.oInit.responsive ||
- DataTable.defaults.responsive
- ) {
- var init = settings.oInit.responsive;
- if ( init !== false ) {
- new Responsive( settings, $.isPlainObject( init ) ? init : {} );
- }
- }
- } );
- return Responsive;
- }));
- /*! RowReorder 1.2.3
- * 2015-2017 SpryMedia Ltd - datatables.net/license
- */
- /**
- * @summary RowReorder
- * @description Row reordering extension for DataTables
- * @version 1.2.3
- * @file dataTables.rowReorder.js
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
- * @contact www.sprymedia.co.uk/contact
- * @copyright Copyright 2015-2017 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license/mit
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- /**
- * RowReorder provides the ability in DataTables to click and drag rows to
- * reorder them. When a row is dropped the data for the rows effected will be
- * updated to reflect the change. Normally this data point should also be the
- * column being sorted upon in the DataTable but this does not need to be the
- * case. RowReorder implements a "data swap" method - so the rows being
- * reordered take the value of the data point from the row that used to occupy
- * the row's new position.
- *
- * Initialisation is done by either:
- *
- * * `rowReorder` parameter in the DataTable initialisation object
- * * `new $.fn.dataTable.RowReorder( table, opts )` after DataTables
- * initialisation.
- *
- * @class
- * @param {object} settings DataTables settings object for the host table
- * @param {object} [opts] Configuration options
- * @requires jQuery 1.7+
- * @requires DataTables 1.10.7+
- */
- var RowReorder = function ( dt, opts ) {
- // Sanity check that we are using DataTables 1.10 or newer
- if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) {
- throw 'DataTables RowReorder requires DataTables 1.10.8 or newer';
- }
- // User and defaults configuration object
- this.c = $.extend( true, {},
- DataTable.defaults.rowReorder,
- RowReorder.defaults,
- opts
- );
- // Internal settings
- this.s = {
- /** @type {integer} Scroll body top cache */
- bodyTop: null,
- /** @type {DataTable.Api} DataTables' API instance */
- dt: new DataTable.Api( dt ),
- /** @type {function} Data fetch function */
- getDataFn: DataTable.ext.oApi._fnGetObjectDataFn( this.c.dataSrc ),
- /** @type {array} Pixel positions for row insertion calculation */
- middles: null,
- /** @type {Object} Cached dimension information for use in the mouse move event handler */
- scroll: {},
- /** @type {integer} Interval object used for smooth scrolling */
- scrollInterval: null,
- /** @type {function} Data set function */
- setDataFn: DataTable.ext.oApi._fnSetObjectDataFn( this.c.dataSrc ),
- /** @type {Object} Mouse down information */
- start: {
- top: 0,
- left: 0,
- offsetTop: 0,
- offsetLeft: 0,
- nodes: []
- },
- /** @type {integer} Window height cached value */
- windowHeight: 0,
- /** @type {integer} Document outer height cached value */
- documentOuterHeight: 0,
- /** @type {integer} DOM clone outer height cached value */
- domCloneOuterHeight: 0
- };
- // DOM items
- this.dom = {
- /** @type {jQuery} Cloned row being moved around */
- clone: null,
- /** @type {jQuery} DataTables scrolling container */
- dtScroll: $('div.dataTables_scrollBody', this.s.dt.table().container())
- };
- // Check if row reorder has already been initialised on this table
- var settings = this.s.dt.settings()[0];
- var exisiting = settings.rowreorder;
- if ( exisiting ) {
- return exisiting;
- }
- settings.rowreorder = this;
- this._constructor();
- };
- $.extend( RowReorder.prototype, {
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constructor
- */
- /**
- * Initialise the RowReorder instance
- *
- * @private
- */
- _constructor: function ()
- {
- var that = this;
- var dt = this.s.dt;
- var table = $( dt.table().node() );
- // Need to be able to calculate the row positions relative to the table
- if ( table.css('position') === 'static' ) {
- table.css( 'position', 'relative' );
- }
- // listen for mouse down on the target column - we have to implement
- // this rather than using HTML5 drag and drop as drag and drop doesn't
- // appear to work on table rows at this time. Also mobile browsers are
- // not supported.
- // Use `table().container()` rather than just the table node for IE8 -
- // otherwise it only works once...
- $(dt.table().container()).on( 'mousedown.rowReorder touchstart.rowReorder', this.c.selector, function (e) {
- if ( ! that.c.enable ) {
- return;
- }
- var tr = $(this).closest('tr');
- var row = dt.row( tr );
- // Double check that it is a DataTable row
- if ( row.any() ) {
- that._emitEvent( 'pre-row-reorder', {
- node: row.node(),
- index: row.index()
- } );
- that._mouseDown( e, tr );
- return false;
- }
- } );
- dt.on( 'destroy.rowReorder', function () {
- $(dt.table().container()).off( '.rowReorder' );
- dt.off( '.rowReorder' );
- } );
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Private methods
- */
-
- /**
- * Cache the measurements that RowReorder needs in the mouse move handler
- * to attempt to speed things up, rather than reading from the DOM.
- *
- * @private
- */
- _cachePositions: function ()
- {
- var dt = this.s.dt;
- // Frustratingly, if we add `position:relative` to the tbody, the
- // position is still relatively to the parent. So we need to adjust
- // for that
- var headerHeight = $( dt.table().node() ).find('thead').outerHeight();
- // Need to pass the nodes through jQuery to get them in document order,
- // not what DataTables thinks it is, since we have been altering the
- // order
- var nodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );
- var tops = $.map( nodes, function ( node, i ) {
- return $(node).position().top - headerHeight;
- } );
- var middles = $.map( tops, function ( top, i ) {
- return tops.length < i-1 ?
- (top + tops[i+1]) / 2 :
- (top + top + $( dt.row( ':last-child' ).node() ).outerHeight() ) / 2;
- } );
- this.s.middles = middles;
- this.s.bodyTop = $( dt.table().body() ).offset().top;
- this.s.windowHeight = $(window).height();
- this.s.documentOuterHeight = $(document).outerHeight();
- },
- /**
- * Clone a row so it can be floated around the screen
- *
- * @param {jQuery} target Node to be cloned
- * @private
- */
- _clone: function ( target )
- {
- var dt = this.s.dt;
- var clone = $( dt.table().node().cloneNode(false) )
- .addClass( 'dt-rowReorder-float' )
- .append('<tbody/>')
- .append( target.clone( false ) );
- // Match the table and column widths - read all sizes before setting
- // to reduce reflows
- var tableWidth = target.outerWidth();
- var tableHeight = target.outerHeight();
- var sizes = target.children().map( function () {
- return $(this).width();
- } );
- clone
- .width( tableWidth )
- .height( tableHeight )
- .find('tr').children().each( function (i) {
- this.style.width = sizes[i]+'px';
- } );
- // Insert into the document to have it floating around
- clone.appendTo( 'body' );
- this.dom.clone = clone;
- this.s.domCloneOuterHeight = clone.outerHeight();
- },
- /**
- * Update the cloned item's position in the document
- *
- * @param {object} e Event giving the mouse's position
- * @private
- */
- _clonePosition: function ( e )
- {
- var start = this.s.start;
- var topDiff = this._eventToPage( e, 'Y' ) - start.top;
- var leftDiff = this._eventToPage( e, 'X' ) - start.left;
- var snap = this.c.snapX;
- var left;
- var top = topDiff + start.offsetTop;
- if ( snap === true ) {
- left = start.offsetLeft;
- }
- else if ( typeof snap === 'number' ) {
- left = start.offsetLeft + snap;
- }
- else {
- left = leftDiff + start.offsetLeft;
- }
- if(top < 0) {
- top = 0
- }
- else if(top + this.s.domCloneOuterHeight > this.s.documentOuterHeight) {
- top = this.s.documentOuterHeight - this.s.domCloneOuterHeight;
- }
- this.dom.clone.css( {
- top: top,
- left: left
- } );
- },
- /**
- * Emit an event on the DataTable for listeners
- *
- * @param {string} name Event name
- * @param {array} args Event arguments
- * @private
- */
- _emitEvent: function ( name, args )
- {
- this.s.dt.iterator( 'table', function ( ctx, i ) {
- $(ctx.nTable).triggerHandler( name+'.dt', args );
- } );
- },
- /**
- * Get pageX/Y position from an event, regardless of if it is a mouse or
- * touch event.
- *
- * @param {object} e Event
- * @param {string} pos X or Y (must be a capital)
- * @private
- */
- _eventToPage: function ( e, pos )
- {
- if ( e.type.indexOf( 'touch' ) !== -1 ) {
- return e.originalEvent.touches[0][ 'page'+pos ];
- }
- return e[ 'page'+pos ];
- },
- /**
- * Mouse down event handler. Read initial positions and add event handlers
- * for the move.
- *
- * @param {object} e Mouse event
- * @param {jQuery} target TR element that is to be moved
- * @private
- */
- _mouseDown: function ( e, target )
- {
- var that = this;
- var dt = this.s.dt;
- var start = this.s.start;
- var offset = target.offset();
- start.top = this._eventToPage( e, 'Y' );
- start.left = this._eventToPage( e, 'X' );
- start.offsetTop = offset.top;
- start.offsetLeft = offset.left;
- start.nodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );
- this._cachePositions();
- this._clone( target );
- this._clonePosition( e );
- this.dom.target = target;
- target.addClass( 'dt-rowReorder-moving' );
- $( document )
- .on( 'mouseup.rowReorder touchend.rowReorder', function (e) {
- that._mouseUp(e);
- } )
- .on( 'mousemove.rowReorder touchmove.rowReorder', function (e) {
- that._mouseMove(e);
- } );
- // Check if window is x-scrolling - if not, disable it for the duration
- // of the drag
- if ( $(window).width() === $(document).width() ) {
- $(document.body).addClass( 'dt-rowReorder-noOverflow' );
- }
- // Cache scrolling information so mouse move doesn't need to read.
- // This assumes that the window and DT scroller will not change size
- // during an row drag, which I think is a fair assumption
- var scrollWrapper = this.dom.dtScroll;
- this.s.scroll = {
- windowHeight: $(window).height(),
- windowWidth: $(window).width(),
- dtTop: scrollWrapper.length ? scrollWrapper.offset().top : null,
- dtLeft: scrollWrapper.length ? scrollWrapper.offset().left : null,
- dtHeight: scrollWrapper.length ? scrollWrapper.outerHeight() : null,
- dtWidth: scrollWrapper.length ? scrollWrapper.outerWidth() : null
- };
- },
- /**
- * Mouse move event handler - move the cloned row and shuffle the table's
- * rows if required.
- *
- * @param {object} e Mouse event
- * @private
- */
- _mouseMove: function ( e )
- {
- this._clonePosition( e );
- // Transform the mouse position into a position in the table's body
- var bodyY = this._eventToPage( e, 'Y' ) - this.s.bodyTop;
- var middles = this.s.middles;
- var insertPoint = null;
- var dt = this.s.dt;
- var body = dt.table().body();
- // Determine where the row should be inserted based on the mouse
- // position
- for ( var i=0, ien=middles.length ; i<ien ; i++ ) {
- if ( bodyY < middles[i] ) {
- insertPoint = i;
- break;
- }
- }
- if ( insertPoint === null ) {
- insertPoint = middles.length;
- }
- // Perform the DOM shuffle if it has changed from last time
- if ( this.s.lastInsert === null || this.s.lastInsert !== insertPoint ) {
- if ( insertPoint === 0 ) {
- this.dom.target.prependTo( body );
- }
- else {
- var nodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );
- if ( insertPoint > this.s.lastInsert ) {
- this.dom.target.insertAfter( nodes[ insertPoint-1 ] );
- }
- else {
- this.dom.target.insertBefore( nodes[ insertPoint ] );
- }
- }
- this._cachePositions();
- this.s.lastInsert = insertPoint;
- }
- this._shiftScroll( e );
- },
- /**
- * Mouse up event handler - release the event handlers and perform the
- * table updates
- *
- * @param {object} e Mouse event
- * @private
- */
- _mouseUp: function ( e )
- {
- var that = this;
- var dt = this.s.dt;
- var i, ien;
- var dataSrc = this.c.dataSrc;
- this.dom.clone.remove();
- this.dom.clone = null;
- this.dom.target.removeClass( 'dt-rowReorder-moving' );
- //this.dom.target = null;
- $(document).off( '.rowReorder' );
- $(document.body).removeClass( 'dt-rowReorder-noOverflow' );
- clearInterval( this.s.scrollInterval );
- this.s.scrollInterval = null;
- // Calculate the difference
- var startNodes = this.s.start.nodes;
- var endNodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );
- var idDiff = {};
- var fullDiff = [];
- var diffNodes = [];
- var getDataFn = this.s.getDataFn;
- var setDataFn = this.s.setDataFn;
- for ( i=0, ien=startNodes.length ; i<ien ; i++ ) {
- if ( startNodes[i] !== endNodes[i] ) {
- var id = dt.row( endNodes[i] ).id();
- var endRowData = dt.row( endNodes[i] ).data();
- var startRowData = dt.row( startNodes[i] ).data();
- if ( id ) {
- idDiff[ id ] = getDataFn( startRowData );
- }
- fullDiff.push( {
- node: endNodes[i],
- oldData: getDataFn( endRowData ),
- newData: getDataFn( startRowData ),
- newPosition: i,
- oldPosition: $.inArray( endNodes[i], startNodes )
- } );
- diffNodes.push( endNodes[i] );
- }
- }
-
- // Create event args
- var eventArgs = [ fullDiff, {
- dataSrc: dataSrc,
- nodes: diffNodes,
- values: idDiff,
- triggerRow: dt.row( this.dom.target )
- } ];
-
- // Emit event
- this._emitEvent( 'row-reorder', eventArgs );
- var update = function () {
- if ( that.c.update ) {
- for ( i=0, ien=fullDiff.length ; i<ien ; i++ ) {
- var row = dt.row( fullDiff[i].node );
- var rowData = row.data();
- setDataFn( rowData, fullDiff[i].newData );
- // Invalidate the cell that has the same data source as the dataSrc
- dt.columns().every( function () {
- if ( this.dataSrc() === dataSrc ) {
- dt.cell( fullDiff[i].node, this.index() ).invalidate( 'data' );
- }
- } );
- }
- // Trigger row reordered event
- that._emitEvent( 'row-reordered', eventArgs );
- dt.draw( false );
- }
- };
- // Editor interface
- if ( this.c.editor ) {
- // Disable user interaction while Editor is submitting
- this.c.enable = false;
- this.c.editor
- .edit(
- diffNodes,
- false,
- $.extend( {submit: 'changed'}, this.c.formOptions )
- )
- .multiSet( dataSrc, idDiff )
- .one( 'submitUnsuccessful.rowReorder', function () {
- dt.draw( false );
- } )
- .one( 'submitSuccess.rowReorder', function () {
- update();
- } )
- .one( 'submitComplete', function () {
- that.c.enable = true;
- that.c.editor.off( '.rowReorder' );
- } )
- .submit();
- }
- else {
- update();
- }
- },
- /**
- * Move the window and DataTables scrolling during a drag to scroll new
- * content into view.
- *
- * This matches the `_shiftScroll` method used in AutoFill, but only
- * horizontal scrolling is considered here.
- *
- * @param {object} e Mouse move event object
- * @private
- */
- _shiftScroll: function ( e )
- {
- var that = this;
- var dt = this.s.dt;
- var scroll = this.s.scroll;
- var runInterval = false;
- var scrollSpeed = 5;
- var buffer = 65;
- var
- windowY = e.pageY - document.body.scrollTop,
- windowVert,
- dtVert;
- // Window calculations - based on the mouse position in the window,
- // regardless of scrolling
- if ( windowY < buffer ) {
- windowVert = scrollSpeed * -1;
- }
- else if ( windowY > scroll.windowHeight - buffer ) {
- windowVert = scrollSpeed;
- }
- // DataTables scrolling calculations - based on the table's position in
- // the document and the mouse position on the page
- if ( scroll.dtTop !== null && e.pageY < scroll.dtTop + buffer ) {
- dtVert = scrollSpeed * -1;
- }
- else if ( scroll.dtTop !== null && e.pageY > scroll.dtTop + scroll.dtHeight - buffer ) {
- dtVert = scrollSpeed;
- }
- // This is where it gets interesting. We want to continue scrolling
- // without requiring a mouse move, so we need an interval to be
- // triggered. The interval should continue until it is no longer needed,
- // but it must also use the latest scroll commands (for example consider
- // that the mouse might move from scrolling up to scrolling left, all
- // with the same interval running. We use the `scroll` object to "pass"
- // this information to the interval. Can't use local variables as they
- // wouldn't be the ones that are used by an already existing interval!
- if ( windowVert || dtVert ) {
- scroll.windowVert = windowVert;
- scroll.dtVert = dtVert;
- runInterval = true;
- }
- else if ( this.s.scrollInterval ) {
- // Don't need to scroll - remove any existing timer
- clearInterval( this.s.scrollInterval );
- this.s.scrollInterval = null;
- }
- // If we need to run the interval to scroll and there is no existing
- // interval (if there is an existing one, it will continue to run)
- if ( ! this.s.scrollInterval && runInterval ) {
- this.s.scrollInterval = setInterval( function () {
- // Don't need to worry about setting scroll <0 or beyond the
- // scroll bound as the browser will just reject that.
- if ( scroll.windowVert ) {
- document.body.scrollTop += scroll.windowVert;
- }
- // DataTables scrolling
- if ( scroll.dtVert ) {
- var scroller = that.dom.dtScroll[0];
- if ( scroll.dtVert ) {
- scroller.scrollTop += scroll.dtVert;
- }
- }
- }, 20 );
- }
- }
- } );
- /**
- * RowReorder default settings for initialisation
- *
- * @namespace
- * @name RowReorder.defaults
- * @static
- */
- RowReorder.defaults = {
- /**
- * Data point in the host row's data source object for where to get and set
- * the data to reorder. This will normally also be the sorting column.
- *
- * @type {Number}
- */
- dataSrc: 0,
- /**
- * Editor instance that will be used to perform the update
- *
- * @type {DataTable.Editor}
- */
- editor: null,
- /**
- * Enable / disable RowReorder's user interaction
- * @type {Boolean}
- */
- enable: true,
- /**
- * Form options to pass to Editor when submitting a change in the row order.
- * See the Editor `from-options` object for details of the options
- * available.
- * @type {Object}
- */
- formOptions: {},
- /**
- * Drag handle selector. This defines the element that when dragged will
- * reorder a row.
- *
- * @type {String}
- */
- selector: 'td:first-child',
- /**
- * Optionally lock the dragged row's x-position. This can be `true` to
- * fix the position match the host table's, `false` to allow free movement
- * of the row, or a number to define an offset from the host table.
- *
- * @type {Boolean|number}
- */
- snapX: false,
- /**
- * Update the table's data on drop
- *
- * @type {Boolean}
- */
- update: true
- };
- /*
- * API
- */
- var Api = $.fn.dataTable.Api;
- // Doesn't do anything - work around for a bug in DT... Not documented
- Api.register( 'rowReorder()', function () {
- return this;
- } );
- Api.register( 'rowReorder.enable()', function ( toggle ) {
- if ( toggle === undefined ) {
- toggle = true;
- }
- return this.iterator( 'table', function ( ctx ) {
- if ( ctx.rowreorder ) {
- ctx.rowreorder.c.enable = toggle;
- }
- } );
- } );
- Api.register( 'rowReorder.disable()', function () {
- return this.iterator( 'table', function ( ctx ) {
- if ( ctx.rowreorder ) {
- ctx.rowreorder.c.enable = false;
- }
- } );
- } );
- /**
- * Version information
- *
- * @name RowReorder.version
- * @static
- */
- RowReorder.version = '1.2.3';
- $.fn.dataTable.RowReorder = RowReorder;
- $.fn.DataTable.RowReorder = RowReorder;
- // Attach a listener to the document which listens for DataTables initialisation
- // events so we can automatically initialise
- $(document).on( 'init.dt.dtr', function (e, settings, json) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- var init = settings.oInit.rowReorder;
- var defaults = DataTable.defaults.rowReorder;
- if ( init || defaults ) {
- var opts = $.extend( {}, init, defaults );
- if ( init !== false ) {
- new RowReorder( settings, opts );
- }
- }
- } );
- return RowReorder;
- }));
- /*! Scroller 1.4.4
- * ©2011-2018 SpryMedia Ltd - datatables.net/license
- */
- /**
- * @summary Scroller
- * @description Virtual rendering for DataTables
- * @version 1.4.4
- * @file dataTables.scroller.js
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
- * @contact www.sprymedia.co.uk/contact
- * @copyright Copyright 2011-2018 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license/mit
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- /**
- * Scroller is a virtual rendering plug-in for DataTables which allows large
- * datasets to be drawn on screen every quickly. What the virtual rendering means
- * is that only the visible portion of the table (and a bit to either side to make
- * the scrolling smooth) is drawn, while the scrolling container gives the
- * visual impression that the whole table is visible. This is done by making use
- * of the pagination abilities of DataTables and moving the table around in the
- * scrolling container DataTables adds to the page. The scrolling container is
- * forced to the height it would be for the full table display using an extra
- * element.
- *
- * Note that rows in the table MUST all be the same height. Information in a cell
- * which expands on to multiple lines will cause some odd behaviour in the scrolling.
- *
- * Scroller is initialised by simply including the letter 'S' in the sDom for the
- * table you want to have this feature enabled on. Note that the 'S' must come
- * AFTER the 't' parameter in `dom`.
- *
- * Key features include:
- * <ul class="limit_length">
- * <li>Speed! The aim of Scroller for DataTables is to make rendering large data sets fast</li>
- * <li>Full compatibility with deferred rendering in DataTables for maximum speed</li>
- * <li>Display millions of rows</li>
- * <li>Integration with state saving in DataTables (scrolling position is saved)</li>
- * <li>Easy to use</li>
- * </ul>
- *
- * @class
- * @constructor
- * @global
- * @param {object} dt DataTables settings object or API instance
- * @param {object} [opts={}] Configuration object for FixedColumns. Options
- * are defined by {@link Scroller.defaults}
- *
- * @requires jQuery 1.7+
- * @requires DataTables 1.10.0+
- *
- * @example
- * $(document).ready(function() {
- * $('#example').DataTable( {
- * "scrollY": "200px",
- * "ajax": "media/dataset/large.txt",
- * "dom": "frtiS",
- * "deferRender": true
- * } );
- * } );
- */
- var Scroller = function ( dt, opts ) {
- /* Sanity check - you just know it will happen */
- if ( ! (this instanceof Scroller) ) {
- alert( "Scroller warning: Scroller must be initialised with the 'new' keyword." );
- return;
- }
- if ( opts === undefined ) {
- opts = {};
- }
- /**
- * Settings object which contains customisable information for the Scroller instance
- * @namespace
- * @private
- * @extends Scroller.defaults
- */
- this.s = {
- /**
- * DataTables settings object
- * @type object
- * @default Passed in as first parameter to constructor
- */
- "dt": $.fn.dataTable.Api( dt ).settings()[0],
- /**
- * Pixel location of the top of the drawn table in the viewport
- * @type int
- * @default 0
- */
- "tableTop": 0,
- /**
- * Pixel location of the bottom of the drawn table in the viewport
- * @type int
- * @default 0
- */
- "tableBottom": 0,
- /**
- * Pixel location of the boundary for when the next data set should be loaded and drawn
- * when scrolling up the way.
- * @type int
- * @default 0
- * @private
- */
- "redrawTop": 0,
- /**
- * Pixel location of the boundary for when the next data set should be loaded and drawn
- * when scrolling down the way. Note that this is actually calculated as the offset from
- * the top.
- * @type int
- * @default 0
- * @private
- */
- "redrawBottom": 0,
- /**
- * Auto row height or not indicator
- * @type bool
- * @default 0
- */
- "autoHeight": true,
- /**
- * Number of rows calculated as visible in the visible viewport
- * @type int
- * @default 0
- */
- "viewportRows": 0,
- /**
- * setTimeout reference for state saving, used when state saving is enabled in the DataTable
- * and when the user scrolls the viewport in order to stop the cookie set taking too much
- * CPU!
- * @type int
- * @default 0
- */
- "stateTO": null,
- /**
- * setTimeout reference for the redraw, used when server-side processing is enabled in the
- * DataTables in order to prevent DoSing the server
- * @type int
- * @default null
- */
- "drawTO": null,
- heights: {
- jump: null,
- page: null,
- virtual: null,
- scroll: null,
- /**
- * Height of rows in the table
- * @type int
- * @default 0
- */
- row: null,
- /**
- * Pixel height of the viewport
- * @type int
- * @default 0
- */
- viewport: null
- },
- topRowFloat: 0,
- scrollDrawDiff: null,
- loaderVisible: false,
- forceReposition: false
- };
- // @todo The defaults should extend a `c` property and the internal settings
- // only held in the `s` property. At the moment they are mixed
- this.s = $.extend( this.s, Scroller.oDefaults, opts );
- // Workaround for row height being read from height object (see above comment)
- this.s.heights.row = this.s.rowHeight;
- /**
- * DOM elements used by the class instance
- * @private
- * @namespace
- *
- */
- this.dom = {
- "force": document.createElement('div'),
- "scroller": null,
- "table": null,
- "loader": null
- };
- // Attach the instance to the DataTables instance so it can be accessed in
- // future. Don't initialise Scroller twice on the same table
- if ( this.s.dt.oScroller ) {
- return;
- }
- this.s.dt.oScroller = this;
- /* Let's do it */
- this._fnConstruct();
- };
- $.extend( Scroller.prototype, {
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Public methods
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * Calculate the pixel position from the top of the scrolling container for
- * a given row
- * @param {int} iRow Row number to calculate the position of
- * @returns {int} Pixels
- * @example
- * $(document).ready(function() {
- * $('#example').dataTable( {
- * "sScrollY": "200px",
- * "sAjaxSource": "media/dataset/large.txt",
- * "sDom": "frtiS",
- * "bDeferRender": true,
- * "fnInitComplete": function (o) {
- * // Find where row 25 is
- * alert( o.oScroller.fnRowToPixels( 25 ) );
- * }
- * } );
- * } );
- */
- "fnRowToPixels": function ( rowIdx, intParse, virtual )
- {
- var pixels;
- if ( virtual ) {
- pixels = this._domain( 'virtualToPhysical', rowIdx * this.s.heights.row );
- }
- else {
- var diff = rowIdx - this.s.baseRowTop;
- pixels = this.s.baseScrollTop + (diff * this.s.heights.row);
- }
- return intParse || intParse === undefined ?
- parseInt( pixels, 10 ) :
- pixels;
- },
- /**
- * Calculate the row number that will be found at the given pixel position
- * (y-scroll).
- *
- * Please note that when the height of the full table exceeds 1 million
- * pixels, Scroller switches into a non-linear mode for the scrollbar to fit
- * all of the records into a finite area, but this function returns a linear
- * value (relative to the last non-linear positioning).
- * @param {int} iPixels Offset from top to calculate the row number of
- * @param {int} [intParse=true] If an integer value should be returned
- * @param {int} [virtual=false] Perform the calculations in the virtual domain
- * @returns {int} Row index
- * @example
- * $(document).ready(function() {
- * $('#example').dataTable( {
- * "sScrollY": "200px",
- * "sAjaxSource": "media/dataset/large.txt",
- * "sDom": "frtiS",
- * "bDeferRender": true,
- * "fnInitComplete": function (o) {
- * // Find what row number is at 500px
- * alert( o.oScroller.fnPixelsToRow( 500 ) );
- * }
- * } );
- * } );
- */
- "fnPixelsToRow": function ( pixels, intParse, virtual )
- {
- var diff = pixels - this.s.baseScrollTop;
- var row = virtual ?
- this._domain( 'physicalToVirtual', pixels ) / this.s.heights.row :
- ( diff / this.s.heights.row ) + this.s.baseRowTop;
- return intParse || intParse === undefined ?
- parseInt( row, 10 ) :
- row;
- },
- /**
- * Calculate the row number that will be found at the given pixel position (y-scroll)
- * @param {int} iRow Row index to scroll to
- * @param {bool} [bAnimate=true] Animate the transition or not
- * @returns {void}
- * @example
- * $(document).ready(function() {
- * $('#example').dataTable( {
- * "sScrollY": "200px",
- * "sAjaxSource": "media/dataset/large.txt",
- * "sDom": "frtiS",
- * "bDeferRender": true,
- * "fnInitComplete": function (o) {
- * // Immediately scroll to row 1000
- * o.oScroller.fnScrollToRow( 1000 );
- * }
- * } );
- *
- * // Sometime later on use the following to scroll to row 500...
- * var oSettings = $('#example').dataTable().fnSettings();
- * oSettings.oScroller.fnScrollToRow( 500 );
- * } );
- */
- "fnScrollToRow": function ( iRow, bAnimate )
- {
- var that = this;
- var ani = false;
- var px = this.fnRowToPixels( iRow );
- // We need to know if the table will redraw or not before doing the
- // scroll. If it will not redraw, then we need to use the currently
- // displayed table, and scroll with the physical pixels. Otherwise, we
- // need to calculate the table's new position from the virtual
- // transform.
- var preRows = ((this.s.displayBuffer-1)/2) * this.s.viewportRows;
- var drawRow = iRow - preRows;
- if ( drawRow < 0 ) {
- drawRow = 0;
- }
- if ( (px > this.s.redrawBottom || px < this.s.redrawTop) && this.s.dt._iDisplayStart !== drawRow ) {
- ani = true;
- px = this.fnRowToPixels( iRow, false, true );
- // If we need records outside the current draw region, but the new
- // scrolling position is inside that (due to the non-linear nature
- // for larger numbers of records), we need to force position update.
- if ( this.s.redrawTop < px && px < this.s.redrawBottom ) {
- this.s.forceReposition = true;
- bAnimate = false;
- }
- }
- if ( typeof bAnimate == 'undefined' || bAnimate )
- {
- this.s.ani = ani;
- $(this.dom.scroller).animate( {
- "scrollTop": px
- }, function () {
- // This needs to happen after the animation has completed and
- // the final scroll event fired
- setTimeout( function () {
- that.s.ani = false;
- }, 25 );
- } );
- }
- else
- {
- $(this.dom.scroller).scrollTop( px );
- }
- },
- /**
- * Calculate and store information about how many rows are to be displayed
- * in the scrolling viewport, based on current dimensions in the browser's
- * rendering. This can be particularly useful if the table is initially
- * drawn in a hidden element - for example in a tab.
- * @param {bool} [bRedraw=true] Redraw the table automatically after the recalculation, with
- * the new dimensions forming the basis for the draw.
- * @returns {void}
- * @example
- * $(document).ready(function() {
- * // Make the example container hidden to throw off the browser's sizing
- * document.getElementById('container').style.display = "none";
- * var oTable = $('#example').dataTable( {
- * "sScrollY": "200px",
- * "sAjaxSource": "media/dataset/large.txt",
- * "sDom": "frtiS",
- * "bDeferRender": true,
- * "fnInitComplete": function (o) {
- * // Immediately scroll to row 1000
- * o.oScroller.fnScrollToRow( 1000 );
- * }
- * } );
- *
- * setTimeout( function () {
- * // Make the example container visible and recalculate the scroller sizes
- * document.getElementById('container').style.display = "block";
- * oTable.fnSettings().oScroller.fnMeasure();
- * }, 3000 );
- */
- "fnMeasure": function ( bRedraw )
- {
- if ( this.s.autoHeight )
- {
- this._fnCalcRowHeight();
- }
- var heights = this.s.heights;
- if ( heights.row ) {
- heights.viewport = $.contains(document, this.dom.scroller) ?
- $(this.dom.scroller).height() :
- this._parseHeight($(this.dom.scroller).css('height'));
- // If collapsed (no height) use the max-height parameter
- if ( ! heights.viewport ) {
- heights.viewport = this._parseHeight($(this.dom.scroller).css('max-height'));
- }
- this.s.viewportRows = parseInt( heights.viewport / heights.row, 10 )+1;
- this.s.dt._iDisplayLength = this.s.viewportRows * this.s.displayBuffer;
- }
- if ( bRedraw === undefined || bRedraw )
- {
- this.s.dt.oInstance.fnDraw( false );
- }
- },
- /**
- * Get information about current displayed record range. This corresponds to
- * the information usually displayed in the "Info" block of the table.
- *
- * @returns {object} info as an object:
- * {
- * start: {int}, // the 0-indexed record at the top of the viewport
- * end: {int}, // the 0-indexed record at the bottom of the viewport
- * }
- */
- "fnPageInfo": function()
- {
- var
- dt = this.s.dt,
- iScrollTop = this.dom.scroller.scrollTop,
- iTotal = dt.fnRecordsDisplay(),
- iPossibleEnd = Math.ceil(this.fnPixelsToRow(iScrollTop + this.s.heights.viewport, false, this.s.ani));
- return {
- start: Math.floor(this.fnPixelsToRow(iScrollTop, false, this.s.ani)),
- end: iTotal < iPossibleEnd ? iTotal-1 : iPossibleEnd-1
- };
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Private methods (they are of course public in JS, but recommended as private)
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * Initialisation for Scroller
- * @returns {void}
- * @private
- */
- "_fnConstruct": function ()
- {
- var that = this;
- /* Sanity check */
- if ( !this.s.dt.oFeatures.bPaginate ) {
- this.s.dt.oApi._fnLog( this.s.dt, 0, 'Pagination must be enabled for Scroller' );
- return;
- }
- /* Insert a div element that we can use to force the DT scrolling container to
- * the height that would be required if the whole table was being displayed
- */
- this.dom.force.style.position = "relative";
- this.dom.force.style.top = "0px";
- this.dom.force.style.left = "0px";
- this.dom.force.style.width = "1px";
- this.dom.scroller = $('div.'+this.s.dt.oClasses.sScrollBody, this.s.dt.nTableWrapper)[0];
- this.dom.scroller.appendChild( this.dom.force );
- this.dom.scroller.style.position = "relative";
- this.dom.table = $('>table', this.dom.scroller)[0];
- this.dom.table.style.position = "absolute";
- this.dom.table.style.top = "0px";
- this.dom.table.style.left = "0px";
- // Add class to 'announce' that we are a Scroller table
- $(this.s.dt.nTableWrapper).addClass('DTS');
- // Add a 'loading' indicator
- if ( this.s.loadingIndicator )
- {
- this.dom.loader = $('<div class="dataTables_processing DTS_Loading">'+this.s.dt.oLanguage.sLoadingRecords+'</div>')
- .css('display', 'none');
- $(this.dom.scroller.parentNode)
- .css('position', 'relative')
- .append( this.dom.loader );
- }
- /* Initial size calculations */
- if ( this.s.heights.row && this.s.heights.row != 'auto' )
- {
- this.s.autoHeight = false;
- }
- this.fnMeasure( false );
- /* Scrolling callback to see if a page change is needed - use a throttled
- * function for the save save callback so we aren't hitting it on every
- * scroll
- */
- this.s.ingnoreScroll = true;
- this.s.stateSaveThrottle = this.s.dt.oApi._fnThrottle( function () {
- that.s.dt.oApi._fnSaveState( that.s.dt );
- }, 500 );
- $(this.dom.scroller).on( 'scroll.DTS', function (e) {
- that._fnScroll.call( that );
- } );
- /* In iOS we catch the touchstart event in case the user tries to scroll
- * while the display is already scrolling
- */
- $(this.dom.scroller).on('touchstart.DTS', function () {
- that._fnScroll.call( that );
- } );
- /* Update the scroller when the DataTable is redrawn */
- this.s.dt.aoDrawCallback.push( {
- "fn": function () {
- if ( that.s.dt.bInitialised ) {
- that._fnDrawCallback.call( that );
- }
- },
- "sName": "Scroller"
- } );
- /* On resize, update the information element, since the number of rows shown might change */
- $(window).on( 'resize.DTS', function () {
- that.fnMeasure( false );
- that._fnInfo();
- } );
- /* Add a state saving parameter to the DT state saving so we can restore the exact
- * position of the scrolling
- */
- var initialStateSave = true;
- this.s.dt.oApi._fnCallbackReg( this.s.dt, 'aoStateSaveParams', function (oS, oData) {
- /* Set iScroller to saved scroll position on initialization.
- */
- if(initialStateSave && that.s.dt.oLoadedState){
- oData.iScroller = that.s.dt.oLoadedState.iScroller;
- oData.iScrollerTopRow = that.s.dt.oLoadedState.iScrollerTopRow;
- initialStateSave = false;
- } else {
- oData.iScroller = that.dom.scroller.scrollTop;
- oData.iScrollerTopRow = that.s.topRowFloat;
- }
- }, "Scroller_State" );
- if ( this.s.dt.oLoadedState ) {
- this.s.topRowFloat = this.s.dt.oLoadedState.iScrollerTopRow || 0;
- }
- // Measure immediately. Scroller will have been added using preInit, so
- // we can reliably do this here. We could potentially also measure on
- // init complete, which would be useful for cases where the data is Ajax
- // loaded and longer than a single line.
- $(this.s.dt.nTable).one( 'init.dt', function () {
- that.fnMeasure();
- } );
- /* Destructor */
- this.s.dt.aoDestroyCallback.push( {
- "sName": "Scroller",
- "fn": function () {
- $(window).off( 'resize.DTS' );
- $(that.dom.scroller).off('touchstart.DTS scroll.DTS');
- $(that.s.dt.nTableWrapper).removeClass('DTS');
- $('div.DTS_Loading', that.dom.scroller.parentNode).remove();
- $(that.s.dt.nTable).off( 'init.dt' );
- that.dom.table.style.position = "";
- that.dom.table.style.top = "";
- that.dom.table.style.left = "";
- }
- } );
- },
- /**
- * Scrolling function - fired whenever the scrolling position is changed.
- * This method needs to use the stored values to see if the table should be
- * redrawn as we are moving towards the end of the information that is
- * currently drawn or not. If needed, then it will redraw the table based on
- * the new position.
- * @returns {void}
- * @private
- */
- "_fnScroll": function ()
- {
- var
- that = this,
- heights = this.s.heights,
- iScrollTop = this.dom.scroller.scrollTop,
- iTopRow;
- if ( this.s.skip ) {
- return;
- }
- if ( this.s.ingnoreScroll ) {
- return;
- }
- /* If the table has been sorted or filtered, then we use the redraw that
- * DataTables as done, rather than performing our own
- */
- if ( this.s.dt.bFiltered || this.s.dt.bSorted ) {
- this.s.lastScrollTop = 0;
- return;
- }
- /* Update the table's information display for what is now in the viewport */
- this._fnInfo();
- /* We don't want to state save on every scroll event - that's heavy
- * handed, so use a timeout to update the state saving only when the
- * scrolling has finished
- */
- clearTimeout( this.s.stateTO );
- this.s.stateTO = setTimeout( function () {
- that.s.dt.oApi._fnSaveState( that.s.dt );
- }, 250 );
- /* Check if the scroll point is outside the trigger boundary which would required
- * a DataTables redraw
- */
- if ( this.s.forceReposition || iScrollTop < this.s.redrawTop || iScrollTop > this.s.redrawBottom ) {
- var preRows = Math.ceil( ((this.s.displayBuffer-1)/2) * this.s.viewportRows );
- if ( Math.abs( iScrollTop - this.s.lastScrollTop ) > heights.viewport || this.s.ani || this.s.forceReposition ) {
- iTopRow = parseInt(this._domain( 'physicalToVirtual', iScrollTop ) / heights.row, 10) - preRows;
- this.s.topRowFloat = this._domain( 'physicalToVirtual', iScrollTop ) / heights.row;
- }
- else {
- iTopRow = this.fnPixelsToRow( iScrollTop ) - preRows;
- this.s.topRowFloat = this.fnPixelsToRow( iScrollTop, false );
- }
- this.s.forceReposition = false;
- if ( iTopRow <= 0 ) {
- /* At the start of the table */
- iTopRow = 0;
- }
- else if ( iTopRow + this.s.dt._iDisplayLength > this.s.dt.fnRecordsDisplay() ) {
- /* At the end of the table */
- iTopRow = this.s.dt.fnRecordsDisplay() - this.s.dt._iDisplayLength;
- if ( iTopRow < 0 ) {
- iTopRow = 0;
- }
- }
- else if ( iTopRow % 2 !== 0 ) {
- // For the row-striping classes (odd/even) we want only to start
- // on evens otherwise the stripes will change between draws and
- // look rubbish
- iTopRow++;
- }
- if ( iTopRow != this.s.dt._iDisplayStart ) {
- /* Cache the new table position for quick lookups */
- this.s.tableTop = $(this.s.dt.nTable).offset().top;
- this.s.tableBottom = $(this.s.dt.nTable).height() + this.s.tableTop;
- var draw = function () {
- if ( that.s.scrollDrawReq === null ) {
- that.s.scrollDrawReq = iScrollTop;
- }
- that.s.dt._iDisplayStart = iTopRow;
- that.s.dt.oApi._fnDraw( that.s.dt );
- };
- /* Do the DataTables redraw based on the calculated start point - note that when
- * using server-side processing we introduce a small delay to not DoS the server...
- */
- if ( this.s.dt.oFeatures.bServerSide ) {
- clearTimeout( this.s.drawTO );
- this.s.drawTO = setTimeout( draw, this.s.serverWait );
- }
- else {
- draw();
- }
- if ( this.dom.loader && ! this.s.loaderVisible ) {
- this.dom.loader.css( 'display', 'block' );
- this.s.loaderVisible = true;
- }
- }
- }
- else {
- this.s.topRowFloat = this._domain( 'physicalToVirtual', iScrollTop ) / heights.row;
- }
- this.s.lastScrollTop = iScrollTop;
- this.s.stateSaveThrottle();
- },
- /**
- * Convert from one domain to another. The physical domain is the actual
- * pixel count on the screen, while the virtual is if we had browsers which
- * had scrolling containers of infinite height (i.e. the absolute value)
- *
- * @param {string} dir Domain transform direction, `virtualToPhysical` or
- * `physicalToVirtual`
- * @returns {number} Calculated transform
- * @private
- */
- _domain: function ( dir, val )
- {
- var heights = this.s.heights;
- var coeff;
- // If the virtual and physical height match, then we use a linear
- // transform between the two, allowing the scrollbar to be linear
- if ( heights.virtual === heights.scroll ) {
- return val;
- }
- // Otherwise, we want a non-linear scrollbar to take account of the
- // redrawing regions at the start and end of the table, otherwise these
- // can stutter badly - on large tables 30px (for example) scroll might
- // be hundreds of rows, so the table would be redrawing every few px at
- // the start and end. Use a simple quadratic to stop this. It does mean
- // the scrollbar is non-linear, but with such massive data sets, the
- // scrollbar is going to be a best guess anyway
- var xMax = (heights.scroll - heights.viewport) / 2;
- var yMax = (heights.virtual - heights.viewport) / 2;
- coeff = yMax / ( xMax * xMax );
- if ( dir === 'virtualToPhysical' ) {
- if ( val < yMax ) {
- return Math.pow(val / coeff, 0.5);
- }
- else {
- val = (yMax*2) - val;
- return val < 0 ?
- heights.scroll :
- (xMax*2) - Math.pow(val / coeff, 0.5);
- }
- }
- else if ( dir === 'physicalToVirtual' ) {
- if ( val < xMax ) {
- return val * val * coeff;
- }
- else {
- val = (xMax*2) - val;
- return val < 0 ?
- heights.virtual :
- (yMax*2) - (val * val * coeff);
- }
- }
- },
- /**
- * Parse CSS height property string as number
- *
- * An attempt is made to parse the string as a number. Currently supported units are 'px',
- * 'vh', and 'rem'. 'em' is partially supported; it works as long as the parent element's
- * font size matches the body element. Zero is returned for unrecognized strings.
- * @param {string} cssHeight CSS height property string
- * @returns {number} height
- * @private
- */
- _parseHeight: function(cssHeight) {
- var height;
- var matches = /^([+-]?(?:\d+(?:\.\d+)?|\.\d+))(px|em|rem|vh)$/.exec(cssHeight);
- if (matches === null) {
- return 0;
- }
- var value = parseFloat(matches[1]);
- var unit = matches[2];
- if ( unit === 'px' ) {
- height = value;
- }
- else if ( unit === 'vh' ) {
- height = ( value / 100 ) * $(window).height();
- }
- else if ( unit === 'rem' ) {
- height = value * parseFloat($(':root').css('font-size'));
- }
- else if ( unit === 'em' ) {
- height = value * parseFloat($('body').css('font-size'));
- }
- return height ?
- height :
- 0;
- },
- /**
- * Draw callback function which is fired when the DataTable is redrawn. The main function of
- * this method is to position the drawn table correctly the scrolling container for the rows
- * that is displays as a result of the scrolling position.
- * @returns {void}
- * @private
- */
- "_fnDrawCallback": function ()
- {
- var
- that = this,
- heights = this.s.heights,
- iScrollTop = this.dom.scroller.scrollTop,
- iActualScrollTop = iScrollTop,
- iScrollBottom = iScrollTop + heights.viewport,
- iTableHeight = $(this.s.dt.nTable).height(),
- displayStart = this.s.dt._iDisplayStart,
- displayLen = this.s.dt._iDisplayLength,
- displayEnd = this.s.dt.fnRecordsDisplay();
- // Disable the scroll event listener while we are updating the DOM
- this.s.skip = true;
- // Resize the scroll forcing element
- this._fnScrollForce();
- // Reposition the scrolling for the updated virtual position if needed
- if ( displayStart === 0 ) {
- // Linear calculation at the top of the table
- iScrollTop = this.s.topRowFloat * heights.row;
- }
- else if ( displayStart + displayLen >= displayEnd ) {
- // Linear calculation that the bottom as well
- iScrollTop = heights.scroll - ((displayEnd - this.s.topRowFloat) * heights.row);
- }
- else {
- // Domain scaled in the middle
- iScrollTop = this._domain( 'virtualToPhysical', this.s.topRowFloat * heights.row );
- }
- this.dom.scroller.scrollTop = iScrollTop;
- // Store positional information so positional calculations can be based
- // upon the current table draw position
- this.s.baseScrollTop = iScrollTop;
- this.s.baseRowTop = this.s.topRowFloat;
- // Position the table in the virtual scroller
- var tableTop = iScrollTop - ((this.s.topRowFloat - displayStart) * heights.row);
- if ( displayStart === 0 ) {
- tableTop = 0;
- }
- else if ( displayStart + displayLen >= displayEnd ) {
- tableTop = heights.scroll - iTableHeight;
- }
- this.dom.table.style.top = tableTop+'px';
- /* Cache some information for the scroller */
- this.s.tableTop = tableTop;
- this.s.tableBottom = iTableHeight + this.s.tableTop;
- // Calculate the boundaries for where a redraw will be triggered by the
- // scroll event listener
- var boundaryPx = (iScrollTop - this.s.tableTop) * this.s.boundaryScale;
- this.s.redrawTop = iScrollTop - boundaryPx;
- this.s.redrawBottom = iScrollTop + boundaryPx > heights.scroll - heights.viewport - heights.row ?
- heights.scroll - heights.viewport - heights.row :
- iScrollTop + boundaryPx;
- this.s.skip = false;
- // Restore the scrolling position that was saved by DataTable's state
- // saving Note that this is done on the second draw when data is Ajax
- // sourced, and the first draw when DOM soured
- if ( this.s.dt.oFeatures.bStateSave && this.s.dt.oLoadedState !== null &&
- typeof this.s.dt.oLoadedState.iScroller != 'undefined' )
- {
- // A quirk of DataTables is that the draw callback will occur on an
- // empty set if Ajax sourced, but not if server-side processing.
- var ajaxSourced = (this.s.dt.sAjaxSource || that.s.dt.ajax) && ! this.s.dt.oFeatures.bServerSide ?
- true :
- false;
- if ( ( ajaxSourced && this.s.dt.iDraw == 2) ||
- (!ajaxSourced && this.s.dt.iDraw == 1) )
- {
- setTimeout( function () {
- $(that.dom.scroller).scrollTop( that.s.dt.oLoadedState.iScroller );
- that.s.redrawTop = that.s.dt.oLoadedState.iScroller - (heights.viewport/2);
- // In order to prevent layout thrashing we need another
- // small delay
- setTimeout( function () {
- that.s.ingnoreScroll = false;
- }, 0 );
- }, 0 );
- }
- }
- else {
- that.s.ingnoreScroll = false;
- }
- // Because of the order of the DT callbacks, the info update will
- // take precedence over the one we want here. So a 'thread' break is
- // needed. Only add the thread break if bInfo is set
- if ( this.s.dt.oFeatures.bInfo ) {
- setTimeout( function () {
- that._fnInfo.call( that );
- }, 0 );
- }
- // Hide the loading indicator
- if ( this.dom.loader && this.s.loaderVisible ) {
- this.dom.loader.css( 'display', 'none' );
- this.s.loaderVisible = false;
- }
- },
- /**
- * Force the scrolling container to have height beyond that of just the
- * table that has been drawn so the user can scroll the whole data set.
- *
- * Note that if the calculated required scrolling height exceeds a maximum
- * value (1 million pixels - hard-coded) the forcing element will be set
- * only to that maximum value and virtual / physical domain transforms will
- * be used to allow Scroller to display tables of any number of records.
- * @returns {void}
- * @private
- */
- _fnScrollForce: function ()
- {
- var heights = this.s.heights;
- var max = 1000000;
- heights.virtual = heights.row * this.s.dt.fnRecordsDisplay();
- heights.scroll = heights.virtual;
- if ( heights.scroll > max ) {
- heights.scroll = max;
- }
- // Minimum height so there is always a row visible (the 'no rows found'
- // if reduced to zero filtering)
- this.dom.force.style.height = heights.scroll > this.s.heights.row ?
- heights.scroll+'px' :
- this.s.heights.row+'px';
- },
- /**
- * Automatic calculation of table row height. This is just a little tricky here as using
- * initialisation DataTables has tale the table out of the document, so we need to create
- * a new table and insert it into the document, calculate the row height and then whip the
- * table out.
- * @returns {void}
- * @private
- */
- "_fnCalcRowHeight": function ()
- {
- var dt = this.s.dt;
- var origTable = dt.nTable;
- var nTable = origTable.cloneNode( false );
- var tbody = $('<tbody/>').appendTo( nTable );
- var container = $(
- '<div class="'+dt.oClasses.sWrapper+' DTS">'+
- '<div class="'+dt.oClasses.sScrollWrapper+'">'+
- '<div class="'+dt.oClasses.sScrollBody+'"></div>'+
- '</div>'+
- '</div>'
- );
- // Want 3 rows in the sizing table so :first-child and :last-child
- // CSS styles don't come into play - take the size of the middle row
- $('tbody tr:lt(4)', origTable).clone().appendTo( tbody );
- while( $('tr', tbody).length < 3 ) {
- tbody.append( '<tr><td> </td></tr>' );
- }
- $('div.'+dt.oClasses.sScrollBody, container).append( nTable );
- // If initialised using `dom`, use the holding element as the insert point
- var insertEl = this.s.dt.nHolding || origTable.parentNode;
- if ( ! $(insertEl).is(':visible') ) {
- insertEl = 'body';
- }
- container.appendTo( insertEl );
- this.s.heights.row = $('tr', tbody).eq(1).outerHeight();
- container.remove();
- },
- /**
- * Update any information elements that are controlled by the DataTable based on the scrolling
- * viewport and what rows are visible in it. This function basically acts in the same way as
- * _fnUpdateInfo in DataTables, and effectively replaces that function.
- * @returns {void}
- * @private
- */
- "_fnInfo": function ()
- {
- if ( !this.s.dt.oFeatures.bInfo )
- {
- return;
- }
- var
- dt = this.s.dt,
- language = dt.oLanguage,
- iScrollTop = this.dom.scroller.scrollTop,
- iStart = Math.floor( this.fnPixelsToRow(iScrollTop, false, this.s.ani)+1 ),
- iMax = dt.fnRecordsTotal(),
- iTotal = dt.fnRecordsDisplay(),
- iPossibleEnd = Math.ceil( this.fnPixelsToRow(iScrollTop+this.s.heights.viewport, false, this.s.ani) ),
- iEnd = iTotal < iPossibleEnd ? iTotal : iPossibleEnd,
- sStart = dt.fnFormatNumber( iStart ),
- sEnd = dt.fnFormatNumber( iEnd ),
- sMax = dt.fnFormatNumber( iMax ),
- sTotal = dt.fnFormatNumber( iTotal ),
- sOut;
- if ( dt.fnRecordsDisplay() === 0 &&
- dt.fnRecordsDisplay() == dt.fnRecordsTotal() )
- {
- /* Empty record set */
- sOut = language.sInfoEmpty+ language.sInfoPostFix;
- }
- else if ( dt.fnRecordsDisplay() === 0 )
- {
- /* Empty record set after filtering */
- sOut = language.sInfoEmpty +' '+
- language.sInfoFiltered.replace('_MAX_', sMax)+
- language.sInfoPostFix;
- }
- else if ( dt.fnRecordsDisplay() == dt.fnRecordsTotal() )
- {
- /* Normal record set */
- sOut = language.sInfo.
- replace('_START_', sStart).
- replace('_END_', sEnd).
- replace('_MAX_', sMax).
- replace('_TOTAL_', sTotal)+
- language.sInfoPostFix;
- }
- else
- {
- /* Record set after filtering */
- sOut = language.sInfo.
- replace('_START_', sStart).
- replace('_END_', sEnd).
- replace('_MAX_', sMax).
- replace('_TOTAL_', sTotal) +' '+
- language.sInfoFiltered.replace(
- '_MAX_',
- dt.fnFormatNumber(dt.fnRecordsTotal())
- )+
- language.sInfoPostFix;
- }
- var callback = language.fnInfoCallback;
- if ( callback ) {
- sOut = callback.call( dt.oInstance,
- dt, iStart, iEnd, iMax, iTotal, sOut
- );
- }
- var n = dt.aanFeatures.i;
- if ( typeof n != 'undefined' )
- {
- for ( var i=0, iLen=n.length ; i<iLen ; i++ )
- {
- $(n[i]).html( sOut );
- }
- }
- // DT doesn't actually (yet) trigger this event, but it will in future
- $(dt.nTable).triggerHandler( 'info.dt' );
- }
- } );
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Statics
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * Scroller default settings for initialisation
- * @namespace
- * @name Scroller.defaults
- * @static
- */
- Scroller.defaults = /** @lends Scroller.defaults */{
- /**
- * Indicate if Scroller show show trace information on the console or not. This can be
- * useful when debugging Scroller or if just curious as to what it is doing, but should
- * be turned off for production.
- * @type bool
- * @default false
- * @static
- * @example
- * var oTable = $('#example').dataTable( {
- * "sScrollY": "200px",
- * "sDom": "frtiS",
- * "bDeferRender": true,
- * "oScroller": {
- * "trace": true
- * }
- * } );
- */
- "trace": false,
- /**
- * Scroller will attempt to automatically calculate the height of rows for it's internal
- * calculations. However the height that is used can be overridden using this parameter.
- * @type int|string
- * @default auto
- * @static
- * @example
- * var oTable = $('#example').dataTable( {
- * "sScrollY": "200px",
- * "sDom": "frtiS",
- * "bDeferRender": true,
- * "oScroller": {
- * "rowHeight": 30
- * }
- * } );
- */
- "rowHeight": "auto",
- /**
- * When using server-side processing, Scroller will wait a small amount of time to allow
- * the scrolling to finish before requesting more data from the server. This prevents
- * you from DoSing your own server! The wait time can be configured by this parameter.
- * @type int
- * @default 200
- * @static
- * @example
- * var oTable = $('#example').dataTable( {
- * "sScrollY": "200px",
- * "sDom": "frtiS",
- * "bDeferRender": true,
- * "oScroller": {
- * "serverWait": 100
- * }
- * } );
- */
- "serverWait": 200,
- /**
- * The display buffer is what Scroller uses to calculate how many rows it should pre-fetch
- * for scrolling. Scroller automatically adjusts DataTables' display length to pre-fetch
- * rows that will be shown in "near scrolling" (i.e. just beyond the current display area).
- * The value is based upon the number of rows that can be displayed in the viewport (i.e.
- * a value of 1), and will apply the display range to records before before and after the
- * current viewport - i.e. a factor of 3 will allow Scroller to pre-fetch 1 viewport's worth
- * of rows before the current viewport, the current viewport's rows and 1 viewport's worth
- * of rows after the current viewport. Adjusting this value can be useful for ensuring
- * smooth scrolling based on your data set.
- * @type int
- * @default 7
- * @static
- * @example
- * var oTable = $('#example').dataTable( {
- * "sScrollY": "200px",
- * "sDom": "frtiS",
- * "bDeferRender": true,
- * "oScroller": {
- * "displayBuffer": 10
- * }
- * } );
- */
- "displayBuffer": 9,
- /**
- * Scroller uses the boundary scaling factor to decide when to redraw the table - which it
- * typically does before you reach the end of the currently loaded data set (in order to
- * allow the data to look continuous to a user scrolling through the data). If given as 0
- * then the table will be redrawn whenever the viewport is scrolled, while 1 would not
- * redraw the table until the currently loaded data has all been shown. You will want
- * something in the middle - the default factor of 0.5 is usually suitable.
- * @type float
- * @default 0.5
- * @static
- * @example
- * var oTable = $('#example').dataTable( {
- * "sScrollY": "200px",
- * "sDom": "frtiS",
- * "bDeferRender": true,
- * "oScroller": {
- * "boundaryScale": 0.75
- * }
- * } );
- */
- "boundaryScale": 0.5,
- /**
- * Show (or not) the loading element in the background of the table. Note that you should
- * include the dataTables.scroller.css file for this to be displayed correctly.
- * @type boolean
- * @default false
- * @static
- * @example
- * var oTable = $('#example').dataTable( {
- * "sScrollY": "200px",
- * "sDom": "frtiS",
- * "bDeferRender": true,
- * "oScroller": {
- * "loadingIndicator": true
- * }
- * } );
- */
- "loadingIndicator": false
- };
- Scroller.oDefaults = Scroller.defaults;
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constants
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * Scroller version
- * @type String
- * @default See code
- * @name Scroller.version
- * @static
- */
- Scroller.version = "1.4.4";
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Initialisation
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- // Legacy `dom` parameter initialisation support
- if ( typeof $.fn.dataTable == "function" &&
- typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
- $.fn.dataTableExt.fnVersionCheck('1.10.0') )
- {
- $.fn.dataTableExt.aoFeatures.push( {
- "fnInit": function( oDTSettings ) {
- var init = oDTSettings.oInit;
- var opts = init.scroller || init.oScroller || {};
-
- new Scroller( oDTSettings, opts );
- },
- "cFeature": "S",
- "sFeature": "Scroller"
- } );
- }
- else
- {
- alert( "Warning: Scroller requires DataTables 1.10.0 or greater - www.datatables.net/download");
- }
- // Attach a listener to the document which listens for DataTables initialisation
- // events so we can automatically initialise
- $(document).on( 'preInit.dt.dtscroller', function (e, settings) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- var init = settings.oInit.scroller;
- var defaults = DataTable.defaults.scroller;
- if ( init || defaults ) {
- var opts = $.extend( {}, init, defaults );
- if ( init !== false ) {
- new Scroller( settings, opts );
- }
- }
- } );
- // Attach Scroller to DataTables so it can be accessed as an 'extra'
- $.fn.dataTable.Scroller = Scroller;
- $.fn.DataTable.Scroller = Scroller;
- // DataTables 1.10 API method aliases
- var Api = $.fn.dataTable.Api;
- Api.register( 'scroller()', function () {
- return this;
- } );
- // Undocumented and deprecated - is it actually useful at all?
- Api.register( 'scroller().rowToPixels()', function ( rowIdx, intParse, virtual ) {
- var ctx = this.context;
- if ( ctx.length && ctx[0].oScroller ) {
- return ctx[0].oScroller.fnRowToPixels( rowIdx, intParse, virtual );
- }
- // undefined
- } );
- // Undocumented and deprecated - is it actually useful at all?
- Api.register( 'scroller().pixelsToRow()', function ( pixels, intParse, virtual ) {
- var ctx = this.context;
- if ( ctx.length && ctx[0].oScroller ) {
- return ctx[0].oScroller.fnPixelsToRow( pixels, intParse, virtual );
- }
- // undefined
- } );
- // Undocumented and deprecated - use `row().scrollTo()` instead
- Api.register( 'scroller().scrollToRow()', function ( row, ani ) {
- this.iterator( 'table', function ( ctx ) {
- if ( ctx.oScroller ) {
- ctx.oScroller.fnScrollToRow( row, ani );
- }
- } );
- return this;
- } );
- Api.register( 'row().scrollTo()', function ( ani ) {
- var that = this;
- this.iterator( 'row', function ( ctx, rowIdx ) {
- if ( ctx.oScroller ) {
- var displayIdx = that
- .rows( { order: 'applied', search: 'applied' } )
- .indexes()
- .indexOf( rowIdx );
- ctx.oScroller.fnScrollToRow( displayIdx, ani );
- }
- } );
- return this;
- } );
- Api.register( 'scroller.measure()', function ( redraw ) {
- this.iterator( 'table', function ( ctx ) {
- if ( ctx.oScroller ) {
- ctx.oScroller.fnMeasure( redraw );
- }
- } );
- return this;
- } );
- Api.register( 'scroller.page()', function() {
- var ctx = this.context;
- if ( ctx.length && ctx[0].oScroller ) {
- return ctx[0].oScroller.fnPageInfo();
- }
- // undefined
- } );
- return Scroller;
- }));
- /*! Select for DataTables 1.2.5
- * 2015-2018 SpryMedia Ltd - datatables.net/license/mit
- */
- /**
- * @summary Select for DataTables
- * @description A collection of API methods, events and buttons for DataTables
- * that provides selection options of the items in a DataTable
- * @version 1.2.5
- * @file dataTables.select.js
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
- * @contact datatables.net/forums
- * @copyright Copyright 2015-2018 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license/mit
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net/extensions/select
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- // Version information for debugger
- DataTable.select = {};
- DataTable.select.version = '1.2.5';
- DataTable.select.init = function ( dt ) {
- var ctx = dt.settings()[0];
- var init = ctx.oInit.select;
- var defaults = DataTable.defaults.select;
- var opts = init === undefined ?
- defaults :
- init;
- // Set defaults
- var items = 'row';
- var style = 'api';
- var blurable = false;
- var info = true;
- var selector = 'td, th';
- var className = 'selected';
- var setStyle = false;
- ctx._select = {};
- // Initialisation customisations
- if ( opts === true ) {
- style = 'os';
- setStyle = true;
- }
- else if ( typeof opts === 'string' ) {
- style = opts;
- setStyle = true;
- }
- else if ( $.isPlainObject( opts ) ) {
- if ( opts.blurable !== undefined ) {
- blurable = opts.blurable;
- }
- if ( opts.info !== undefined ) {
- info = opts.info;
- }
- if ( opts.items !== undefined ) {
- items = opts.items;
- }
- if ( opts.style !== undefined ) {
- style = opts.style;
- setStyle = true;
- }
- if ( opts.selector !== undefined ) {
- selector = opts.selector;
- }
- if ( opts.className !== undefined ) {
- className = opts.className;
- }
- }
- dt.select.selector( selector );
- dt.select.items( items );
- dt.select.style( style );
- dt.select.blurable( blurable );
- dt.select.info( info );
- ctx._select.className = className;
- // Sort table based on selected rows. Requires Select Datatables extension
- $.fn.dataTable.ext.order['select-checkbox'] = function ( settings, col ) {
- return this.api().column( col, {order: 'index'} ).nodes().map( function ( td ) {
- if ( settings._select.items === 'row' ) {
- return $( td ).parent().hasClass( settings._select.className );
- } else if ( settings._select.items === 'cell' ) {
- return $( td ).hasClass( settings._select.className );
- }
- return false;
- });
- };
- // If the init options haven't enabled select, but there is a selectable
- // class name, then enable
- if ( ! setStyle && $( dt.table().node() ).hasClass( 'selectable' ) ) {
- dt.select.style( 'os' );
- }
- };
- /*
- Select is a collection of API methods, event handlers, event emitters and
- buttons (for the `Buttons` extension) for DataTables. It provides the following
- features, with an overview of how they are implemented:
- ## Selection of rows, columns and cells. Whether an item is selected or not is
- stored in:
- * rows: a `_select_selected` property which contains a boolean value of the
- DataTables' `aoData` object for each row
- * columns: a `_select_selected` property which contains a boolean value of the
- DataTables' `aoColumns` object for each column
- * cells: a `_selected_cells` property which contains an array of boolean values
- of the `aoData` object for each row. The array is the same length as the
- columns array, with each element of it representing a cell.
- This method of using boolean flags allows Select to operate when nodes have not
- been created for rows / cells (DataTables' defer rendering feature).
- ## API methods
- A range of API methods are available for triggering selection and de-selection
- of rows. Methods are also available to configure the selection events that can
- be triggered by an end user (such as which items are to be selected). To a large
- extent, these of API methods *is* Select. It is basically a collection of helper
- functions that can be used to select items in a DataTable.
- Configuration of select is held in the object `_select` which is attached to the
- DataTables settings object on initialisation. Select being available on a table
- is not optional when Select is loaded, but its default is for selection only to
- be available via the API - so the end user wouldn't be able to select rows
- without additional configuration.
- The `_select` object contains the following properties:
- ```
- {
- items:string - Can be `rows`, `columns` or `cells`. Defines what item
- will be selected if the user is allowed to activate row
- selection using the mouse.
- style:string - Can be `none`, `single`, `multi` or `os`. Defines the
- interaction style when selecting items
- blurable:boolean - If row selection can be cleared by clicking outside of
- the table
- info:boolean - If the selection summary should be shown in the table
- information elements
- }
- ```
- In addition to the API methods, Select also extends the DataTables selector
- options for rows, columns and cells adding a `selected` option to the selector
- options object, allowing the developer to select only selected items or
- unselected items.
- ## Mouse selection of items
- Clicking on items can be used to select items. This is done by a simple event
- handler that will select the items using the API methods.
- */
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Local functions
- */
- /**
- * Add one or more cells to the selection when shift clicking in OS selection
- * style cell selection.
- *
- * Cell range is more complicated than row and column as we want to select
- * in the visible grid rather than by index in sequence. For example, if you
- * click first in cell 1-1 and then shift click in 2-2 - cells 1-2 and 2-1
- * should also be selected (and not 1-3, 1-4. etc)
- *
- * @param {DataTable.Api} dt DataTable
- * @param {object} idx Cell index to select to
- * @param {object} last Cell index to select from
- * @private
- */
- function cellRange( dt, idx, last )
- {
- var indexes;
- var columnIndexes;
- var rowIndexes;
- var selectColumns = function ( start, end ) {
- if ( start > end ) {
- var tmp = end;
- end = start;
- start = tmp;
- }
-
- var record = false;
- return dt.columns( ':visible' ).indexes().filter( function (i) {
- if ( i === start ) {
- record = true;
- }
-
- if ( i === end ) { // not else if, as start might === end
- record = false;
- return true;
- }
- return record;
- } );
- };
- var selectRows = function ( start, end ) {
- var indexes = dt.rows( { search: 'applied' } ).indexes();
- // Which comes first - might need to swap
- if ( indexes.indexOf( start ) > indexes.indexOf( end ) ) {
- var tmp = end;
- end = start;
- start = tmp;
- }
- var record = false;
- return indexes.filter( function (i) {
- if ( i === start ) {
- record = true;
- }
-
- if ( i === end ) {
- record = false;
- return true;
- }
- return record;
- } );
- };
- if ( ! dt.cells( { selected: true } ).any() && ! last ) {
- // select from the top left cell to this one
- columnIndexes = selectColumns( 0, idx.column );
- rowIndexes = selectRows( 0 , idx.row );
- }
- else {
- // Get column indexes between old and new
- columnIndexes = selectColumns( last.column, idx.column );
- rowIndexes = selectRows( last.row , idx.row );
- }
- indexes = dt.cells( rowIndexes, columnIndexes ).flatten();
- if ( ! dt.cells( idx, { selected: true } ).any() ) {
- // Select range
- dt.cells( indexes ).select();
- }
- else {
- // Deselect range
- dt.cells( indexes ).deselect();
- }
- }
- /**
- * Disable mouse selection by removing the selectors
- *
- * @param {DataTable.Api} dt DataTable to remove events from
- * @private
- */
- function disableMouseSelection( dt )
- {
- var ctx = dt.settings()[0];
- var selector = ctx._select.selector;
- $( dt.table().container() )
- .off( 'mousedown.dtSelect', selector )
- .off( 'mouseup.dtSelect', selector )
- .off( 'click.dtSelect', selector );
- $('body').off( 'click.dtSelect' + dt.table().node().id );
- }
- /**
- * Attach mouse listeners to the table to allow mouse selection of items
- *
- * @param {DataTable.Api} dt DataTable to remove events from
- * @private
- */
- function enableMouseSelection ( dt )
- {
- var container = $( dt.table().container() );
- var ctx = dt.settings()[0];
- var selector = ctx._select.selector;
- container
- .on( 'mousedown.dtSelect', selector, function(e) {
- // Disallow text selection for shift clicking on the table so multi
- // element selection doesn't look terrible!
- if ( e.shiftKey || e.metaKey || e.ctrlKey ) {
- container
- .css( '-moz-user-select', 'none' )
- .one('selectstart.dtSelect', selector, function () {
- return false;
- } );
- }
- } )
- .on( 'mouseup.dtSelect', selector, function() {
- // Allow text selection to occur again, Mozilla style (tested in FF
- // 35.0.1 - still required)
- container.css( '-moz-user-select', '' );
- } )
- .on( 'click.dtSelect', selector, function ( e ) {
- var items = dt.select.items();
- var idx;
- // If text was selected (click and drag), then we shouldn't change
- // the row's selected state
- if ( window.getSelection ) {
- var selection = window.getSelection();
- // If the element that contains the selection is not in the table, we can ignore it
- // This can happen if the developer selects text from the click event
- if ( ! selection.anchorNode || $(selection.anchorNode).closest('table')[0] === dt.table().node() ) {
- if ( $.trim(selection.toString()) !== '' ) {
- return;
- }
- }
- }
- var ctx = dt.settings()[0];
- // Ignore clicks inside a sub-table
- if ( $(e.target).closest('div.dataTables_wrapper')[0] != dt.table().container() ) {
- return;
- }
- var cell = dt.cell( $(e.target).closest('td, th') );
- // Check the cell actually belongs to the host DataTable (so child
- // rows, etc, are ignored)
- if ( ! cell.any() ) {
- return;
- }
- var event = $.Event('user-select.dt');
- eventTrigger( dt, event, [ items, cell, e ] );
- if ( event.isDefaultPrevented() ) {
- return;
- }
- var cellIndex = cell.index();
- if ( items === 'row' ) {
- idx = cellIndex.row;
- typeSelect( e, dt, ctx, 'row', idx );
- }
- else if ( items === 'column' ) {
- idx = cell.index().column;
- typeSelect( e, dt, ctx, 'column', idx );
- }
- else if ( items === 'cell' ) {
- idx = cell.index();
- typeSelect( e, dt, ctx, 'cell', idx );
- }
- ctx._select_lastCell = cellIndex;
- } );
- // Blurable
- $('body').on( 'click.dtSelect' + dt.table().node().id, function ( e ) {
- if ( ctx._select.blurable ) {
- // If the click was inside the DataTables container, don't blur
- if ( $(e.target).parents().filter( dt.table().container() ).length ) {
- return;
- }
- // Ignore elements which have been removed from the DOM (i.e. paging
- // buttons)
- if ( $(e.target).parents('html').length === 0 ) {
- return;
- }
- // Don't blur in Editor form
- if ( $(e.target).parents('div.DTE').length ) {
- return;
- }
- clear( ctx, true );
- }
- } );
- }
- /**
- * Trigger an event on a DataTable
- *
- * @param {DataTable.Api} api DataTable to trigger events on
- * @param {boolean} selected true if selected, false if deselected
- * @param {string} type Item type acting on
- * @param {boolean} any Require that there are values before
- * triggering
- * @private
- */
- function eventTrigger ( api, type, args, any )
- {
- if ( any && ! api.flatten().length ) {
- return;
- }
- if ( typeof type === 'string' ) {
- type = type +'.dt';
- }
- args.unshift( api );
- $(api.table().node()).trigger( type, args );
- }
- /**
- * Update the information element of the DataTable showing information about the
- * items selected. This is done by adding tags to the existing text
- *
- * @param {DataTable.Api} api DataTable to update
- * @private
- */
- function info ( api )
- {
- var ctx = api.settings()[0];
- if ( ! ctx._select.info || ! ctx.aanFeatures.i ) {
- return;
- }
- if ( api.select.style() === 'api' ) {
- return;
- }
- var rows = api.rows( { selected: true } ).flatten().length;
- var columns = api.columns( { selected: true } ).flatten().length;
- var cells = api.cells( { selected: true } ).flatten().length;
- var add = function ( el, name, num ) {
- el.append( $('<span class="select-item"/>').append( api.i18n(
- 'select.'+name+'s',
- { _: '%d '+name+'s selected', 0: '', 1: '1 '+name+' selected' },
- num
- ) ) );
- };
- // Internal knowledge of DataTables to loop over all information elements
- $.each( ctx.aanFeatures.i, function ( i, el ) {
- el = $(el);
- var output = $('<span class="select-info"/>');
- add( output, 'row', rows );
- add( output, 'column', columns );
- add( output, 'cell', cells );
- var exisiting = el.children('span.select-info');
- if ( exisiting.length ) {
- exisiting.remove();
- }
- if ( output.text() !== '' ) {
- el.append( output );
- }
- } );
- }
- /**
- * Initialisation of a new table. Attach event handlers and callbacks to allow
- * Select to operate correctly.
- *
- * This will occur _after_ the initial DataTables initialisation, although
- * before Ajax data is rendered, if there is ajax data
- *
- * @param {DataTable.settings} ctx Settings object to operate on
- * @private
- */
- function init ( ctx ) {
- var api = new DataTable.Api( ctx );
- // Row callback so that classes can be added to rows and cells if the item
- // was selected before the element was created. This will happen with the
- // `deferRender` option enabled.
- //
- // This method of attaching to `aoRowCreatedCallback` is a hack until
- // DataTables has proper events for row manipulation If you are reviewing
- // this code to create your own plug-ins, please do not do this!
- ctx.aoRowCreatedCallback.push( {
- fn: function ( row, data, index ) {
- var i, ien;
- var d = ctx.aoData[ index ];
- // Row
- if ( d._select_selected ) {
- $( row ).addClass( ctx._select.className );
- }
- // Cells and columns - if separated out, we would need to do two
- // loops, so it makes sense to combine them into a single one
- for ( i=0, ien=ctx.aoColumns.length ; i<ien ; i++ ) {
- if ( ctx.aoColumns[i]._select_selected || (d._selected_cells && d._selected_cells[i]) ) {
- $(d.anCells[i]).addClass( ctx._select.className );
- }
- }
- },
- sName: 'select-deferRender'
- } );
- // On Ajax reload we want to reselect all rows which are currently selected,
- // if there is an rowId (i.e. a unique value to identify each row with)
- api.on( 'preXhr.dt.dtSelect', function () {
- // note that column selection doesn't need to be cached and then
- // reselected, as they are already selected
- var rows = api.rows( { selected: true } ).ids( true ).filter( function ( d ) {
- return d !== undefined;
- } );
- var cells = api.cells( { selected: true } ).eq(0).map( function ( cellIdx ) {
- var id = api.row( cellIdx.row ).id( true );
- return id ?
- { row: id, column: cellIdx.column } :
- undefined;
- } ).filter( function ( d ) {
- return d !== undefined;
- } );
- // On the next draw, reselect the currently selected items
- api.one( 'draw.dt.dtSelect', function () {
- api.rows( rows ).select();
- // `cells` is not a cell index selector, so it needs a loop
- if ( cells.any() ) {
- cells.each( function ( id ) {
- api.cells( id.row, id.column ).select();
- } );
- }
- } );
- } );
- // Update the table information element with selected item summary
- api.on( 'draw.dtSelect.dt select.dtSelect.dt deselect.dtSelect.dt info.dt', function () {
- info( api );
- } );
- // Clean up and release
- api.on( 'destroy.dtSelect', function () {
- disableMouseSelection( api );
- api.off( '.dtSelect' );
- } );
- }
- /**
- * Add one or more items (rows or columns) to the selection when shift clicking
- * in OS selection style
- *
- * @param {DataTable.Api} dt DataTable
- * @param {string} type Row or column range selector
- * @param {object} idx Item index to select to
- * @param {object} last Item index to select from
- * @private
- */
- function rowColumnRange( dt, type, idx, last )
- {
- // Add a range of rows from the last selected row to this one
- var indexes = dt[type+'s']( { search: 'applied' } ).indexes();
- var idx1 = $.inArray( last, indexes );
- var idx2 = $.inArray( idx, indexes );
- if ( ! dt[type+'s']( { selected: true } ).any() && idx1 === -1 ) {
- // select from top to here - slightly odd, but both Windows and Mac OS
- // do this
- indexes.splice( $.inArray( idx, indexes )+1, indexes.length );
- }
- else {
- // reverse so we can shift click 'up' as well as down
- if ( idx1 > idx2 ) {
- var tmp = idx2;
- idx2 = idx1;
- idx1 = tmp;
- }
- indexes.splice( idx2+1, indexes.length );
- indexes.splice( 0, idx1 );
- }
- if ( ! dt[type]( idx, { selected: true } ).any() ) {
- // Select range
- dt[type+'s']( indexes ).select();
- }
- else {
- // Deselect range - need to keep the clicked on row selected
- indexes.splice( $.inArray( idx, indexes ), 1 );
- dt[type+'s']( indexes ).deselect();
- }
- }
- /**
- * Clear all selected items
- *
- * @param {DataTable.settings} ctx Settings object of the host DataTable
- * @param {boolean} [force=false] Force the de-selection to happen, regardless
- * of selection style
- * @private
- */
- function clear( ctx, force )
- {
- if ( force || ctx._select.style === 'single' ) {
- var api = new DataTable.Api( ctx );
-
- api.rows( { selected: true } ).deselect();
- api.columns( { selected: true } ).deselect();
- api.cells( { selected: true } ).deselect();
- }
- }
- /**
- * Select items based on the current configuration for style and items.
- *
- * @param {object} e Mouse event object
- * @param {DataTables.Api} dt DataTable
- * @param {DataTable.settings} ctx Settings object of the host DataTable
- * @param {string} type Items to select
- * @param {int|object} idx Index of the item to select
- * @private
- */
- function typeSelect ( e, dt, ctx, type, idx )
- {
- var style = dt.select.style();
- var isSelected = dt[type]( idx, { selected: true } ).any();
- if ( style === 'os' ) {
- if ( e.ctrlKey || e.metaKey ) {
- // Add or remove from the selection
- dt[type]( idx ).select( ! isSelected );
- }
- else if ( e.shiftKey ) {
- if ( type === 'cell' ) {
- cellRange( dt, idx, ctx._select_lastCell || null );
- }
- else {
- rowColumnRange( dt, type, idx, ctx._select_lastCell ?
- ctx._select_lastCell[type] :
- null
- );
- }
- }
- else {
- // No cmd or shift click - deselect if selected, or select
- // this row only
- var selected = dt[type+'s']( { selected: true } );
- if ( isSelected && selected.flatten().length === 1 ) {
- dt[type]( idx ).deselect();
- }
- else {
- selected.deselect();
- dt[type]( idx ).select();
- }
- }
- } else if ( style == 'multi+shift' ) {
- if ( e.shiftKey ) {
- if ( type === 'cell' ) {
- cellRange( dt, idx, ctx._select_lastCell || null );
- }
- else {
- rowColumnRange( dt, type, idx, ctx._select_lastCell ?
- ctx._select_lastCell[type] :
- null
- );
- }
- }
- else {
- dt[ type ]( idx ).select( ! isSelected );
- }
- }
- else {
- dt[ type ]( idx ).select( ! isSelected );
- }
- }
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables selectors
- */
- // row and column are basically identical just assigned to different properties
- // and checking a different array, so we can dynamically create the functions to
- // reduce the code size
- $.each( [
- { type: 'row', prop: 'aoData' },
- { type: 'column', prop: 'aoColumns' }
- ], function ( i, o ) {
- DataTable.ext.selector[ o.type ].push( function ( settings, opts, indexes ) {
- var selected = opts.selected;
- var data;
- var out = [];
- if ( selected !== true && selected !== false ) {
- return indexes;
- }
- for ( var i=0, ien=indexes.length ; i<ien ; i++ ) {
- data = settings[ o.prop ][ indexes[i] ];
- if ( (selected === true && data._select_selected === true) ||
- (selected === false && ! data._select_selected )
- ) {
- out.push( indexes[i] );
- }
- }
- return out;
- } );
- } );
- DataTable.ext.selector.cell.push( function ( settings, opts, cells ) {
- var selected = opts.selected;
- var rowData;
- var out = [];
- if ( selected === undefined ) {
- return cells;
- }
- for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
- rowData = settings.aoData[ cells[i].row ];
- if ( (selected === true && rowData._selected_cells && rowData._selected_cells[ cells[i].column ] === true) ||
- (selected === false && ( ! rowData._selected_cells || ! rowData._selected_cells[ cells[i].column ] ) )
- ) {
- out.push( cells[i] );
- }
- }
- return out;
- } );
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables API
- *
- * For complete documentation, please refer to the docs/api directory or the
- * DataTables site
- */
- // Local variables to improve compression
- var apiRegister = DataTable.Api.register;
- var apiRegisterPlural = DataTable.Api.registerPlural;
- apiRegister( 'select()', function () {
- return this.iterator( 'table', function ( ctx ) {
- DataTable.select.init( new DataTable.Api( ctx ) );
- } );
- } );
- apiRegister( 'select.blurable()', function ( flag ) {
- if ( flag === undefined ) {
- return this.context[0]._select.blurable;
- }
- return this.iterator( 'table', function ( ctx ) {
- ctx._select.blurable = flag;
- } );
- } );
- apiRegister( 'select.info()', function ( flag ) {
- if ( info === undefined ) {
- return this.context[0]._select.info;
- }
- return this.iterator( 'table', function ( ctx ) {
- ctx._select.info = flag;
- } );
- } );
- apiRegister( 'select.items()', function ( items ) {
- if ( items === undefined ) {
- return this.context[0]._select.items;
- }
- return this.iterator( 'table', function ( ctx ) {
- ctx._select.items = items;
- eventTrigger( new DataTable.Api( ctx ), 'selectItems', [ items ] );
- } );
- } );
- // Takes effect from the _next_ selection. None disables future selection, but
- // does not clear the current selection. Use the `deselect` methods for that
- apiRegister( 'select.style()', function ( style ) {
- if ( style === undefined ) {
- return this.context[0]._select.style;
- }
- return this.iterator( 'table', function ( ctx ) {
- ctx._select.style = style;
- if ( ! ctx._select_init ) {
- init( ctx );
- }
- // Add / remove mouse event handlers. They aren't required when only
- // API selection is available
- var dt = new DataTable.Api( ctx );
- disableMouseSelection( dt );
-
- if ( style !== 'api' ) {
- enableMouseSelection( dt );
- }
- eventTrigger( new DataTable.Api( ctx ), 'selectStyle', [ style ] );
- } );
- } );
- apiRegister( 'select.selector()', function ( selector ) {
- if ( selector === undefined ) {
- return this.context[0]._select.selector;
- }
- return this.iterator( 'table', function ( ctx ) {
- disableMouseSelection( new DataTable.Api( ctx ) );
- ctx._select.selector = selector;
- if ( ctx._select.style !== 'api' ) {
- enableMouseSelection( new DataTable.Api( ctx ) );
- }
- } );
- } );
- apiRegisterPlural( 'rows().select()', 'row().select()', function ( select ) {
- var api = this;
- if ( select === false ) {
- return this.deselect();
- }
- this.iterator( 'row', function ( ctx, idx ) {
- clear( ctx );
- ctx.aoData[ idx ]._select_selected = true;
- $( ctx.aoData[ idx ].nTr ).addClass( ctx._select.className );
- } );
- this.iterator( 'table', function ( ctx, i ) {
- eventTrigger( api, 'select', [ 'row', api[i] ], true );
- } );
- return this;
- } );
- apiRegisterPlural( 'columns().select()', 'column().select()', function ( select ) {
- var api = this;
- if ( select === false ) {
- return this.deselect();
- }
- this.iterator( 'column', function ( ctx, idx ) {
- clear( ctx );
- ctx.aoColumns[ idx ]._select_selected = true;
- var column = new DataTable.Api( ctx ).column( idx );
- $( column.header() ).addClass( ctx._select.className );
- $( column.footer() ).addClass( ctx._select.className );
- column.nodes().to$().addClass( ctx._select.className );
- } );
- this.iterator( 'table', function ( ctx, i ) {
- eventTrigger( api, 'select', [ 'column', api[i] ], true );
- } );
- return this;
- } );
- apiRegisterPlural( 'cells().select()', 'cell().select()', function ( select ) {
- var api = this;
- if ( select === false ) {
- return this.deselect();
- }
- this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {
- clear( ctx );
- var data = ctx.aoData[ rowIdx ];
- if ( data._selected_cells === undefined ) {
- data._selected_cells = [];
- }
- data._selected_cells[ colIdx ] = true;
- if ( data.anCells ) {
- $( data.anCells[ colIdx ] ).addClass( ctx._select.className );
- }
- } );
- this.iterator( 'table', function ( ctx, i ) {
- eventTrigger( api, 'select', [ 'cell', api[i] ], true );
- } );
- return this;
- } );
- apiRegisterPlural( 'rows().deselect()', 'row().deselect()', function () {
- var api = this;
- this.iterator( 'row', function ( ctx, idx ) {
- ctx.aoData[ idx ]._select_selected = false;
- $( ctx.aoData[ idx ].nTr ).removeClass( ctx._select.className );
- } );
- this.iterator( 'table', function ( ctx, i ) {
- eventTrigger( api, 'deselect', [ 'row', api[i] ], true );
- } );
- return this;
- } );
- apiRegisterPlural( 'columns().deselect()', 'column().deselect()', function () {
- var api = this;
- this.iterator( 'column', function ( ctx, idx ) {
- ctx.aoColumns[ idx ]._select_selected = false;
- var api = new DataTable.Api( ctx );
- var column = api.column( idx );
- $( column.header() ).removeClass( ctx._select.className );
- $( column.footer() ).removeClass( ctx._select.className );
- // Need to loop over each cell, rather than just using
- // `column().nodes()` as cells which are individually selected should
- // not have the `selected` class removed from them
- api.cells( null, idx ).indexes().each( function (cellIdx) {
- var data = ctx.aoData[ cellIdx.row ];
- var cellSelected = data._selected_cells;
- if ( data.anCells && (! cellSelected || ! cellSelected[ cellIdx.column ]) ) {
- $( data.anCells[ cellIdx.column ] ).removeClass( ctx._select.className );
- }
- } );
- } );
- this.iterator( 'table', function ( ctx, i ) {
- eventTrigger( api, 'deselect', [ 'column', api[i] ], true );
- } );
- return this;
- } );
- apiRegisterPlural( 'cells().deselect()', 'cell().deselect()', function () {
- var api = this;
- this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {
- var data = ctx.aoData[ rowIdx ];
- data._selected_cells[ colIdx ] = false;
- // Remove class only if the cells exist, and the cell is not column
- // selected, in which case the class should remain (since it is selected
- // in the column)
- if ( data.anCells && ! ctx.aoColumns[ colIdx ]._select_selected ) {
- $( data.anCells[ colIdx ] ).removeClass( ctx._select.className );
- }
- } );
- this.iterator( 'table', function ( ctx, i ) {
- eventTrigger( api, 'deselect', [ 'cell', api[i] ], true );
- } );
- return this;
- } );
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Buttons
- */
- function i18n( label, def ) {
- return function (dt) {
- return dt.i18n( 'buttons.'+label, def );
- };
- }
- // Common events with suitable namespaces
- function namespacedEvents ( config ) {
- var unique = config._eventNamespace;
- return 'draw.dt.DT'+unique+' select.dt.DT'+unique+' deselect.dt.DT'+unique;
- }
- function enabled ( dt, config ) {
- if ( $.inArray( 'rows', config.limitTo ) !== -1 && dt.rows( { selected: true } ).any() ) {
- return true;
- }
- if ( $.inArray( 'columns', config.limitTo ) !== -1 && dt.columns( { selected: true } ).any() ) {
- return true;
- }
- if ( $.inArray( 'cells', config.limitTo ) !== -1 && dt.cells( { selected: true } ).any() ) {
- return true;
- }
- return false;
- }
- var _buttonNamespace = 0;
- $.extend( DataTable.ext.buttons, {
- selected: {
- text: i18n( 'selected', 'Selected' ),
- className: 'buttons-selected',
- limitTo: [ 'rows', 'columns', 'cells' ],
- init: function ( dt, node, config ) {
- var that = this;
- config._eventNamespace = '.select'+(_buttonNamespace++);
- // .DT namespace listeners are removed by DataTables automatically
- // on table destroy
- dt.on( namespacedEvents(config), function () {
- that.enable( enabled(dt, config) );
- } );
- this.disable();
- },
- destroy: function ( dt, node, config ) {
- dt.off( config._eventNamespace );
- }
- },
- selectedSingle: {
- text: i18n( 'selectedSingle', 'Selected single' ),
- className: 'buttons-selected-single',
- init: function ( dt, node, config ) {
- var that = this;
- config._eventNamespace = '.select'+(_buttonNamespace++);
- dt.on( namespacedEvents(config), function () {
- var count = dt.rows( { selected: true } ).flatten().length +
- dt.columns( { selected: true } ).flatten().length +
- dt.cells( { selected: true } ).flatten().length;
- that.enable( count === 1 );
- } );
- this.disable();
- },
- destroy: function ( dt, node, config ) {
- dt.off( config._eventNamespace );
- }
- },
- selectAll: {
- text: i18n( 'selectAll', 'Select all' ),
- className: 'buttons-select-all',
- action: function () {
- var items = this.select.items();
- this[ items+'s' ]().select();
- }
- },
- selectNone: {
- text: i18n( 'selectNone', 'Deselect all' ),
- className: 'buttons-select-none',
- action: function () {
- clear( this.settings()[0], true );
- },
- init: function ( dt, node, config ) {
- var that = this;
- config._eventNamespace = '.select'+(_buttonNamespace++);
- dt.on( namespacedEvents(config), function () {
- var count = dt.rows( { selected: true } ).flatten().length +
- dt.columns( { selected: true } ).flatten().length +
- dt.cells( { selected: true } ).flatten().length;
- that.enable( count > 0 );
- } );
- this.disable();
- },
- destroy: function ( dt, node, config ) {
- dt.off( config._eventNamespace );
- }
- }
- } );
- $.each( [ 'Row', 'Column', 'Cell' ], function ( i, item ) {
- var lc = item.toLowerCase();
- DataTable.ext.buttons[ 'select'+item+'s' ] = {
- text: i18n( 'select'+item+'s', 'Select '+lc+'s' ),
- className: 'buttons-select-'+lc+'s',
- action: function () {
- this.select.items( lc );
- },
- init: function ( dt ) {
- var that = this;
- dt.on( 'selectItems.dt.DT', function ( e, ctx, items ) {
- that.active( items === lc );
- } );
- }
- };
- } );
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Initialisation
- */
- // DataTables creation - check if select has been defined in the options. Note
- // this required that the table be in the document! If it isn't then something
- // needs to trigger this method unfortunately. The next major release of
- // DataTables will rework the events and address this.
- $(document).on( 'preInit.dt.dtSelect', function (e, ctx) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- DataTable.select.init( new DataTable.Api( ctx ) );
- } );
- return DataTable.select;
- }));
|