| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348183491835018351183521835318354183551835618357183581835918360183611836218363183641836518366183671836818369183701837118372183731837418375183761837718378183791838018381183821838318384183851838618387183881838918390183911839218393183941839518396183971839818399184001840118402184031840418405184061840718408184091841018411184121841318414184151841618417184181841918420184211842218423184241842518426184271842818429184301843118432184331843418435184361843718438184391844018441184421844318444184451844618447184481844918450184511845218453184541845518456184571845818459184601846118462184631846418465184661846718468184691847018471184721847318474184751847618477184781847918480184811848218483184841848518486184871848818489184901849118492184931849418495184961849718498184991850018501185021850318504185051850618507185081850918510185111851218513185141851518516185171851818519185201852118522185231852418525185261852718528185291853018531185321853318534185351853618537185381853918540185411854218543185441854518546185471854818549185501855118552185531855418555185561855718558185591856018561185621856318564185651856618567185681856918570185711857218573185741857518576185771857818579185801858118582185831858418585185861858718588185891859018591185921859318594185951859618597185981859918600186011860218603186041860518606186071860818609186101861118612186131861418615186161861718618186191862018621186221862318624186251862618627186281862918630186311863218633186341863518636186371863818639186401864118642186431864418645186461864718648186491865018651186521865318654186551865618657186581865918660186611866218663186641866518666186671866818669186701867118672186731867418675186761867718678186791868018681186821868318684186851868618687186881868918690186911869218693186941869518696186971869818699187001870118702187031870418705187061870718708187091871018711187121871318714187151871618717187181871918720187211872218723187241872518726187271872818729187301873118732187331873418735187361873718738187391874018741187421874318744187451874618747187481874918750187511875218753187541875518756187571875818759187601876118762187631876418765187661876718768187691877018771187721877318774187751877618777187781877918780187811878218783187841878518786187871878818789187901879118792187931879418795187961879718798187991880018801188021880318804188051880618807188081880918810188111881218813188141881518816188171881818819188201882118822188231882418825188261882718828188291883018831188321883318834188351883618837188381883918840188411884218843188441884518846188471884818849188501885118852188531885418855188561885718858188591886018861188621886318864188651886618867188681886918870188711887218873188741887518876188771887818879188801888118882188831888418885188861888718888188891889018891188921889318894188951889618897188981889918900189011890218903189041890518906189071890818909189101891118912189131891418915189161891718918189191892018921189221892318924189251892618927189281892918930189311893218933189341893518936189371893818939189401894118942189431894418945189461894718948189491895018951189521895318954189551895618957189581895918960189611896218963189641896518966189671896818969189701897118972189731897418975189761897718978189791898018981189821898318984189851898618987189881898918990189911899218993189941899518996189971899818999190001900119002190031900419005190061900719008190091901019011190121901319014190151901619017190181901919020190211902219023190241902519026190271902819029190301903119032190331903419035190361903719038190391904019041190421904319044190451904619047190481904919050190511905219053190541905519056190571905819059190601906119062190631906419065190661906719068190691907019071190721907319074190751907619077190781907919080190811908219083190841908519086190871908819089190901909119092190931909419095190961909719098190991910019101191021910319104191051910619107191081910919110191111911219113191141911519116191171911819119191201912119122191231912419125191261912719128191291913019131191321913319134191351913619137191381913919140191411914219143191441914519146191471914819149191501915119152191531915419155191561915719158191591916019161191621916319164191651916619167191681916919170191711917219173191741917519176191771917819179191801918119182191831918419185191861918719188191891919019191191921919319194191951919619197191981919919200192011920219203192041920519206192071920819209192101921119212192131921419215192161921719218192191922019221192221922319224192251922619227192281922919230192311923219233192341923519236192371923819239192401924119242192431924419245192461924719248192491925019251192521925319254192551925619257192581925919260192611926219263192641926519266192671926819269192701927119272192731927419275192761927719278192791928019281192821928319284192851928619287192881928919290192911929219293192941929519296192971929819299193001930119302193031930419305193061930719308193091931019311193121931319314193151931619317193181931919320193211932219323193241932519326193271932819329193301933119332193331933419335193361933719338193391934019341193421934319344193451934619347193481934919350193511935219353193541935519356193571935819359193601936119362193631936419365193661936719368193691937019371193721937319374193751937619377193781937919380193811938219383193841938519386193871938819389193901939119392193931939419395193961939719398193991940019401194021940319404194051940619407194081940919410194111941219413194141941519416194171941819419194201942119422194231942419425194261942719428194291943019431194321943319434194351943619437194381943919440194411944219443194441944519446194471944819449194501945119452194531945419455194561945719458194591946019461194621946319464194651946619467194681946919470194711947219473194741947519476194771947819479194801948119482194831948419485194861948719488194891949019491194921949319494194951949619497194981949919500195011950219503195041950519506195071950819509195101951119512195131951419515195161951719518195191952019521195221952319524195251952619527195281952919530195311953219533195341953519536195371953819539195401954119542195431954419545195461954719548195491955019551195521955319554195551955619557195581955919560195611956219563195641956519566195671956819569195701957119572195731957419575195761957719578195791958019581195821958319584195851958619587195881958919590195911959219593195941959519596195971959819599196001960119602196031960419605196061960719608196091961019611196121961319614196151961619617196181961919620196211962219623196241962519626196271962819629196301963119632196331963419635196361963719638196391964019641196421964319644196451964619647196481964919650196511965219653196541965519656196571965819659196601966119662196631966419665196661966719668196691967019671196721967319674196751967619677196781967919680196811968219683196841968519686196871968819689196901969119692196931969419695196961969719698196991970019701197021970319704197051970619707197081970919710197111971219713197141971519716197171971819719197201972119722197231972419725197261972719728197291973019731197321973319734197351973619737197381973919740197411974219743197441974519746197471974819749197501975119752197531975419755197561975719758197591976019761197621976319764197651976619767197681976919770197711977219773197741977519776197771977819779197801978119782197831978419785197861978719788197891979019791197921979319794197951979619797197981979919800198011980219803198041980519806198071980819809198101981119812198131981419815198161981719818198191982019821198221982319824198251982619827198281982919830198311983219833198341983519836198371983819839198401984119842198431984419845198461984719848198491985019851198521985319854198551985619857198581985919860198611986219863198641986519866198671986819869198701987119872198731987419875198761987719878198791988019881198821988319884198851988619887198881988919890198911989219893198941989519896198971989819899199001990119902199031990419905199061990719908199091991019911199121991319914199151991619917199181991919920199211992219923199241992519926199271992819929199301993119932199331993419935199361993719938199391994019941199421994319944199451994619947199481994919950199511995219953199541995519956199571995819959199601996119962199631996419965199661996719968199691997019971199721997319974199751997619977199781997919980199811998219983199841998519986199871998819989199901999119992199931999419995199961999719998199992000020001200022000320004200052000620007200082000920010200112001220013200142001520016200172001820019200202002120022200232002420025200262002720028200292003020031200322003320034200352003620037200382003920040200412004220043200442004520046200472004820049200502005120052200532005420055200562005720058200592006020061200622006320064200652006620067200682006920070200712007220073200742007520076200772007820079200802008120082200832008420085200862008720088200892009020091200922009320094200952009620097200982009920100201012010220103201042010520106201072010820109201102011120112201132011420115201162011720118201192012020121201222012320124201252012620127201282012920130201312013220133201342013520136201372013820139201402014120142201432014420145201462014720148201492015020151201522015320154201552015620157201582015920160201612016220163201642016520166201672016820169201702017120172201732017420175201762017720178201792018020181201822018320184201852018620187201882018920190201912019220193201942019520196201972019820199202002020120202202032020420205202062020720208202092021020211202122021320214202152021620217202182021920220202212022220223202242022520226202272022820229202302023120232202332023420235202362023720238202392024020241202422024320244202452024620247202482024920250202512025220253202542025520256202572025820259202602026120262202632026420265202662026720268202692027020271202722027320274202752027620277202782027920280202812028220283202842028520286202872028820289202902029120292202932029420295202962029720298202992030020301203022030320304203052030620307203082030920310203112031220313203142031520316203172031820319203202032120322203232032420325203262032720328203292033020331203322033320334203352033620337203382033920340203412034220343203442034520346203472034820349203502035120352203532035420355203562035720358203592036020361203622036320364203652036620367203682036920370203712037220373203742037520376203772037820379203802038120382203832038420385203862038720388203892039020391203922039320394203952039620397203982039920400204012040220403204042040520406204072040820409204102041120412204132041420415204162041720418204192042020421204222042320424204252042620427204282042920430204312043220433204342043520436204372043820439204402044120442204432044420445204462044720448204492045020451204522045320454204552045620457204582045920460204612046220463204642046520466204672046820469204702047120472204732047420475204762047720478204792048020481204822048320484204852048620487204882048920490204912049220493204942049520496204972049820499205002050120502205032050420505205062050720508205092051020511205122051320514205152051620517205182051920520205212052220523205242052520526205272052820529205302053120532205332053420535205362053720538205392054020541205422054320544205452054620547205482054920550205512055220553205542055520556205572055820559205602056120562205632056420565205662056720568205692057020571205722057320574205752057620577205782057920580205812058220583205842058520586205872058820589205902059120592205932059420595205962059720598205992060020601206022060320604206052060620607206082060920610206112061220613206142061520616206172061820619206202062120622206232062420625206262062720628206292063020631206322063320634206352063620637206382063920640206412064220643206442064520646206472064820649206502065120652206532065420655206562065720658206592066020661206622066320664206652066620667206682066920670206712067220673206742067520676206772067820679206802068120682206832068420685206862068720688206892069020691206922069320694206952069620697206982069920700207012070220703207042070520706207072070820709207102071120712207132071420715207162071720718207192072020721207222072320724207252072620727207282072920730207312073220733207342073520736207372073820739207402074120742207432074420745207462074720748207492075020751207522075320754207552075620757207582075920760207612076220763207642076520766207672076820769207702077120772207732077420775207762077720778207792078020781207822078320784207852078620787207882078920790207912079220793207942079520796207972079820799208002080120802208032080420805208062080720808208092081020811208122081320814208152081620817208182081920820208212082220823208242082520826208272082820829208302083120832208332083420835208362083720838208392084020841208422084320844208452084620847208482084920850208512085220853208542085520856208572085820859208602086120862208632086420865208662086720868208692087020871208722087320874208752087620877208782087920880208812088220883208842088520886208872088820889208902089120892208932089420895208962089720898208992090020901209022090320904209052090620907209082090920910209112091220913209142091520916209172091820919209202092120922209232092420925209262092720928209292093020931209322093320934209352093620937209382093920940209412094220943209442094520946209472094820949209502095120952209532095420955209562095720958209592096020961209622096320964209652096620967209682096920970209712097220973209742097520976209772097820979209802098120982209832098420985209862098720988209892099020991209922099320994209952099620997209982099921000210012100221003210042100521006210072100821009210102101121012210132101421015210162101721018210192102021021210222102321024210252102621027210282102921030210312103221033210342103521036210372103821039210402104121042210432104421045210462104721048210492105021051210522105321054210552105621057210582105921060210612106221063210642106521066210672106821069210702107121072210732107421075210762107721078210792108021081210822108321084210852108621087210882108921090210912109221093210942109521096210972109821099211002110121102211032110421105211062110721108211092111021111211122111321114211152111621117211182111921120211212112221123211242112521126211272112821129211302113121132211332113421135211362113721138211392114021141211422114321144211452114621147211482114921150211512115221153211542115521156211572115821159211602116121162211632116421165211662116721168211692117021171211722117321174211752117621177211782117921180211812118221183211842118521186211872118821189211902119121192211932119421195211962119721198211992120021201212022120321204212052120621207212082120921210212112121221213212142121521216212172121821219212202122121222212232122421225212262122721228212292123021231212322123321234212352123621237212382123921240212412124221243212442124521246212472124821249212502125121252212532125421255212562125721258212592126021261212622126321264212652126621267212682126921270212712127221273212742127521276212772127821279212802128121282212832128421285212862128721288212892129021291212922129321294212952129621297212982129921300213012130221303213042130521306213072130821309213102131121312213132131421315213162131721318213192132021321213222132321324213252132621327213282132921330213312133221333213342133521336213372133821339213402134121342213432134421345213462134721348213492135021351213522135321354213552135621357213582135921360213612136221363213642136521366213672136821369213702137121372213732137421375213762137721378213792138021381213822138321384213852138621387213882138921390213912139221393213942139521396213972139821399214002140121402214032140421405214062140721408214092141021411214122141321414214152141621417214182141921420214212142221423214242142521426214272142821429214302143121432214332143421435214362143721438214392144021441214422144321444214452144621447214482144921450214512145221453214542145521456214572145821459214602146121462214632146421465214662146721468214692147021471214722147321474214752147621477214782147921480214812148221483214842148521486214872148821489214902149121492214932149421495214962149721498214992150021501215022150321504215052150621507215082150921510215112151221513215142151521516215172151821519215202152121522215232152421525215262152721528215292153021531215322153321534215352153621537215382153921540215412154221543215442154521546215472154821549215502155121552215532155421555215562155721558215592156021561215622156321564215652156621567215682156921570215712157221573215742157521576215772157821579215802158121582215832158421585215862158721588215892159021591215922159321594215952159621597215982159921600216012160221603216042160521606216072160821609216102161121612216132161421615216162161721618216192162021621216222162321624216252162621627216282162921630216312163221633216342163521636216372163821639216402164121642216432164421645216462164721648216492165021651216522165321654216552165621657216582165921660216612166221663216642166521666216672166821669216702167121672216732167421675216762167721678216792168021681216822168321684216852168621687216882168921690216912169221693216942169521696216972169821699217002170121702217032170421705217062170721708217092171021711217122171321714217152171621717217182171921720217212172221723217242172521726217272172821729217302173121732217332173421735217362173721738217392174021741217422174321744217452174621747217482174921750217512175221753217542175521756217572175821759217602176121762217632176421765217662176721768217692177021771217722177321774217752177621777217782177921780217812178221783217842178521786217872178821789217902179121792217932179421795217962179721798217992180021801218022180321804218052180621807218082180921810218112181221813218142181521816218172181821819218202182121822218232182421825218262182721828218292183021831218322183321834218352183621837218382183921840218412184221843218442184521846218472184821849218502185121852218532185421855218562185721858218592186021861218622186321864218652186621867218682186921870218712187221873218742187521876218772187821879218802188121882218832188421885218862188721888218892189021891218922189321894218952189621897218982189921900219012190221903219042190521906219072190821909219102191121912219132191421915219162191721918219192192021921219222192321924219252192621927219282192921930219312193221933219342193521936219372193821939219402194121942219432194421945219462194721948219492195021951219522195321954219552195621957219582195921960219612196221963219642196521966219672196821969219702197121972219732197421975219762197721978219792198021981219822198321984219852198621987219882198921990219912199221993219942199521996219972199821999220002200122002220032200422005220062200722008220092201022011220122201322014220152201622017220182201922020220212202222023220242202522026220272202822029220302203122032220332203422035220362203722038220392204022041220422204322044220452204622047220482204922050220512205222053220542205522056220572205822059220602206122062220632206422065220662206722068220692207022071220722207322074220752207622077220782207922080220812208222083220842208522086220872208822089220902209122092220932209422095220962209722098220992210022101221022210322104221052210622107221082210922110221112211222113221142211522116221172211822119221202212122122221232212422125221262212722128221292213022131221322213322134221352213622137221382213922140221412214222143221442214522146221472214822149221502215122152221532215422155221562215722158221592216022161221622216322164221652216622167221682216922170221712217222173221742217522176221772217822179221802218122182221832218422185221862218722188221892219022191221922219322194221952219622197221982219922200222012220222203222042220522206222072220822209222102221122212222132221422215222162221722218222192222022221222222222322224222252222622227222282222922230222312223222233222342223522236222372223822239222402224122242222432224422245222462224722248222492225022251222522225322254222552225622257222582225922260222612226222263222642226522266222672226822269222702227122272222732227422275222762227722278222792228022281222822228322284222852228622287222882228922290222912229222293222942229522296222972229822299223002230122302223032230422305223062230722308223092231022311223122231322314223152231622317223182231922320223212232222323223242232522326223272232822329223302233122332223332233422335223362233722338223392234022341223422234322344223452234622347223482234922350223512235222353223542235522356223572235822359223602236122362223632236422365223662236722368223692237022371223722237322374223752237622377223782237922380223812238222383223842238522386223872238822389223902239122392223932239422395223962239722398223992240022401224022240322404224052240622407224082240922410224112241222413224142241522416224172241822419224202242122422224232242422425224262242722428224292243022431224322243322434224352243622437224382243922440224412244222443224442244522446224472244822449224502245122452224532245422455224562245722458224592246022461224622246322464224652246622467224682246922470224712247222473224742247522476224772247822479224802248122482224832248422485224862248722488224892249022491224922249322494224952249622497224982249922500225012250222503225042250522506225072250822509225102251122512225132251422515225162251722518225192252022521225222252322524225252252622527225282252922530225312253222533225342253522536225372253822539225402254122542225432254422545225462254722548225492255022551225522255322554225552255622557225582255922560225612256222563225642256522566225672256822569225702257122572225732257422575225762257722578225792258022581225822258322584225852258622587225882258922590225912259222593225942259522596225972259822599226002260122602226032260422605226062260722608226092261022611226122261322614226152261622617226182261922620226212262222623226242262522626226272262822629226302263122632226332263422635226362263722638226392264022641226422264322644226452264622647226482264922650226512265222653226542265522656226572265822659226602266122662226632266422665226662266722668226692267022671226722267322674226752267622677226782267922680226812268222683226842268522686226872268822689226902269122692226932269422695226962269722698226992270022701227022270322704227052270622707227082270922710227112271222713227142271522716227172271822719227202272122722227232272422725227262272722728227292273022731227322273322734227352273622737227382273922740227412274222743227442274522746227472274822749227502275122752227532275422755227562275722758227592276022761227622276322764227652276622767227682276922770227712277222773227742277522776227772277822779227802278122782227832278422785227862278722788227892279022791227922279322794227952279622797227982279922800228012280222803228042280522806228072280822809228102281122812228132281422815228162281722818228192282022821228222282322824228252282622827228282282922830228312283222833228342283522836228372283822839228402284122842228432284422845228462284722848228492285022851228522285322854228552285622857228582285922860228612286222863228642286522866228672286822869228702287122872228732287422875228762287722878228792288022881228822288322884228852288622887228882288922890228912289222893228942289522896228972289822899229002290122902229032290422905229062290722908229092291022911229122291322914229152291622917229182291922920229212292222923229242292522926229272292822929229302293122932229332293422935229362293722938229392294022941229422294322944229452294622947229482294922950229512295222953229542295522956229572295822959229602296122962229632296422965229662296722968229692297022971229722297322974229752297622977229782297922980229812298222983229842298522986229872298822989229902299122992229932299422995229962299722998229992300023001230022300323004230052300623007230082300923010230112301223013230142301523016230172301823019230202302123022230232302423025230262302723028230292303023031230322303323034230352303623037230382303923040230412304223043230442304523046230472304823049230502305123052230532305423055230562305723058230592306023061230622306323064230652306623067230682306923070230712307223073230742307523076230772307823079230802308123082230832308423085230862308723088230892309023091230922309323094230952309623097230982309923100231012310223103231042310523106231072310823109231102311123112231132311423115231162311723118231192312023121231222312323124231252312623127231282312923130231312313223133231342313523136231372313823139231402314123142231432314423145231462314723148231492315023151231522315323154231552315623157231582315923160231612316223163231642316523166231672316823169231702317123172231732317423175231762317723178231792318023181231822318323184231852318623187231882318923190231912319223193231942319523196231972319823199232002320123202232032320423205232062320723208232092321023211232122321323214232152321623217232182321923220232212322223223232242322523226232272322823229232302323123232232332323423235232362323723238232392324023241232422324323244232452324623247232482324923250232512325223253232542325523256232572325823259232602326123262232632326423265232662326723268232692327023271232722327323274232752327623277232782327923280232812328223283232842328523286232872328823289232902329123292232932329423295232962329723298232992330023301233022330323304233052330623307233082330923310233112331223313233142331523316233172331823319233202332123322233232332423325233262332723328233292333023331233322333323334233352333623337233382333923340233412334223343233442334523346233472334823349233502335123352233532335423355233562335723358233592336023361233622336323364233652336623367233682336923370233712337223373233742337523376233772337823379233802338123382233832338423385233862338723388233892339023391233922339323394233952339623397233982339923400234012340223403234042340523406234072340823409234102341123412234132341423415234162341723418234192342023421234222342323424234252342623427234282342923430234312343223433234342343523436234372343823439234402344123442234432344423445234462344723448234492345023451234522345323454234552345623457234582345923460234612346223463234642346523466234672346823469234702347123472234732347423475234762347723478234792348023481234822348323484234852348623487234882348923490234912349223493234942349523496234972349823499235002350123502235032350423505235062350723508235092351023511235122351323514235152351623517235182351923520235212352223523235242352523526235272352823529235302353123532235332353423535235362353723538235392354023541235422354323544235452354623547235482354923550235512355223553235542355523556235572355823559235602356123562235632356423565235662356723568235692357023571235722357323574235752357623577235782357923580235812358223583235842358523586235872358823589235902359123592235932359423595235962359723598235992360023601236022360323604236052360623607236082360923610236112361223613236142361523616236172361823619236202362123622236232362423625236262362723628236292363023631236322363323634236352363623637236382363923640236412364223643236442364523646236472364823649236502365123652236532365423655236562365723658236592366023661236622366323664236652366623667236682366923670236712367223673236742367523676236772367823679236802368123682236832368423685236862368723688236892369023691236922369323694236952369623697236982369923700237012370223703237042370523706237072370823709237102371123712237132371423715237162371723718237192372023721237222372323724237252372623727237282372923730237312373223733237342373523736237372373823739237402374123742237432374423745237462374723748237492375023751237522375323754237552375623757237582375923760237612376223763237642376523766237672376823769237702377123772237732377423775237762377723778237792378023781237822378323784237852378623787237882378923790237912379223793237942379523796237972379823799238002380123802238032380423805238062380723808238092381023811238122381323814238152381623817238182381923820238212382223823238242382523826238272382823829238302383123832238332383423835238362383723838238392384023841238422384323844238452384623847238482384923850238512385223853238542385523856238572385823859238602386123862238632386423865238662386723868238692387023871238722387323874238752387623877238782387923880238812388223883238842388523886238872388823889238902389123892238932389423895238962389723898238992390023901239022390323904239052390623907239082390923910239112391223913239142391523916239172391823919239202392123922239232392423925239262392723928239292393023931239322393323934239352393623937239382393923940239412394223943239442394523946239472394823949239502395123952239532395423955239562395723958239592396023961239622396323964239652396623967239682396923970239712397223973239742397523976239772397823979239802398123982239832398423985239862398723988239892399023991239922399323994239952399623997239982399924000240012400224003240042400524006240072400824009240102401124012240132401424015240162401724018240192402024021240222402324024240252402624027240282402924030240312403224033240342403524036240372403824039240402404124042240432404424045240462404724048240492405024051240522405324054240552405624057240582405924060240612406224063240642406524066240672406824069240702407124072240732407424075240762407724078240792408024081240822408324084240852408624087240882408924090240912409224093240942409524096240972409824099241002410124102241032410424105241062410724108241092411024111241122411324114241152411624117241182411924120241212412224123241242412524126241272412824129241302413124132241332413424135241362413724138241392414024141241422414324144241452414624147241482414924150241512415224153241542415524156241572415824159241602416124162241632416424165241662416724168241692417024171241722417324174241752417624177241782417924180241812418224183241842418524186241872418824189241902419124192241932419424195241962419724198241992420024201242022420324204242052420624207242082420924210242112421224213242142421524216242172421824219242202422124222242232422424225242262422724228242292423024231242322423324234242352423624237242382423924240242412424224243242442424524246242472424824249242502425124252242532425424255242562425724258242592426024261242622426324264242652426624267242682426924270242712427224273242742427524276242772427824279242802428124282242832428424285242862428724288242892429024291242922429324294242952429624297242982429924300243012430224303243042430524306243072430824309243102431124312243132431424315243162431724318243192432024321243222432324324243252432624327243282432924330243312433224333243342433524336243372433824339243402434124342243432434424345243462434724348243492435024351243522435324354243552435624357243582435924360243612436224363243642436524366243672436824369243702437124372243732437424375243762437724378243792438024381243822438324384243852438624387243882438924390243912439224393243942439524396243972439824399244002440124402244032440424405244062440724408244092441024411244122441324414244152441624417244182441924420244212442224423244242442524426244272442824429244302443124432244332443424435244362443724438244392444024441244422444324444244452444624447244482444924450244512445224453244542445524456244572445824459244602446124462244632446424465244662446724468244692447024471244722447324474244752447624477244782447924480244812448224483244842448524486244872448824489244902449124492244932449424495244962449724498244992450024501245022450324504245052450624507245082450924510245112451224513245142451524516245172451824519245202452124522245232452424525245262452724528245292453024531245322453324534245352453624537245382453924540245412454224543245442454524546245472454824549245502455124552245532455424555245562455724558245592456024561245622456324564245652456624567245682456924570245712457224573245742457524576245772457824579245802458124582245832458424585245862458724588245892459024591245922459324594245952459624597245982459924600246012460224603246042460524606246072460824609246102461124612246132461424615246162461724618246192462024621246222462324624246252462624627246282462924630246312463224633246342463524636246372463824639246402464124642246432464424645246462464724648246492465024651246522465324654246552465624657246582465924660246612466224663246642466524666246672466824669246702467124672246732467424675246762467724678246792468024681246822468324684246852468624687246882468924690246912469224693246942469524696246972469824699247002470124702247032470424705247062470724708247092471024711247122471324714247152471624717247182471924720247212472224723247242472524726247272472824729247302473124732247332473424735247362473724738247392474024741247422474324744247452474624747247482474924750247512475224753247542475524756247572475824759247602476124762247632476424765247662476724768247692477024771247722477324774247752477624777247782477924780247812478224783247842478524786247872478824789247902479124792247932479424795247962479724798247992480024801248022480324804248052480624807248082480924810248112481224813248142481524816248172481824819248202482124822248232482424825248262482724828248292483024831248322483324834248352483624837248382483924840248412484224843248442484524846248472484824849248502485124852248532485424855248562485724858248592486024861248622486324864248652486624867248682486924870248712487224873248742487524876248772487824879248802488124882248832488424885248862488724888248892489024891248922489324894248952489624897248982489924900249012490224903249042490524906249072490824909249102491124912249132491424915249162491724918249192492024921249222492324924249252492624927249282492924930249312493224933249342493524936249372493824939249402494124942249432494424945249462494724948249492495024951249522495324954249552495624957249582495924960249612496224963249642496524966249672496824969249702497124972249732497424975249762497724978249792498024981249822498324984249852498624987249882498924990249912499224993249942499524996249972499824999250002500125002250032500425005250062500725008250092501025011250122501325014250152501625017250182501925020250212502225023250242502525026250272502825029250302503125032250332503425035250362503725038250392504025041250422504325044250452504625047250482504925050250512505225053250542505525056250572505825059250602506125062250632506425065250662506725068250692507025071250722507325074250752507625077250782507925080250812508225083250842508525086250872508825089250902509125092250932509425095250962509725098250992510025101251022510325104251052510625107251082510925110251112511225113251142511525116251172511825119251202512125122251232512425125251262512725128251292513025131251322513325134251352513625137251382513925140251412514225143251442514525146251472514825149251502515125152251532515425155251562515725158251592516025161251622516325164251652516625167251682516925170251712517225173251742517525176251772517825179251802518125182251832518425185251862518725188251892519025191251922519325194251952519625197251982519925200252012520225203252042520525206252072520825209252102521125212252132521425215252162521725218252192522025221252222522325224252252522625227252282522925230252312523225233252342523525236252372523825239252402524125242252432524425245252462524725248252492525025251252522525325254252552525625257252582525925260252612526225263252642526525266252672526825269252702527125272252732527425275252762527725278252792528025281252822528325284252852528625287252882528925290252912529225293252942529525296252972529825299253002530125302253032530425305253062530725308253092531025311253122531325314253152531625317253182531925320253212532225323253242532525326253272532825329253302533125332253332533425335253362533725338253392534025341253422534325344253452534625347253482534925350253512535225353253542535525356253572535825359253602536125362253632536425365253662536725368253692537025371253722537325374253752537625377253782537925380253812538225383253842538525386253872538825389253902539125392253932539425395253962539725398253992540025401254022540325404254052540625407254082540925410254112541225413254142541525416254172541825419254202542125422254232542425425254262542725428254292543025431254322543325434254352543625437254382543925440254412544225443254442544525446254472544825449254502545125452254532545425455254562545725458254592546025461254622546325464254652546625467254682546925470254712547225473254742547525476254772547825479254802548125482254832548425485254862548725488254892549025491254922549325494254952549625497254982549925500255012550225503255042550525506255072550825509255102551125512255132551425515255162551725518255192552025521255222552325524255252552625527255282552925530255312553225533255342553525536255372553825539255402554125542255432554425545255462554725548255492555025551255522555325554255552555625557255582555925560255612556225563255642556525566255672556825569255702557125572255732557425575255762557725578255792558025581255822558325584255852558625587255882558925590255912559225593255942559525596255972559825599256002560125602256032560425605256062560725608256092561025611256122561325614256152561625617256182561925620256212562225623256242562525626256272562825629256302563125632256332563425635256362563725638256392564025641256422564325644256452564625647256482564925650256512565225653256542565525656256572565825659256602566125662256632566425665256662566725668256692567025671256722567325674256752567625677256782567925680256812568225683256842568525686256872568825689256902569125692256932569425695256962569725698256992570025701257022570325704257052570625707257082570925710257112571225713257142571525716257172571825719257202572125722257232572425725257262572725728257292573025731257322573325734257352573625737257382573925740257412574225743257442574525746257472574825749257502575125752257532575425755257562575725758257592576025761257622576325764257652576625767257682576925770257712577225773257742577525776257772577825779257802578125782257832578425785257862578725788257892579025791257922579325794257952579625797257982579925800258012580225803258042580525806258072580825809258102581125812258132581425815258162581725818258192582025821258222582325824258252582625827258282582925830258312583225833258342583525836258372583825839258402584125842258432584425845258462584725848258492585025851258522585325854258552585625857258582585925860258612586225863258642586525866258672586825869258702587125872258732587425875258762587725878258792588025881258822588325884258852588625887258882588925890258912589225893258942589525896258972589825899259002590125902259032590425905259062590725908259092591025911259122591325914259152591625917259182591925920259212592225923259242592525926259272592825929259302593125932259332593425935259362593725938259392594025941259422594325944259452594625947259482594925950259512595225953259542595525956259572595825959259602596125962259632596425965259662596725968259692597025971259722597325974259752597625977259782597925980259812598225983259842598525986259872598825989259902599125992259932599425995259962599725998259992600026001260022600326004260052600626007260082600926010260112601226013260142601526016260172601826019260202602126022260232602426025260262602726028260292603026031260322603326034260352603626037260382603926040260412604226043260442604526046260472604826049260502605126052260532605426055260562605726058260592606026061260622606326064260652606626067260682606926070260712607226073260742607526076260772607826079260802608126082260832608426085260862608726088260892609026091260922609326094260952609626097260982609926100261012610226103261042610526106261072610826109261102611126112261132611426115261162611726118261192612026121261222612326124261252612626127261282612926130261312613226133261342613526136261372613826139261402614126142261432614426145261462614726148261492615026151261522615326154261552615626157261582615926160261612616226163261642616526166261672616826169261702617126172261732617426175261762617726178261792618026181261822618326184261852618626187261882618926190261912619226193261942619526196261972619826199262002620126202262032620426205262062620726208262092621026211262122621326214262152621626217262182621926220262212622226223262242622526226262272622826229262302623126232262332623426235262362623726238262392624026241262422624326244262452624626247262482624926250262512625226253262542625526256262572625826259262602626126262262632626426265262662626726268262692627026271262722627326274262752627626277262782627926280262812628226283262842628526286262872628826289262902629126292262932629426295262962629726298262992630026301263022630326304263052630626307263082630926310263112631226313263142631526316263172631826319263202632126322263232632426325263262632726328263292633026331263322633326334263352633626337263382633926340263412634226343263442634526346263472634826349263502635126352263532635426355263562635726358263592636026361263622636326364263652636626367263682636926370263712637226373263742637526376263772637826379263802638126382263832638426385263862638726388263892639026391263922639326394263952639626397263982639926400264012640226403264042640526406264072640826409264102641126412264132641426415264162641726418264192642026421264222642326424264252642626427264282642926430264312643226433264342643526436264372643826439264402644126442264432644426445264462644726448264492645026451264522645326454264552645626457264582645926460264612646226463264642646526466264672646826469264702647126472264732647426475264762647726478264792648026481264822648326484264852648626487264882648926490264912649226493264942649526496264972649826499265002650126502265032650426505265062650726508265092651026511265122651326514265152651626517265182651926520265212652226523265242652526526265272652826529265302653126532265332653426535265362653726538265392654026541265422654326544265452654626547265482654926550265512655226553265542655526556265572655826559265602656126562265632656426565265662656726568265692657026571265722657326574265752657626577265782657926580265812658226583265842658526586265872658826589265902659126592265932659426595265962659726598265992660026601266022660326604266052660626607266082660926610266112661226613266142661526616266172661826619266202662126622266232662426625266262662726628266292663026631266322663326634266352663626637266382663926640266412664226643266442664526646266472664826649266502665126652266532665426655266562665726658266592666026661266622666326664266652666626667266682666926670266712667226673266742667526676266772667826679266802668126682266832668426685266862668726688266892669026691266922669326694266952669626697266982669926700267012670226703267042670526706267072670826709267102671126712267132671426715267162671726718267192672026721267222672326724267252672626727267282672926730267312673226733267342673526736267372673826739267402674126742267432674426745267462674726748267492675026751267522675326754267552675626757267582675926760267612676226763267642676526766267672676826769267702677126772267732677426775267762677726778267792678026781267822678326784267852678626787267882678926790267912679226793267942679526796267972679826799268002680126802268032680426805268062680726808268092681026811268122681326814268152681626817268182681926820268212682226823268242682526826268272682826829268302683126832268332683426835268362683726838268392684026841268422684326844268452684626847268482684926850268512685226853268542685526856268572685826859268602686126862268632686426865268662686726868268692687026871268722687326874268752687626877268782687926880268812688226883268842688526886268872688826889268902689126892268932689426895268962689726898268992690026901269022690326904269052690626907269082690926910269112691226913269142691526916269172691826919269202692126922269232692426925269262692726928269292693026931269322693326934269352693626937269382693926940269412694226943269442694526946269472694826949269502695126952269532695426955269562695726958269592696026961269622696326964269652696626967269682696926970269712697226973269742697526976269772697826979269802698126982269832698426985269862698726988269892699026991269922699326994269952699626997269982699927000270012700227003270042700527006270072700827009270102701127012270132701427015270162701727018270192702027021270222702327024270252702627027270282702927030270312703227033270342703527036270372703827039270402704127042270432704427045270462704727048270492705027051270522705327054270552705627057270582705927060270612706227063270642706527066270672706827069270702707127072270732707427075270762707727078270792708027081270822708327084270852708627087270882708927090270912709227093270942709527096270972709827099271002710127102271032710427105271062710727108271092711027111271122711327114271152711627117271182711927120271212712227123271242712527126271272712827129271302713127132271332713427135271362713727138271392714027141271422714327144271452714627147271482714927150271512715227153271542715527156271572715827159271602716127162271632716427165271662716727168271692717027171271722717327174271752717627177271782717927180271812718227183271842718527186271872718827189271902719127192271932719427195271962719727198271992720027201272022720327204272052720627207272082720927210272112721227213272142721527216272172721827219272202722127222272232722427225272262722727228272292723027231272322723327234272352723627237272382723927240272412724227243272442724527246272472724827249272502725127252272532725427255272562725727258272592726027261272622726327264272652726627267272682726927270272712727227273272742727527276272772727827279272802728127282272832728427285272862728727288272892729027291272922729327294272952729627297272982729927300273012730227303273042730527306273072730827309273102731127312273132731427315273162731727318273192732027321273222732327324273252732627327273282732927330273312733227333273342733527336273372733827339273402734127342273432734427345273462734727348273492735027351273522735327354273552735627357273582735927360273612736227363273642736527366273672736827369273702737127372273732737427375273762737727378273792738027381273822738327384273852738627387273882738927390273912739227393273942739527396273972739827399274002740127402274032740427405274062740727408274092741027411274122741327414274152741627417274182741927420274212742227423274242742527426274272742827429274302743127432274332743427435274362743727438274392744027441274422744327444274452744627447274482744927450274512745227453274542745527456274572745827459274602746127462274632746427465274662746727468274692747027471274722747327474274752747627477274782747927480274812748227483274842748527486274872748827489274902749127492274932749427495274962749727498274992750027501275022750327504275052750627507275082750927510275112751227513275142751527516275172751827519275202752127522275232752427525275262752727528275292753027531275322753327534275352753627537275382753927540275412754227543275442754527546275472754827549275502755127552275532755427555275562755727558275592756027561275622756327564275652756627567275682756927570275712757227573275742757527576275772757827579275802758127582275832758427585275862758727588275892759027591275922759327594275952759627597275982759927600276012760227603276042760527606276072760827609276102761127612276132761427615276162761727618276192762027621276222762327624276252762627627276282762927630276312763227633276342763527636276372763827639276402764127642276432764427645276462764727648276492765027651276522765327654276552765627657276582765927660276612766227663276642766527666276672766827669276702767127672276732767427675276762767727678276792768027681276822768327684276852768627687276882768927690276912769227693276942769527696276972769827699277002770127702277032770427705277062770727708277092771027711277122771327714277152771627717277182771927720277212772227723277242772527726277272772827729277302773127732277332773427735277362773727738277392774027741277422774327744277452774627747277482774927750277512775227753277542775527756277572775827759277602776127762277632776427765277662776727768277692777027771277722777327774277752777627777277782777927780277812778227783277842778527786277872778827789277902779127792277932779427795277962779727798277992780027801278022780327804278052780627807278082780927810278112781227813278142781527816278172781827819278202782127822278232782427825278262782727828278292783027831278322783327834278352783627837278382783927840278412784227843278442784527846278472784827849278502785127852278532785427855278562785727858278592786027861278622786327864278652786627867278682786927870278712787227873278742787527876278772787827879278802788127882278832788427885278862788727888278892789027891278922789327894278952789627897278982789927900279012790227903279042790527906279072790827909279102791127912279132791427915279162791727918279192792027921279222792327924279252792627927279282792927930279312793227933279342793527936279372793827939279402794127942279432794427945279462794727948279492795027951279522795327954279552795627957279582795927960279612796227963279642796527966279672796827969279702797127972279732797427975279762797727978279792798027981279822798327984279852798627987279882798927990279912799227993279942799527996279972799827999280002800128002280032800428005280062800728008280092801028011280122801328014280152801628017280182801928020280212802228023280242802528026280272802828029280302803128032280332803428035280362803728038280392804028041280422804328044280452804628047280482804928050280512805228053280542805528056280572805828059280602806128062280632806428065280662806728068280692807028071280722807328074280752807628077280782807928080280812808228083280842808528086280872808828089280902809128092280932809428095280962809728098280992810028101281022810328104281052810628107281082810928110281112811228113281142811528116281172811828119281202812128122281232812428125281262812728128281292813028131281322813328134281352813628137281382813928140281412814228143281442814528146281472814828149281502815128152281532815428155281562815728158281592816028161281622816328164281652816628167281682816928170281712817228173281742817528176281772817828179281802818128182281832818428185281862818728188281892819028191281922819328194281952819628197281982819928200282012820228203282042820528206282072820828209282102821128212282132821428215282162821728218282192822028221282222822328224282252822628227282282822928230282312823228233282342823528236282372823828239282402824128242282432824428245282462824728248282492825028251282522825328254282552825628257282582825928260282612826228263282642826528266282672826828269282702827128272282732827428275282762827728278282792828028281282822828328284282852828628287282882828928290282912829228293282942829528296282972829828299283002830128302283032830428305283062830728308283092831028311283122831328314283152831628317283182831928320283212832228323283242832528326283272832828329283302833128332283332833428335283362833728338283392834028341283422834328344283452834628347283482834928350283512835228353283542835528356283572835828359283602836128362283632836428365283662836728368283692837028371283722837328374283752837628377283782837928380283812838228383283842838528386283872838828389283902839128392283932839428395283962839728398283992840028401284022840328404284052840628407284082840928410284112841228413284142841528416284172841828419284202842128422284232842428425284262842728428284292843028431284322843328434284352843628437284382843928440284412844228443284442844528446284472844828449284502845128452284532845428455284562845728458284592846028461284622846328464284652846628467284682846928470284712847228473284742847528476284772847828479284802848128482284832848428485284862848728488284892849028491284922849328494284952849628497284982849928500285012850228503285042850528506285072850828509285102851128512285132851428515285162851728518285192852028521285222852328524285252852628527285282852928530285312853228533285342853528536285372853828539285402854128542285432854428545285462854728548285492855028551285522855328554285552855628557 |
- /*
- * 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/#bs5/dt-2.0.7/b-3.0.2/b-html5-3.0.2/b-print-3.0.2/fh-4.0.1/sb-1.7.1/sp-2.3.1/sl-2.0.1
- *
- * Included libraries:
- * DataTables 2.0.7, Buttons 3.0.2, HTML5 export 3.0.2, Print view 3.0.2, FixedHeader 4.0.1, SearchBuilder 1.7.1, SearchPanes 2.3.1, Select 2.0.1
- */
- /*! DataTables 2.0.7
- * © SpryMedia Ltd - datatables.net/license
- */
- /**
- * @summary DataTables
- * @description Paginate, search and order HTML tables
- * @version 2.0.7
- * @author SpryMedia Ltd
- * @contact www.datatables.net
- * @copyright SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - https://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: https://www.datatables.net
- */
- (function( factory ) {
- "use strict";
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- // jQuery's factory checks for a global window - if it isn't present then it
- // returns a factory function that expects the window object
- var jq = require('jquery');
- if (typeof window === 'undefined') {
- 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 ( ! $ ) {
- $ = jq( root );
- }
- return factory( $, root, root.document );
- };
- }
- else {
- module.exports = factory( jq, window, window.document );
- }
- }
- else {
- // Browser
- window.DataTable = factory( jQuery, window, document );
- }
- }(function( $, window, document ) {
- "use strict";
-
- var DataTable = function ( selector, options )
- {
- // Check if called with a window or jQuery object for DOM less applications
- // This is for backwards compatibility
- if (DataTable.factory(selector, options)) {
- return DataTable;
- }
-
- // When creating with `new`, create a new DataTable, returning the API instance
- if (this instanceof DataTable) {
- return $(selector).DataTable(options);
- }
- else {
- // Argument switching
- options = selector;
- }
-
- var _that = this;
- var emptyInit = options === undefined;
- var len = this.length;
-
- if ( emptyInit ) {
- options = {};
- }
-
- // Method to get DT API instance from jQuery object
- this.api = function ()
- {
- return new _Api( this );
- };
-
- 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;
-
-
- var i=0, iLen;
- 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;
- }
-
- $(this).trigger( 'options.dt', oInit );
-
- /* 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() ), true );
-
-
-
- /* 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 && 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 )
- {
- new DataTable.Api(s).destroy();
- 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,
- colgroup: $('<colgroup>').prependTo(this),
- fastData: function (row, column, type) {
- return _fnGetCellData(oSettings, row, column, type);
- }
- } );
- oSettings.nTable = this;
- oSettings.oInit = oInit;
-
- allSettings.push( oSettings );
-
- // Make a single API instance available for internal handling
- oSettings.api = new _Api( 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 the length menu is given, but the init display length is not, use the length menu
- if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
- {
- oInit.iDisplayLength = Array.isArray(oInit.aLengthMenu[0])
- ? oInit.aLengthMenu[0][0]
- : $.isPlainObject( oInit.aLengthMenu[0] )
- ? oInit.aLengthMenu[0].value
- : 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, [
- "ajax",
- "fnFormatNumber",
- "sServerMethod",
- "aaSorting",
- "aaSortingFixed",
- "aLengthMenu",
- "sPaginationType",
- "iStateDuration",
- "bSortCellsTop",
- "iTabIndex",
- "sDom",
- "fnStateLoadCallback",
- "fnStateSaveCallback",
- "renderer",
- "searchDelay",
- "rowId",
- "caption",
- "layout",
- [ "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 );
- _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams );
- _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams );
- _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded );
- _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback );
- _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow );
- _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback );
- _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback );
- _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete );
- _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback );
-
- oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
-
- /* Browser support detection */
- _fnBrowserDetect( oSettings );
-
- var oClasses = oSettings.oClasses;
-
- $.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
- $this.addClass( oClasses.table );
-
- if (! oSettings.oFeatures.bPaginate) {
- oInit.iDisplayStart = 0;
- }
-
- if ( oSettings.iInitDisplayStart === undefined )
- {
- /* Display start point, taking into account the save saving */
- oSettings.iInitDisplayStart = oInit.iDisplayStart;
- oSettings._iDisplayStart = oInit.iDisplayStart;
- }
-
- /* 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 ) {
- _fnCamelToHungarian( defaults.oLanguage, json );
- $.extend( true, oLanguage, json, oSettings.oInit.oLanguage );
-
- _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true);
- _fnInitialise( oSettings );
- },
- error: function () {
- // Error occurred loading language file
- _fnLog( oSettings, 0, 'i18n file loading error', 21 );
-
- // continue on as best we can
- _fnInitialise( oSettings );
- }
- } );
- bInitHandedOff = true;
- }
- else {
- _fnCallbackFire( oSettings, null, 'i18n', [oSettings]);
- }
-
- /*
- * Columns
- * See if we should load columns automatically or use defined ones
- */
- var columnsInit = [];
- var thead = this.getElementsByTagName('thead');
- var initHeaderLayout = _fnDetectHeader( oSettings, thead[0] );
-
- // If we don't have a columns array, then generate one with nulls
- if ( oInit.aoColumns ) {
- columnsInit = oInit.aoColumns;
- }
- else if ( initHeaderLayout.length ) {
- for ( i=0, iLen=initHeaderLayout[0].length ; i<iLen ; i++ ) {
- columnsInit.push( null );
- }
- }
-
- // Add the columns
- for ( i=0, iLen=columnsInit.length ; i<iLen ; i++ ) {
- _fnAddColumn( oSettings );
- }
-
- // Apply the column definitions
- _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, columnsInit, initHeaderLayout, function (iCol, oDef) {
- _fnColumnOptions( oSettings, iCol, oDef );
- } );
-
- /* HTML5 attribute detection - build an mData object automatically if the
- * attributes are found
- */
- var rowOne = $this.children('tbody').find('tr').eq(0);
-
- 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) {
- _fnLog( oSettings, 0, 'Incorrect column count', 18 );
- }
-
- 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
- };
- col._isArrayHost = true;
-
- _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 );
-
- _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
- if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
- _fnSortingClasses( oSettings );
- }
- } );
-
-
- /*
- * Final init
- * Cache the header, body and footer as required, creating them if needed
- */
- var caption = $this.children('caption');
-
- if ( oSettings.caption ) {
- if ( caption.length === 0 ) {
- caption = $('<caption/>').appendTo( $this );
- }
-
- caption.html( oSettings.caption );
- }
-
- // Store the caption side, so we can remove the element from the document
- // when creating the element
- if (caption.length) {
- caption[0]._captionSide = caption.css('caption-side');
- oSettings.captionNode = caption[0];
- }
-
- if ( thead.length === 0 ) {
- thead = $('<thead/>').appendTo($this);
- }
- oSettings.nTHead = thead[0];
- $('tr', thead).addClass(oClasses.thead.row);
-
- var tbody = $this.children('tbody');
- if ( tbody.length === 0 ) {
- tbody = $('<tbody/>').insertAfter(thead);
- }
- oSettings.nTBody = tbody[0];
-
- var tfoot = $this.children('tfoot');
- if ( tfoot.length === 0 ) {
- // 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);
- }
- oSettings.nTFoot = tfoot[0];
- $('tr', tfoot).addClass(oClasses.tfoot.row);
-
- // 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 ( _fnDataSource( oSettings ) == 'dom' ) {
- // Grab the data from the page
- _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! */
- _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState );
-
- if ( oInit.bStateSave )
- {
- features.bStateSave = true;
- _fnLoadState( oSettings, oInit, loadedInit );
- }
- else {
- loadedInit();
- }
-
- } );
- _that = null;
- return this;
- };
-
-
-
- /**
- * 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
- */
- builder: "bs5/dt-2.0.7/b-3.0.2/b-html5-3.0.2/b-print-3.0.2/fh-4.0.1/sb-1.7.1/sp-2.3.1/sl-2.0.1",
-
-
- /**
- * 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",
-
-
- /**
- * Legacy so v1 plug-ins don't throw js errors on load
- */
- feature: [],
-
- /**
- * Feature plug-ins.
- *
- * This is an object of callbacks which provide the features for DataTables
- * to be initialised via the `layout` option.
- */
- features: {},
-
-
- /**
- * 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: []
- },
-
-
- /**
- * 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: {
- /**
- * Automatic column class assignment
- */
- className: {},
-
- /**
- * 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 ( ! data.substring(1).match(/[0-9]/) ) {
- * return null;
- * }
- *
- * // Check prefixed by currency
- * if ( data.charAt(0) == '$' || data.charAt(0) == '£' ) {
- * return 'currency';
- * }
- * return null;
- * }
- * );
- */
- detect: [],
-
- /**
- * Automatic renderer assignment
- */
- render: {},
-
-
- /**
- * 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 compatibility 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,
-
-
- /**
- * 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,
- oStdClasses: _ext.classes,
- oPagination: _ext.pager
- } );
-
-
- $.extend( DataTable.ext.classes, {
- container: 'dt-container',
- empty: {
- row: 'dt-empty'
- },
- info: {
- container: 'dt-info'
- },
- length: {
- container: 'dt-length',
- select: 'dt-input'
- },
- order: {
- canAsc: 'dt-orderable-asc',
- canDesc: 'dt-orderable-desc',
- isAsc: 'dt-ordering-asc',
- isDesc: 'dt-ordering-desc',
- none: 'dt-orderable-none',
- position: 'sorting_'
- },
- processing: {
- container: 'dt-processing'
- },
- scrolling: {
- body: 'dt-scroll-body',
- container: 'dt-scroll',
- footer: {
- self: 'dt-scroll-foot',
- inner: 'dt-scroll-footInner'
- },
- header: {
- self: 'dt-scroll-head',
- inner: 'dt-scroll-headInner'
- }
- },
- search: {
- container: 'dt-search',
- input: 'dt-input'
- },
- table: 'dataTable',
- tbody: {
- cell: '',
- row: ''
- },
- thead: {
- cell: '',
- row: ''
- },
- tfoot: {
- cell: '',
- row: ''
- },
- paging: {
- active: 'current',
- button: 'dt-paging-button',
- container: 'dt-paging',
- disabled: 'disabled'
- }
- } );
-
-
- /*
- * 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_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\u2028]/g;
- var _re_html = /<([^>]*>)/g;
- var _max_str_len = Math.pow(2, 28);
-
- // 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' );
-
- // https://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
- // - Ƀ - Bitcoin
- // - Ξ - Ethereum
- // standards as thousands separators.
- var _re_formatted_numeric = /['\u00A0,$£€¥%\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 type = typeof d;
- var strType = type === 'string';
-
- if ( type === 'number' || type === 'bigint') {
- return true;
- }
-
- // 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';
- };
-
- // Is a string a number surrounded by HTML?
- var _htmlNumeric = function ( d, decimalPoint, formatted ) {
- if ( _empty( d ) ) {
- return true;
- }
-
- // input and select strings mean that this isn't just a number
- if (typeof d === 'string' && d.match(/<(input|select)/i)) {
- return null;
- }
-
- 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++ ) {
- if ( a[ order[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;
- };
-
- // Replaceable function in api.util
- var _stripHtml = function (input) {
- // Irrelevant check to workaround CodeQL's false positive on the regex
- if (input.length > _max_str_len) {
- throw new Error('Exceeded max str len');
- }
-
- var previous;
-
- input = input.replace(_re_html, ''); // Complete tags
-
- // Safety for incomplete script tag - use do / while to ensure that
- // we get all instances
- do {
- previous = input;
- input = input.replace(/<script/i, '');
- } while (input !== previous);
-
- return previous;
- };
-
- // Replaceable function in api.util
- var _escapeHtml = function ( d ) {
- if (Array.isArray(d)) {
- d = d.join(',');
- }
-
- return typeof d === 'string' ?
- d
- .replace(/&/g, '&')
- .replace(/</g, '<')
- .replace(/>/g, '>')
- .replace(/"/g, '"') :
- d;
- };
-
- // Remove diacritics from a string by decomposing it and then removing
- // non-ascii characters
- var _normalize = function (str, both) {
- if (typeof str !== 'string') {
- return str;
- }
-
- // It is faster to just run `normalize` than it is to check if
- // we need to with a regex!
- var res = str.normalize("NFD");
-
- // Equally, here we check if a regex is needed or not
- return res.length !== str.length
- ? (both === true ? str + ' ' : '' ) + res.replace(/[\u0300-\u036f]/g, "")
- : res;
- }
-
- /**
- * 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 (Array.from && Set) {
- return Array.from(new Set(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.app/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;
- };
-
- // Surprisingly this is faster than [].concat.apply
- // https://jsperf.com/flatten-an-array-loop-vs-reduce/2
- var _flatten = function (out, val) {
- if (Array.isArray(val)) {
- for (var i=0 ; i<val.length ; i++) {
- _flatten(out, val[i]);
- }
- }
- else {
- out.push(val);
- }
-
- return out;
- }
-
- // Similar to jQuery's addClass, but use classList.add
- function _addClass(el, name) {
- if (name) {
- name.split(' ').forEach(function (n) {
- if (n) {
- // `add` does deduplication, so no need to check `contains`
- el.classList.add(n);
- }
- });
- }
- }
-
- /**
- * 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 = {
- /**
- * Return a string with diacritic characters decomposed
- * @param {*} mixed Function or string to normalize
- * @param {*} both Return original string and the normalized string
- * @returns String or undefined
- */
- diacritics: function (mixed, both) {
- var type = typeof mixed;
-
- if (type !== 'function') {
- return _normalize(mixed, both);
- }
- _normalize = mixed;
- },
-
- /**
- * Debounce a function
- *
- * @param {function} fn Function to be called
- * @param {integer} freq Call frequency in mS
- * @return {function} Wrapped function
- */
- debounce: function ( fn, timeout ) {
- var timer;
-
- return function () {
- var that = this;
- var args = arguments;
-
- clearTimeout(timer);
-
- timer = setTimeout( function () {
- fn.apply(that, args);
- }, timeout || 250 );
- };
- },
-
- /**
- * 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 function that will write to a nested object or array
- * @param {*} source JSON notation string
- * @returns Write function
- */
- set: function ( source ) {
- if ( $.isPlainObject( source ) ) {
- /* 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 DataTable.util.set( source._ );
- }
- else if ( source === null ) {
- // Nothing to do when the data source is null
- return function () {};
- }
- else if ( typeof source === 'function' ) {
- return function (data, val, meta) {
- source( data, 'set', val, meta );
- };
- }
- else if (
- typeof source === 'string' && (source.indexOf('.') !== -1 ||
- source.indexOf('[') !== -1 || source.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++ ) {
- // Protect against prototype pollution
- if (a[i] === '__proto__' || a[i] === 'constructor') {
- throw new Error('Cannot set prototype values');
- }
-
- // 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 ( Array.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, source );
- };
- }
- else {
- // Array or flat object mapping
- return function (data, val) { // meta is also passed in, but not used
- data[source] = val;
- };
- }
- },
-
- /**
- * Create a function that will read nested objects from arrays, based on JSON notation
- * @param {*} source JSON notation string
- * @returns Value read
- */
- get: function ( source ) {
- if ( $.isPlainObject( source ) ) {
- // Build an object of get functions, and wrap them in a single call
- var o = {};
- $.each( source, function (key, val) {
- if ( val ) {
- o[key] = DataTable.util.get( val );
- }
- } );
-
- return function (data, type, row, meta) {
- var t = o[type] || o._;
- return t !== undefined ?
- t(data, type, row, meta) :
- data;
- };
- }
- else if ( source === 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 source === 'function' ) {
- return function (data, type, row, meta) {
- return source( data, type, row, meta );
- };
- }
- else if (
- typeof source === 'string' && (source.indexOf('.') !== -1 ||
- source.indexOf('[') !== -1 || source.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 ( Array.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] ] === null) {
- return null;
- }
- else if ( data === undefined || 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, source );
- };
- }
- else {
- // Array or flat object mapping
- return function (data) { // row and meta also passed, but not used
- return data[source];
- };
- }
- },
-
- stripHtml: function (mixed) {
- var type = typeof mixed;
-
- if (type === 'function') {
- _stripHtml = mixed;
- return;
- }
- else if (type === 'string') {
- return _stripHtml(mixed);
- }
- return mixed;
- },
-
- escapeHtml: function (mixed) {
- var type = typeof mixed;
-
- if (type === 'function') {
- _escapeHtml = mixed;
- return;
- }
- else if (type === 'string' || Array.isArray(mixed)) {
- return _escapeHtml(mixed);
- }
- return mixed;
- },
-
- unique: _unique
- };
-
-
-
- /**
- * 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) {
- 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) {
- 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 ];
- }
- }
- } );
- }
-
- /**
- * 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] );
- }
- }
- }
-
- // Enable search delay if server-side processing is enabled
- if (init.serverSide && ! init.searchDelay) {
- init.searchDelay = 400;
- }
- }
-
-
- /**
- * 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' && ! Array.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: -1 * window.pageXOffset, // 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();
-
- // Get scrollbar width
- browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;
-
- // 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;
-
- n.remove();
- }
-
- $.extend( settings.oBrowser, DataTable.__browser );
- settings.oScroll.iBarWidth = DataTable.__browser.barWidth;
- }
-
- /**
- * Add a column to the list used for the table with default values
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnAddColumn( oSettings )
- {
- // Add column to aoColumns array
- var oDefaults = DataTable.defaults.column;
- var iCol = oSettings.aoColumns.length;
- var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
- "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
- "mData": oDefaults.mData ? oDefaults.mData : iCol,
- idx: iCol,
- searchFixed: {},
- colEl: $('<col>').attr('data-dt-column', 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 ] );
- }
-
-
- /**
- * 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 ];
-
- /* 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, true );
-
- /* 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;
- }
-
- var origClass = oCol.sClass;
-
- $.extend( oCol, oOptions );
- _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
-
- // Merge class from previously defined classes with this one, rather than just
- // overwriting it in the extend above
- if (origClass !== oCol.sClass) {
- oCol.sClass = origClass + ' ' + oCol.sClass;
- }
-
- /* 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 );
-
- // The `render` option can be given as an array to access the helper rendering methods.
- // The first element is the rendering method to use, the rest are the parameters to pass
- if ( oCol.mRender && Array.isArray( oCol.mRender ) ) {
- var copy = oCol.mRender.slice();
- var name = copy.shift();
-
- oCol.mRender = DataTable.render[name].apply(window, copy);
- }
-
- oCol._render = 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 oCol._render && type ?
- oCol._render( 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' && ! oCol._isArrayHost ) {
- oSettings._rowReadObject = true;
- }
-
- /* Feature sorting overrides column specific when off */
- if ( !oSettings.oFeatures.bSort )
- {
- oCol.bSortable = false;
- }
- }
-
-
- /**
- * 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 )
- {
- _fnCalculateColumnWidths( settings );
- _fnColumnSizes( settings );
-
- var scroll = settings.oScroll;
- if ( scroll.sY !== '' || scroll.sX !== '') {
- _fnScrollDraw( settings );
- }
-
- _fnCallbackFire( settings, null, 'column-sizing', [settings] );
- }
-
- /**
- * Apply column sizes
- *
- * @param {*} settings DataTables settings object
- */
- function _fnColumnSizes ( settings )
- {
- var cols = settings.aoColumns;
-
- for (var i=0 ; i<cols.length ; i++) {
- var width = _fnColumnsSumWidth(settings, [i], false, false);
-
- cols[i].colEl.css('width', width);
- }
- }
-
-
- /**
- * Convert 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;
- }
-
-
- /**
- * Convert 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 = aiVis.indexOf(iMatch);
-
- 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( settings )
- {
- var layout = settings.aoHeader;
- var columns = settings.aoColumns;
- var vis = 0;
-
- if ( layout.length ) {
- for ( var i=0, ien=layout[0].length ; i<ien ; i++ ) {
- if ( columns[i].bVisible && $(layout[0][i].cell).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 = [];
-
- oSettings.aoColumns.map( 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, 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++ ) {
-
- if (! data[k]) {
- continue;
- }
-
- // 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-2 ) {
- break;
- }
-
- // Only a single match is needed for html type since it is
- // bottom of the pile and very similar to string - but it
- // must not be empty
- if ( detectedType === 'html' && ! _empty(cache[k]) ) {
- 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';
- }
- }
-
- // Set class names for header / footer for auto type classes
- var autoClass = _ext.type.className[col.sType];
-
- if (autoClass) {
- _columnAutoClass(settings.aoHeader, i, autoClass);
- _columnAutoClass(settings.aoFooter, i, autoClass);
- }
-
- var renderer = _ext.type.render[col.sType];
-
- // This can only happen once! There is no way to remover
- // a renderer. After the first time the renderer has
- // already been set so createTr will run the renderer itself.
- if (renderer && ! col._render) {
- col._render = DataTable.util.get(renderer);
-
- _columnAutoRender(settings, i);
- }
- }
- }
-
- /**
- * Apply an auto detected renderer to data which doesn't yet have
- * a renderer
- */
- function _columnAutoRender(settings, colIdx) {
- var data = settings.aoData;
-
- for (var i=0 ; i<data.length ; i++) {
- if (data[i].nTr) {
- // We have to update the display here since there is no
- // invalidation check for the data
- var display = _fnGetCellData( settings, i, colIdx, 'display' );
-
- data[i].displayData[colIdx] = display;
- _fnWriteCell(data[i].anCells[colIdx], display);
-
- // No need to update sort / filter data since it has
- // been invalidated and will be re-read with the
- // renderer now applied
- }
- }
- }
-
- /**
- * Apply a class name to a column's header cells
- */
- function _columnAutoClass(container, colIdx, className) {
- container.forEach(function (row) {
- if (row[colIdx] && row[colIdx].unique) {
- _addClass(row[colIdx].cell, className);
- }
- });
- }
-
- /**
- * 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 {array} headerLayout Layout for header as it was loaded
- * @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, headerLayout, fn )
- {
- var i, iLen, j, jLen, k, kLen, def;
- var columns = oSettings.aoColumns;
-
- if ( aoCols ) {
- for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) {
- if (aoCols[i] && aoCols[i].name) {
- columns[i].sName = aoCols[i].name;
- }
- }
- }
-
- // 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.target !== undefined
- ? def.target
- : def.targets !== undefined
- ? def.targets
- : def.aTargets;
-
- if ( ! Array.isArray( aTargets ) )
- {
- aTargets = [ aTargets ];
- }
-
- for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
- {
- var target = aTargets[j];
-
- if ( typeof target === 'number' && target >= 0 )
- {
- /* Add columns that we don't yet know about */
- while( columns.length <= target )
- {
- _fnAddColumn( oSettings );
- }
-
- /* Integer, basic index */
- fn( target, def );
- }
- else if ( typeof target === 'number' && target < 0 )
- {
- /* Negative integer, right to left column counting */
- fn( columns.length+target, def );
- }
- else if ( typeof target === 'string' )
- {
- for ( k=0, kLen=columns.length ; k<kLen ; k++ ) {
- if (target === '_all') {
- // Apply to all columns
- fn( k, def );
- }
- else if (target.indexOf(':name') !== -1) {
- // Column selector
- if (columns[k].sName === target.replace(':name', '')) {
- fn( k, def );
- }
- }
- else {
- // Cell selector
- headerLayout.forEach(function (row) {
- if (row[k]) {
- var cell = $(row[k].cell);
-
- // Legacy support. Note that it means that we don't support
- // an element name selector only, since they are treated as
- // class names for 1.x compat.
- if (target.match(/^[a-z][\w-]*$/i)) {
- target = '.' + target;
- }
-
- if (cell.is( target )) {
- fn( k, def );
- }
- }
- });
- }
- }
- }
- }
- }
- }
-
- // Statically defined columns array
- if ( aoCols ) {
- for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) {
- fn( i, aoCols[i] );
- }
- }
- }
-
-
- /**
- * Get the width for a given set of columns
- *
- * @param {*} settings DataTables settings object
- * @param {*} targets Columns - comma separated string or array of numbers
- * @param {*} original Use the original width (true) or calculated (false)
- * @param {*} incVisible Include visible columns (true) or not (false)
- * @returns Combined CSS value
- */
- function _fnColumnsSumWidth( settings, targets, original, incVisible ) {
- if ( ! Array.isArray( targets ) ) {
- targets = _fnColumnsFromHeader( targets );
- }
-
- var sum = 0;
- var unit;
- var columns = settings.aoColumns;
-
- for ( var i=0, ien=targets.length ; i<ien ; i++ ) {
- var column = columns[ targets[i] ];
- var definedWidth = original ?
- column.sWidthOrig :
- column.sWidth;
-
- if ( ! incVisible && column.bVisible === false ) {
- continue;
- }
-
- if ( definedWidth === null || definedWidth === undefined ) {
- return null; // can't determine a defined width - browser defined
- }
- else if ( typeof definedWidth === 'number' ) {
- unit = 'px';
- sum += definedWidth;
- }
- else {
- var matched = definedWidth.match(/([\d\.]+)([^\d]*)/);
-
- if ( matched ) {
- sum += matched[1] * 1;
- unit = matched.length === 3 ?
- matched[2] :
- 'px';
- }
- }
- }
-
- return sum + unit;
- }
-
- function _fnColumnsFromHeader( cell )
- {
- var attr = $(cell).closest('[data-dt-column]').attr('data-dt-column');
-
- if ( ! attr ) {
- return [];
- }
-
- return attr.split(',').map( function (val) {
- return val * 1;
- } );
- }
- /**
- * 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} settings dataTables settings object
- * @param {array} data data array to be added
- * @param {node} [tr] TR element to add to the table - optional. If not given,
- * DataTables will create a row automatically
- * @param {array} [tds] 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 ( settings, dataIn, tr, tds )
- {
- /* Create the object for storing information about this new row */
- var rowIdx = settings.aoData.length;
- var rowModel = $.extend( true, {}, DataTable.models.oRow, {
- src: tr ? 'dom' : 'data',
- idx: rowIdx
- } );
-
- rowModel._aData = dataIn;
- settings.aoData.push( rowModel );
-
- var columns = settings.aoColumns;
-
- for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
- {
- // Invalidate the column types as the new data needs to be revalidated
- columns[i].sType = null;
- }
-
- /* Add to the display array */
- settings.aiDisplayMaster.push( rowIdx );
-
- var id = settings.rowIdFn( dataIn );
- if ( id !== undefined ) {
- settings.aIds[ id ] = rowModel;
- }
-
- /* Create the DOM information, or register it if already present */
- if ( tr || ! settings.oFeatures.bDeferRender )
- {
- _fnCreateTr( settings, rowIdx, tr, tds );
- }
-
- return rowIdx;
- }
-
-
- /**
- * 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 );
- } );
- }
-
-
- /**
- * 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|search' 'sort|order')
- * @returns {*} Cell data
- * @memberof DataTable#oApi
- */
- function _fnGetCellData( settings, rowIdx, colIdx, type )
- {
- if (type === 'search') {
- type = 'filter';
- }
- else if (type === 'order') {
- type = 'sort';
- }
-
- var row = settings.aoData[rowIdx];
-
- if (! row) {
- return undefined;
- }
-
- var draw = settings.iDraw;
- var col = settings.aoColumns[colIdx];
- var rowData = row._aData;
- var defaultContent = col.sDefaultContent;
- var cellData = col.fnGetData( rowData, type, {
- settings: settings,
- row: rowIdx,
- col: colIdx
- } );
-
- // Allow for a node being returned for non-display types
- if (type !== 'display' && cellData && typeof cellData === 'object' && cellData.nodeName) {
- cellData = cellData.innerHTML;
- }
-
- 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 '';
- }
-
- if ( type === 'filter' ) {
- var fomatters = DataTable.ext.type.search;
-
- if ( fomatters[ col.sType ] ) {
- cellData = fomatters[ col.sType ]( cellData );
- }
- }
-
- 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
- } );
- }
-
- /**
- * Write a value to a cell
- * @param {*} td Cell
- * @param {*} val Value
- */
- function _fnWriteCell(td, val)
- {
- if (val && typeof val === 'object' && val.nodeName) {
- $(td)
- .empty()
- .append(val);
- }
- else {
- td.innerHTML = val;
- }
- }
-
-
- // 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 )
- {
- var parts = str.match(/(\\.|[^.])+/g) || [''];
-
- return parts.map( 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
- */
- var _fnGetObjectDataFn = DataTable.util.get;
-
-
- /**
- * 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
- */
- var _fnSetObjectDataFn = DataTable.util.set;
-
-
- /**
- * 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 = {};
- }
-
-
- /**
- * 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;
-
- // Remove the cached data for the row
- row._aSortData = null;
- row._aFilterData = null;
- row.displayData = null;
-
- // 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;
- var display = _fnGetRowDisplay(settings, rowIdx);
-
- if ( cells ) {
- if ( colIdx !== undefined ) {
- _fnWriteCell(cells[colIdx], display[colIdx]);
- }
- else {
- for ( i=0, ien=cells.length ; i<ien ; i++ ) {
- _fnWriteCell(cells[i], display[i]);
- }
- }
- }
- }
-
- // Column specific invalidation
- var cols = settings.aoColumns;
- if ( colIdx !== undefined ) {
- // Type - the data might have changed
- cols[ colIdx ].sType = null;
-
- // Max length string. Its a fairly cheep recalculation, so not worth
- // something more complicated
- cols[ colIdx ].maxLenString = null;
- }
- else {
- for ( i=0, ien=cols.length ; i<ien ; i++ ) {
- cols[i].sType = null;
- cols[i].maxLenString = 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, 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 = (cell.innerHTML).trim();
-
- 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
- };
- }
-
- /**
- * Render and cache a row's display data for the columns, if required
- * @returns
- */
- function _fnGetRowDisplay (settings, rowIdx) {
- let rowModal = settings.aoData[rowIdx];
- let columns = settings.aoColumns;
-
- if (! rowModal.displayData) {
- // Need to render and cache
- rowModal.displayData = [];
-
- for ( var colIdx=0, len=columns.length ; colIdx<len ; colIdx++ ) {
- rowModal.displayData.push(
- _fnGetCellData( settings, rowIdx, colIdx, 'display' )
- );
- }
- }
-
- return rowModal.displayData;
- }
-
- /**
- * 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, create,
- trClass = oSettings.oClasses.tbody.row;
-
- if ( row.nTr === null )
- {
- nTr = nTrIn || document.createElement('tr');
-
- row.nTr = nTr;
- row.anCells = cells;
-
- _addClass(nTr, trClass);
-
- /* 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];
- create = nTrIn && anTds[i] ? false : true;
-
- nTd = create ? document.createElement( oCol.sCellType ) : anTds[i];
-
- if (! nTd) {
- _fnLog( oSettings, 0, 'Incorrect column count', 18 );
- }
-
- nTd._DT_CellIndex = {
- row: iRow,
- column: i
- };
-
- cells.push( nTd );
-
- var display = _fnGetRowDisplay(oSettings, iRow);
-
- // Need to create the HTML if new, or if a rendering function is defined
- if (
- create ||
- (
- (oCol.mRender || oCol.mData !== i) &&
- (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')
- )
- ) {
- _fnWriteCell(nTd, display[i]);
- }
-
- // Visibility - add or remove as required
- if ( oCol.bVisible && create )
- {
- nTr.appendChild( nTd );
- }
- else if ( ! oCol.bVisible && ! create )
- {
- nTd.parentNode.removeChild( nTd );
- }
-
- if ( oCol.fnCreatedCell )
- {
- oCol.fnCreatedCell.call( oSettings.oInstance,
- nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
- );
- }
- }
-
- _fnCallbackFire( oSettings, 'aoRowCreatedCallback', 'row-created', [nTr, rowData, iRow, cells] );
- }
- else {
- _addClass(row.nTr, trClass);
- }
- }
-
-
- /**
- * 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( settings, side )
- {
- var classes = settings.oClasses;
- var columns = settings.aoColumns;
- var i, ien, row;
- var target = side === 'header'
- ? settings.nTHead
- : settings.nTFoot;
- var titleProp = side === 'header' ? 'sTitle' : side;
-
- // Footer might be defined
- if (! target) {
- return;
- }
-
- // If no cells yet and we have content for them, then create
- if (side === 'header' || _pluck(settings.aoColumns, titleProp).join('')) {
- row = $('tr', target);
-
- // Add a row if needed
- if (! row.length) {
- row = $('<tr/>').appendTo(target)
- }
-
- // Add the number of cells needed to make up to the number of columns
- if (row.length === 1) {
- var cells = $('td, th', row);
-
- for ( i=cells.length, ien=columns.length ; i<ien ; i++ ) {
- $('<th/>')
- .html( columns[i][titleProp] || '' )
- .appendTo( row );
- }
- }
- }
-
- var detected = _fnDetectHeader( settings, target, true );
-
- if (side === 'header') {
- settings.aoHeader = detected;
- }
- else {
- settings.aoFooter = detected;
- }
-
- // ARIA role for the rows
- $(target).children('tr').attr('role', 'row');
-
- // Every cell needs to be passed through the renderer
- $(target).children('tr').children('th, td')
- .each( function () {
- _fnRenderer( settings, side )(
- settings, $(this), classes
- );
- } );
- }
-
- /**
- * Build a layout structure for a header or footer
- *
- * @param {*} settings DataTables settings
- * @param {*} source Source layout array
- * @param {*} incColumns What columns should be included
- * @returns Layout array
- */
- function _fnHeaderLayout( settings, source, incColumns )
- {
- var row, column, cell;
- var local = [];
- var structure = [];
- var columns = settings.aoColumns;
- var columnCount = columns.length;
- var rowspan, colspan;
-
- if ( ! source ) {
- return;
- }
-
- // Default is to work on only visible columns
- if ( ! incColumns ) {
- incColumns = _range(columnCount)
- .filter(function (idx) {
- return columns[idx].bVisible;
- });
- }
-
- // Make a copy of the master layout array, but with only the columns we want
- for ( row=0 ; row<source.length ; row++ ) {
- // Remove any columns we haven't selected
- local[row] = source[row].slice().filter(function (cell, i) {
- return incColumns.includes(i);
- });
-
- // Prep the structure array - it needs an element for each row
- structure.push( [] );
- }
-
- for ( row=0 ; row<local.length ; row++ ) {
- for ( column=0 ; column<local[row].length ; column++ ) {
- rowspan = 1;
- colspan = 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 ( structure[row][column] === undefined ) {
- cell = local[row][column].cell;
-
- // Expand for rowspan
- while (
- local[row+rowspan] !== undefined &&
- local[row][column].cell == local[row+rowspan][column].cell
- ) {
- structure[row+rowspan][column] = null;
- rowspan++;
- }
-
- // And for colspan
- while (
- local[row][column+colspan] !== undefined &&
- local[row][column].cell == local[row][column+colspan].cell
- ) {
- // Which also needs to go over rows
- for ( var k=0 ; k<rowspan ; k++ ) {
- structure[row+k][column+colspan] = null;
- }
-
- colspan++;
- }
-
- var titleSpan = $('span.dt-column-title', cell);
-
- structure[row][column] = {
- cell: cell,
- colspan: colspan,
- rowspan: rowspan,
- title: titleSpan.length
- ? titleSpan.html()
- : $(cell).html()
- };
- }
- }
- }
-
- return structure;
- }
-
-
- /**
- * Draw the header (or footer) element based on the column visibility states.
- *
- * @param object oSettings dataTables settings object
- * @param array aoSource Layout array from _fnDetectHeader
- * @memberof DataTable#oApi
- */
- function _fnDrawHead( settings, source )
- {
- var layout = _fnHeaderLayout(settings, source);
- var tr, n;
-
- for ( var row=0 ; row<source.length ; row++ ) {
- tr = source[row].row;
-
- // All cells are going to be replaced, so empty out the row
- // Can't use $().empty() as that kills event handlers
- if (tr) {
- while( (n = tr.firstChild) ) {
- tr.removeChild( n );
- }
- }
-
- for ( var column=0 ; column<layout[row].length ; column++ ) {
- var point = layout[row][column];
-
- if (point) {
- $(point.cell)
- .appendTo(tr)
- .attr('rowspan', point.rowspan)
- .attr('colspan', point.colspan);
- }
- }
- }
- }
-
-
- /**
- * Insert the required TR nodes into the table for display
- * @param {object} oSettings dataTables settings object
- * @param ajaxComplete true after ajax call to complete rendering
- * @memberof DataTable#oApi
- */
- function _fnDraw( oSettings, ajaxComplete )
- {
- // Allow for state saving and a custom start position
- _fnStart( 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 ( aPreDraw.indexOf(false) !== -1 )
- {
- _fnProcessingDisplay( oSettings, false );
- return;
- }
-
- var anRows = [];
- var iRowCount = 0;
- var bServerSide = _fnDataSource( oSettings ) == 'ssp';
- var aiDisplay = oSettings.aiDisplay;
- var iDisplayStart = oSettings._iDisplayStart;
- var iDisplayEnd = oSettings.fnDisplayEnd();
- var columns = oSettings.aoColumns;
- var body = $(oSettings.nTBody);
-
- oSettings.bDrawing = true;
-
- /* Server-side processing draw intercept */
- if ( !bServerSide )
- {
- oSettings.iDraw++;
- }
- else if ( !oSettings.bDestroying && !ajaxComplete)
- {
- // Show loading message for server-side processing
- if (oSettings.iDraw === 0) {
- body.empty().append(_emptyRow(oSettings));
- }
-
- _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;
-
- // Add various classes as needed
- for (var i=0 ; i<columns.length ; i++) {
- var col = columns[i];
- var td = aoData.anCells[i];
-
- _addClass(td, _ext.type.className[col.sType]); // auto class
- _addClass(td, col.sClass); // column class
- _addClass(td, oSettings.oClasses.tbody.cell); // all cells
- }
-
- // 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, iDataIndex] );
-
- anRows.push( nRow );
- iRowCount++;
- }
- }
- else
- {
- anRows[ 0 ] = _emptyRow(oSettings);
- }
-
- /* 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 ] );
-
- // replaceChildren is faster, but only became widespread in 2020,
- // so a fall back in jQuery is provided for older browsers.
- if (body[0].replaceChildren) {
- body[0].replaceChildren.apply(body[0], anRows);
- }
- else {
- body.children().detach();
- body.append( $(anRows) );
- }
-
- // Empty table needs a specific class
- $(oSettings.nTableWrapper).toggleClass('dt-empty-footer', $('tr', oSettings.nTFoot).length === 0);
-
- /* Call all required callback functions for the end of a draw */
- _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings], true );
-
- /* 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, recompute )
- {
- var
- features = settings.oFeatures,
- sort = features.bSort,
- filter = features.bFilter;
-
- if (recompute === undefined || recompute === true) {
- 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;
- }
-
-
- /*
- * Table is empty - create a row with an empty message in it
- */
- function _emptyRow ( settings ) {
- var oLang = settings.oLanguage;
- var zero = oLang.sZeroRecords;
- var dataSrc = _fnDataSource( settings );
-
- if (
- (settings.iDraw < 1 && dataSrc === 'ssp') ||
- (settings.iDraw <= 1 && dataSrc === 'ajax')
- ) {
- zero = oLang.sLoadingRecords;
- }
- else if ( oLang.sEmptyTable && settings.fnRecordsTotal() === 0 )
- {
- zero = oLang.sEmptyTable;
- }
-
- return $( '<tr/>' )
- .append( $('<td />', {
- 'colSpan': _fnVisbleColumns( settings ),
- 'class': settings.oClasses.empty.row
- } ).html( zero ) )[0];
- }
-
-
- /**
- * Convert a `layout` object given by a user to the object structure needed
- * for the renderer. This is done twice, once for above and once for below
- * the table. Ordering must also be considered.
- *
- * @param {*} settings DataTables settings object
- * @param {*} layout Layout object to convert
- * @param {string} side `top` or `bottom`
- * @returns Converted array structure - one item for each row.
- */
- function _layoutArray ( settings, layout, side )
- {
- var groups = {};
-
- // Combine into like groups (e.g. `top`, `top2`, etc)
- $.each( layout, function ( pos, val ) {
- if (val === null) {
- return;
- }
-
- var splitPos = pos.replace(/([A-Z])/g, ' $1').split(' ');
-
- if ( ! groups[ splitPos[0] ] ) {
- groups[ splitPos[0] ] = {};
- }
-
- var align = splitPos.length === 1 ?
- 'full' :
- splitPos[1].toLowerCase();
- var group = groups[ splitPos[0] ];
- var groupRun = function (contents, innerVal) {
- // If it is an object, then there can be multiple features contained in it
- if ( $.isPlainObject( innerVal ) ) {
- Object.keys(innerVal).map(function (key) {
- contents.push( {
- feature: key,
- opts: innerVal[key]
- });
- });
- }
- else {
- contents.push(innerVal);
- }
- }
-
- // Transform to an object with a contents property
- if (! group[align] || ! group[align].contents) {
- group[align] = { contents: [] };
- }
-
- // Allow for an array or just a single object
- if ( Array.isArray(val)) {
- for (var i=0 ; i<val.length ; i++) {
- groupRun(group[align].contents, val[i]);
- }
- }
- else {
- groupRun(group[ align ].contents, val);
- }
-
- // And make contents an array
- if ( ! Array.isArray( group[ align ].contents ) ) {
- group[ align ].contents = [ group[ align ].contents ];
- }
- } );
-
- var filtered = Object.keys(groups)
- .map( function ( pos ) {
- // Filter to only the side we need
- if ( pos.indexOf(side) !== 0 ) {
- return null;
- }
-
- return {
- name: pos,
- val: groups[pos]
- };
- } )
- .filter( function (item) {
- return item !== null;
- });
-
- // Order by item identifier
- filtered.sort( function ( a, b ) {
- var order1 = a.name.replace(/[^0-9]/g, '') * 1;
- var order2 = b.name.replace(/[^0-9]/g, '') * 1;
-
- return order2 - order1;
- } );
-
- if ( side === 'bottom' ) {
- filtered.reverse();
- }
-
- // Split into rows
- var rows = [];
- for ( var i=0, ien=filtered.length ; i<ien ; i++ ) {
- if ( filtered[i].val.full ) {
- rows.push( { full: filtered[i].val.full } );
- _layoutResolve( settings, rows[ rows.length - 1 ] );
-
- delete filtered[i].val.full;
- }
-
- if ( Object.keys(filtered[i].val).length ) {
- rows.push( filtered[i].val );
- _layoutResolve( settings, rows[ rows.length - 1 ] );
- }
- }
-
- return rows;
- }
-
-
- /**
- * Convert the contents of a row's layout object to nodes that can be inserted
- * into the document by a renderer. Execute functions, look up plug-ins, etc.
- *
- * @param {*} settings DataTables settings object
- * @param {*} row Layout object for this row
- */
- function _layoutResolve( settings, row ) {
- var getFeature = function (feature, opts) {
- if ( ! _ext.features[ feature ] ) {
- _fnLog( settings, 0, 'Unknown feature: '+ feature );
- }
-
- return _ext.features[ feature ].apply( this, [settings, opts] );
- };
-
- var resolve = function ( item ) {
- var line = row[ item ].contents;
-
- for ( var i=0, ien=line.length ; i<ien ; i++ ) {
- if ( ! line[i] ) {
- continue;
- }
- else if ( typeof line[i] === 'string' ) {
- line[i] = getFeature( line[i], null );
- }
- else if ( $.isPlainObject(line[i]) ) {
- // If it's an object, it just has feature and opts properties from
- // the transform in _layoutArray
- line[i] = getFeature(line[i].feature, line[i].opts);
- }
- else if ( typeof line[i].node === 'function' ) {
- line[i] = line[i].node( settings );
- }
- else if ( typeof line[i] === 'function' ) {
- var inst = line[i]( settings );
-
- line[i] = typeof inst.node === 'function' ?
- inst.node() :
- inst;
- }
- }
- };
-
- $.each( row, function ( key ) {
- resolve( key );
- } );
- }
-
-
- /**
- * Add the options to the page HTML for the table
- * @param {object} settings DataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnAddOptionsHtml ( settings )
- {
- var classes = settings.oClasses;
- var table = $(settings.nTable);
-
- // Wrapper div around everything DataTables controls
- var insert = $('<div/>')
- .attr({
- id: settings.sTableId+'_wrapper',
- 'class': classes.container
- })
- .insertBefore(table);
-
- settings.nTableWrapper = insert[0];
-
- if (settings.sDom) {
- // Legacy
- _fnLayoutDom(settings, settings.sDom, insert);
- }
- else {
- var top = _layoutArray( settings, settings.layout, 'top' );
- var bottom = _layoutArray( settings, settings.layout, 'bottom' );
- var renderer = _fnRenderer( settings, 'layout' );
-
- // Everything above - the renderer will actually insert the contents into the document
- top.forEach(function (item) {
- renderer( settings, insert, item );
- });
-
- // The table - always the center of attention
- renderer( settings, insert, {
- full: {
- table: true,
- contents: [ _fnFeatureHtmlTable(settings) ]
- }
- } );
-
- // Everything below
- bottom.forEach(function (item) {
- renderer( settings, insert, item );
- });
- }
-
- // Processing floats on top, so it isn't an inserted feature
- _processingHtml( settings );
- }
-
- /**
- * Draw the table with the legacy DOM property
- * @param {*} settings DT settings object
- * @param {*} dom DOM string
- * @param {*} insert Insert point
- */
- function _fnLayoutDom( settings, dom, insert )
- {
- var parts = dom.match(/(".*?")|('.*?')|./g);
- var featureNode, option, newNode, next, attr;
-
- for ( var i=0 ; i<parts.length ; i++ ) {
- featureNode = null;
- option = parts[i];
-
- if ( option == '<' ) {
- // New container div
- newNode = $('<div/>');
-
- // Check to see if we should append an id and/or a class name to the container
- next = parts[i+1];
-
- if ( next[0] == "'" || next[0] == '"' ) {
- attr = next.replace(/['"]/g, '');
-
- var id = '', className;
-
- /* 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 ( attr.indexOf('.') != -1 ) {
- var split = attr.split('.');
-
- id = split[0];
- className = split[1];
- }
- else if ( attr[0] == "#" ) {
- id = attr;
- }
- else {
- className = attr;
- }
-
- newNode
- .attr('id', id.substring(1))
- .addClass(className);
-
- i++; // Move along the position array
- }
-
- insert.append( newNode );
- insert = newNode;
- }
- else if ( option == '>' ) {
- // End container div
- insert = insert.parent();
- }
- else if ( option == 't' ) {
- // Table
- featureNode = _fnFeatureHtmlTable( settings );
- }
- else
- {
- DataTable.ext.feature.forEach(function(feature) {
- if ( option == feature.cFeature ) {
- featureNode = feature.fnInit( settings );
- }
- });
- }
-
- // Add to the display
- if ( featureNode ) {
- insert.append( featureNode );
- }
- }
- }
-
-
- /**
- * 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 {node} thead The header/footer element for the table
- * @returns {array} Calculated layout array
- * @memberof DataTable#oApi
- */
- function _fnDetectHeader ( settings, thead, write )
- {
- var columns = settings.aoColumns;
- var rows = $(thead).children('tr');
- var row, cell;
- var i, k, l, iLen, shifted, column, colspan, rowspan;
- var isHeader = thead && thead.nodeName.toLowerCase() === 'thead';
- var layout = [];
- var unique;
- var shift = function ( a, i, j ) {
- var k = a[i];
- while ( k[j] ) {
- j++;
- }
- return j;
- };
-
- // We know how many rows there are in the layout - so prep it
- for ( i=0, iLen=rows.length ; i<iLen ; i++ ) {
- layout.push( [] );
- }
-
- for ( i=0, iLen=rows.length ; i<iLen ; i++ ) {
- row = rows[i];
- column = 0;
-
- // For every cell in the row..
- cell = row.firstChild;
- while ( cell ) {
- if (
- cell.nodeName.toUpperCase() == 'TD' ||
- cell.nodeName.toUpperCase() == 'TH'
- ) {
- var cols = [];
-
- // Get the col and rowspan attributes from the DOM and sanitise them
- colspan = cell.getAttribute('colspan') * 1;
- rowspan = cell.getAttribute('rowspan') * 1;
- colspan = (!colspan || colspan===0 || colspan===1) ? 1 : colspan;
- rowspan = (!rowspan || rowspan===0 || rowspan===1) ? 1 : rowspan;
-
- // There might be colspan cells already in this row, so shift our target
- // accordingly
- shifted = shift( layout, i, column );
-
- // Cache calculation for unique columns
- unique = colspan === 1 ?
- true :
- false;
-
- // Perform header setup
- if ( write ) {
- if (unique) {
- // Allow column options to be set from HTML attributes
- _fnColumnOptions( settings, shifted, $(cell).data() );
-
- // Get the width for the column. This can be defined from the
- // width attribute, style attribute or `columns.width` option
- var columnDef = columns[shifted];
- var width = cell.getAttribute('width') || null;
- var t = cell.style.width.match(/width:\s*(\d+[pxem%]+)/);
- if ( t ) {
- width = t[1];
- }
-
- columnDef.sWidthOrig = columnDef.sWidth || width;
-
- if (isHeader) {
- // Column title handling - can be user set, or read from the DOM
- // This happens before the render, so the original is still in place
- if ( columnDef.sTitle !== null && ! columnDef.autoTitle ) {
- cell.innerHTML = columnDef.sTitle;
- }
-
- if (! columnDef.sTitle && unique) {
- columnDef.sTitle = _stripHtml(cell.innerHTML);
- columnDef.autoTitle = true;
- }
- }
- else {
- // Footer specific operations
- if (columnDef.footer) {
- cell.innerHTML = columnDef.footer;
- }
- }
-
- // Fall back to the aria-label attribute on the table header if no ariaTitle is
- // provided.
- if (! columnDef.ariaTitle) {
- columnDef.ariaTitle = $(cell).attr("aria-label") || columnDef.sTitle;
- }
-
- // Column specific class names
- if ( columnDef.className ) {
- $(cell).addClass( columnDef.className );
- }
- }
-
- // Wrap the column title so we can write to it in future
- if ( $('span.dt-column-title', cell).length === 0) {
- $('<span>')
- .addClass('dt-column-title')
- .append(cell.childNodes)
- .appendTo(cell);
- }
-
- if ( isHeader && $('span.dt-column-order', cell).length === 0) {
- $('<span>')
- .addClass('dt-column-order')
- .appendTo(cell);
- }
- }
-
- // If there is col / rowspan, copy the information into the layout grid
- for ( l=0 ; l<colspan ; l++ ) {
- for ( k=0 ; k<rowspan ; k++ ) {
- layout[i+k][shifted+l] = {
- cell: cell,
- unique: unique
- };
-
- layout[i+k].row = row;
- }
-
- cols.push( shifted+l );
- }
-
- // Assign an attribute so spanning cells can still be identified
- // as belonging to a column
- cell.setAttribute('data-dt-column', _unique(cols).join(','));
- }
-
- cell = cell.nextSibling;
- }
- }
-
- return layout;
- }
-
- /**
- * Set the start position for draw
- * @param {object} oSettings dataTables settings object
- */
- function _fnStart( oSettings )
- {
- var bServerSide = _fnDataSource( oSettings ) == 'ssp';
- var iInitDisplayStart = oSettings.iInitDisplayStart;
-
- // 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;
- }
- }
-
- /**
- * 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 )
- {
- var ajaxData;
- var ajax = oSettings.ajax;
- var instance = oSettings.oInstance;
- var callback = function ( json ) {
- var status = oSettings.jqXHR
- ? oSettings.jqXHR.status
- : null;
-
- if ( json === null || (typeof status === 'number' && status == 204 ) ) {
- json = {};
- _fnAjaxDataSrc( oSettings, json, [] );
- }
-
- var error = json.error || json.sError;
- if ( error ) {
- _fnLog( oSettings, 0, error );
- }
-
- oSettings.json = json;
-
- _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR], true );
- fn( json );
- };
-
- if ( $.isPlainObject( ajax ) && ajax.data )
- {
- ajaxData = ajax.data;
-
- var newData = typeof ajaxData === 'function' ?
- 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 = typeof ajaxData === 'function' && 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 = {
- "url": typeof ajax === 'string' ?
- ajax :
- '',
- "data": data,
- "success": callback,
- "dataType": "json",
- "cache": false,
- "type": oSettings.sServerMethod,
- "error": function (xhr, error) {
- var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR], true );
-
- if ( ret.indexOf(true) === -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 );
- }
- };
-
- // If `ajax` option is an object, extend and override our default base
- if ( $.isPlainObject( ajax ) ) {
- $.extend( baseAjax, ajax )
- }
-
- // 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, baseAjax], true );
-
- if ( typeof ajax === 'function' )
- {
- // Is a function - let the caller define what needs to be done
- oSettings.jqXHR = ajax.call( instance, data, callback, oSettings );
- }
- else if (ajax.url === '') {
- // No url, so don't load any data. Just apply an empty data array
- // to the object for the callback.
- var empty = {};
-
- DataTable.util.set(ajax.dataSrc)(empty, []);
- callback(empty);
- }
- else {
- // Object to extend the base settings
- oSettings.jqXHR = $.ajax( baseAjax );
-
- // Restore for next time around
- if ( ajaxData ) {
- 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 )
- {
- settings.iDraw++;
- _fnProcessingDisplay( settings, true );
-
- _fnBuildAjax(
- settings,
- _fnAjaxParameters( settings ),
- function(json) {
- _fnAjaxUpdateDraw( settings, json );
- }
- );
- }
-
-
- /**
- * Build up the parameters in an object needed for a server-side processing
- * request.
- * @param {object} oSettings dataTables settings object
- * @returns {bool} block the table drawing or not
- * @memberof DataTable#oApi
- */
- function _fnAjaxParameters( settings )
- {
- var
- columns = settings.aoColumns,
- features = settings.oFeatures,
- preSearch = settings.oPreviousSearch,
- preColSearch = settings.aoPreSearchCols,
- colData = function ( idx, prop ) {
- return typeof columns[idx][prop] === 'function' ?
- 'function' :
- columns[idx][prop];
- };
-
- return {
- draw: settings.iDraw,
- columns: columns.map( function ( column, i ) {
- return {
- data: colData(i, 'mData'),
- name: column.sName,
- searchable: column.bSearchable,
- orderable: column.bSortable,
- search: {
- value: preColSearch[i].search,
- regex: preColSearch[i].regex,
- fixed: Object.keys(column.searchFixed).map( function(name) {
- return {
- name: name,
- term: column.searchFixed[name].toString()
- }
- })
- }
- };
- } ),
- order: _fnSortFlatten( settings ).map( function ( val ) {
- return {
- column: val.col,
- dir: val.dir,
- name: colData(val.col, 'sName')
- };
- } ),
- start: settings._iDisplayStart,
- length: features.bPaginate ?
- settings._iDisplayLength :
- -1,
- search: {
- value: preSearch.search,
- regex: preSearch.regex,
- fixed: Object.keys(settings.searchFixed).map( function(name) {
- return {
- name: name,
- term: settings.searchFixed[name].toString()
- }
- })
- }
- };
- }
-
-
- /**
- * 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 )
- {
- var data = _fnAjaxDataSrc(settings, json);
- var draw = _fnAjaxDataSrcParam(settings, 'draw', json);
- var recordsTotal = _fnAjaxDataSrcParam(settings, 'recordsTotal', json);
- var recordsFiltered = _fnAjaxDataSrcParam(settings, 'recordsFiltered', json);
-
- if ( draw !== undefined ) {
- // Protect against out of sequence returns
- if ( draw*1 < settings.iDraw ) {
- return;
- }
- settings.iDraw = draw * 1;
- }
-
- // No data in returned object, so rather than an array, we show an empty table
- if ( ! data ) {
- data = [];
- }
-
- _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();
-
- _fnDraw( settings, true );
- _fnInitComplete( settings );
- _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} settings dataTables settings object
- * @param {object} json Data source object / array from the server
- * @return {array} Array of data to use
- */
- function _fnAjaxDataSrc ( settings, json, write )
- {
- var dataProp = 'data';
-
- if ($.isPlainObject( settings.ajax ) && settings.ajax.dataSrc !== undefined) {
- // Could in inside a `dataSrc` object, or not!
- var dataSrc = settings.ajax.dataSrc;
-
- // string, function and object are valid types
- if (typeof dataSrc === 'string' || typeof dataSrc === 'function') {
- dataProp = dataSrc;
- }
- else if (dataSrc.data !== undefined) {
- dataProp = dataSrc.data;
- }
- }
-
- if ( ! write ) {
- if ( dataProp === 'data' ) {
- // If the default, then we still want to support the old style, and safely ignore
- // it if possible
- return json.aaData || json[dataProp];
- }
-
- return dataProp !== "" ?
- _fnGetObjectDataFn( dataProp )( json ) :
- json;
- }
-
- // set
- _fnSetObjectDataFn( dataProp )( json, write );
- }
-
- /**
- * Very similar to _fnAjaxDataSrc, but for the other SSP properties
- * @param {*} settings DataTables settings object
- * @param {*} param Target parameter
- * @param {*} json JSON data
- * @returns Resolved value
- */
- function _fnAjaxDataSrcParam (settings, param, json) {
- var dataSrc = $.isPlainObject( settings.ajax )
- ? settings.ajax.dataSrc
- : null;
-
- if (dataSrc && dataSrc[param]) {
- // Get from custom location
- return _fnGetObjectDataFn( dataSrc[param] )( json );
- }
-
- // else - Default behaviour
- var old = '';
-
- // Legacy support
- if (param === 'draw') {
- old = 'sEcho';
- }
- else if (param === 'recordsTotal') {
- old = 'iTotalRecords';
- }
- else if (param === 'recordsFiltered') {
- old = 'iTotalDisplayRecords';
- }
-
- return json[old] !== undefined
- ? json[old]
- : json[param];
- }
-
-
- /**
- * Filter the table using both the global filter and column based filtering
- * @param {object} settings dataTables settings object
- * @param {object} input search information
- * @memberof DataTable#oApi
- */
- function _fnFilterComplete ( settings, input )
- {
- var columnsSearch = settings.aoPreSearchCols;
-
- // 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( settings );
-
- // In server-side processing all filtering is done by the server, so no point hanging around here
- if ( _fnDataSource( settings ) != 'ssp' )
- {
- // Check if any of the rows were invalidated
- _fnFilterData( settings );
-
- // Start from the full data set
- settings.aiDisplay = settings.aiDisplayMaster.slice();
-
- // Global filter first
- _fnFilter( settings.aiDisplay, settings, input.search, input );
-
- $.each(settings.searchFixed, function (name, term) {
- _fnFilter(settings.aiDisplay, settings, term, {});
- });
-
- // Then individual column filters
- for ( var i=0 ; i<columnsSearch.length ; i++ )
- {
- var col = columnsSearch[i];
-
- _fnFilter(
- settings.aiDisplay,
- settings,
- col.search,
- col,
- i
- );
-
- $.each(settings.aoColumns[i].searchFixed, function (name, term) {
- _fnFilter(settings.aiDisplay, settings, term, {}, i);
- });
- }
-
- // And finally global filtering
- _fnFilterCustom( settings );
- }
-
- // Tell the draw function we have been filtering
- settings.bFiltered = true;
-
- _fnCallbackFire( settings, null, 'search', [settings] );
- }
-
-
- /**
- * Apply custom filtering functions
- *
- * This is legacy now that we have named functions, but it is widely used
- * from 1.x, so it is not yet deprecated.
- * @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;
- displayRows.push.apply(displayRows, rows);
- }
- }
-
-
- /**
- * Filter the data table based on user input and draw the table
- */
- function _fnFilter( searchRows, settings, input, options, column )
- {
- if ( input === '' ) {
- return;
- }
-
- var i = 0;
- var matched = [];
-
- // Search term can be a function, regex or string - if a string we apply our
- // smart filtering regex (assuming the options require that)
- var searchFunc = typeof input === 'function' ? input : null;
- var rpSearch = input instanceof RegExp
- ? input
- : searchFunc
- ? null
- : _fnFilterCreateSearch( input, options );
-
- // Then for each row, does the test pass. If not, lop the row from the array
- for (i=0 ; i<searchRows.length ; i++) {
- var row = settings.aoData[ searchRows[i] ];
- var data = column === undefined
- ? row._sFilterRow
- : row._aFilterData[ column ];
-
- if ( (searchFunc && searchFunc(data, row._aData, searchRows[i], column)) || (rpSearch && rpSearch.test(data)) ) {
- matched.push(searchRows[i]);
- }
- }
-
- // Mutate the searchRows array
- searchRows.length = matched.length;
-
- for (i=0 ; i<matched.length ; i++) {
- searchRows[i] = matched[i];
- }
- }
-
-
- /**
- * 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, inOpts )
- {
- var not = [];
- var options = $.extend({}, {
- boundary: false,
- caseInsensitive: true,
- exact: false,
- regex: false,
- smart: true
- }, inOpts);
-
- if (typeof search !== 'string') {
- search = search.toString();
- }
-
- // Remove diacritics if normalize is set up to do so
- search = _normalize(search);
-
- if (options.exact) {
- return new RegExp(
- '^'+_fnEscapeRegex(search)+'$',
- options.caseInsensitive ? 'i' : ''
- );
- }
-
- search = options.regex ?
- search :
- _fnEscapeRegex( search );
-
- if ( options.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. And a negative look around for
- * finding rows which don't contain a given string.
- *
- * So this is the sort of thing we want to generate:
- *
- * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
- */
- var parts = search.match( /!?["\u201C][^"\u201D]+["\u201D]|[^ ]+/g ) || [''];
- var a = parts.map( function ( word ) {
- var negative = false;
- var m;
-
- // Determine if it is a "does not include"
- if ( word.charAt(0) === '!' ) {
- negative = true;
- word = word.substring(1);
- }
-
- // Strip the quotes from around matched phrases
- if ( word.charAt(0) === '"' ) {
- m = word.match( /^"(.*)"$/ );
- word = m ? m[1] : word;
- }
- else if ( word.charAt(0) === '\u201C' ) {
- // Smart quote match (iPhone users)
- m = word.match( /^\u201C(.*)\u201D$/ );
- word = m ? m[1] : word;
- }
-
- // For our "not" case, we need to modify the string that is
- // allowed to match at the end of the expression.
- if (negative) {
- if (word.length > 1) {
- not.push('(?!'+word+')');
- }
-
- word = '';
- }
-
- return word.replace(/"/g, '');
- } );
-
- var match = not.length
- ? not.join('')
- : '';
-
- var boundary = options.boundary
- ? '\\b'
- : '';
-
- search = '^(?=.*?'+boundary+a.join( ')(?=.*?'+boundary )+')('+match+'.)*$';
- }
-
- return new RegExp( search, options.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 data = settings.aoData;
- var column;
- var j, jen, filterData, cellData, row;
- var wasInvalidated = false;
-
- for ( var rowIdx=0 ; rowIdx<data.length ; rowIdx++ ) {
- if (! data[rowIdx]) {
- continue;
- }
-
- row = data[rowIdx];
-
- if ( ! row._aFilterData ) {
- filterData = [];
-
- for ( j=0, jen=columns.length ; j<jen ; j++ ) {
- column = columns[j];
-
- if ( column.bSearchable ) {
- cellData = _fnGetCellData( settings, rowIdx, j, 'filter' );
-
- // Search in DataTables is string based
- 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 https://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\u2028]/g, '');
- }
-
- filterData.push( cellData );
- }
-
- row._aFilterData = filterData;
- row._sFilterRow = filterData.join(' ');
- wasInvalidated = true;
- }
- }
-
- return wasInvalidated;
- }
-
-
- /**
- * 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, iAjaxStart=settings.iInitDisplayStart;
-
- /* Ensure that the table data is fully initialised */
- if ( ! settings.bInitialised ) {
- setTimeout( function(){ _fnInitialise( settings ); }, 200 );
- return;
- }
-
- /* Build and draw the header / footer for the table */
- _fnBuildHead( settings, 'header' );
- _fnBuildHead( settings, 'footer' );
- _fnDrawHead( settings, settings.aoHeader );
- _fnDrawHead( settings, settings.aoFooter );
-
- // Enable features
- _fnAddOptionsHtml( settings );
- _fnSortInit( settings );
-
- _colGroup( settings );
-
- /* Okay to show that something is going on now */
- _fnProcessingDisplay( settings, true );
-
- _fnCallbackFire( settings, null, 'preInit', [settings], true );
-
- // 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 );
-
- var dataSrc = _fnDataSource( settings );
-
- // Server-side processing init complete is done by _fnAjaxUpdateDraw
- if ( dataSrc != 'ssp' ) {
- // 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 );
- }, settings );
- }
- else {
- _fnInitComplete( settings );
- _fnProcessingDisplay( settings, false );
- }
- }
- }
-
-
- /**
- * Draw the table for the first time, adding all required features
- * @param {object} settings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnInitComplete ( settings )
- {
- if (settings._bInitComplete) {
- return;
- }
-
- var args = [settings, settings.json];
-
- settings._bInitComplete = true;
-
- // Table is fully set up and we have data, so calculate the
- // column widths
- _fnAdjustColumnSizing( settings );
-
- _fnCallbackFire( settings, null, 'plugin-init', args, true );
- _fnCallbackFire( settings, 'aoInitComplete', 'init', args, true );
- }
-
- function _fnLengthChange ( settings, val )
- {
- var len = parseInt( val, 10 );
- settings._iDisplayLength = len;
-
- _fnLengthOverflow( settings );
-
- // Fire length change event
- _fnCallbackFire( settings, null, 'length', [settings, len] );
- }
-
- /**
- * 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 if ( action === 'ellipsis' )
- {
- return;
- }
- else
- {
- _fnLog( settings, 0, "Unknown paging action: "+action, 5 );
- }
-
- var changed = settings._iDisplayStart !== start;
- settings._iDisplayStart = start;
-
- _fnCallbackFire( settings, null, changed ? 'page' : 'page-nc', [settings] );
-
- if ( changed && redraw ) {
- _fnDraw( settings );
- }
-
- return changed;
- }
-
-
- /**
- * Generate the node required for the processing node
- * @param {object} settings DataTables settings object
- */
- function _processingHtml ( settings )
- {
- var table = settings.nTable;
- var scrolling = settings.oScroll.sX !== '' || settings.oScroll.sY !== '';
-
- if ( settings.oFeatures.bProcessing ) {
- var n = $('<div/>', {
- 'id': settings.sTableId + '_processing',
- 'class': settings.oClasses.processing.container,
- 'role': 'status'
- } )
- .html( settings.oLanguage.sProcessing )
- .append('<div><div></div><div></div><div></div><div></div></div>');
-
- // Different positioning depending on if scrolling is enabled or not
- if (scrolling) {
- n.prependTo( $('div.dt-scroll', settings.nTableWrapper) );
- }
- else {
- n.insertBefore( table );
- }
-
- $(table).on( 'processing.dt.DT', function (e, s, show) {
- n.css( 'display', show ? 'block' : 'none' );
- } );
- }
- }
-
-
- /**
- * Display or hide the processing indicator
- * @param {object} settings DataTables settings object
- * @param {bool} show Show the processing indicator (true) or not (false)
- */
- function _fnProcessingDisplay ( settings, show )
- {
- _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);
-
- // 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.scrolling;
- var caption = settings.captionNode;
- var captionSide = caption ? caption._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.container } )
- .append(
- $(_div, { 'class': classes.header.self } )
- .css( {
- overflow: 'hidden',
- position: 'relative',
- border: 0,
- width: scrollX ? size(scrollX) : '100%'
- } )
- .append(
- $(_div, { 'class': classes.header.inner } )
- .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.body } )
- .css( {
- position: 'relative',
- overflow: 'auto',
- width: size( scrollX )
- } )
- .append( table )
- );
-
- if ( footer ) {
- scroller.append(
- $(_div, { 'class': classes.footer.self } )
- .css( {
- overflow: 'hidden',
- border: 0,
- width: scrollX ? size(scrollX) : '100%'
- } )
- .append(
- $(_div, { 'class': classes.footer.inner } )
- .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
- $(scrollBody).on( 'scroll.DT', function () {
- var scrollLeft = this.scrollLeft;
-
- scrollHead.scrollLeft = scrollLeft;
-
- if ( footer ) {
- scrollFoot.scrollLeft = scrollLeft;
- }
- } );
-
- // When focus is put on the header cells, we might need to scroll the body
- $('th, td', scrollHead).on('focus', function () {
- var scrollLeft = scrollHead.scrollLeft;
-
- scrollBody.scrollLeft = scrollLeft;
-
- if ( footer ) {
- scrollBody.scrollLeft = scrollLeft;
- }
- });
-
- $(scrollBody).css('max-height', scrollY);
- if (! scroll.bCollapse) {
- $(scrollBody).css('height', scrollY);
- }
-
- settings.nScrollHead = scrollHead;
- settings.nScrollBody = scrollBody;
- settings.nScrollFoot = scrollFoot;
-
- // On redraw - align columns
- settings.aoDrawCallback.push(_fnScrollDraw);
-
- 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. Correct colgroup > col values if needed
- * 3. Copy colgroup > col over to header and footer
- * 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,
- barWidth = scroll.iBarWidth,
- divHeader = $(settings.nScrollHead),
- divHeaderInner = divHeader.children('div'),
- divHeaderTable = divHeaderInner.children('table'),
- divBodyEl = settings.nScrollBody,
- divBody = $(divBodyEl),
- divFooter = $(settings.nScrollFoot),
- divFooterInner = divFooter.children('div'),
- divFooterTable = divFooterInner.children('table'),
- header = $(settings.nTHead),
- table = $(settings.nTable),
- footer = settings.nTFoot && $('th, td', settings.nTFoot).length ? $(settings.nTFoot) : null,
- browser = settings.oBrowser,
- headerCopy, footerCopy;
-
- // 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();
-
- // Clone the current header and footer elements and then place it into the inner table
- headerCopy = header.clone().prependTo( table );
- headerCopy.find('th, td').removeAttr('tabindex');
- headerCopy.find('[id]').removeAttr('id');
-
- if ( footer ) {
- footerCopy = footer.clone().prependTo( table );
- footerCopy.find('[id]').removeAttr('id');
- }
-
- // 2. Correct colgroup > col values if needed
- // It is possible that the cell sizes are smaller than the content, so we need to
- // correct colgroup>col for such cases. This can happen if the auto width detection
- // uses a cell which has a longer string, but isn't the widest! For example
- // "Chief Executive Officer (CEO)" is the longest string in the demo, but
- // "Systems Administrator" is actually the widest string since it doesn't collapse.
- // Note the use of translating into a column index to get the `col` element. This
- // is because of Responsive which might remove `col` elements, knocking the alignment
- // of the indexes out.
- if (settings.aiDisplay.length) {
- // Get the column sizes from the first row in the table
- var colSizes = table.children('tbody').eq(0).children('tr').eq(0).children('th, td').map(function (vis) {
- return {
- idx: _fnVisibleToColumnIndex(settings, vis),
- width: $(this).outerWidth()
- }
- });
-
- // Check against what the colgroup > col is set to and correct if needed
- for (var i=0 ; i<colSizes.length ; i++) {
- var colEl = settings.aoColumns[ colSizes[i].idx ].colEl[0];
- var colWidth = colEl.style.width.replace('px', '');
-
- if (colWidth !== colSizes[i].width) {
- colEl.style.width = colSizes[i].width + 'px';
- }
- }
- }
-
- // 3. Copy the colgroup over to the header and footer
- divHeaderTable
- .find('colgroup')
- .remove();
-
- divHeaderTable.append(settings.colgroup.clone());
-
- if ( footer ) {
- divFooterTable
- .find('colgroup')
- .remove();
-
- divFooterTable.append(settings.colgroup.clone());
- }
-
- // "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.
- $('th, td', headerCopy).each(function () {
- $(this.childNodes).wrapAll('<div class="dt-scroll-sizing">');
- });
-
- if ( footer ) {
- $('th, td', footerCopy).each(function () {
- $(this.childNodes).wrapAll('<div class="dt-scroll-sizing">');
- });
- }
-
- // 4. Clean up
- // 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 isScrolling = Math.floor(table.height()) > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
- var paddingSide = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
-
- // Set the width's of the header and footer tables
- var outerWidth = table.outerWidth();
-
- divHeaderTable.css('width', _fnStringToCss( outerWidth ));
- divHeaderInner
- .css('width', _fnStringToCss( outerWidth ))
- .css(paddingSide, isScrolling ? barWidth+"px" : "0px");
-
- if ( footer ) {
- divFooterTable.css('width', _fnStringToCss( outerWidth ));
- divFooterInner
- .css('width', _fnStringToCss( outerWidth ))
- .css(paddingSide, isScrolling ? barWidth+"px" : "0px");
- }
-
- // Correct DOM ordering for colgroup - comes before the thead
- table.children('colgroup').prependTo(table);
-
- // Adjust the position of the header in case we loose the y-scrollbar
- divBody.trigger('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;
- }
- }
-
- /**
- * Calculate the width of columns for the table
- * @param {object} settings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnCalculateColumnWidths ( settings )
- {
- // Not interested in doing column width calculation if auto-width is disabled
- if (! settings.oFeatures.bAutoWidth) {
- return;
- }
-
- var
- table = settings.nTable,
- columns = settings.aoColumns,
- scroll = settings.oScroll,
- scrollY = scroll.sY,
- scrollX = scroll.sX,
- scrollXInner = scroll.sXInner,
- visibleColumns = _fnGetColumns( settings, 'bVisible' ),
- tableWidthAttr = table.getAttribute('width'), // from DOM element
- tableContainer = table.parentNode,
- i, column, columnIdx;
-
- var styleWidth = table.style.width;
- if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
- tableWidthAttr = styleWidth;
- }
-
- // Let plug-ins know that we are doing a recalc, in case they have changed any of the
- // visible columns their own way (e.g. Responsive uses display:none).
- _fnCallbackFire(
- settings,
- null,
- 'column-calc',
- {visible: visibleColumns},
- false
- );
-
- // 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.cloneNode())
- .css( 'visibility', 'hidden' )
- .removeAttr( 'id' );
-
- // Clean up the table body
- tmpTable.append('<tbody>')
- 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
- .append( $(settings.nTHead).clone() )
- .append( $(settings.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
- tmpTable.find('thead th, thead td').each( function () {
- // Get the `width` from the header layout
- var width = _fnColumnsSumWidth( settings, this, true, false );
-
- if ( width ) {
- this.style.width = width;
-
- // 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 ( scrollX ) {
- $( this ).append( $('<div/>').css( {
- width: width,
- margin: 0,
- padding: 0,
- border: 0,
- height: 1
- } ) );
- }
- }
- else {
- this.style.width = '';
- }
- } );
-
- // Find the widest piece of data for each column and put it into the table
- for ( i=0 ; i<visibleColumns.length ; i++ ) {
- columnIdx = visibleColumns[i];
- column = columns[ columnIdx ];
-
- var longest = _fnGetMaxLenString(settings, columnIdx);
- var autoClass = _ext.type.className[column.sType];
- var text = longest + column.sContentPadding;
- var insert = longest.indexOf('<') === -1
- ? document.createTextNode(text)
- : text
-
- $('<td/>')
- .addClass(autoClass)
- .addClass(column.sClass)
- .append(insert)
- .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
- var total = 0;
- var bodyCells = tmpTable.find('tbody tr').eq(0).children();
-
- for ( i=0 ; i<visibleColumns.length ; i++ ) {
- // Use getBounding for sub-pixel accuracy, which we then want to round up!
- var bounding = bodyCells[i].getBoundingClientRect().width;
-
- // Total is tracked to remove any sub-pixel errors as the outerWidth
- // of the table might not equal the total given here
- total += bounding;
-
- // Width for each column to use
- columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding );
- }
-
- 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) && ! settings._reszEvt ) {
- var bindResize = function () {
- $(window).on('resize.DT-'+settings.sInstance, DataTable.util.throttle( function () {
- if (! settings.bDestroying) {
- _fnAdjustColumnSizing( settings );
- }
- } ) );
- };
-
- bindResize();
-
- settings._reszEvt = true;
- }
- }
-
-
- /**
- * Get the maximum strlen for each data column
- * @param {object} settings dataTables settings object
- * @param {int} colIdx column of interest
- * @returns {string} string of the max length
- * @memberof DataTable#oApi
- */
- function _fnGetMaxLenString( settings, colIdx )
- {
- var column = settings.aoColumns[colIdx];
-
- if (! column.maxLenString) {
- var s, max='', maxLen = -1;
-
- for ( var i=0, ien=settings.aiDisplayMaster.length ; i<ien ; i++ ) {
- var rowIdx = settings.aiDisplayMaster[i];
- var data = _fnGetRowDisplay(settings, rowIdx)[colIdx];
-
- var cellString = data && typeof data === 'object' && data.nodeType
- ? data.innerHTML
- : data+'';
-
- // Remove id / name attributes from elements so they
- // don't interfere with existing elements
- cellString = cellString
- .replace(/id=".*?"/g, '')
- .replace(/name=".*?"/g, '');
-
- s = _stripHtml(cellString)
- .replace( / /g, ' ' );
-
- if ( s.length > maxLen ) {
- // We want the HTML in the string, but the length that
- // is important is the stripped string
- max = cellString;
- maxLen = s.length;
- }
- }
-
- column.maxLenString = max;
- }
-
- return column.maxLenString;
- }
-
-
- /**
- * 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;
- }
-
- /**
- * Re-insert the `col` elements for current visibility
- *
- * @param {*} settings DT settings
- */
- function _colGroup( settings ) {
- var cols = settings.aoColumns;
-
- settings.colgroup.empty();
-
- for (i=0 ; i<cols.length ; i++) {
- if (cols[i].bVisible) {
- settings.colgroup.append(cols[i].colEl);
- }
- }
- }
-
-
- function _fnSortInit( settings ) {
- var target = settings.nTHead;
- var headerRows = target.querySelectorAll('tr');
- var legacyTop = settings.bSortCellsTop;
- var notSelector = ':not([data-dt-order="disable"]):not([data-dt-order="icon-only"])';
-
- // Legacy support for `orderCellsTop`
- if (legacyTop === true) {
- target = headerRows[0];
- }
- else if (legacyTop === false) {
- target = headerRows[ headerRows.length - 1 ];
- }
-
- _fnSortAttachListener(
- settings,
- target,
- target === settings.nTHead
- ? 'tr'+notSelector+' th'+notSelector+', tr'+notSelector+' td'+notSelector
- : 'th'+notSelector+', td'+notSelector
- );
-
- // Need to resolve the user input array into our internal structure
- var order = [];
- _fnSortResolve( settings, order, settings.aaSorting );
-
- settings.aaSorting = order;
- }
-
-
- function _fnSortAttachListener(settings, node, selector, column, callback) {
- _fnBindAction( node, selector, function (e) {
- var run = false;
- var columns = column === undefined
- ? _fnColumnsFromHeader( e.target )
- : [column];
-
- if ( columns.length ) {
- for ( var i=0, ien=columns.length ; i<ien ; i++ ) {
- var ret = _fnSortAdd( settings, columns[i], i, e.shiftKey );
-
- if (ret !== false) {
- run = true;
- }
-
- // If the first entry is no sort, then subsequent
- // sort columns are ignored
- if (settings.aaSorting.length === 1 && settings.aaSorting[0][1] === '') {
- break;
- }
- }
-
- if (run) {
- _fnProcessingDisplay( settings, true );
-
- // Allow the processing display to show
- setTimeout( function () {
- _fnSort( settings );
- _fnSortDisplay( settings, settings.aiDisplay );
-
- // Sort processing done - redraw has its own processing display
- _fnProcessingDisplay( settings, false );
-
- _fnReDraw( settings, false, false );
-
- if (callback) {
- callback();
- }
- }, 0);
- }
- }
- } );
- }
-
- /**
- * Sort the display array to match the master's order
- * @param {*} settings
- */
- function _fnSortDisplay(settings, display) {
- if (display.length < 2) {
- return;
- }
-
- var master = settings.aiDisplayMaster;
- var masterMap = {};
- var map = {};
- var i;
-
- // Rather than needing an `indexOf` on master array, we can create a map
- for (i=0 ; i<master.length ; i++) {
- masterMap[master[i]] = i;
- }
-
- // And then cache what would be the indexOf fom the display
- for (i=0 ; i<display.length ; i++) {
- map[display[i]] = masterMap[display[i]];
- }
-
- display.sort(function(a, b){
- // Short version of this function is simply `master.indexOf(a) - master.indexOf(b);`
- return map[a] - map[b];
- });
- }
-
-
- function _fnSortResolve (settings, nestedSort, sort) {
- var push = function ( a ) {
- if ($.isPlainObject(a)) {
- if (a.idx !== undefined) {
- // Index based ordering
- nestedSort.push([a.idx, a.dir]);
- }
- else if (a.name) {
- // Name based ordering
- var cols = _pluck( settings.aoColumns, 'sName');
- var idx = cols.indexOf(a.name);
-
- if (idx !== -1) {
- nestedSort.push([idx, a.dir]);
- }
- }
- }
- else {
- // Plain column index and direction pair
- nestedSort.push(a);
- }
- };
-
- if ( $.isPlainObject(sort) ) {
- // Object
- push(sort);
- }
- else if ( sort.length && typeof sort[0] === 'number' ) {
- // 1D array
- push(sort);
- }
- else if ( sort.length ) {
- // 2D array
- for (var z=0; z<sort.length; z++) {
- push(sort[z]); // Object or array
- }
- }
- }
-
-
- function _fnSortFlatten ( settings )
- {
- var
- i, k, kLen,
- aSort = [],
- extSort = DataTable.ext.type.order,
- aoColumns = settings.aoColumns,
- aDataSort, iCol, sType, srcCol,
- fixed = settings.aaSortingFixed,
- fixedObj = $.isPlainObject( fixed ),
- nestedSort = [];
-
- if ( ! settings.oFeatures.bSort ) {
- return aSort;
- }
-
- // Build the sort array, with pre-fix and post-fix options if they have been
- // specified
- if ( Array.isArray( fixed ) ) {
- _fnSortResolve( settings, nestedSort, fixed );
- }
-
- if ( fixedObj && fixed.pre ) {
- _fnSortResolve( settings, nestedSort, fixed.pre );
- }
-
- _fnSortResolve( settings, nestedSort, settings.aaSorting );
-
- if (fixedObj && fixed.post ) {
- _fnSortResolve( settings, nestedSort, fixed.post );
- }
-
- for ( i=0 ; i<nestedSort.length ; i++ )
- {
- srcCol = nestedSort[i][0];
-
- if ( aoColumns[ srcCol ] ) {
- 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 = aoColumns[iCol].asSorting.indexOf(nestedSort[i][1]);
- }
-
- if ( nestedSort[i][1] ) {
- aSort.push( {
- src: srcCol,
- col: iCol,
- dir: nestedSort[i][1],
- index: nestedSort[i]._idx,
- type: sType,
- formatter: extSort[ sType+"-pre" ],
- sorter: extSort[ sType+"-"+nestedSort[i][1] ]
- } );
- }
- }
- }
- }
-
- return aSort;
- }
-
- /**
- * Change the order of the table
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnSort ( oSettings, col, dir )
- {
- var
- i, ien, iLen,
- aiOrig = [],
- extSort = DataTable.ext.type.order,
- aoData = oSettings.aoData,
- 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 );
-
- // Allow a specific column to be sorted, which will _not_ alter the display
- // master
- if (col !== undefined) {
- var srcCol = oSettings.aoColumns[col];
- aSort = [{
- src: col,
- col: col,
- dir: dir,
- index: 0,
- type: srcCol.sType,
- formatter: extSort[ srcCol.sType+"-pre" ],
- sorter: extSort[ srcCol.sType+"-"+dir ]
- }];
- displayMaster = displayMaster.slice();
- }
- else {
- aSort = _fnSortFlatten( oSettings );
- }
-
- for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
- sortCol = aSort[i];
-
- // 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 )
- {
- // Reset the initial positions on each pass so we get a stable sort
- for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
- aiOrig[ i ] = i;
- }
-
- // If the first sort is desc, then reverse the array to preserve original
- // order, just in reverse
- if (aSort.length && aSort[0].dir === 'desc') {
- aiOrig.reverse();
- }
-
- /* 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 test;
- * test = oSort['string-asc']('data11', 'data12');
- * if (test !== 0)
- * return test;
- * test = oSort['numeric-desc']('data21', 'data22');
- * if (test !== 0)
- * return test;
- * 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.
- */
- 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];
-
- // Data, which may have already been through a `-pre` function
- x = dataA[ sort.col ];
- y = dataB[ sort.col ];
-
- if (sort.sorter) {
- // If there is a custom sorter (`-asc` or `-desc`) for this
- // data type, use it
- test = sort.sorter(x, y);
-
- if ( test !== 0 ) {
- return test;
- }
- }
- else {
- // Otherwise, use generic sorting
- 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 if ( aSort.length === 0 ) {
- // Apply index order
- displayMaster.sort(function (x, y) {
- return x<y ? -1 : x>y ? 1 : 0;
- });
- }
-
- if (col === undefined) {
- // Tell the draw function that we have sorted the data
- oSettings.bSorted = true;
-
- _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort] );
- }
-
- return displayMaster;
- }
-
-
- /**
- * 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 {int} addIndex Counter
- * @param {boolean} [shift=false] Shift click add
- * @param {function} [callback] callback function
- * @memberof DataTable#oApi
- */
- function _fnSortAdd ( settings, colIdx, addIndex, shift )
- {
- 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 = asSorting.indexOf(a[1]);
- }
-
- return idx+1 < asSorting.length ?
- idx+1 :
- overflow ?
- null :
- 0;
- };
-
- if ( ! col.bSortable ) {
- return false;
- }
-
- // 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 ( (shift || addIndex) && settings.oFeatures.bSortMulti ) {
- // Are we already doing some kind of sort on this column?
- var sortIdx = _pluck(sorting, '0').indexOf(colIdx);
-
- 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 if (shift) {
- // No sort on this column yet, being added by shift click
- // add it as itself
- sorting.push( [ colIdx, asSorting[0], 0 ] );
- sorting[sorting.length-1]._idx = 0;
- }
- else {
- // No sort on this column yet, being added from a colspan
- // so add with same direction as first column
- sorting.push( [ colIdx, sorting[0][1], 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;
- }
- }
-
-
- /**
- * 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.order.position;
- 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, colIdx )
- {
- // Custom sorting function - provided by the sort data type
- var column = settings.aoColumns[ colIdx ];
- var customSort = DataTable.ext.order[ column.sSortDataType ];
- var customData;
-
- if ( customSort ) {
- customData = customSort.call( settings.oInstance, settings, colIdx,
- _fnColumnIndexToVisible( settings, colIdx )
- );
- }
-
- // Use / populate cache
- var row, cellData;
- var formatter = DataTable.ext.type.order[ column.sType+"-pre" ];
- var data = settings.aoData;
-
- for ( var rowIdx=0 ; rowIdx<data.length ; rowIdx++ ) {
- // Sparse array
- if (! data[rowIdx]) {
- continue;
- }
-
- row = data[rowIdx];
-
- if ( ! row._aSortData ) {
- row._aSortData = [];
- }
-
- if ( ! row._aSortData[colIdx] || customSort ) {
- cellData = customSort ?
- customData[rowIdx] : // If there was a custom sort function, use data from there
- _fnGetCellData( settings, rowIdx, colIdx, 'sort' );
-
- row._aSortData[ colIdx ] = formatter ?
- formatter( cellData, settings ) :
- cellData;
- }
- }
- }
-
-
- /**
- * State information for a table
- *
- * @param {*} settings
- * @returns State object
- */
- function _fnSaveState ( settings )
- {
- if (settings._bLoadingState) {
- return;
- }
-
- /* Store the interesting variables */
- var state = {
- time: +new Date(),
- start: settings._iDisplayStart,
- length: settings._iDisplayLength,
- order: $.extend( true, [], settings.aaSorting ),
- search: $.extend({}, settings.oPreviousSearch),
- columns: settings.aoColumns.map( function ( col, i ) {
- return {
- visible: col.bVisible,
- search: $.extend({}, settings.aoPreSearchCols[i])
- };
- } )
- };
-
- settings.oSavedState = state;
- _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] );
-
- if ( settings.oFeatures.bStateSave && !settings.bDestroying )
- {
- 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, init, callback )
- {
- if ( ! settings.oFeatures.bStateSave ) {
- callback();
- return;
- }
-
- var loaded = function(state) {
- _fnImplementState(settings, state, callback);
- }
-
- var state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded );
-
- if ( state !== undefined ) {
- _fnImplementState( settings, state, callback );
- }
- // otherwise, wait for the loaded callback to be executed
-
- return true;
- }
-
- function _fnImplementState ( settings, s, callback) {
- var i, ien;
- var columns = settings.aoColumns;
- settings._bLoadingState = true;
-
- // When StateRestore was introduced the state could now be implemented at any time
- // Not just initialisation. To do this an api instance is required in some places
- var api = settings._bInitComplete ? new DataTable.Api(settings) : null;
-
- if ( ! s || ! s.time ) {
- settings._bLoadingState = false;
- callback();
- return;
- }
-
- // Reject old data
- var duration = settings.iStateDuration;
- if ( duration > 0 && s.time < +new Date() - (duration*1000) ) {
- settings._bLoadingState = false;
- 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 ( abStateLoad.indexOf(false) !== -1 ) {
- settings._bLoadingState = false;
- callback();
- return;
- }
-
- // Number of columns have changed - all bets are off, no restore of settings
- if ( s.columns && columns.length !== s.columns.length ) {
- settings._bLoadingState = false;
- callback();
- return;
- }
-
- // Store the saved state so it might be accessed at any time
- settings.oLoadedState = $.extend( true, {}, s );
-
- // This is needed for ColReorder, which has to happen first to allow all
- // the stored indexes to be usable. It is not publicly documented.
- _fnCallbackFire( settings, null, 'stateLoadInit', [settings, s], true );
-
- // Page Length
- if ( s.length !== undefined ) {
- // If already initialised just set the value directly so that the select element is also updated
- if (api) {
- api.page.len(s.length)
- }
- else {
- settings._iDisplayLength = s.length;
- }
- }
-
- // Restore key features - todo - for 1.11 this needs to be done by
- // subscribed events
- if ( s.start !== undefined ) {
- if(api === null) {
- settings._iDisplayStart = s.start;
- settings.iInitDisplayStart = s.start;
- }
- else {
- _fnPageChange(settings, s.start/settings._iDisplayLength);
- }
- }
-
- // 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, 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 ) {
- // If the api is defined, the table has been initialised so we need to use it rather than internal settings
- if (api) {
- // Don't redraw the columns on every iteration of this loop, we will do this at the end instead
- api.column(i).visible(col.visible, false);
- }
- else {
- columns[i].bVisible = col.visible;
- }
- }
-
- // Search
- if ( col.search !== undefined ) {
- $.extend( settings.aoPreSearchCols[i], col.search );
- }
- }
-
- // If the api is defined then we need to adjust the columns once the visibility has been changed
- if (api) {
- api.columns.adjust();
- }
- }
-
- settings._bLoadingState = false;
- _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, s] );
- callback();
- }
-
- /**
- * 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 '+
- 'https://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, 'dt-error', [ settings, tn, msg ], true );
- }
-
- 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 ( Array.isArray( name ) ) {
- $.each( name, function (i, val) {
- if ( Array.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 ( Object.prototype.hasOwnProperty.call(extender, 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' && Array.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|string} selector Selector (for delegated events) or 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, selector, fn )
- {
- $(n)
- .on( 'click.DT', selector, function (e) {
- fn(e);
- } )
- .on( 'keypress.DT', selector, function (e){
- if ( e.which === 13 ) {
- e.preventDefault();
- fn(e);
- }
- } )
- .on( 'selectstart.DT', selector, function () {
- // Don't want a double click resulting in 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} settings dataTables settings object
- * @param {string} store Name of the array storage for the callbacks in oSettings
- * @param {function} fn Function to be called back
- * @memberof DataTable#oApi
- */
- function _fnCallbackReg( settings, store, fn )
- {
- if ( fn ) {
- settings[store].push(fn);
- }
- }
-
-
- /**
- * 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
- * @param {boolean} [bubbles] True if the event should bubble
- * @memberof DataTable#oApi
- */
- function _fnCallbackFire( settings, callbackArr, eventName, args, bubbles )
- {
- var ret = [];
-
- if ( callbackArr ) {
- ret = settings[callbackArr].slice().reverse().map( function (val) {
- return val.apply( settings.oInstance, args );
- } );
- }
-
- if ( eventName !== null) {
- var e = $.Event( eventName+'.dt' );
- var table = $(settings.nTable);
-
- // Expose the DataTables API on the event object for easy access
- e.dt = settings.api;
-
- table[bubbles ? 'trigger' : 'triggerHandler']( e, args );
-
- // If not yet attached to the document, trigger the event
- // on the body directly to sort of simulate the bubble
- if (bubbles && table.parents('body').length === 0) {
- $('body').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 ) {
- return 'ajax';
- }
- return 'dom';
- }
-
- /**
- * Common replacement for language strings
- *
- * @param {*} settings DT settings object
- * @param {*} str String with values to replace
- * @param {*} entries Plural number for _ENTRIES_ - can be undefined
- * @returns String
- */
- function _fnMacros ( settings, str, entries )
- {
- // 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(),
- max = settings.fnRecordsTotal(),
- 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, max ) ).
- 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 ) ) ).
- replace(/_ENTRIES_/g, settings.api.i18n('entries', '', entries) ).
- replace(/_ENTRIES-MAX_/g, settings.api.i18n('entries', '', max) ).
- replace(/_ENTRIES-TOTAL_/g, settings.api.i18n('entries', '', vis) );
- }
-
-
-
- /**
- * 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 = _pluck(settings, 'nTable');
-
- if ( ! mixed ) {
- return [];
- }
- else if ( mixed.nTable && mixed.oFeatures ) {
- // DataTables settings object
- return [ mixed ];
- }
- else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) {
- // Table node
- idx = tables.indexOf(mixed);
- 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).get();
- }
- else if ( mixed instanceof $ ) {
- // jQuery object (also DataTables instance)
- jq = mixed.get();
- }
-
- if ( jq ) {
- return settings.filter(function (v, idx) {
- return jq.includes(tables[idx]);
- });
- }
- };
-
-
- /**
- * 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 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.push.apply( settings, a );
- }
- };
-
- if ( Array.isArray( context ) ) {
- for ( var i=0, ien=context.length ; i<ien ; i++ ) {
- ctxSettings( context[i] );
- }
- }
- else {
- ctxSettings( context );
- }
-
- // Remove duplicates
- this.context = settings.length > 1
- ? _unique( settings )
- : settings;
-
- // Initial data
- if ( data ) {
- this.push.apply(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;
- },
-
- 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 = __arrayProto.filter.call( this, fn, this );
-
- return new _Api( this.context, a );
- },
-
- flatten: function ()
- {
- var a = [];
-
- return new _Api( this.context, a.concat.apply( a, this.toArray() ) );
- },
-
- get: function ( idx )
- {
- return this[ idx ];
- },
-
- join: __arrayProto.join,
-
- includes: function ( find ) {
- return this.indexOf( find ) === -1 ? false : true;
- },
-
- indexOf: __arrayProto.indexOf,
-
- 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 === 'every' || 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,
-
- length: 0,
-
- map: function ( fn )
- {
- var a = __arrayProto.map.call( this, fn, this );
-
- return new _Api( this.context, a );
- },
-
- pluck: function ( prop )
- {
- var fn = DataTable.util.get(prop);
-
- return this.map( function ( el ) {
- return fn(el);
- } );
- },
-
- pop: __arrayProto.pop,
-
- push: __arrayProto.push,
-
- reduce: __arrayProto.reduce,
-
- reduceRight: __arrayProto.reduceRight,
-
- 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,
-
- 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.toArray()) );
- },
-
- unshift: __arrayProto.unshift
- } );
-
-
- function _api_scope( scope, fn, struc ) {
- return function () {
- var ret = fn.apply( scope || this, arguments );
-
- // Method extension
- _Api.extend( ret, ret, struc.methodExt );
- return ret;
- };
- }
-
- function _api_find( src, name ) {
- for ( var i=0, ien=src.length ; i<ien ; i++ ) {
- if ( src[i].name === name ) {
- return src[i];
- }
- }
- return null;
- }
-
- window.__apiStruct = __apiStruct;
-
- _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,
- struct;
-
- for ( i=0, ien=ext.length ; i<ien ; i++ ) {
- struct = ext[i];
-
- if (struct.name === '__proto__') {
- continue;
- }
-
- // Value
- obj[ struct.name ] = struct.type === 'function' ?
- _api_scope( scope, struct.val, struct ) :
- struct.type === 'object' ?
- {} :
- struct.val;
-
- obj[ struct.name ].__dt_wrapper = true;
-
- // Property extension
- _Api.extend( scope, obj[ struct.name ], struct.propExt );
- }
- };
-
- // [
- // {
- // 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 ( Array.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;
-
- for ( i=0, ien=heir.length ; i<ien ; i++ ) {
- method = heir[i].indexOf('()') !== -1;
- key = method ?
- heir[i].replace('()', '') :
- heir[i];
-
- var src = _api_find( struct, key );
- if ( ! src ) {
- src = {
- name: key,
- val: {},
- methodExt: [],
- propExt: [],
- type: 'object'
- };
- struct.push( src );
- }
-
- if ( i === ien-1 ) {
- src.val = val;
- src.type = typeof val === 'function' ?
- 'function' :
- $.isPlainObject( val ) ?
- 'object' :
- 'other';
- }
- 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 ?
- Array.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 )
- {
- if ( Array.isArray(selector) ) {
- var result = [];
-
- selector.forEach(function (sel) {
- var inner = __table_selector(sel, a);
-
- result.push.apply(result, inner);
- });
-
- return result.filter( function (item) {
- return item;
- });
- }
-
- // 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 = a.map( function (el) {
- return el.nTable;
- } );
-
- return $(nodes)
- .filter( selector )
- .map( function () {
- // Need to translate back from the table node to the settings
- var idx = nodes.indexOf(this);
- 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 !== undefined && selector !== null ?
- 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;
- } );
-
- // Common methods, combined to reduce size
- [
- ['nodes', 'node', 'nTable'],
- ['body', 'body', 'nTBody'],
- ['header', 'header', 'nTHead'],
- ['footer', 'footer', 'nTFoot'],
- ].forEach(function (item) {
- _api_registerPlural(
- 'tables().' + item[0] + '()',
- 'table().' + item[1] + '()' ,
- function () {
- return this.iterator( 'table', function ( ctx ) {
- return ctx[item[2]];
- }, 1 );
- }
- );
- });
-
- // Structure methods
- [
- ['header', 'aoHeader'],
- ['footer', 'aoFooter'],
- ].forEach(function (item) {
- _api_register( 'table().' + item[0] + '.structure()' , function (selector) {
- var indexes = this.columns(selector).indexes().flatten();
- var ctx = this.context[0];
-
- return _fnHeaderLayout(ctx, ctx[item[1]], indexes);
- } );
- })
-
-
- _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
- return this.iterator( 'table', function ( ctx ) {
- return ctx.nTableWrapper;
- }, 1 );
- } );
-
- _api_register( 'tables().every()', function ( fn ) {
- var that = this;
-
- return this.iterator('table', function (s, i) {
- fn.call(that.table(i), i);
- });
- });
-
- _api_register( 'caption()', function ( value, side ) {
- var context = this.context;
-
- // Getter - return existing node's content
- if ( value === undefined ) {
- var caption = context[0].captionNode;
-
- return caption && context.length ?
- caption.innerHTML :
- null;
- }
-
- return this.iterator( 'table', function ( ctx ) {
- var table = $(ctx.nTable);
- var caption = $(ctx.captionNode);
- var container = $(ctx.nTableWrapper);
-
- // Create the node if it doesn't exist yet
- if ( ! caption.length ) {
- caption = $('<caption/>').html( value );
- ctx.captionNode = caption[0];
-
- // If side isn't set, we need to insert into the document to let the
- // CSS decide so we can read it back, otherwise there is no way to
- // know if the CSS would put it top or bottom for scrolling
- if (! side) {
- table.prepend(caption);
-
- side = caption.css('caption-side');
- }
- }
-
- caption.html( value );
-
- if ( side ) {
- caption.css( 'caption-side', side );
- caption[0]._captionSide = side;
- }
-
- if (container.find('div.dataTables_scroll').length) {
- var selector = (side === 'top' ? 'Head' : 'Foot');
-
- container.find('div.dataTables_scroll'+ selector +' table').prepend(caption);
- }
- else {
- table.prepend(caption);
- }
- }, 1 );
- } );
-
- _api_register( 'caption.node()', function () {
- var ctx = this.context;
-
- return ctx.length ? ctx[0].captionNode : null;
- } );
-
-
- /**
- * 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 () {
- 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 );
- _fnInitComplete( settings );
- _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 $.isPlainObject( ctx.ajax ) ?
- ctx.ajax.url :
- ctx.ajax;
- }
-
- // set
- return this.iterator( 'table', function ( settings ) {
- if ( $.isPlainObject( settings.ajax ) ) {
- settings.ajax.url = url;
- }
- else {
- settings.ajax = url;
- }
- } );
- } );
-
-
- /**
- * 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' ? (a[j]).trim() : a[j] );
-
- // Remove empty items
- res = res.filter( function (item) {
- return item !== null && item !== undefined;
- });
-
- 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 );
- };
-
-
- // Reduce the API instance to the first item found
- var _selector_first = function ( old )
- {
- let inst = new _Api(old.context[0]);
-
- // Use a push rather than passing to the constructor, since it will
- // merge arrays down automatically, which isn't what is wanted here
- if (old.length) {
- inst.push( old[0] );
- }
-
- inst.selector = old.selector;
-
- // Limit to a single row / column / cell
- if (inst.length && inst[0].length > 1) {
- inst[0].splice(1);
- }
-
- 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 ( page == 'current' ) {
- // Current page implies that order=current and filter=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' ) {
- if ( search == 'none') {
- a = displayMaster.slice();
- }
- else if ( search == 'applied' ) {
- a = displayFiltered.slice();
- }
- else if ( search == 'removed' ) {
- // O(n+m) solution by creating a hash map
- var displayFilteredMap = {};
-
- for ( i=0, ien=displayFiltered.length ; i<ien ; i++ ) {
- displayFilteredMap[displayFiltered[i]] = null;
- }
-
- displayMaster.forEach(function (item) {
- if (! Object.prototype.hasOwnProperty.call(displayFilteredMap, item)) {
- a.push(item);
- }
- });
- }
- }
- else if ( order == 'index' || order == 'original' ) {
- for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
- if (! settings.aoData[i]) {
- continue;
- }
-
- if ( search == 'none' ) {
- a.push( i );
- }
- else { // applied | removed
- tmp = displayFiltered.indexOf(i);
-
- if ((tmp === -1 && search == 'removed') ||
- (tmp >= 0 && search == 'applied') )
- {
- a.push( i );
- }
- }
- }
- }
- else if ( typeof order === 'number' ) {
- // Order the rows by the given column
- var ordered = _fnSort(settings, order, 'asc');
-
- if (search === 'none') {
- a = ordered;
- }
- else { // applied | removed
- for (i=0; i<ordered.length; i++) {
- tmp = displayFiltered.indexOf(ordered[i]);
-
- if ((tmp === -1 && search == 'removed') ||
- (tmp >= 0 && search == 'applied') )
- {
- a.push( ordered[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 aoData = settings.aoData;
-
- // 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 && rows.indexOf(selInt) !== -1 ) {
- // Selector - integer
- return [ selInt ];
- }
- else if ( sel === null || sel === undefined || sel === '' ) {
- // Selector - none
- return rows;
- }
-
- // Selector - function
- if ( typeof sel === 'function' ) {
- return rows.map( function (idx) {
- var row = aoData[ idx ];
- return sel( idx, row._aData, row.nTr ) ? idx : null;
- } );
- }
-
- // Selector - node
- if ( sel.nodeName ) {
- var rowIdx = sel._DT_RowIndex; // Property added by DT for fast lookup
- var cellIdx = sel._DT_CellIndex;
-
- if ( rowIdx !== undefined ) {
- // Make sure that the row is actually still present in the table
- return aoData[ rowIdx ] && aoData[ rowIdx ].nTr === sel ?
- [ rowIdx ] :
- [];
- }
- else if ( cellIdx ) {
- return aoData[ cellIdx.row ] && aoData[ cellIdx.row ].nTr === sel.parentNode ?
- [ cellIdx.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
- }
-
- // Get nodes in the order from the `rows` array with null values removed
- var nodes = _removeEmpty(
- _pluck_order( settings.aoData, rows, 'nTr' )
- );
-
- // 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();
- };
-
- var matched = _selector_run( 'row', selector, run, settings, opts );
-
- if (opts.order === 'current' || opts.order === 'applied') {
- _fnSortDisplay(settings, matched);
- }
-
- return matched;
- };
-
-
- _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 () {
- this.iterator( 'row', function ( settings, row ) {
- var data = settings.aoData;
- var rowData = data[ row ];
-
- // Delete from the display arrays
- var idx = settings.aiDisplayMaster.indexOf(row);
- if (idx !== -1) {
- settings.aiDisplayMaster.splice(idx, 1);
- }
-
- // 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 ];
- }
-
- data[row] = null;
- } );
-
- 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();
- modRows.push.apply(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 && this[0].length ?
- ctx[0].aoData[ this[0] ]._aData :
- undefined;
- }
-
- // Set
- var row = ctx[0].aoData[ this[0] ];
- row._aData = data;
-
- // If the DOM has an id, and the data source is an array
- if ( Array.isArray( data ) && row.nTr && row.nTr.id ) {
- _fnSetObjectDataFn( ctx[0].rowId )( data, row.nTr.id );
- }
-
- // Automatically invalidate
- _fnInvalidate( ctx[0], this[0], 'data' );
-
- return this;
- } );
-
-
- _api_register( 'row().node()', function () {
- var ctx = this.context;
-
- if (ctx.length && this.length && this[0].length) {
- var row = ctx[0].aoData[ this[0] ];
-
- if (row && row.nTr) {
- return row.nTr;
- }
- }
-
- return 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] );
- } );
-
-
- $(document).on('plugin-init.dt', function (e, context) {
- var api = new _Api( context );
-
- api.on( 'stateSaveParams.DT', function ( e, settings, d ) {
- // This could be more compact with the API, but it is a lot faster as a simple
- // internal loop
- var idFn = settings.rowIdFn;
- var rows = settings.aiDisplayMaster;
- var ids = [];
-
- for (var i=0 ; i<rows.length ; i++) {
- var rowIdx = rows[i];
- var data = settings.aoData[rowIdx];
-
- if (data._detailsShow) {
- ids.push( '#' + idFn(data._aData) );
- }
- }
-
- d.childRows = ids;
- });
-
- // For future state loads (e.g. with StateRestore)
- api.on( 'stateLoaded.DT', function (e, settings, state) {
- __details_state_load( api, state );
- });
-
- // And the initial load state
- __details_state_load( api, api.state.loaded() );
- });
-
- var __details_state_load = function (api, state)
- {
- if ( state && state.childRows ) {
- api
- .rows( state.childRows.map(function (id) {
- // Escape any `:` characters from the row id. Accounts for
- // already escaped characters.
- return id.replace(/([^:\\]*(?:\\.[^:\\]*)*):/g, "$1\\:");
- }) )
- .every( function () {
- _fnCallbackFire( api.settings()[0], null, 'requestChild', [ this ] )
- });
- }
- }
-
- 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 ( Array.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' ) {
- r.setAttribute( 'data-dt-row', row.idx );
- rows.push( r );
- }
- else {
- // Otherwise create a row with a wrapper
- var created = $('<tr><td></td></tr>')
- .attr( 'data-dt-row', row.idx )
- .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 );
- }
- };
-
-
- // Make state saving of child row details async to allow them to be batch processed
- var __details_state = DataTable.util.throttle(
- function (ctx) {
- _fnSaveState( ctx[0] )
- },
- 500
- );
-
-
- 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;
- $( row.nTr ).removeClass( 'dt-hasChild' );
- __details_state( ctx );
- }
- }
- };
-
-
- 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 );
- $( row.nTr ).addClass( 'dt-hasChild' );
- }
- else {
- row._details.detach();
- $( row.nTr ).removeClass( 'dt-hasChild' );
- }
-
- _fnCallbackFire( ctx[0], null, 'childRow', [ show, api.row( api[0] ) ] )
-
- __details_events( ctx[0] );
- __details_state( ctx );
- }
- }
- };
-
-
- var __details_events = function ( settings )
- {
- var api = new _Api( settings );
- var namespace = '.dt.DT_details';
- var drawEvent = 'draw'+namespace;
- var colvisEvent = 'column-sizing'+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 ) {
- 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 && row._details ) {
- row._details.each(function () {
- var el = $(this).children('td');
-
- if (el.length == 1) {
- el.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] && 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] ]
- ? 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 () { // 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|title|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, type ) {
- var a = [];
- for ( var row=0, ien=rows.length ; row<ien ; row++ ) {
- a.push( _fnGetCellData( settings, rows[row], column, type ) );
- }
- return a;
- };
-
-
- var __column_header = function ( settings, column, row ) {
- var header = settings.aoHeader;
- var target = row !== undefined
- ? row
- : settings.bSortCellsTop // legacy support
- ? 0
- : header.length - 1;
-
- return header[target][column].cell;
- };
-
- var __column_selector = function ( settings, selector, opts )
- {
- var
- columns = settings.aoColumns,
- names = _pluck( columns, 'sName' ),
- titles = _pluck( columns, 'sTitle' ),
- cells = DataTable.util.get('[].[].cell')(settings.aoHeader),
- nodes = _unique( _flatten([], cells) );
-
- 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 columns.map(function (col, idx) {
- return s(
- idx,
- __columnData( settings, idx, 0, 0, rows ),
- __column_header( settings, 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 = columns.map( 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 names.map( function (name, i) {
- return name === match[1] ? i : null;
- } );
-
- case 'title':
- // match by column title
- return titles.map( function (title, i) {
- return title === 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 _fnColumnsFromHeader( this ); // `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,
- cells, i, ien, tr;
-
- // Get
- if ( vis === undefined ) {
- return col.bVisible;
- }
-
- // Set
- // No change
- if ( col.bVisible === vis ) {
- return false;
- }
-
- if ( vis ) {
- // Insert column
- // Need to decide if we should use appendChild or insertBefore
- var insertBefore = _pluck(cols, 'bVisible').indexOf(true, column+1);
-
- for ( i=0, ien=data.length ; i<ien ; i++ ) {
- if (data[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;
-
- _colGroup(settings);
-
- return true;
- };
-
-
- _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 ( row ) {
- return this.iterator( 'column', function (settings, column) {
- return __column_header(settings, column, row);
- }, 1 );
- } );
-
- _api_registerPlural( 'columns().footer()', 'column().footer()', function ( row ) {
- return this.iterator( 'column', function ( settings, column ) {
- var footer = settings.aoFooter;
-
- if (! footer.length) {
- return null;
- }
-
- return settings.aoFooter[row !== undefined ? row : 0][column].cell;
- }, 1 );
- } );
-
- _api_registerPlural( 'columns().data()', 'column().data()', function () {
- return this.iterator( 'column-rows', __columnData, 1 );
- } );
-
- _api_registerPlural( 'columns().render()', 'column().render()', function ( type ) {
- return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
- return __columnData( settings, column, i, j, rows, type );
- }, 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().init()', 'column().init()', function () {
- return this.iterator( 'column', function ( settings, column ) {
- return settings.aoColumns[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().titles()', 'column().title()', function (title, row) {
- return this.iterator( 'column', function ( settings, column ) {
- // Argument shifting
- if (typeof title === 'number') {
- row = title;
- title = undefined;
- }
-
- var span = $('span.dt-column-title', this.column(column).header(row));
-
- if (title !== undefined) {
- span.html(title);
- return this;
- }
-
- return span.html();
- }, 1 );
- } );
-
- _api_registerPlural( 'columns().types()', 'column().type()', function () {
- return this.iterator( 'column', function ( settings, column ) {
- var type = settings.aoColumns[column].sType;
-
- // If the type was invalidated, then resolve it. This actually does
- // all columns at the moment. Would only happen once if getting all
- // column's data types.
- if (! type) {
- _fnColumnTypes(settings);
- }
-
- return type;
- }, 1 );
- } );
-
- _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {
- var that = this;
- var changed = [];
- var ret = this.iterator( 'column', function ( settings, column ) {
- if ( vis === undefined ) {
- return settings.aoColumns[ column ].bVisible;
- } // else
-
- if (__setColumnVis( settings, column, vis )) {
- changed.push(column);
- }
- } );
-
- // Group the column visibility changes
- if ( vis !== undefined ) {
- this.iterator( 'table', function ( settings ) {
- // Redraw the header after changes
- _fnDrawHead( settings, settings.aoHeader );
- _fnDrawHead( settings, settings.aoFooter );
-
- // Update colspan for no records display. Child rows and extensions will use their own
- // listeners to do this - only need to update the empty table item here
- if ( ! settings.aiDisplay.length ) {
- $(settings.nTBody).find('td[colspan]').attr('colspan', _fnVisbleColumns(settings));
- }
-
- _fnSaveState( settings );
-
- // Second loop once the first is done for events
- that.iterator( 'column', function ( settings, column ) {
- if (changed.includes(column)) {
- _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] );
- }
- } );
-
- if ( changed.length && (calc === undefined || calc) ) {
- that.columns.adjust();
- }
- });
- }
-
- return ret;
- } );
-
- _api_registerPlural( 'columns().widths()', 'column().width()', function () {
- // Injects a fake row into the table for just a moment so the widths can
- // be read, regardless of colspan in the header and rows being present in
- // the body
- var columns = this.columns(':visible').count();
- var row = $('<tr>').html('<td>' + Array(columns).join('</td><td>') + '</td>');
-
- $(this.table().body()).append(row);
-
- var widths = row.children().map(function () {
- return $(this).outerWidth();
- });
-
- row.remove();
-
- return this.iterator( 'column', function ( settings, column ) {
- var visIdx = _fnColumnIndexToVisible( settings, column );
-
- return visIdx !== null ? widths[visIdx] : 0;
- }, 1);
- } );
-
- _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 = $(_flatten( [], 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 ) ) {
- // Valid cell index and its in the array of selectable rows
- return s.column !== undefined && s.row !== undefined && rows.indexOf(s.row) !== -1 ?
- [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 ) );
- } );
- }
-
- // The default built in options need to apply to row and columns
- var internalOpts = opts ? {
- page: opts.page,
- order: opts.order,
- search: opts.search
- } : {};
-
- // Row + column selector
- var columns = this.columns( columnSelector, internalOpts );
- var rows = this.rows( rowSelector, internalOpts );
- var i, ien, j, jen;
-
- var cellsNoOpts = this.iterator( 'table', function ( settings, idx ) {
- var 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 );
-
- // There is currently only one extension which uses a cell selector extension
- // It is a _major_ performance drag to run this if it isn't needed, so this is
- // an extension specific check at the moment
- var cells = opts && opts.selected ?
- this.cells( cellsNoOpts, opts ) :
- cellsNoOpts;
-
- $.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;
- var args = Array.prototype.slice.call( arguments );
-
- 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 ( args.length > 1 ) {
- // Arguments passed in (list of 1D arrays)
- order = args;
- }
- // otherwise a 2D array was passed in
-
- return this.iterator( 'table', function ( settings ) {
- settings.aaSorting = Array.isArray(order) ? order.slice() : order;
- } );
- } );
-
-
- /**
- * 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 Array.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;
-
- if ( ! dir ) {
- return this.iterator( 'column', function ( settings, idx ) {
- var sort = _fnSortFlatten( settings );
-
- for ( var i=0, ien=sort.length ; i<ien ; i++ ) {
- if ( sort[i].col === idx ) {
- return sort[i].dir;
- }
- }
-
- return null;
- }, 1 );
- }
- else {
- return this.iterator( 'table', function ( settings, i ) {
- settings.aaSorting = that[i].map( function (col) {
- return [ col, dir ];
- } );
- } );
- }
- } );
-
- _api_registerPlural('columns().orderable()', 'column().orderable()', function ( directions ) {
- return this.iterator( 'column', function ( settings, idx ) {
- var col = settings.aoColumns[idx];
-
- return directions ?
- col.asSorting :
- col.bSortable;
- }, 1 );
- } );
-
-
- _api_register( 'processing()', function ( show ) {
- return this.iterator( 'table', function ( ctx ) {
- _fnProcessingDisplay( ctx, show );
- } );
- } );
-
-
- _api_register( 'search()', function ( input, regex, smart, caseInsen ) {
- var ctx = this.context;
-
- if ( input === undefined ) {
- // get
- return ctx.length !== 0 ?
- ctx[0].oPreviousSearch.search :
- undefined;
- }
-
- // set
- return this.iterator( 'table', function ( settings ) {
- if ( ! settings.oFeatures.bFilter ) {
- return;
- }
-
- if (typeof regex === 'object') {
- // New style options to pass to the search builder
- _fnFilterComplete( settings, $.extend( settings.oPreviousSearch, regex, {
- search: input
- } ) );
- }
- else {
- // Compat for the old options
- _fnFilterComplete( settings, $.extend( settings.oPreviousSearch, {
- search: input,
- regex: regex === null ? false : regex,
- smart: smart === null ? true : smart,
- caseInsensitive: caseInsen === null ? true : caseInsen
- } ) );
- }
- } );
- } );
-
- _api_register( 'search.fixed()', function ( name, search ) {
- var ret = this.iterator( true, 'table', function ( settings ) {
- var fixed = settings.searchFixed;
-
- if (! name) {
- return Object.keys(fixed)
- }
- else if (search === undefined) {
- return fixed[name];
- }
- else if (search === null) {
- delete fixed[name];
- }
- else {
- fixed[name] = search;
- }
-
- return this;
- } );
-
- return name !== undefined && search === undefined
- ? ret[0]
- : ret;
- } );
-
- _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 ].search;
- }
-
- // set
- if ( ! settings.oFeatures.bFilter ) {
- return;
- }
-
- if (typeof regex === 'object') {
- // New style options to pass to the search builder
- $.extend( preSearch[ column ], regex, {
- search: input
- } );
- }
- else {
- // Old style (with not all options available)
- $.extend( preSearch[ column ], {
- search: input,
- regex: regex === null ? false : regex,
- smart: smart === null ? true : smart,
- caseInsensitive: caseInsen === null ? true : caseInsen
- } );
- }
-
- _fnFilterComplete( settings, settings.oPreviousSearch );
- } );
- }
- );
-
- _api_register([
- 'columns().search.fixed()',
- 'column().search.fixed()'
- ],
- function ( name, search ) {
- var ret = this.iterator( true, 'column', function ( settings, colIdx ) {
- var fixed = settings.aoColumns[colIdx].searchFixed;
-
- if (! name) {
- return Object.keys(fixed)
- }
- else if (search === undefined) {
- return fixed[name];
- }
- else if (search === null) {
- delete fixed[name];
- }
- else {
- fixed[name] = search;
- }
-
- return this;
- } );
-
- return name !== undefined && search === undefined
- ? ret[0]
- : ret;
- }
- );
- /*
- * State API methods
- */
-
- _api_register( 'state()', function ( set, ignoreTime ) {
- // getter
- if ( ! set ) {
- return this.context.length ?
- this.context[0].oSavedState :
- null;
- }
-
- var setMutate = $.extend( true, {}, set );
-
- // setter
- return this.iterator( 'table', function ( settings ) {
- if ( ignoreTime !== false ) {
- setMutate.time = +new Date() + 100;
- }
-
- _fnImplementState( settings, setMutate, function(){} );
- } );
- } );
-
-
- _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 );
- } );
- } );
-
- /**
- * Set the jQuery or window object to be used by DataTables
- *
- * @param {*} module Library / container object
- * @param {string} [type] Library or container type `lib`, `win` or `datetime`.
- * If not provided, automatic detection is attempted.
- */
- DataTable.use = function (module, type) {
- if (type === 'lib' || module.fn) {
- $ = module;
- }
- else if (type == 'win' || module.document) {
- window = module;
- document = module.document;
- }
- else if (type === 'datetime' || module.type === 'DateTime') {
- DataTable.DateTime = module;
- }
- }
-
- /**
- * CommonJS factory function pass through. This will check if the arguments
- * given are a window object or a jQuery object. If so they are set
- * accordingly.
- * @param {*} root Window
- * @param {*} jq jQUery
- * @returns {boolean} Indicator
- */
- DataTable.factory = function (root, jq) {
- var is = false;
-
- // Test if the first parameter is a window object
- if (root && root.document) {
- window = root;
- document = root.document;
- }
-
- // Test if the second parameter is a jQuery object
- if (jq && jq.fn && jq.fn.jquery) {
- $ = jq;
- is = true;
- }
-
- return is;
- }
-
- /**
- * 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.
- * @param {string} [version2=current DataTables version] As above, but optional.
- * If not given the current DataTables version will be used.
- * @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 = function( version, version2 )
- {
- var aThis = version2 ?
- version2.split('.') :
- 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 = 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 = function ( visible )
- {
- var api = false;
-
- if ( $.isPlainObject( visible ) ) {
- api = visible.api;
- visible = visible.visible;
- }
-
- var a = DataTable.settings
- .filter( function (o) {
- return !visible || (visible && $(o.nTable).is(':visible'))
- ? true
- : false;
- } )
- .map( function (o) {
- 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] = args[0].split( /\s/ ).map( 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( 'error()', function (msg) {
- return this.iterator( 'table', function ( settings ) {
- _fnLog( settings, 0, msg );
- } );
- } );
-
-
- _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( 'trigger()', function ( name, args, bubbles ) {
- return this.iterator( 'table', function ( settings ) {
- return _fnCallbackFire( settings, null, name, args, bubbles );
- } ).flatten();
- } );
-
-
- _api_register( 'ready()', function ( fn ) {
- var ctx = this.context;
-
- // Get status of first table
- if (! fn) {
- return ctx.length
- ? (ctx[0]._bInitComplete || false)
- : null;
- }
-
- // Function to run either once the table becomes ready or
- // immediately if it is already ready.
- return this.tables().every(function () {
- if (this.context[0]._bInitComplete) {
- fn.call(this);
- }
- else {
- this.on('init', function () {
- fn.call(this);
- });
- }
- } );
- } );
-
-
- _api_register( 'destroy()', function ( remove ) {
- remove = remove || false;
-
- return this.iterator( 'table', function ( settings ) {
- 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 = settings.aoData.map( function (r) { return r ? r.nTr : null; } );
- var orderClasses = classes.order;
-
- // 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], true );
-
- // 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.colgroup.remove();
-
- settings.aaSorting = [];
- settings.aaSortingFixed = [];
- _fnSortingClasses( settings );
-
- $('th, td', thead)
- .removeClass(
- orderClasses.canAsc + ' ' +
- orderClasses.canDesc + ' ' +
- orderClasses.isAsc + ' ' +
- orderClasses.isDesc
- )
- .css('width', '');
-
- // Add the TR elements back into the table in their original order
- jqTbody.children().detach();
- jqTbody.append( rows );
-
- var orig = settings.nTableWrapper.parentNode;
- var insertBefore = settings.nTableWrapper.nextSibling;
-
- // 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, insertBefore );
-
- // 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.table );
- }
-
- /* Remove the settings object from the settings array */
- var idx = DataTable.settings.indexOf(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;
- var inst;
- var counter = 0;
-
- return this.iterator( 'every', function ( settings, selectedIdx, tableIdx ) {
- inst = api[ type ](selectedIdx, opts);
-
- if (type === 'cell') {
- fn.call(inst, inst[0][0].row, inst[0][0].column, tableIdx, counter);
- }
- else {
- fn.call(inst, selectedIdx, tableIdx, counter);
- }
-
- counter++;
- } );
- } );
- } );
-
-
- // 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 ( $.isPlainObject( resolved ) ) {
- resolved = plural !== undefined && resolved[ plural ] !== undefined ?
- resolved[ plural ] :
- resolved._;
- }
-
- return typeof resolved === 'string'
- ? resolved.replace( '%d', plural ) // nb: plural might be undefined,
- : resolved;
- } );
-
- /**
- * 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 https://semver.org/ for more information.
- * @member
- * @type string
- * @default Version number
- */
- DataTable.version = "2.0.7";
-
- /**
- * 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
- */
- "caseInsensitive": true,
-
- /**
- * Applied search term
- */
- "search": "",
-
- /**
- * 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.
- */
- "regex": false,
-
- /**
- * Flag to indicate if DataTables is to use its smart filtering or not.
- */
- "smart": true,
-
- /**
- * Flag to indicate if DataTables should only trigger a search when
- * the return key is pressed.
- */
- "return": false
- };
-
-
-
-
- /**
- * 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
- */
- "nTr": null,
-
- /**
- * Array of TD elements for each row. This is null until the row has been
- * created.
- */
- "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.
- */
- "_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.
- */
- "_aSortData": null,
-
- /**
- * Per cell filtering data cache. As per the sort data cache, used to
- * increase the performance of the filtering in DataTables
- */
- "_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)
- */
- "_sFilterRow": null,
-
- /**
- * 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.
- */
- "src": null,
-
- /**
- * Index in the aoData array. This saves an indexOf lookup when we have the
- * object, but want to know the index
- */
- "idx": -1,
-
- /**
- * Cached display value
- */
- displayData: null
- };
-
-
- /**
- * 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.
- */
- "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).
- */
- "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.
- */
- "asSorting": null,
-
- /**
- * Flag to indicate if the column is searchable, and thus should be included
- * in the filtering or not.
- */
- "bSearchable": null,
-
- /**
- * Flag to indicate if the column is sortable or not.
- */
- "bSortable": null,
-
- /**
- * Flag to indicate if the column is currently visible in the table or not
- */
- "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.
- */
- "_sManualType": null,
-
- /**
- * Flag to indicate if HTML5 data attributes should be used as the data
- * source for filtering or sorting. True is either are.
- */
- "_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.
- */
- "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
- */
- "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
- */
- "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.
- */
- "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.
- */
- "mRender": null,
-
- /**
- * The class to apply to all TD elements in the table's TBODY for the column
- */
- "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.
- */
- "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).
- */
- "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).
- */
- "sName": null,
-
- /**
- * Custom sorting data type - defines which of the available plug-ins in
- * afnSortData the custom sorting will use - if any is defined.
- */
- "sSortDataType": 'std',
-
- /**
- * Class to be applied to the header element when sorting on this column
- */
- "sSortingClass": null,
-
- /**
- * Title of the column - what is seen in the TH element (nTh).
- */
- "sTitle": null,
-
- /**
- * Column sorting and filtering type
- */
- "sType": null,
-
- /**
- * Width of the column
- */
- "sWidth": null,
-
- /**
- * Width of the column when it was first "encountered"
- */
- "sWidthOrig": null,
-
- /** Cached string which is the longest in the column */
- maxLenString: null,
-
- /**
- * Store for named searches
- */
- searchFixed: 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 add 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.
- */
- "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').
- */
- "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.
- */
- "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](https://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.
- *
- * * `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
- */
- "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.
- */
- "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).
- */
- "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>
- */
- "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.
- */
- "aoSearchCols": [],
-
-
- /**
- * 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`.
- */
- "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.
- */
- "bDeferRender": true,
-
-
- /**
- * 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.
- */
- "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}.
- */
- "bFilter": true,
-
- /**
- * Used only for compatiblity with DT1
- * @deprecated
- */
- "bInfo": true,
-
- /**
- * Used only for compatiblity with DT1
- * @deprecated
- */
- "bLengthChange": true,
-
- /**
- * Enable or disable pagination.
- */
- "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.
- */
- "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.
- */
- "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.
- */
- "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.
- */
- "bServerSide": false,
-
-
- /**
- * Enable or disable sorting of columns. Sorting of individual columns can be
- * disabled by the `sortable` option for each column.
- */
- "bSort": true,
-
-
- /**
- * Enable or display DataTables' ability to sort multiple columns at the
- * same time (activated by shift-click by the user).
- */
- "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.
- */
- "bSortCellsTop": null,
-
-
- /**
- * 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.
- */
- "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.
- */
- "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).
- */
- "fnCreatedRow": null,
-
-
- /**
- * This function is called on every 'draw' event, and allows you to
- * dynamically modify any aspect you want about the created DOM.
- */
- "fnDrawCallback": null,
-
-
- /**
- * Identical to fnHeaderCallback() but for the table footer this function
- * allows you to modify the table footer on every 'draw' event.
- */
- "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.
- */
- "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.
- */
- "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.
- */
- "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.
- */
- "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).
- */
- "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.
- */
- "fnRowCallback": 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.
- */
- "fnStateLoadCallback": function ( settings ) {
- try {
- return JSON.parse(
- (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
- 'DataTables_'+settings.sInstance+'_'+location.pathname
- )
- );
- } catch (e) {
- return {};
- }
- },
-
-
- /**
- * 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.
- */
- "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.
- */
- "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.
- */
- "fnStateSaveCallback": function ( settings, data ) {
- try {
- (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
- 'DataTables_'+settings.sInstance+'_'+location.pathname,
- JSON.stringify( data )
- );
- } catch (e) {
- // noop
- }
- },
-
-
- /**
- * 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.
- */
- "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.
- */
- "iStateDuration": 7200,
-
-
- /**
- * 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.
- */
- "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".
- */
- "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.
- */
- "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).
- */
- "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.
- */
- "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).
- */
- "oAria": {
- /**
- * ARIA label that is added to the table headers when the column may be sorted
- */
- "orderable": ": Activate to sort",
-
- /**
- * ARIA label that is added to the table headers when the column is currently being sorted
- */
- "orderableReverse": ": Activate to invert sorting",
-
- /**
- * ARIA label that is added to the table headers when the column is currently being
- * sorted and next step is to remove sorting
- */
- "orderableRemove": ": Activate to remove sorting",
-
- paginate: {
- first: 'First',
- last: 'Last',
- next: 'Next',
- previous: 'Previous'
- }
- },
-
- /**
- * Pagination string used by DataTables for the built-in pagination
- * control types.
- */
- "oPaginate": {
- /**
- * Label and character for first page button («)
- */
- "sFirst": "\u00AB",
-
- /**
- * Last page button (»)
- */
- "sLast": "\u00BB",
-
- /**
- * Next page button (›)
- */
- "sNext": "\u203A",
-
- /**
- * Previous page button (‹)
- */
- "sPrevious": "\u2039",
- },
-
- /**
- * Plural object for the data type the table is showing
- */
- entries: {
- _: "entries",
- 1: "entry"
- },
-
- /**
- * 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).
- */
- "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
- */
- "sInfo": "Showing _START_ to _END_ of _TOTAL_ _ENTRIES-TOTAL_",
-
-
- /**
- * Display information string for when the table is empty. Typically the
- * format of this string should match `info`.
- */
- "sInfoEmpty": "Showing 0 to 0 of 0 _ENTRIES-TOTAL_",
-
-
- /**
- * 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.
- */
- "sInfoFiltered": "(filtered from _MAX_ total _ENTRIES-MAX_)",
-
-
- /**
- * 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.
- */
- "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.
- */
- "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.
- */
- "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.
- */
- "sLengthMenu": "_MENU_ _ENTRIES_ per page",
-
-
- /**
- * 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.
- */
- "sLoadingRecords": "Loading...",
-
-
- /**
- * Text which is displayed when the table is processing a user action
- * (usually a sort command or similar).
- */
- "sProcessing": "",
-
-
- /**
- * 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.
- */
- "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.
- */
- "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).
- */
- "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.
- */
- "oSearch": $.extend( {}, DataTable.models.oSearch ),
-
-
- /**
- * Table and control layout. This replaces the legacy `dom` option.
- */
- layout: {
- topStart: 'pageLength',
- topEnd: 'search',
- bottomStart: 'info',
- bottomEnd: 'paging'
- },
-
-
- /**
- * Legacy DOM layout option
- */
- "sDom": null,
-
-
- /**
- * 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.
- */
- "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
- */
- "sPaginationType": "full_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.
- */
- "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).
- */
- "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).
- */
- "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.
- */
- "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
- */
- "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.
- */
- "rowId": "DT_RowId",
-
-
- /**
- * Caption value
- */
- "caption": null
- };
-
- _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.
- */
- "aDataSort": null,
- "iDataSort": -1,
-
- ariaTitle: '',
-
-
- /**
- * 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.
- */
- "asSorting": [ 'asc', 'desc', '' ],
-
-
- /**
- * Enable or disable filtering on the data in this column.
- */
- "bSearchable": true,
-
-
- /**
- * Enable or disable ordering on this column.
- */
- "bSortable": true,
-
-
- /**
- * Enable or disable the display of this column.
- */
- "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.
- */
- "fnCreatedCell": null,
-
-
- /**
- * 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.
- */
- "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.
- */
- "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).
- */
- "sCellType": "td",
-
-
- /**
- * Class to give to each cell in this column.
- */
- "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!
- */
- "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).
- */
- "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).
- */
- "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.
- */
- "sSortDataType": "std",
-
-
- /**
- * The title of this column.
- */
- "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.
- */
- "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.
- */
- "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.
- */
- DataTable.models.oSettings = {
- /**
- * Primary features of DataTables and their enablement state.
- */
- "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}.
- */
- "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 for 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}.
- */
- "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}.
- */
- "bFilter": null,
-
- /**
- * Used only for compatiblity with DT1
- * @deprecated
- */
- "bInfo": true,
-
- /**
- * Used only for compatiblity with DT1
- * @deprecated
- */
- "bLengthChange": true,
-
- /**
- * 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}.
- */
- "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}.
- */
- "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}.
- */
- "bServerSide": null,
-
- /**
- * Sorting enablement flag.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- */
- "bSort": null,
-
- /**
- * Multi-column sorting
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- */
- "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}.
- */
- "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}.
- */
- "bStateSave": null
- },
-
-
- /**
- * Scrolling settings for a table.
- */
- "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}.
- */
- "bCollapse": null,
-
- /**
- * Width of the scrollbar for the web-browser's platform. Calculated
- * during table initialisation.
- */
- "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}.
- */
- "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}.
- * @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}.
- */
- "sY": null
- },
-
- /**
- * Language information for the table.
- */
- "oLanguage": {
- /**
- * Information callback function. See
- * {@link DataTable.defaults.fnInfoCallback}
- */
- "fnInfoCallback": null
- },
-
- /**
- * Browser support parameters
- */
- "oBrowser": {
- /**
- * 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).
- */
- "bScrollbarLeft": false,
-
- /**
- * Browser scrollbar width
- */
- "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>
- */
- "aanFeatures": [],
-
- /**
- * Store data information - see {@link DataTable.models.oRow} for detailed
- * information.
- */
- "aoData": [],
-
- /**
- * Array of indexes which are in the current display (after filtering etc)
- */
- "aiDisplay": [],
-
- /**
- * Array of indexes for display - no filtering
- */
- "aiDisplayMaster": [],
-
- /**
- * Map of row ids to data indexes
- */
- "aIds": {},
-
- /**
- * Store information about each column that is in use
- */
- "aoColumns": [],
-
- /**
- * Store information about the table's header
- */
- "aoHeader": [],
-
- /**
- * Store information about the table's footer
- */
- "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}.
- */
- "oPreviousSearch": {},
-
- /**
- * Store for named searches
- */
- searchFixed: {},
-
- /**
- * 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.
- */
- "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}.
- */
- "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}.
- */
- "aaSortingFixed": [],
-
- /**
- * If restoring a table - we should restore its width
- */
- "sDestroyWidth": 0,
-
- /**
- * Callback functions array for every time a row is inserted (i.e. on a draw).
- */
- "aoRowCallback": [],
-
- /**
- * Callback functions for the header on each draw.
- */
- "aoHeaderCallback": [],
-
- /**
- * Callback function for the footer on each draw.
- */
- "aoFooterCallback": [],
-
- /**
- * Array of callback functions for draw callback functions
- */
- "aoDrawCallback": [],
-
- /**
- * Array of callback functions for row created function
- */
- "aoRowCreatedCallback": [],
-
- /**
- * Callback functions for just before the table is redrawn. A return of
- * false will be used to cancel the draw.
- */
- "aoPreDrawCallback": [],
-
- /**
- * Callback functions for when the table has been initialised.
- */
- "aoInitComplete": [],
-
-
- /**
- * Callbacks for modifying the settings to be stored for state saving, prior to
- * saving state.
- */
- "aoStateSaveParams": [],
-
- /**
- * Callbacks for modifying the settings that have been stored for state saving
- * prior to using the stored values to restore the state.
- */
- "aoStateLoadParams": [],
-
- /**
- * Callbacks for operating on the settings object once the saved state has been
- * loaded
- */
- "aoStateLoaded": [],
-
- /**
- * Cache the table ID for quick access
- */
- "sTableId": "",
-
- /**
- * The TABLE node for the main table
- */
- "nTable": null,
-
- /**
- * Permanent ref to the thead element
- */
- "nTHead": null,
-
- /**
- * Permanent ref to the tfoot element - if it exists
- */
- "nTFoot": null,
-
- /**
- * Permanent ref to the tbody element
- */
- "nTBody": null,
-
- /**
- * Cache the wrapper node (contains all DataTables controlled elements)
- */
- "nTableWrapper": null,
-
- /**
- * Indicate if all required information has been read in
- */
- "bInitialised": false,
-
- /**
- * Information about open rows. Each object in the array has the parameters
- * 'nTr' and 'nParent'
- */
- "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}.
- */
- "sDom": null,
-
- /**
- * Search delay (in mS)
- */
- "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}.
- */
- "sPaginationType": "two_button",
-
- /**
- * Number of paging controls on the page. Only used for backwards compatibility
- */
- pagingControls: 0,
-
- /**
- * 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}.
- */
- "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>
- */
- "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>
- */
- "aoStateLoad": [],
-
- /**
- * State that was saved. Useful for back reference
- */
- "oSavedState": null,
-
- /**
- * State that was loaded. Useful for back reference
- */
- "oLoadedState": null,
-
- /**
- * Note if draw should be blocked while getting data
- */
- "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
- */
- "jqXHR": null,
-
- /**
- * JSON returned from the server in the last Ajax request
- */
- "json": undefined,
-
- /**
- * Data submitted as part of the last Ajax request
- */
- "oAjaxData": undefined,
-
- /**
- * 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}.
- */
- "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}.
- */
- "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}.
- */
- "aLengthMenu": null,
-
- /**
- * Counter for the draws that the table does. Also used as a tracker for
- * server-side processing
- */
- "iDraw": 0,
-
- /**
- * Indicate if a redraw is being done - useful for Ajax
- */
- "bDrawing": false,
-
- /**
- * Draw index (iDraw) of the last error when parsing the returned data
- */
- "iDrawError": -1,
-
- /**
- * Paging display length
- */
- "_iDisplayLength": 10,
-
- /**
- * Paging start point - aiDisplay index
- */
- "_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.
- */
- "_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.
- */
- "_iRecordsDisplay": 0,
-
- /**
- * The classes to use for the table
- */
- "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.
- * @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.
- * @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}.
- */
- "bSortCellsTop": null,
-
- /**
- * Initialisation object that is used for the table
- */
- "oInit": null,
-
- /**
- * Destroy callback functions - for plug-ins to attach themselves to the
- * destroy so they can clean up markup and events.
- */
- "aoDestroyCallback": [],
-
-
- /**
- * Get the number of records in the current record set, before filtering
- */
- "fnRecordsTotal": function ()
- {
- return _fnDataSource( this ) == 'ssp' ?
- this._iRecordsTotal * 1 :
- this.aiDisplayMaster.length;
- },
-
- /**
- * Get the number of records in the current record set, after filtering
- */
- "fnRecordsDisplay": function ()
- {
- return _fnDataSource( this ) == 'ssp' ?
- this._iRecordsDisplay * 1 :
- this.aiDisplay.length;
- },
-
- /**
- * Get the display end point - aiDisplay index
- */
- "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
- */
- "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.
- */
- "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
- */
- "aLastSort": [],
-
- /**
- * Stored plug-in instances
- */
- "oPlugins": {},
-
- /**
- * Function used to get a row's id from the row's data
- */
- "rowIdFn": null,
-
- /**
- * Data location where to store a row's id
- */
- "rowId": null,
-
- caption: '',
-
- captionNode: null,
-
- colgroup: 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
- */
-
-
- var extPagination = DataTable.ext.pager;
-
- // Paging buttons configuration
- $.extend( extPagination, {
- simple: function () {
- return [ 'previous', 'next' ];
- },
-
- full: function () {
- return [ 'first', 'previous', 'next', 'last' ];
- },
-
- numbers: function () {
- return [ 'numbers' ];
- },
-
- simple_numbers: function () {
- return [ 'previous', 'numbers', 'next' ];
- },
-
- full_numbers: function () {
- return [ 'first', 'previous', 'numbers', 'next', 'last' ];
- },
-
- first_last: function () {
- return ['first', 'last'];
- },
-
- first_last_numbers: function () {
- return ['first', 'numbers', 'last'];
- },
-
- // For testing and plug-ins to use
- _numbers: _pagingNumbers,
-
- // Number of number buttons - legacy, use `numbers` option for paging feature
- numbers_length: 7
- } );
-
-
- $.extend( true, DataTable.ext.renderer, {
- pagingButton: {
- _: function (settings, buttonType, content, active, disabled) {
- var classes = settings.oClasses.paging;
- var btnClasses = [classes.button];
- var btn;
-
- if (active) {
- btnClasses.push(classes.active);
- }
-
- if (disabled) {
- btnClasses.push(classes.disabled)
- }
-
- if (buttonType === 'ellipsis') {
- btn = $('<span class="ellipsis"></span>').html(content)[0];
- }
- else {
- btn = $('<button>', {
- class: btnClasses.join(' '),
- role: 'link',
- type: 'button'
- }).html(content);
- }
-
- return {
- display: btn,
- clicker: btn
- }
- }
- },
-
- pagingContainer: {
- _: function (settings, buttons) {
- // No wrapping element - just append directly to the host
- return buttons;
- }
- }
- } );
-
- // Common function to remove new lines, strip HTML and diacritic control
- var _filterString = function (stripHtml, normalize) {
- return function (str) {
- if (_empty(str) || typeof str !== 'string') {
- return str;
- }
-
- str = str.replace( _re_new_lines, " " );
-
- if (stripHtml) {
- str = _stripHtml(str);
- }
-
- if (normalize) {
- str = _normalize(str, false);
- }
-
- return str;
- };
- }
-
- /*
- * 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.
- */
-
- function __mldFnName(name) {
- return name.replace(/[\W]/g, '_')
- }
-
- // Common logic for moment, luxon or a date action
- function __mld( dt, momentFn, luxonFn, dateFn, arg1 ) {
- if (window.moment) {
- return dt[momentFn]( arg1 );
- }
- else if (window.luxon) {
- return dt[luxonFn]( arg1 );
- }
-
- return dateFn ? dt[dateFn]( arg1 ) : dt;
- }
-
-
- var __mlWarning = false;
- function __mldObj (d, format, locale) {
- var dt;
-
- if (window.moment) {
- dt = window.moment.utc( d, format, locale, true );
-
- if (! dt.isValid()) {
- return null;
- }
- }
- else if (window.luxon) {
- dt = format && typeof d === 'string'
- ? window.luxon.DateTime.fromFormat( d, format )
- : window.luxon.DateTime.fromISO( d );
-
- if (! dt.isValid) {
- return null;
- }
-
- dt.setLocale(locale);
- }
- else if (! format) {
- // No format given, must be ISO
- dt = new Date(d);
- }
- else {
- if (! __mlWarning) {
- alert('DataTables warning: Formatted date without Moment.js or Luxon - https://datatables.net/tn/17');
- }
-
- __mlWarning = true;
- }
-
- return dt;
- }
-
- // Wrapper for date, datetime and time which all operate the same way with the exception of
- // the output string for auto locale support
- function __mlHelper (localeString) {
- return function ( from, to, locale, def ) {
- // Luxon and Moment support
- // Argument shifting
- if ( arguments.length === 0 ) {
- locale = 'en';
- to = null; // means toLocaleString
- from = null; // means iso8601
- }
- else if ( arguments.length === 1 ) {
- locale = 'en';
- to = from;
- from = null;
- }
- else if ( arguments.length === 2 ) {
- locale = to;
- to = from;
- from = null;
- }
-
- var typeName = 'datetime' + (to ? '-' + __mldFnName(to) : '');
-
- // Add type detection and sorting specific to this date format - we need to be able to identify
- // date type columns as such, rather than as numbers in extensions. Hence the need for this.
- if (! DataTable.ext.type.order[typeName]) {
- DataTable.type(typeName, {
- detect: function (d) {
- // The renderer will give the value to type detect as the type!
- return d === typeName ? typeName : false;
- },
- order: {
- pre: function (d) {
- // The renderer gives us Moment, Luxon or Date obects for the sorting, all of which have a
- // `valueOf` which gives milliseconds epoch
- return d.valueOf();
- }
- },
- className: 'dt-right'
- });
- }
-
- return function ( d, type ) {
- // Allow for a default value
- if (d === null || d === undefined) {
- if (def === '--now') {
- // We treat everything as UTC further down, so no changes are
- // made, as such need to get the local date / time as if it were
- // UTC
- var local = new Date();
- d = new Date( Date.UTC(
- local.getFullYear(), local.getMonth(), local.getDate(),
- local.getHours(), local.getMinutes(), local.getSeconds()
- ) );
- }
- else {
- d = '';
- }
- }
-
- if (type === 'type') {
- // Typing uses the type name for fast matching
- return typeName;
- }
-
- if (d === '') {
- return type !== 'sort'
- ? ''
- : __mldObj('0000-01-01 00:00:00', null, locale);
- }
-
- // Shortcut. If `from` and `to` are the same, we are using the renderer to
- // format for ordering, not display - its already in the display format.
- if ( to !== null && from === to && type !== 'sort' && type !== 'type' && ! (d instanceof Date) ) {
- return d;
- }
-
- var dt = __mldObj(d, from, locale);
-
- if (dt === null) {
- return d;
- }
-
- if (type === 'sort') {
- return dt;
- }
-
- var formatted = to === null
- ? __mld(dt, 'toDate', 'toJSDate', '')[localeString]()
- : __mld(dt, 'format', 'toFormat', 'toISOString', to);
-
- // XSS protection
- return type === 'display' ?
- _escapeHtml( formatted ) :
- formatted;
- };
- }
- }
-
- // Based on locale, determine standard number formatting
- // Fallback for legacy browsers is US English
- var __thousands = ',';
- var __decimal = '.';
-
- if (window.Intl !== undefined) {
- try {
- var num = new Intl.NumberFormat().formatToParts(100000.1);
-
- for (var i=0 ; i<num.length ; i++) {
- if (num[i].type === 'group') {
- __thousands = num[i].value;
- }
- else if (num[i].type === 'decimal') {
- __decimal = num[i].value;
- }
- }
- }
- catch (e) {
- // noop
- }
- }
-
- // Formatted date time detection - use by declaring the formats you are going to use
- DataTable.datetime = function ( format, locale ) {
- var typeName = 'datetime-detect-' + __mldFnName(format);
-
- if (! locale) {
- locale = 'en';
- }
-
- if (! DataTable.ext.type.order[typeName]) {
- DataTable.type(typeName, {
- detect: function (d) {
- var dt = __mldObj(d, format, locale);
- return d === '' || dt ? typeName : false;
- },
- order: {
- pre: function (d) {
- return __mldObj(d, format, locale) || 0;
- }
- },
- className: 'dt-right'
- });
- }
- }
-
- /**
- * 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:
- *
- * * `moment` - Uses the MomentJS library to convert from a given format into another.
- * This renderer has three overloads:
- * * 1 parameter:
- * * `string` - Format to convert to (assumes input is ISO8601 and locale is `en`)
- * * 2 parameters:
- * * `string` - Format to convert from
- * * `string` - Format to convert to. Assumes `en` locale
- * * 3 parameters:
- * * `string` - Format to convert from
- * * `string` - Format to convert to
- * * `string` - Locale
- * * `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 = {
- date: __mlHelper('toLocaleDateString'),
- datetime: __mlHelper('toLocaleString'),
- time: __mlHelper('toLocaleTimeString'),
- number: function ( thousands, decimal, precision, prefix, postfix ) {
- // Auto locale detection
- if (thousands === null || thousands === undefined) {
- thousands = __thousands;
- }
-
- if (decimal === null || decimal === undefined) {
- decimal = __decimal;
- }
-
- return {
- display: function ( d ) {
- if ( typeof d !== 'number' && typeof d !== 'string' ) {
- return d;
- }
-
- if (d === '' || d === null) {
- return d;
- }
-
- var negative = d < 0 ? '-' : '';
- var flo = parseFloat( d );
- var abs = Math.abs(flo);
-
- // Scientific notation for large and small numbers
- if (abs >= 100000000000 || (abs < 0.0001 && abs !== 0) ) {
- var exp = flo.toExponential(precision).split(/e\+?/);
- return exp[0] + ' x 10<sup>' + exp[1] + '</sup>';
- }
-
- // 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 _escapeHtml( d );
- }
-
- flo = flo.toFixed( precision );
- d = Math.abs( flo );
-
- var intPart = parseInt( d, 10 );
- var floatPart = precision ?
- decimal+(d - intPart).toFixed( precision ).substring( 2 ):
- '';
-
- // If zero, then can't have a negative prefix
- if (intPart === 0 && parseFloat(floatPart) === 0) {
- negative = '';
- }
-
- return negative + (prefix||'') +
- intPart.toString().replace(
- /\B(?=(\d{3})+(?!\d))/g, thousands
- ) +
- floatPart +
- (postfix||'');
- }
- };
- },
-
- text: function () {
- return {
- display: _escapeHtml,
- filter: _escapeHtml
- };
- }
- };
-
-
- var _extTypes = DataTable.ext.type;
-
- // Get / set type
- DataTable.type = function (name, prop, val) {
- if (! prop) {
- return {
- className: _extTypes.className[name],
- detect: _extTypes.detect.find(function (fn) {
- return fn.name === name;
- }),
- order: {
- pre: _extTypes.order[name + '-pre'],
- asc: _extTypes.order[name + '-asc'],
- desc: _extTypes.order[name + '-desc']
- },
- render: _extTypes.render[name],
- search: _extTypes.search[name]
- };
- }
-
- var setProp = function(prop, propVal) {
- _extTypes[prop][name] = propVal;
- };
- var setDetect = function (fn) {
- // Wrap to allow the function to return `true` rather than
- // specifying the type name.
- var cb = function (d, s) {
- var ret = fn(d, s);
-
- return ret === true
- ? name
- : ret;
- };
- Object.defineProperty(cb, "name", {value: name});
-
- var idx = _extTypes.detect.findIndex(function (fn) {
- return fn.name === name;
- });
-
- if (idx === -1) {
- _extTypes.detect.unshift(cb);
- }
- else {
- _extTypes.detect.splice(idx, 1, cb);
- }
- };
- var setOrder = function (obj) {
- _extTypes.order[name + '-pre'] = obj.pre; // can be undefined
- _extTypes.order[name + '-asc'] = obj.asc; // can be undefined
- _extTypes.order[name + '-desc'] = obj.desc; // can be undefined
- };
-
- // prop is optional
- if (val === undefined) {
- val = prop;
- prop = null;
- }
-
- if (prop === 'className') {
- setProp('className', val);
- }
- else if (prop === 'detect') {
- setDetect(val);
- }
- else if (prop === 'order') {
- setOrder(val);
- }
- else if (prop === 'render') {
- setProp('render', val);
- }
- else if (prop === 'search') {
- setProp('search', val);
- }
- else if (! prop) {
- if (val.className) {
- setProp('className', val.className);
- }
-
- if (val.detect !== undefined) {
- setDetect(val.detect);
- }
-
- if (val.order) {
- setOrder(val.order);
- }
-
- if (val.render !== undefined) {
- setProp('render', val.render);
- }
-
- if (val.search !== undefined) {
- setProp('search', val.search);
- }
- }
- }
-
- // Get a list of types
- DataTable.types = function () {
- return _extTypes.detect.map(function (fn) {
- return fn.name;
- });
- };
-
- //
- // Built in data types
- //
-
- DataTable.type('string', {
- detect: function () {
- return 'string';
- },
- order: {
- 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();
- }
- },
- search: _filterString(false, true)
- });
-
-
- DataTable.type('html', {
- detect: function ( d ) {
- return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
- 'html' : null;
- },
- order: {
- pre: function ( a ) {
- return _empty(a) ?
- '' :
- a.replace ?
- _stripHtml(a).trim().toLowerCase() :
- a+'';
- }
- },
- search: _filterString(true, true)
- });
-
-
- DataTable.type('date', {
- className: 'dt-type-date',
- detect: function ( d )
- {
- // 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;
- },
- order: {
- pre: function ( d ) {
- var ts = Date.parse( d );
- return isNaN(ts) ? -Infinity : ts;
- }
- }
- });
-
-
- DataTable.type('html-num-fmt', {
- className: 'dt-type-numeric',
- detect: function ( d, settings )
- {
- var decimal = settings.oLanguage.sDecimal;
- return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt' : null;
- },
- order: {
- pre: function ( d, s ) {
- var dp = s.oLanguage.sDecimal;
- return __numericReplace( d, dp, _re_html, _re_formatted_numeric );
- }
- },
- search: _filterString(true, true)
- });
-
-
- DataTable.type('html-num', {
- className: 'dt-type-numeric',
- detect: function ( d, settings )
- {
- var decimal = settings.oLanguage.sDecimal;
- return _htmlNumeric( d, decimal ) ? 'html-num' : null;
- },
- order: {
- pre: function ( d, s ) {
- var dp = s.oLanguage.sDecimal;
- return __numericReplace( d, dp, _re_html );
- }
- },
- search: _filterString(true, true)
- });
-
-
- DataTable.type('num-fmt', {
- className: 'dt-type-numeric',
- detect: function ( d, settings )
- {
- var decimal = settings.oLanguage.sDecimal;
- return _isNumber( d, decimal, true ) ? 'num-fmt' : null;
- },
- order: {
- pre: function ( d, s ) {
- var dp = s.oLanguage.sDecimal;
- return __numericReplace( d, dp, _re_formatted_numeric );
- }
- }
- });
-
-
- DataTable.type('num', {
- className: 'dt-type-numeric',
- detect: function ( d, settings )
- {
- var decimal = settings.oLanguage.sDecimal;
- return _isNumber( d, decimal ) ? 'num' : null;
- },
- order: {
- pre: function (d, s) {
- var dp = s.oLanguage.sDecimal;
- return __numericReplace( d, dp );
- }
- }
- });
-
-
-
-
- var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
- if ( d !== 0 && (!d || d === '-') ) {
- return -Infinity;
- }
-
- var type = typeof d;
-
- if (type === 'number' || type === 'bigint') {
- return d;
- }
-
- // 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;
- };
-
-
- $.extend( true, DataTable.ext.renderer, {
- footer: {
- _: function ( settings, cell, classes ) {
- cell.addClass(classes.tfoot.cell);
- }
- },
-
- header: {
- _: function ( settings, cell, classes ) {
- cell.addClass(classes.thead.cell);
-
- if (! settings.oFeatures.bSort) {
- cell.addClass(classes.order.none);
- }
-
- var legacyTop = settings.bSortCellsTop;
- var headerRows = cell.closest('thead').find('tr');
- var rowIdx = cell.parent().index();
-
- // Conditions to not apply the ordering icons
- if (
- // Cells and rows which have the attribute to disable the icons
- cell.attr('data-dt-order') === 'disable' ||
- cell.parent().attr('data-dt-order') === 'disable' ||
-
- // Legacy support for `orderCellsTop`. If it is set, then cells
- // which are not in the top or bottom row of the header (depending
- // on the value) do not get the sorting classes applied to them
- (legacyTop === true && rowIdx !== 0) ||
- (legacyTop === false && rowIdx !== headerRows.length - 1)
- ) {
- return;
- }
-
- // 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 ) {
- if ( settings !== ctx ) { // need to check this this is the host
- return; // table, not a nested one
- }
-
- var orderClasses = classes.order;
- var columns = ctx.api.columns( cell );
- var col = settings.aoColumns[columns.flatten()[0]];
- var orderable = columns.orderable().includes(true);
- var ariaType = '';
- var indexes = columns.indexes();
- var sortDirs = columns.orderable(true).flatten();
- var orderedColumns = ',' + sorting.map( function (val) {
- return val.col;
- } ).join(',') + ',';
-
- cell
- .removeClass(
- orderClasses.isAsc +' '+
- orderClasses.isDesc
- )
- .toggleClass( orderClasses.none, ! orderable )
- .toggleClass( orderClasses.canAsc, orderable && sortDirs.includes('asc') )
- .toggleClass( orderClasses.canDesc, orderable && sortDirs.includes('desc') );
-
- var sortIdx = orderedColumns.indexOf( ',' + indexes.toArray().join(',') + ',' );
-
- if ( sortIdx !== -1 ) {
- // Get the ordering direction for the columns under this cell
- // Note that it is possible for a cell to be asc and desc sorting
- // (column spanning cells)
- var orderDirs = columns.order();
-
- cell.addClass(
- orderDirs.includes('asc') ? orderClasses.isAsc : '' +
- orderDirs.includes('desc') ? orderClasses.isDesc : ''
- );
- }
-
- // The ARIA spec says that only one column should be marked with aria-sort
- if ( sortIdx === 0 ) {
- var firstSort = sorting[0];
- var sortOrder = col.asSorting;
-
- cell.attr('aria-sort', firstSort.dir === 'asc' ? 'ascending' : 'descending');
-
- // Determine if the next click will remove sorting or change the sort
- ariaType = ! sortOrder[firstSort.index + 1] ? 'Remove' : 'Reverse';
- }
- else {
- cell.removeAttr('aria-sort');
- }
-
- cell.attr('aria-label', orderable
- ? col.ariaTitle + ctx.api.i18n('oAria.orderable' + ariaType)
- : col.ariaTitle
- );
-
- if (orderable) {
- cell.find('.dt-column-title').attr('role', 'button');
- cell.attr('tabindex', 0)
- }
- } );
- }
- },
-
- layout: {
- _: function ( settings, container, items ) {
- var row = $('<div/>')
- .addClass('dt-layout-row')
- .appendTo( container );
-
- $.each( items, function (key, val) {
- var klass = ! val.table ?
- 'dt-'+key+' ' :
- '';
-
- if (val.table) {
- row.addClass('dt-layout-table');
- }
-
- $('<div/>')
- .attr({
- id: val.id || null,
- "class": 'dt-layout-cell '+klass+(val.className || '')
- })
- .append( val.contents )
- .appendTo( row );
- } );
- }
- }
- } );
-
-
- DataTable.feature = {};
-
- // Third parameter is internal only!
- DataTable.feature.register = function ( name, cb, legacy ) {
- DataTable.ext.features[ name ] = cb;
-
- if (legacy) {
- _ext.feature.push({
- cFeature: legacy,
- fnInit: cb
- });
- }
- };
-
- DataTable.feature.register( 'info', function ( settings, opts ) {
- // For compatibility with the legacy `info` top level option
- if (! settings.oFeatures.bInfo) {
- return null;
- }
-
- var
- lang = settings.oLanguage,
- tid = settings.sTableId,
- n = $('<div/>', {
- 'class': settings.oClasses.info.container,
- } );
-
- opts = $.extend({
- callback: lang.fnInfoCallback,
- empty: lang.sInfoEmpty,
- postfix: lang.sInfoPostFix,
- search: lang.sInfoFiltered,
- text: lang.sInfo,
- }, opts);
-
-
- // Update display on each draw
- settings.aoDrawCallback.push(function (s) {
- _fnUpdateInfo(s, opts, n);
- });
-
- // For the first info display in the table, we add a callback and aria information.
- if (! settings._infoEl) {
- n.attr({
- 'aria-live': 'polite',
- id: tid+'_info',
- role: 'status'
- });
-
- // Table is described by our info div
- $(settings.nTable).attr( 'aria-describedby', tid+'_info' );
-
- settings._infoEl = n;
- }
-
- return n;
- }, 'i' );
-
- /**
- * Update the information elements in the display
- * @param {object} settings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnUpdateInfo ( settings, opts, node )
- {
- var
- start = settings._iDisplayStart+1,
- end = settings.fnDisplayEnd(),
- max = settings.fnRecordsTotal(),
- total = settings.fnRecordsDisplay(),
- out = total
- ? opts.text
- : opts.empty;
-
- if ( total !== max ) {
- // Record set after filtering
- out += ' ' + opts.search;
- }
-
- // Convert the macros
- out += opts.postfix;
- out = _fnMacros( settings, out );
-
- if ( opts.callback ) {
- out = opts.callback.call( settings.oInstance,
- settings, start, end, max, total, out
- );
- }
-
- node.html( out );
-
- _fnCallbackFire(settings, null, 'info', [settings, node[0], out]);
- }
-
- var __searchCounter = 0;
-
- // opts
- // - text
- // - placeholder
- DataTable.feature.register( 'search', function ( settings, opts ) {
- // Don't show the input if filtering isn't available on the table
- if (! settings.oFeatures.bFilter) {
- return null;
- }
-
- var classes = settings.oClasses.search;
- var tableId = settings.sTableId;
- var language = settings.oLanguage;
- var previousSearch = settings.oPreviousSearch;
- var input = '<input type="search" class="'+classes.input+'"/>';
-
- opts = $.extend({
- placeholder: language.sSearchPlaceholder,
- text: language.sSearch
- }, opts);
-
- // The _INPUT_ is optional - is appended if not present
- if (opts.text.indexOf('_INPUT_') === -1) {
- opts.text += '_INPUT_';
- }
-
- opts.text = _fnMacros(settings, opts.text);
-
- // We can put the <input> outside of the label if it is at the start or end
- // which helps improve accessability (not all screen readers like implicit
- // for elements).
- var end = opts.text.match(/_INPUT_$/);
- var start = opts.text.match(/^_INPUT_/);
- var removed = opts.text.replace(/_INPUT_/, '');
- var str = '<label>' + opts.text + '</label>';
-
- if (start) {
- str = '_INPUT_<label>' + removed + '</label>';
- }
- else if (end) {
- str = '<label>' + removed + '</label>_INPUT_';
- }
-
- var filter = $('<div>')
- .addClass(classes.container)
- .append(str.replace(/_INPUT_/, input));
-
- // add for and id to label and input
- filter.find('label').attr('for', 'dt-search-' + __searchCounter);
- filter.find('input').attr('id', 'dt-search-' + __searchCounter);
- __searchCounter++;
-
- var searchFn = function(event) {
- var val = this.value;
-
- if(previousSearch.return && event.key !== "Enter") {
- return;
- }
-
- /* Now do the filter */
- if ( val != previousSearch.search ) {
- previousSearch.search = val;
-
- _fnFilterComplete( settings, previousSearch );
-
- // Need to redraw, without resorting
- settings._iDisplayStart = 0;
- _fnDraw( settings );
- }
- };
-
- var searchDelay = settings.searchDelay !== null ?
- settings.searchDelay :
- 0;
-
- var jqFilter = $('input', filter)
- .val( previousSearch.search )
- .attr( 'placeholder', opts.placeholder )
- .on(
- 'keyup.DT search.DT input.DT paste.DT cut.DT',
- searchDelay ?
- DataTable.util.debounce( searchFn, searchDelay ) :
- searchFn
- )
- .on( 'mouseup.DT', function(e) {
- // Edge fix! Edge 17 does not trigger anything other than mouse events when clicking
- // on the clear icon (Edge bug 17584515). This is safe in other browsers as `searchFn`
- // checks the value to see if it has changed. In other browsers it won't have.
- setTimeout( function () {
- searchFn.call(jqFilter[0], e);
- }, 10);
- } )
- .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 && jqFilter[0] !== document.activeElement ) {
- jqFilter.val( typeof previousSearch.search !== 'function'
- ? previousSearch.search
- : ''
- );
- }
- } );
-
- return filter;
- }, 'f' );
-
- // opts
- // - type - button configuration
- // - buttons - number of buttons to show - must be odd
- DataTable.feature.register( 'paging', function ( settings, opts ) {
- // Don't show the paging input if the table doesn't have paging enabled
- if (! settings.oFeatures.bPaginate) {
- return null;
- }
-
- opts = $.extend({
- buttons: DataTable.ext.pager.numbers_length,
- type: settings.sPaginationType,
- boundaryNumbers: true
- }, opts);
-
- // To be removed in 2.1
- if (opts.numbers) {
- opts.buttons = opts.numbers;
- }
-
- var host = $('<div/>').addClass( settings.oClasses.paging.container + ' paging_' + opts.type );
- var draw = function () {
- _pagingDraw(settings, host, opts);
- };
-
- settings.aoDrawCallback.push(draw);
-
- // Responsive redraw of paging control
- $(settings.nTable).on('column-sizing.dt.DT', draw);
-
- return host;
- }, 'p' );
-
- function _pagingDraw(settings, host, opts) {
- if (! settings._bInitComplete) {
- return;
- }
-
- var
- plugin = DataTable.ext.pager[ opts.type ],
- aria = settings.oLanguage.oAria.paginate || {},
- 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()
- .map(function (val) {
- return val === 'numbers'
- ? _pagingNumbers(page, pages, opts.buttons, opts.boundaryNumbers)
- : val;
- })
- .flat();
-
- var buttonEls = [];
-
- for (var i=0 ; i<buttons.length ; i++) {
- var button = buttons[i];
-
- var btnInfo = _pagingButtonInfo(settings, button, page, pages);
- var btn = _fnRenderer( settings, 'pagingButton' )(
- settings,
- button,
- btnInfo.display,
- btnInfo.active,
- btnInfo.disabled
- );
-
- // Common attributes
- $(btn.clicker).attr({
- 'aria-controls': settings.sTableId,
- 'aria-disabled': btnInfo.disabled ? 'true' : null,
- 'aria-current': btnInfo.active ? 'page' : null,
- 'aria-label': aria[ button ],
- 'data-dt-idx': button,
- 'tabIndex': btnInfo.disabled ? -1 : settings.iTabIndex,
- });
-
- if (typeof button !== 'number') {
- $(btn.clicker).addClass(button);
- }
-
- _fnBindAction(
- btn.clicker, {action: button}, function(e) {
- e.preventDefault();
-
- _fnPageChange( settings, e.data.action, true );
- }
- );
-
- buttonEls.push(btn.display);
- }
-
- var wrapped = _fnRenderer(settings, 'pagingContainer')(
- settings, buttonEls
- );
-
- var activeEl = host.find(document.activeElement).data('dt-idx');
-
- host.empty().append(wrapped);
-
- if ( activeEl !== undefined ) {
- host.find( '[data-dt-idx='+activeEl+']' ).trigger('focus');
- }
-
- // Responsive - check if the buttons are over two lines based on the
- // height of the buttons and the container.
- if (
- buttonEls.length && // any buttons
- opts.numbers > 1 && // prevent infinite
- $(host).height() >= ($(buttonEls[0]).outerHeight() * 2) - 10
- ) {
- _pagingDraw(settings, host, $.extend({}, opts, { numbers: opts.numbers - 2 }));
- }
- }
-
- /**
- * Get properties for a button based on the current paging state of the table
- *
- * @param {*} settings DT settings object
- * @param {*} button The button type in question
- * @param {*} page Table's current page
- * @param {*} pages Number of pages
- * @returns Info object
- */
- function _pagingButtonInfo(settings, button, page, pages) {
- var lang = settings.oLanguage.oPaginate;
- var o = {
- display: '',
- active: false,
- disabled: false
- };
-
- switch ( button ) {
- case 'ellipsis':
- o.display = '…';
- o.disabled = true;
- break;
-
- case 'first':
- o.display = lang.sFirst;
-
- if (page === 0) {
- o.disabled = true;
- }
- break;
-
- case 'previous':
- o.display = lang.sPrevious;
-
- if ( page === 0 ) {
- o.disabled = true;
- }
- break;
-
- case 'next':
- o.display = lang.sNext;
-
- if ( pages === 0 || page === pages-1 ) {
- o.disabled = true;
- }
- break;
-
- case 'last':
- o.display = lang.sLast;
-
- if ( pages === 0 || page === pages-1 ) {
- o.disabled = true;
- }
- break;
-
- default:
- if ( typeof button === 'number' ) {
- o.display = settings.fnFormatNumber( button + 1 );
-
- if (page === button) {
- o.active = true;
- }
- }
- break;
- }
-
- return o;
- }
-
- /**
- * Compute what number buttons to show in the paging control
- *
- * @param {*} page Current page
- * @param {*} pages Total number of pages
- * @param {*} buttons Target number of number buttons
- * @param {boolean} addFirstLast Indicate if page 1 and end should be included
- * @returns Buttons to show
- */
- function _pagingNumbers ( page, pages, buttons, addFirstLast ) {
- var
- numbers = [],
- half = Math.floor(buttons / 2),
- before = addFirstLast ? 2 : 1,
- after = addFirstLast ? 1 : 0;
-
- if ( pages <= buttons ) {
- numbers = _range(0, pages);
- }
- else if (buttons === 1) {
- // Single button - current page only
- numbers = [page];
- }
- else if (buttons === 3) {
- // Special logic for just three buttons
- if (page <= 1) {
- numbers = [0, 1, 'ellipsis'];
- }
- else if (page >= pages - 2) {
- numbers = _range(pages-2, pages);
- numbers.unshift('ellipsis');
- }
- else {
- numbers = ['ellipsis', page, 'ellipsis'];
- }
- }
- else if ( page <= half ) {
- numbers = _range(0, buttons-before);
- numbers.push('ellipsis');
-
- if (addFirstLast) {
- numbers.push(pages-1);
- }
- }
- else if ( page >= pages - 1 - half ) {
- numbers = _range(pages-(buttons-before), pages);
- numbers.unshift('ellipsis');
-
- if (addFirstLast) {
- numbers.unshift(0);
- }
- }
- else {
- numbers = _range(page-half+before, page+half-after);
- numbers.push('ellipsis');
- numbers.unshift('ellipsis');
-
- if (addFirstLast) {
- numbers.push(pages-1);
- numbers.unshift(0);
- }
- }
-
- return numbers;
- }
-
- var __lengthCounter = 0;
-
- // opts
- // - menu
- // - text
- DataTable.feature.register( 'pageLength', function ( settings, opts ) {
- var features = settings.oFeatures;
-
- // For compatibility with the legacy `pageLength` top level option
- if (! features.bPaginate || ! features.bLengthChange) {
- return null;
- }
-
- opts = $.extend({
- menu: settings.aLengthMenu,
- text: settings.oLanguage.sLengthMenu
- }, opts);
-
- var
- classes = settings.oClasses.length,
- tableId = settings.sTableId,
- menu = opts.menu,
- lengths = [],
- language = [],
- i;
-
- // Options can be given in a number of ways
- if (Array.isArray( menu[0] )) {
- // Old 1.x style - 2D array
- lengths = menu[0];
- language = menu[1];
- }
- else {
- for ( i=0 ; i<menu.length ; i++ ) {
- // An object with different label and value
- if ($.isPlainObject(menu[i])) {
- lengths.push(menu[i].value);
- language.push(menu[i].label);
- }
- else {
- // Or just a number to display and use
- lengths.push(menu[i]);
- language.push(menu[i]);
- }
- }
- }
-
- // We can put the <select> outside of the label if it is at the start or
- // end which helps improve accessability (not all screen readers like
- // implicit for elements).
- var end = opts.text.match(/_MENU_$/);
- var start = opts.text.match(/^_MENU_/);
- var removed = opts.text.replace(/_MENU_/, '');
- var str = '<label>' + opts.text + '</label>';
-
- if (start) {
- str = '_MENU_<label>' + removed + '</label>';
- }
- else if (end) {
- str = '<label>' + removed + '</label>_MENU_';
- }
-
- // Wrapper element - use a span as a holder for where the select will go
- var div = $('<div/>')
- .addClass( classes.container )
- .append(
- str.replace( '_MENU_', '<span></span>' )
- );
-
- // Save text node content for macro updating
- var textNodes = [];
- div.find('label')[0].childNodes.forEach(function (el) {
- if (el.nodeType === Node.TEXT_NODE) {
- textNodes.push({
- el: el,
- text: el.textContent
- });
- }
- })
-
- // Update the label text in case it has an entries value
- var updateEntries = function (len) {
- textNodes.forEach(function (node) {
- node.el.textContent = _fnMacros(settings, node.text, len);
- });
- }
-
- // Next, the select itself, along with the options
- var select = $('<select/>', {
- 'name': tableId+'_length',
- 'aria-controls': tableId,
- 'class': classes.select
- } );
-
- for ( i=0 ; i<lengths.length ; i++ ) {
- select[0][ i ] = new Option(
- typeof language[i] === 'number' ?
- settings.fnFormatNumber( language[i] ) :
- language[i],
- lengths[i]
- );
- }
-
- // add for and id to label and input
- div.find('label').attr('for', 'dt-length-' + __lengthCounter);
- select.attr('id', 'dt-length-' + __lengthCounter);
- __lengthCounter++;
-
- // Swap in the select list
- div.find('span').replaceWith(select);
-
- // 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() {
- _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 );
-
- // Resolve plurals in the text for the new length
- updateEntries(len);
- }
- } );
-
- updateEntries(settings._iDisplayLength);
-
- return div;
- }, 'l' );
-
- // 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;
- } );
- return DataTable;
- }));
- /*! DataTables Bootstrap 5 integration
- * © 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
- var jq = require('jquery');
- var cjsRequires = function (root, $) {
- if ( ! $.fn.dataTable ) {
- require('datatables.net')(root, $);
- }
- };
- if (typeof window === 'undefined') {
- 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 ( ! $ ) {
- $ = jq( root );
- }
- cjsRequires( root, $ );
- return factory( $, root, root.document );
- };
- }
- else {
- cjsRequires( window, jq );
- module.exports = factory( jq, window, window.document );
- }
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- /**
- * DataTables integration for Bootstrap 5.
- *
- * This file sets the defaults and adds options to DataTables to style its
- * controls using Bootstrap. See https://datatables.net/manual/styling/bootstrap
- * for further information.
- */
- /* Set the defaults for DataTables initialisation */
- $.extend( true, DataTable.defaults, {
- renderer: 'bootstrap'
- } );
- /* Default class modification */
- $.extend( true, DataTable.ext.classes, {
- container: "dt-container dt-bootstrap5",
- search: {
- input: "form-control form-control-sm"
- },
- length: {
- select: "form-select form-select-sm"
- },
- processing: {
- container: "dt-processing card"
- }
- } );
- /* Bootstrap paging button renderer */
- DataTable.ext.renderer.pagingButton.bootstrap = function (settings, buttonType, content, active, disabled) {
- var btnClasses = ['dt-paging-button', 'page-item'];
- if (active) {
- btnClasses.push('active');
- }
- if (disabled) {
- btnClasses.push('disabled')
- }
- var li = $('<li>').addClass(btnClasses.join(' '));
- var a = $('<a>', {
- 'href': disabled ? null : '#',
- 'class': 'page-link'
- })
- .html(content)
- .appendTo(li);
- return {
- display: li,
- clicker: a
- };
- };
- DataTable.ext.renderer.pagingContainer.bootstrap = function (settings, buttonEls) {
- return $('<ul/>').addClass('pagination').append(buttonEls);
- };
- DataTable.ext.renderer.layout.bootstrap = function ( settings, container, items ) {
- var row = $( '<div/>', {
- "class": items.full ?
- 'row mt-2 justify-content-md-center mb-3' :
- 'row mt-2 justify-content-between mb-3'
- } )
- .appendTo( container );
- $.each( items, function (key, val) {
- var klass;
- // Apply start / end (left / right when ltr) margins
- if (val.table) {
- klass = 'col-12';
- }
- else if (key === 'start') {
- klass = 'col-md-auto me-auto';
- }
- else if (key === 'end') {
- klass = 'col-md-auto ms-auto';
- }
- else {
- klass = 'col-md';
- }
- $( '<div/>', {
- id: val.id || null,
- "class": klass + ' ' + (val.className || '')
- } )
- .append( val.contents )
- .appendTo( row );
- } );
- };
- return DataTable;
- }));
- /*! Buttons for DataTables 3.0.2
- * © 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
- var jq = require('jquery');
- var cjsRequires = function (root, $) {
- if ( ! $.fn.dataTable ) {
- require('datatables.net')(root, $);
- }
- };
- if (typeof window === 'undefined') {
- 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 ( ! $ ) {
- $ = jq( root );
- }
- cjsRequires( root, $ );
- return factory( $, root, root.document );
- };
- }
- else {
- cjsRequires( window, jq );
- module.exports = factory( jq, window, window.document );
- }
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document ) {
- '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;
- // Custom entity decoder for data export
- var _entityDecoder = null;
- // Allow for jQuery slim
- function _fadeIn(el, duration, fn) {
- if ($.fn.animate) {
- el.stop().fadeIn(duration, fn);
- }
- else {
- el.css('display', 'block');
- if (fn) {
- fn.call(el);
- }
- }
- }
- function _fadeOut(el, duration, fn) {
- if ($.fn.animate) {
- el.stop().fadeOut(duration, fn);
- }
- else {
- el.css('display', 'none');
- if (fn) {
- fn.call(el);
- }
- }
- }
- /**
- * [Buttons description]
- * @param {[type]}
- * @param {[type]}
- */
- var Buttons = function (dt, config) {
- if (!DataTable.versionCheck('2')) {
- throw 'Warning: Buttons requires DataTables 2 or newer';
- }
- // If not created with a `new` keyword then we return a wrapper function that
- // will take the settings object for a DT. This allows easy use of new instances
- // with the `layout` option - e.g. `topLeft: $.fn.dataTable.Buttons( ... )`.
- if (!(this instanceof Buttons)) {
- return function (settings) {
- return new Buttons(settings, dt).container();
- };
- }
- // 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 (Array.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 (
- button.inCollection &&
- this.c.dom.collection.button &&
- this.c.dom.collection.button.active !== undefined
- ) {
- klass = this.c.dom.collection.button.active;
- }
- 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
- * @param {boolean} [draw=true] Trigger a draw. Set a false when adding
- * lots of buttons, until the last button.
- * @return {Buttons} Self for chaining
- */
- add: function (config, idx, draw) {
- 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,
- config !== undefined ? config.split : undefined,
- (config === undefined ||
- config.split === undefined ||
- config.split.length === 0) &&
- base !== undefined,
- false,
- idx
- );
- if (draw === undefined || draw === true) {
- this._draw();
- }
- return this;
- },
- /**
- * Clear buttons from a collection and then insert new buttons
- */
- collectionRebuild: function (node, newButtons) {
- var button = this._nodeToButton(node);
- if (newButtons !== undefined) {
- var i;
- // Need to reverse the array
- for (i = button.buttons.length - 1; i >= 0; i--) {
- this.remove(button.buttons[i].node);
- }
- // If the collection has prefix and / or postfix buttons we need to add them in
- if (button.conf.prefixButtons) {
- newButtons.unshift.apply(newButtons, button.conf.prefixButtons);
- }
- if (button.conf.postfixButtons) {
- newButtons.push.apply(newButtons, button.conf.postfixButtons);
- }
- for (i = 0; i < newButtons.length; i++) {
- var newBtn = newButtons[i];
- this._expandButton(
- button.buttons,
- newBtn,
- newBtn !== undefined &&
- newBtn.config !== undefined &&
- newBtn.config.split !== undefined,
- true,
- newBtn.parentConf !== undefined &&
- newBtn.parentConf.split !== undefined,
- null,
- newBtn.parentConf
- );
- }
- }
- this._draw(button.collection, button.buttons);
- },
- /**
- * 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)
- .prop('disabled', true);
- 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)
- .prop('disabled', false);
- return this;
- },
- /**
- * Get a button's index
- *
- * This is internally recursive
- * @param {element} node Button to get the index of
- * @return {string} Button index
- */
- index: function (node, nested, buttons) {
- if (!nested) {
- nested = '';
- buttons = this.s.buttons;
- }
- for (var i = 0, ien = buttons.length; i < ien; i++) {
- var inner = buttons[i].buttons;
- if (buttons[i].node === node) {
- return nested + i;
- }
- if (inner && inner.length) {
- var match = this.index(node, i + '-', inner);
- if (match !== null) {
- return match;
- }
- }
- }
- return null;
- },
- /**
- * Get the instance name for the button set selector
- * @return {string} Instance name
- */
- name: function () {
- return this.c.name;
- },
- /**
- * Get a button's node of the buttons container if no button is given
- * @param {node} [node] Button node
- * @return {jQuery} Button element, or container
- */
- node: function (node) {
- if (!node) {
- return this.dom.container;
- }
- var button = this._nodeToButton(node);
- return $(button.node);
- },
- /**
- * Set / get a processing class on the selected button
- * @param {element} node Triggering button node
- * @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 dt = this.s.dt;
- var button = this._nodeToButton(node);
- if (flag === undefined) {
- return $(button.node).hasClass('processing');
- }
- $(button.node).toggleClass('processing', flag);
- $(dt.table().node()).triggerHandler('buttons-processing.dt', [
- flag,
- dt.button(node),
- dt,
- $(node),
- button.conf
- ]);
- 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);
- }
- }
- button.conf.destroying = true;
- // 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 textNode = button.textNode;
- 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;
- textNode.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 (e, settings) {
- if (settings === dtSettings) {
- 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,
- split,
- inCollection,
- inSplit,
- attachPoint,
- parentConf
- ) {
- var dt = this.s.dt;
- var isSplit = false;
- var domCollection = this.c.dom.collection;
- var buttons = !Array.isArray(button) ? [button] : button;
- if (button === undefined) {
- buttons = !Array.isArray(split) ? [split] : split;
- }
- for (var i = 0, ien = buttons.length; i < ien; i++) {
- var conf = this._resolveExtends(buttons[i]);
- if (!conf) {
- continue;
- }
- isSplit = conf.config && conf.config.split ? true : false;
- // If the configuration is an array, then expand the buttons at this
- // point
- if (Array.isArray(conf)) {
- this._expandButton(
- attachTo,
- conf,
- built !== undefined && built.conf !== undefined
- ? built.conf.split
- : undefined,
- inCollection,
- parentConf !== undefined && parentConf.split !== undefined,
- attachPoint,
- parentConf
- );
- continue;
- }
- var built = this._buildButton(
- conf,
- inCollection,
- conf.split !== undefined ||
- (conf.config !== undefined &&
- conf.config.split !== undefined),
- inSplit
- );
- if (!built) {
- continue;
- }
- if (attachPoint !== undefined && attachPoint !== null) {
- attachTo.splice(attachPoint, 0, built);
- attachPoint++;
- }
- else {
- attachTo.push(built);
- }
- // Create the dropdown for a collection
- if (built.conf.buttons) {
- built.collection = $(
- '<' + domCollection.container.content.tag + '/>'
- );
- built.conf._collection = built.collection;
- $(built.node).append(domCollection.action.dropHtml);
- this._expandButton(
- built.buttons,
- built.conf.buttons,
- built.conf.split,
- !isSplit,
- isSplit,
- attachPoint,
- built.conf
- );
- }
- // And the split collection
- if (built.conf.split) {
- built.collection = $('<' + domCollection.container.tag + '/>');
- built.conf._collection = built.collection;
- for (var j = 0; j < built.conf.split.length; j++) {
- var item = built.conf.split[j];
- if (typeof item === 'object') {
- item.parent = parentConf;
- if (item.collectionLayout === undefined) {
- item.collectionLayout = built.conf.collectionLayout;
- }
- if (item.dropup === undefined) {
- item.dropup = built.conf.dropup;
- }
- if (item.fade === undefined) {
- item.fade = built.conf.fade;
- }
- }
- }
- this._expandButton(
- built.buttons,
- built.conf.buttons,
- built.conf.split,
- !isSplit,
- isSplit,
- attachPoint,
- built.conf
- );
- }
- built.conf.parent = parentConf;
- // 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);
- }
- }
- },
- /**
- * Create an individual button
- * @param {object} config Resolved button configuration
- * @param {boolean} inCollection `true` if a collection button
- * @return {object} Completed button description object
- * @private
- */
- _buildButton: function (config, inCollection, isSplit, inSplit) {
- var that = this;
- var configDom = this.c.dom;
- var textNode;
- var dt = this.s.dt;
- var text = function (opt) {
- return typeof opt === 'function' ? opt(dt, button, config) : opt;
- };
- // Create an object that describes the button which can be in `dom.button`, or
- // `dom.collection.button` or `dom.split.button` or `dom.collection.split.button`!
- // Each should extend from `dom.button`.
- var dom = $.extend(true, {}, configDom.button);
- if (inCollection && isSplit && configDom.collection.split) {
- $.extend(true, dom, configDom.collection.split.action);
- }
- else if (inSplit || inCollection) {
- $.extend(true, dom, configDom.collection.button);
- }
- else if (isSplit) {
- $.extend(true, dom, configDom.split.button);
- }
- // Spacers don't do much other than insert an element into the DOM
- if (config.spacer) {
- var spacer = $('<' + dom.spacer.tag + '/>')
- .addClass(
- 'dt-button-spacer ' +
- config.style +
- ' ' +
- dom.spacer.className
- )
- .html(text(config.text));
- return {
- conf: config,
- node: spacer,
- inserter: spacer,
- buttons: [],
- inCollection: inCollection,
- isSplit: isSplit,
- collection: null,
- textNode: spacer
- };
- }
- // Make sure that the button is available based on whatever requirements
- // it has. For example, PDF button require pdfmake
- if (
- config.available &&
- !config.available(dt, config) &&
- !config.html
- ) {
- return false;
- }
- var button;
- if (!config.html) {
- var run = function (e, dt, button, config, done) {
- config.action.call(dt.button(button), e, dt, button, config, done);
- $(dt.table().node()).triggerHandler('buttons-action.dt', [
- dt.button(button),
- dt,
- button,
- config
- ]);
- };
- var action = function(e, dt, button, config) {
- if (config.async) {
- that.processing(button[0], true);
- setTimeout(function () {
- run(e, dt, button, config, function () {
- that.processing(button[0], false);
- });
- }, config.async);
- }
- else {
- run(e, dt, button, config, function () {});
- }
- }
- var tag = config.tag || dom.tag;
- var clickBlurs =
- config.clickBlurs === undefined ? true : config.clickBlurs;
- button = $('<' + tag + '/>')
- .addClass(dom.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(dom.disabled) && config.action) {
- action(e, dt, button, config);
- }
- if (clickBlurs) {
- button.trigger('blur');
- }
- })
- .on('keypress.dtb', function (e) {
- if (e.keyCode === 13) {
- e.preventDefault();
- if (!button.hasClass(dom.disabled) && config.action) {
- action(e, dt, button, config);
- }
- }
- });
- // Make `a` tags act like a link
- if (tag.toLowerCase() === 'a') {
- button.attr('href', '#');
- }
- // Button tags should have `type=button` so they don't have any default behaviour
- if (tag.toLowerCase() === 'button') {
- button.attr('type', 'button');
- }
- if (dom.liner.tag) {
- var liner = $('<' + dom.liner.tag + '/>')
- .html(text(config.text))
- .addClass(dom.liner.className);
- if (dom.liner.tag.toLowerCase() === 'a') {
- liner.attr('href', '#');
- }
- button.append(liner);
- textNode = liner;
- }
- else {
- button.html(text(config.text));
- textNode = button;
- }
- if (config.enabled === false) {
- button.addClass(dom.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++;
- }
- if (config.config !== undefined && config.config.split) {
- config.split = config.config.split;
- }
- }
- else {
- button = $(config.html);
- }
- 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);
- // Style integration callback for DOM manipulation
- // Note that this is _not_ documented. It is currently
- // for style integration only
- if (this.c.buttonCreated) {
- inserter = this.c.buttonCreated(config, inserter);
- }
- var splitDiv;
- if (isSplit) {
- var dropdownConf = inCollection
- ? $.extend(true, this.c.dom.split, this.c.dom.collection.split)
- : this.c.dom.split;
- var wrapperConf = dropdownConf.wrapper;
- splitDiv = $('<' + wrapperConf.tag + '/>')
- .addClass(wrapperConf.className)
- .append(button);
- var dropButtonConfig = $.extend(config, {
- align: dropdownConf.dropdown.align,
- attr: {
- 'aria-haspopup': 'dialog',
- 'aria-expanded': false
- },
- className: dropdownConf.dropdown.className,
- closeButton: false,
- splitAlignClass: dropdownConf.dropdown.splitAlignClass,
- text: dropdownConf.dropdown.text
- });
- this._addKey(dropButtonConfig);
- var splitAction = function (e, dt, button, config) {
- _dtButtons.split.action.call(
- dt.button(splitDiv),
- e,
- dt,
- button,
- config
- );
- $(dt.table().node()).triggerHandler('buttons-action.dt', [
- dt.button(button),
- dt,
- button,
- config
- ]);
- button.attr('aria-expanded', true);
- };
- var dropButton = $(
- '<button class="' +
- dropdownConf.dropdown.className +
- ' dt-button"></button>'
- )
- .html(dropdownConf.dropdown.dropHtml)
- .on('click.dtb', function (e) {
- e.preventDefault();
- e.stopPropagation();
- if (!dropButton.hasClass(dom.disabled)) {
- splitAction(e, dt, dropButton, dropButtonConfig);
- }
- if (clickBlurs) {
- dropButton.trigger('blur');
- }
- })
- .on('keypress.dtb', function (e) {
- if (e.keyCode === 13) {
- e.preventDefault();
- if (!dropButton.hasClass(dom.disabled)) {
- splitAction(e, dt, dropButton, dropButtonConfig);
- }
- }
- });
- if (config.split.length === 0) {
- dropButton.addClass('dtb-hide-drop');
- }
- splitDiv.append(dropButton).attr(dropButtonConfig.attr);
- }
- return {
- conf: config,
- node: isSplit ? splitDiv.get(0) : button.get(0),
- inserter: isSplit ? splitDiv : inserter,
- buttons: [],
- inCollection: inCollection,
- isSplit: isSplit,
- inSplit: inSplit,
- collection: null,
- textNode: textNode
- };
- },
- /**
- * 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 that = this;
- 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) && !Array.isArray(base)) {
- if (base === undefined) {
- return;
- }
- if (typeof base === 'function') {
- base = base.call(that, dt, conf);
- if (!base) {
- return false;
- }
- }
- else if (typeof base === 'string') {
- if (!_dtButtons[base]) {
- return { html: base };
- }
- base = _dtButtons[base];
- }
- loop++;
- if (loop > 30) {
- // Protect against misconfiguration killing the browser
- throw 'Buttons: Too many iterations';
- }
- }
- return Array.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 (Array.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;
- if (conf.config !== undefined && objArray.config !== undefined) {
- conf.config = $.extend({}, objArray.config, conf.config);
- }
- 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;
- }
- // 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;
- }
- // 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]);
- }
- }
- 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]);
- }
- }
- return conf;
- },
- /**
- * Display (and replace if there is an existing one) a popover attached to a button
- * @param {string|node} content Content to show
- * @param {DataTable.Api} hostButton DT API instance of the button
- * @param {object} inOpts Options (see object below for all options)
- */
- _popover: function (content, hostButton, inOpts) {
- var dt = hostButton;
- var c = this.c;
- var closed = false;
- var options = $.extend(
- {
- align: 'button-left', // button-right, dt-container, split-left, split-right
- autoClose: false,
- background: true,
- backgroundClassName: 'dt-button-background',
- closeButton: true,
- containerClassName: c.dom.collection.container.className,
- contentClassName: c.dom.collection.container.content.className,
- collectionLayout: '',
- collectionTitle: '',
- dropup: false,
- fade: 400,
- popoverTitle: '',
- rightAlignClassName: 'dt-button-right',
- tag: c.dom.collection.container.tag
- },
- inOpts
- );
- var containerSelector =
- options.tag + '.' + options.containerClassName.replace(/ /g, '.');
- var hostNode = hostButton.node();
- var close = function () {
- closed = true;
- _fadeOut($(containerSelector), options.fade, function () {
- $(this).detach();
- });
- $(
- dt
- .buttons('[aria-haspopup="dialog"][aria-expanded="true"]')
- .nodes()
- ).attr('aria-expanded', 'false');
- $('div.dt-button-background').off('click.dtb-collection');
- Buttons.background(
- false,
- options.backgroundClassName,
- options.fade,
- hostNode
- );
- $(window).off('resize.resize.dtb-collection');
- $('body').off('.dtb-collection');
- dt.off('buttons-action.b-internal');
- dt.off('destroy');
- };
- if (content === false) {
- close();
- return;
- }
- var existingExpanded = $(
- dt.buttons('[aria-haspopup="dialog"][aria-expanded="true"]').nodes()
- );
- if (existingExpanded.length) {
- // Reuse the current position if the button that was triggered is inside an existing collection
- if (hostNode.closest(containerSelector).length) {
- hostNode = existingExpanded.eq(0);
- }
- close();
- }
- // Try to be smart about the layout
- var cnt = $('.dt-button', content).length;
- var mod = '';
- if (cnt === 3) {
- mod = 'dtb-b3';
- }
- else if (cnt === 2) {
- mod = 'dtb-b2';
- }
- else if (cnt === 1) {
- mod = 'dtb-b1';
- }
- var display = $('<' + options.tag + '/>')
- .addClass(options.containerClassName)
- .addClass(options.collectionLayout)
- .addClass(options.splitAlignClass)
- .addClass(mod)
- .css('display', 'none')
- .attr({
- 'aria-modal': true,
- role: 'dialog'
- });
- content = $(content)
- .addClass(options.contentClassName)
- .attr('role', 'menu')
- .appendTo(display);
- hostNode.attr('aria-expanded', 'true');
- if (hostNode.parents('body')[0] !== document.body) {
- hostNode = document.body.lastChild;
- }
- if (options.popoverTitle) {
- display.prepend(
- '<div class="dt-button-collection-title">' +
- options.popoverTitle +
- '</div>'
- );
- }
- else if (options.collectionTitle) {
- display.prepend(
- '<div class="dt-button-collection-title">' +
- options.collectionTitle +
- '</div>'
- );
- }
- if (options.closeButton) {
- display
- .prepend('<div class="dtb-popover-close">×</div>')
- .addClass('dtb-collection-closeable');
- }
- _fadeIn(display.insertAfter(hostNode), options.fade);
- var tableContainer = $(hostButton.table().container());
- var position = display.css('position');
- if (options.span === 'container' || options.align === 'dt-container') {
- hostNode = hostNode.parent();
- display.css('width', tableContainer.width());
- }
- // Align the popover relative to the DataTables container
- // Useful for wide popovers such as SearchPanes
- if (position === 'absolute') {
- // Align relative to the host button
- var offsetParent = $(hostNode[0].offsetParent);
- var buttonPosition = hostNode.position();
- var buttonOffset = hostNode.offset();
- var tableSizes = offsetParent.offset();
- var containerPosition = offsetParent.position();
- var computed = window.getComputedStyle(offsetParent[0]);
- tableSizes.height = offsetParent.outerHeight();
- tableSizes.width =
- offsetParent.width() + parseFloat(computed.paddingLeft);
- tableSizes.right = tableSizes.left + tableSizes.width;
- tableSizes.bottom = tableSizes.top + tableSizes.height;
- // Set the initial position so we can read height / width
- var top = buttonPosition.top + hostNode.outerHeight();
- var left = buttonPosition.left;
- display.css({
- top: top,
- left: left
- });
- // Get the popover position
- computed = window.getComputedStyle(display[0]);
- var popoverSizes = display.offset();
- popoverSizes.height = display.outerHeight();
- popoverSizes.width = display.outerWidth();
- popoverSizes.right = popoverSizes.left + popoverSizes.width;
- popoverSizes.bottom = popoverSizes.top + popoverSizes.height;
- popoverSizes.marginTop = parseFloat(computed.marginTop);
- popoverSizes.marginBottom = parseFloat(computed.marginBottom);
- // First position per the class requirements - pop up and right align
- if (options.dropup) {
- top =
- buttonPosition.top -
- popoverSizes.height -
- popoverSizes.marginTop -
- popoverSizes.marginBottom;
- }
- if (
- options.align === 'button-right' ||
- display.hasClass(options.rightAlignClassName)
- ) {
- left =
- buttonPosition.left -
- popoverSizes.width +
- hostNode.outerWidth();
- }
- // Container alignment - make sure it doesn't overflow the table container
- if (
- options.align === 'dt-container' ||
- options.align === 'container'
- ) {
- if (left < buttonPosition.left) {
- left = -buttonPosition.left;
- }
- }
- // Window adjustment
- if (
- containerPosition.left + left + popoverSizes.width >
- $(window).width()
- ) {
- // Overflowing the document to the right
- left =
- $(window).width() -
- popoverSizes.width -
- containerPosition.left;
- }
- if (buttonOffset.left + left < 0) {
- // Off to the left of the document
- left = -buttonOffset.left;
- }
- if (
- containerPosition.top + top + popoverSizes.height >
- $(window).height() + $(window).scrollTop()
- ) {
- // Pop up if otherwise we'd need the user to scroll down
- top =
- buttonPosition.top -
- popoverSizes.height -
- popoverSizes.marginTop -
- popoverSizes.marginBottom;
- }
- if (containerPosition.top + top < $(window).scrollTop()) {
- // Correction for when the top is beyond the top of the page
- top = buttonPosition.top + hostNode.outerHeight();
- }
- // Calculations all done - now set it
- display.css({
- top: top,
- left: left
- });
- }
- else {
- // Fix position - centre on screen
- var place = function () {
- var half = $(window).height() / 2;
- var top = display.height() / 2;
- if (top > half) {
- top = half;
- }
- display.css('marginTop', top * -1);
- };
- place();
- $(window).on('resize.dtb-collection', function () {
- place();
- });
- }
- if (options.background) {
- Buttons.background(
- true,
- options.backgroundClassName,
- options.fade,
- options.backgroundHost || hostNode
- );
- }
- // 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 () {}
- );
- if (options.autoClose) {
- setTimeout(function () {
- dt.on('buttons-action.b-internal', function (e, btn, dt, node) {
- if (node[0] === hostNode[0]) {
- return;
- }
- close();
- });
- }, 0);
- }
- $(display).trigger('buttons-popover.dt');
- dt.on('destroy', close);
- setTimeout(function () {
- closed = false;
- $('body')
- .on('click.dtb-collection', function (e) {
- if (closed) {
- return;
- }
- // andSelf is deprecated in jQ1.8, but we want 1.7 compat
- var back = $.fn.addBack ? 'addBack' : 'andSelf';
- var parent = $(e.target).parent()[0];
- if (
- (!$(e.target).parents()[back]().filter(content)
- .length &&
- !$(parent).hasClass('dt-buttons')) ||
- $(e.target).hasClass('dt-button-background')
- ) {
- close();
- }
- })
- .on('keyup.dtb-collection', function (e) {
- if (e.keyCode === 27) {
- close();
- }
- })
- .on('keydown.dtb-collection', function (e) {
- // Focus trap for tab key
- var elements = $('a, button', content);
- var active = document.activeElement;
- if (e.keyCode !== 9) {
- // tab
- return;
- }
- if (elements.index(active) === -1) {
- // If current focus is not inside the popover
- elements.first().focus();
- e.preventDefault();
- }
- else if (e.shiftKey) {
- // Reverse tabbing order when shift key is pressed
- if (active === elements[0]) {
- elements.last().focus();
- e.preventDefault();
- }
- }
- else {
- if (active === elements.last()[0]) {
- elements.first().focus();
- e.preventDefault();
- }
- }
- });
- }, 0);
- }
- });
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * 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, insertPoint) {
- if (fade === undefined) {
- fade = 400;
- }
- if (!insertPoint) {
- insertPoint = document.body;
- }
- if (show) {
- _fadeIn(
- $('<div/>')
- .addClass(className)
- .css('display', 'none')
- .insertAfter(insertPoint),
- fade
- );
- }
- else {
- _fadeOut($('div.' + className), 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 === undefined || group === null) {
- 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 (Array.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(input.trim(), names);
- if (idx !== -1) {
- ret.push(buttons[idx].inst);
- }
- }
- }
- else if (typeof input === 'number') {
- // Index selector
- ret.push(buttons[input].inst);
- }
- else if (typeof input === 'object' && input.nodeName) {
- // Element selector
- for (var j = 0; j < buttons.length; j++) {
- if (buttons[j].inst.dom.container[0] === input) {
- ret.push(buttons[j].inst);
- }
- }
- }
- else if (typeof input === 'object') {
- // Actual instance selector
- ret.push(input);
- }
- };
- 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 (Array.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
- if (inst.s.buttons[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(a[i].trim(), 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;
- };
- /**
- * Default function used for formatting output data.
- * @param {*} str Data to strip
- */
- Buttons.stripData = function (str, config) {
- if (typeof str !== 'string') {
- return str;
- }
- // Always remove script tags
- str = Buttons.stripHtmlScript(str);
- // Always remove comments
- str = Buttons.stripHtmlComments(str);
- if (!config || config.stripHtml) {
- str = DataTable.util.stripHtml(str);
- }
- if (!config || config.trim) {
- str = str.trim();
- }
- if (!config || config.stripNewlines) {
- str = str.replace(/\n/g, ' ');
- }
- if (!config || config.decodeEntities) {
- if (_entityDecoder) {
- str = _entityDecoder(str);
- }
- else {
- _exportTextarea.innerHTML = str;
- str = _exportTextarea.value;
- }
- }
- return str;
- };
- /**
- * Provide a custom entity decoding function - e.g. a regex one, which can be
- * much faster than the built in DOM option, but also larger code size.
- * @param {function} fn
- */
- Buttons.entityDecoder = function (fn) {
- _entityDecoder = fn;
- };
- /**
- * Common function for stripping HTML comments
- *
- * @param {*} input
- * @returns
- */
- Buttons.stripHtmlComments = function (input) {
- var previous;
-
- do {
- previous = input;
- input = input.replace(/(<!--.*?--!?>)|(<!--[\S\s]+?--!?>)|(<!--[\S\s]*?$)/g, '');
- } while (input !== previous);
- return input;
- };
- /**
- * Common function for stripping HTML script tags
- *
- * @param {*} input
- * @returns
- */
- Buttons.stripHtmlScript = function (input) {
- var previous;
-
- do {
- previous = input;
- input = input.replace(/<script\b[^<]*(?:(?!<\/script[^>]*>)<[^<]*)*<\/script[^>]*>/gi, '');
- } while (input !== previous);
- return input;
- };
- /**
- * 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: {
- action: {
- // action button
- dropHtml: '<span class="dt-button-down-arrow">▼</span>'
- },
- container: {
- // The element used for the dropdown
- className: 'dt-button-collection',
- content: {
- className: '',
- tag: 'div'
- },
- tag: 'div'
- }
- // optionally
- // , button: IButton - buttons inside the collection container
- // , split: ISplit - splits inside the collection container
- },
- button: {
- tag: 'button',
- className: 'dt-button',
- active: 'dt-button-active', // class name
- disabled: 'disabled', // class name
- spacer: {
- className: 'dt-button-spacer',
- tag: 'span'
- },
- liner: {
- tag: 'span',
- className: ''
- }
- },
- split: {
- action: {
- // action button
- className: 'dt-button-split-drop-button dt-button',
- tag: 'button'
- },
- dropdown: {
- // button to trigger the dropdown
- align: 'split-right',
- className: 'dt-button-split-drop',
- dropHtml: '<span class="dt-button-down-arrow">▼</span>',
- splitAlignClass: 'dt-button-split-left',
- tag: 'button'
- },
- wrapper: {
- // wrap around both
- className: 'dt-button-split',
- tag: 'div'
- }
- }
- }
- };
- /**
- * Version information
- * @type {string}
- * @static
- */
- Buttons.version = '3.0.2';
- $.extend(_dtButtons, {
- collection: {
- text: function (dt) {
- return dt.i18n('buttons.collection', 'Collection');
- },
- className: 'buttons-collection',
- closeButton: false,
- init: function (dt, button) {
- button.attr('aria-expanded', false);
- },
- action: function (e, dt, button, config) {
- if (config._collection.parents('body').length) {
- this.popover(false, config);
- }
- else {
- this.popover(config._collection, config);
- }
- // When activated using a key - auto focus on the
- // first item in the popover
- if (e.type === 'keypress') {
- $('a, button', config._collection).eq(0).focus();
- }
- },
- attr: {
- 'aria-haspopup': 'dialog'
- }
- // Also the popover options, defined in Buttons.popover
- },
- split: {
- text: function (dt) {
- return dt.i18n('buttons.split', 'Split');
- },
- className: 'buttons-split',
- closeButton: false,
- init: function (dt, button) {
- return button.attr('aria-expanded', false);
- },
- action: function (e, dt, button, config) {
- this.popover(config._collection, config);
- },
- attr: {
- 'aria-haspopup': 'dialog'
- }
- // Also the popover options, defined in Buttons.popover
- },
- copy: function () {
- if (_dtButtons.copyHtml5) {
- return 'copyHtml5';
- }
- },
- csv: function (dt, conf) {
- if (_dtButtons.csvHtml5 && _dtButtons.csvHtml5.available(dt, conf)) {
- return 'csvHtml5';
- }
- },
- excel: function (dt, conf) {
- if (
- _dtButtons.excelHtml5 &&
- _dtButtons.excelHtml5.available(dt, conf)
- ) {
- return 'excelHtml5';
- }
- },
- pdf: function (dt, conf) {
- if (_dtButtons.pdfHtml5 && _dtButtons.pdfHtml5.available(dt, conf)) {
- return 'pdfHtml5';
- }
- },
- pageLength: function (dt) {
- var lengthMenu = dt.settings()[0].aLengthMenu;
- var vals = [];
- var lang = [];
- var text = function (dt) {
- return dt.i18n(
- 'buttons.pageLength',
- {
- '-1': 'Show all rows',
- _: 'Show %d rows'
- },
- dt.page.len()
- );
- };
- // Support for DataTables 1.x 2D array
- if (Array.isArray(lengthMenu[0])) {
- vals = lengthMenu[0];
- lang = lengthMenu[1];
- }
- else {
- for (var i = 0; i < lengthMenu.length; i++) {
- var option = lengthMenu[i];
- // Support for DataTables 2 object in the array
- if ($.isPlainObject(option)) {
- vals.push(option.value);
- lang.push(option.label);
- }
- else {
- vals.push(option);
- lang.push(option);
- }
- }
- }
- 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(conf.text);
- });
- },
- destroy: function (dt, node, conf) {
- dt.off('length.dt' + conf.namespace);
- }
- };
- },
- spacer: {
- style: 'empty',
- spacer: true,
- text: function (dt) {
- return dt.i18n('buttons.spacer', '');
- }
- }
- });
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * 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);
- });
- }
- );
- // Collection control
- DataTable.Api.registerPlural(
- 'buttons().collectionRebuild()',
- 'button().collectionRebuild()',
- function (buttons) {
- return this.each(function (set) {
- for (var i = 0; i < buttons.length; i++) {
- if (typeof buttons[i] === 'object') {
- buttons[i].parentConf = set;
- }
- }
- set.inst.collectionRebuild(set.node, buttons);
- });
- }
- );
- // 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);
- });
- }
- );
- // Button index
- DataTable.Api.register('button().index()', function () {
- var idx = null;
- this.each(function (set) {
- var res = set.inst.index(set.node);
- if (res !== null) {
- idx = res;
- }
- });
- return idx;
- });
- // 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');
- });
- }
- );
- // Button resolver to the popover
- DataTable.Api.register('button().popover()', function (content, options) {
- return this.map(function (set) {
- return set.inst._popover(content, this.button(this[0].node), options);
- });
- });
- // Get the container elements
- DataTable.Api.register('buttons().containers()', 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;
- });
- DataTable.Api.register('buttons().container()', function () {
- // API level of nesting is `buttons()` so we can zip into the containers method
- return this.containers().eq(0);
- });
- // Add a new button
- DataTable.Api.register('button().add()', function (idx, conf, draw) {
- 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, draw);
- }
- }
- 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) {
- this.off('destroy.btn-info');
- _fadeOut($('#datatables_buttons_info'), 400, 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>' : '';
- _fadeIn(
- $('<div id="datatables_buttons_info" class="dt-button-info"/>')
- .html(title)
- .append(
- $('<div/>')[typeof message === 'string' ? 'html' : 'append'](
- message
- )
- )
- .css('display', 'none')
- .appendTo('body')
- );
- if (time !== undefined && time !== 0) {
- _infoTimer = setTimeout(function () {
- that.buttons.info(false);
- }, time);
- }
- this.on('destroy.btn-info', function () {
- that.buttons.info(false);
- });
- 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, this),
- title: _title(conf, this),
- messageTop: _message(this, conf, conf.message || conf.messageTop, 'top'),
- messageBottom: _message(this, conf, conf.messageBottom, 'bottom')
- };
- });
- /**
- * Get the file name for an exported file.
- *
- * @param {object} config Button configuration
- * @param {object} dt DataTable instance
- */
- var _filename = function (config, dt) {
- // 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(config, dt);
- }
- if (filename === undefined || filename === null) {
- return null;
- }
- if (filename.indexOf('*') !== -1) {
- filename = filename.replace(/\*/g, $('head > title').text()).trim();
- }
- // Strip characters which the OS will object to
- filename = filename.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, '');
- var extension = _stringOrFunction(config.extension, config, dt);
- 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, config, dt) {
- if (option === null || option === undefined) {
- return null;
- }
- else if (typeof option === 'function') {
- return option(config, dt);
- }
- return option;
- };
- /**
- * Get the title for an exported file.
- *
- * @param {object} config Button configuration
- */
- var _title = function (config, dt) {
- var title = _stringOrFunction(config.title, config, dt);
- return title === null
- ? null
- : title.indexOf('*') !== -1
- ? title.replace(/\*/g, $('head > title').text() || 'Exported data')
- : title;
- };
- var _message = function (dt, config, option, position) {
- var message = _stringOrFunction(option, config, dt);
- 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 Buttons.stripData(d, config);
- },
- footer: function (d) {
- return Buttons.stripData(d, config);
- },
- body: function (d) {
- return Buttons.stripData(d, config);
- }
- },
- customizeData: null,
- customizeZip: null
- },
- inOpts
- );
- var header = dt
- .columns(config.columns)
- .indexes()
- .map(function (idx) {
- var col = dt.column(idx);
- return config.format.header(col.title(), idx, col.header());
- })
- .toArray();
- var footer = dt.table().footer()
- ? dt
- .columns(config.columns)
- .indexes()
- .map(function (idx) {
- var el = dt.column(idx).footer();
- var val = '';
- if (el) {
- var inner = $('.dt-column-title', el);
- val = inner.length
- ? inner.html()
- : $(el).html();
- }
- return config.format.footer(val, 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, {
- order: modifier.order
- });
- var cells = selectedCells.render(config.orthogonal).toArray();
- var cellNodes = selectedCells.nodes().toArray();
- var cellIndexes = selectedCells.indexes().toArray();
- var columns = dt.columns(config.columns).count();
- var rows = columns > 0 ? cells.length / columns : 0;
- var body = [];
- 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],
- cellIndexes[cellCounter].row,
- cellIndexes[cellCounter].column,
- cellNodes[cellCounter]
- );
- cellCounter++;
- }
- body[i] = row;
- }
- var data = {
- header: header,
- headerStructure: _headerFormatter(
- config.format.header,
- dt.table().header.structure(config.columns)
- ),
- footer: footer,
- footerStructure: _headerFormatter(
- config.format.footer,
- dt.table().footer.structure(config.columns)
- ),
- body: body
- };
- if (config.customizeData) {
- config.customizeData(data);
- }
- return data;
- };
- function _headerFormatter(formatter, struct) {
- for (var i=0 ; i<struct.length ; i++) {
- for (var j=0 ; j<struct[i].length ; j++) {
- var item = struct[i][j];
- if (item) {
- item.title = formatter(
- item.title,
- j,
- item.cell
- );
- }
- }
- }
- return struct;
- }
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * 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();
- }
- });
- function _init(settings, options) {
- var api = new DataTable.Api(settings);
- var opts = options
- ? options
- : api.init().buttons || DataTable.defaults.buttons;
- return new Buttons(api, opts).container();
- }
- // DataTables 1 `dom` feature option
- DataTable.ext.feature.push({
- fnInit: _init,
- cFeature: 'B'
- });
- // DataTables 2 layout feature
- if (DataTable.feature) {
- DataTable.feature.register('buttons', _init);
- }
- return DataTable;
- }));
- /*! Bootstrap integration for DataTables' Buttons
- * © SpryMedia Ltd - datatables.net/license
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net-bs5', 'datatables.net-buttons'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- var jq = require('jquery');
- var cjsRequires = function (root, $) {
- if ( ! $.fn.dataTable ) {
- require('datatables.net-bs5')(root, $);
- }
- if ( ! $.fn.dataTable.Buttons ) {
- require('datatables.net-buttons')(root, $);
- }
- };
- if (typeof window === 'undefined') {
- 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 ( ! $ ) {
- $ = jq( root );
- }
- cjsRequires( root, $ );
- return factory( $, root, root.document );
- };
- }
- else {
- cjsRequires( window, jq );
- module.exports = factory( jq, window, window.document );
- }
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- $.extend(true, DataTable.Buttons.defaults, {
- dom: {
- container: {
- className: 'dt-buttons btn-group flex-wrap'
- },
- button: {
- className: 'btn btn-secondary',
- active: 'active'
- },
- collection: {
- action: {
- dropHtml: ''
- },
- container: {
- tag: 'div',
- className: 'dropdown-menu dt-button-collection'
- },
- closeButton: false,
- button: {
- tag: 'a',
- className: 'dt-button dropdown-item',
- active: 'dt-button-active',
- disabled: 'disabled',
- spacer: {
- className: 'dropdown-divider',
- tag: 'hr'
- }
- }
- },
- split: {
- action: {
- tag: 'a',
- className: 'btn btn-secondary dt-button-split-drop-button',
- closeButton: false
- },
- dropdown: {
- tag: 'button',
- dropHtml: '',
- className:
- 'btn btn-secondary dt-button-split-drop dropdown-toggle dropdown-toggle-split',
- closeButton: false,
- align: 'split-left',
- splitAlignClass: 'dt-button-split-left'
- },
- wrapper: {
- tag: 'div',
- className: 'dt-button-split btn-group',
- closeButton: false
- }
- }
- },
- buttonCreated: function (config, button) {
- return config.buttons ? $('<div class="btn-group"/>').append(button) : button;
- }
- });
- DataTable.ext.buttons.collection.className += ' dropdown-toggle';
- DataTable.ext.buttons.collection.rightAlignClassName = 'dropdown-menu-right';
- return DataTable;
- }));
- /*!
- * HTML5 export buttons for Buttons and DataTables.
- * © SpryMedia Ltd - datatables.net/license
- *
- * FileSaver.js (1.3.3) - MIT license
- * Copyright © 2016 Eli Grey - http://eligrey.com
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net', 'datatables.net-buttons'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- var jq = require('jquery');
- var cjsRequires = function (root, $) {
- if ( ! $.fn.dataTable ) {
- require('datatables.net')(root, $);
- }
- if ( ! $.fn.dataTable.Buttons ) {
- require('datatables.net-buttons')(root, $);
- }
- };
- if (typeof window === 'undefined') {
- 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 ( ! $ ) {
- $ = jq( root );
- }
- cjsRequires( root, $ );
- return factory( $, root, root.document );
- };
- }
- else {
- cjsRequires( window, jq );
- module.exports = factory( jq, window, window.document );
- }
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- // Allow the constructor to pass in JSZip and PDFMake from external requires.
- // Otherwise, use globally defined variables, if they are available.
- var useJszip;
- var usePdfmake;
- function _jsZip() {
- return useJszip || window.JSZip;
- }
- function _pdfMake() {
- return usePdfmake || window.pdfMake;
- }
- DataTable.Buttons.pdfMake = function (_) {
- if (!_) {
- return _pdfMake();
- }
- usePdfmake = _;
- };
- DataTable.Buttons.jszip = function (_) {
- if (!_) {
- return _jsZip();
- }
- useJszip = _;
- };
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * FileSaver.js dependency
- */
- /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
- var _saveAs = (function (view) {
- 'use strict';
- // IE <10 is explicitly unsupported
- if (
- typeof view === 'undefined' ||
- (typeof navigator !== 'undefined' &&
- /MSIE [1-9]\./.test(navigator.userAgent))
- ) {
- return;
- }
- var doc = view.document,
- // only get URL when necessary in case Blob.js hasn't overridden it yet
- get_URL = function () {
- return view.URL || view.webkitURL || view;
- },
- save_link = doc.createElementNS('http://www.w3.org/1999/xhtml', 'a'),
- can_use_save_link = 'download' in save_link,
- click = function (node) {
- var event = new MouseEvent('click');
- node.dispatchEvent(event);
- },
- is_safari = /constructor/i.test(view.HTMLElement) || view.safari,
- is_chrome_ios = /CriOS\/[\d]+/.test(navigator.userAgent),
- throw_outside = function (ex) {
- (view.setImmediate || view.setTimeout)(function () {
- throw ex;
- }, 0);
- },
- force_saveable_type = 'application/octet-stream',
- // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
- arbitrary_revoke_timeout = 1000 * 40, // in ms
- revoke = function (file) {
- var revoker = function () {
- if (typeof file === 'string') {
- // file is an object URL
- get_URL().revokeObjectURL(file);
- }
- else {
- // file is a File
- file.remove();
- }
- };
- setTimeout(revoker, arbitrary_revoke_timeout);
- },
- dispatch = function (filesaver, event_types, event) {
- event_types = [].concat(event_types);
- var i = event_types.length;
- while (i--) {
- var listener = filesaver['on' + event_types[i]];
- if (typeof listener === 'function') {
- try {
- listener.call(filesaver, event || filesaver);
- } catch (ex) {
- throw_outside(ex);
- }
- }
- }
- },
- auto_bom = function (blob) {
- // prepend BOM for UTF-8 XML and text/* types (including HTML)
- // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
- if (
- /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(
- blob.type
- )
- ) {
- return new Blob([String.fromCharCode(0xfeff), blob], {
- type: blob.type
- });
- }
- return blob;
- },
- FileSaver = function (blob, name, no_auto_bom) {
- if (!no_auto_bom) {
- blob = auto_bom(blob);
- }
- // First try a.download, then web filesystem, then object URLs
- var filesaver = this,
- type = blob.type,
- force = type === force_saveable_type,
- object_url,
- dispatch_all = function () {
- dispatch(
- filesaver,
- 'writestart progress write writeend'.split(' ')
- );
- },
- // on any filesys errors revert to saving with object URLs
- fs_error = function () {
- if (
- (is_chrome_ios || (force && is_safari)) &&
- view.FileReader
- ) {
- // Safari doesn't allow downloading of blob urls
- var reader = new FileReader();
- reader.onloadend = function () {
- var url = is_chrome_ios
- ? reader.result
- : reader.result.replace(
- /^data:[^;]*;/,
- 'data:attachment/file;'
- );
- var popup = view.open(url, '_blank');
- if (!popup) view.location.href = url;
- url = undefined; // release reference before dispatching
- filesaver.readyState = filesaver.DONE;
- dispatch_all();
- };
- reader.readAsDataURL(blob);
- filesaver.readyState = filesaver.INIT;
- return;
- }
- // don't create more object URLs than needed
- if (!object_url) {
- object_url = get_URL().createObjectURL(blob);
- }
- if (force) {
- view.location.href = object_url;
- }
- else {
- var opened = view.open(object_url, '_blank');
- if (!opened) {
- // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
- view.location.href = object_url;
- }
- }
- filesaver.readyState = filesaver.DONE;
- dispatch_all();
- revoke(object_url);
- };
- filesaver.readyState = filesaver.INIT;
- if (can_use_save_link) {
- object_url = get_URL().createObjectURL(blob);
- setTimeout(function () {
- save_link.href = object_url;
- save_link.download = name;
- click(save_link);
- dispatch_all();
- revoke(object_url);
- filesaver.readyState = filesaver.DONE;
- });
- return;
- }
- fs_error();
- },
- FS_proto = FileSaver.prototype,
- saveAs = function (blob, name, no_auto_bom) {
- return new FileSaver(
- blob,
- name || blob.name || 'download',
- no_auto_bom
- );
- };
- // IE 10+ (native saveAs)
- if (typeof navigator !== 'undefined' && navigator.msSaveOrOpenBlob) {
- return function (blob, name, no_auto_bom) {
- name = name || blob.name || 'download';
- if (!no_auto_bom) {
- blob = auto_bom(blob);
- }
- return navigator.msSaveOrOpenBlob(blob, name);
- };
- }
- FS_proto.abort = function () {};
- FS_proto.readyState = FS_proto.INIT = 0;
- FS_proto.WRITING = 1;
- FS_proto.DONE = 2;
- FS_proto.error =
- FS_proto.onwritestart =
- FS_proto.onprogress =
- FS_proto.onwrite =
- FS_proto.onabort =
- FS_proto.onerror =
- FS_proto.onwriteend =
- null;
- return saveAs;
- })(
- (typeof self !== 'undefined' && self) ||
- (typeof window !== 'undefined' && window) ||
- this.content
- );
- // Expose file saver on the DataTables API. Can't attach to `DataTables.Buttons`
- // since this file can be loaded before Button's core!
- DataTable.fileSave = _saveAs;
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Local (private) functions
- */
- /**
- * Get the sheet name for Excel exports.
- *
- * @param {object} config Button configuration
- */
- var _sheetname = function (config) {
- var sheetName = 'Sheet1';
- if (config.sheetName) {
- sheetName = config.sheetName.replace(/[\[\]\*\/\\\?\:]/g, '');
- }
- return sheetName;
- };
- /**
- * Get the newline character(s)
- *
- * @param {object} config Button configuration
- * @return {string} Newline character
- */
- var _newLine = function (config) {
- return config.newline
- ? config.newline
- : navigator.userAgent.match(/Windows/)
- ? '\r\n'
- : '\n';
- };
- /**
- * Combine the data from the `buttons.exportData` method into a string that
- * will be used in the export file.
- *
- * @param {DataTable.Api} dt DataTables API instance
- * @param {object} config Button configuration
- * @return {object} The data to export
- */
- var _exportData = function (dt, config) {
- var newLine = _newLine(config);
- var data = dt.buttons.exportData(config.exportOptions);
- var boundary = config.fieldBoundary;
- var separator = config.fieldSeparator;
- var reBoundary = new RegExp(boundary, 'g');
- var escapeChar = config.escapeChar !== undefined ? config.escapeChar : '\\';
- var join = function (a) {
- var s = '';
- // If there is a field boundary, then we might need to escape it in
- // the source data
- for (var i = 0, ien = a.length; i < ien; i++) {
- if (i > 0) {
- s += separator;
- }
- s += boundary
- ? boundary +
- ('' + a[i]).replace(reBoundary, escapeChar + boundary) +
- boundary
- : a[i];
- }
- return s;
- };
- var header = '';
- var footer = '';
- var body = [];
- if (config.header) {
- header =
- data.headerStructure
- .map(function (row) {
- return join(
- row.map(function (cell) {
- return cell ? cell.title : '';
- })
- );
- })
- .join(newLine) + newLine;
- }
- if (config.footer && data.footer) {
- footer =
- data.footerStructure
- .map(function (row) {
- return join(
- row.map(function (cell) {
- return cell ? cell.title : '';
- })
- );
- })
- .join(newLine) + newLine;
- }
- for (var i = 0, ien = data.body.length; i < ien; i++) {
- body.push(join(data.body[i]));
- }
- return {
- str: header + body.join(newLine) + newLine + footer,
- rows: body.length
- };
- };
- /**
- * Older versions of Safari (prior to tech preview 18) don't support the
- * download option required.
- *
- * @return {Boolean} `true` if old Safari
- */
- var _isDuffSafari = function () {
- var safari =
- navigator.userAgent.indexOf('Safari') !== -1 &&
- navigator.userAgent.indexOf('Chrome') === -1 &&
- navigator.userAgent.indexOf('Opera') === -1;
- if (!safari) {
- return false;
- }
- var version = navigator.userAgent.match(/AppleWebKit\/(\d+\.\d+)/);
- if (version && version.length > 1 && version[1] * 1 < 603.1) {
- return true;
- }
- return false;
- };
- /**
- * Convert from numeric position to letter for column names in Excel
- * @param {int} n Column number
- * @return {string} Column letter(s) name
- */
- function createCellPos(n) {
- var ordA = 'A'.charCodeAt(0);
- var ordZ = 'Z'.charCodeAt(0);
- var len = ordZ - ordA + 1;
- var s = '';
- while (n >= 0) {
- s = String.fromCharCode((n % len) + ordA) + s;
- n = Math.floor(n / len) - 1;
- }
- return s;
- }
- try {
- var _serialiser = new XMLSerializer();
- var _ieExcel;
- } catch (t) {
- // noop
- }
- /**
- * Recursively add XML files from an object's structure to a ZIP file. This
- * allows the XSLX file to be easily defined with an object's structure matching
- * the files structure.
- *
- * @param {JSZip} zip ZIP package
- * @param {object} obj Object to add (recursive)
- */
- function _addToZip(zip, obj) {
- if (_ieExcel === undefined) {
- // Detect if we are dealing with IE's _awful_ serialiser by seeing if it
- // drop attributes
- _ieExcel =
- _serialiser
- .serializeToString(
- new window.DOMParser().parseFromString(
- excelStrings['xl/worksheets/sheet1.xml'],
- 'text/xml'
- )
- )
- .indexOf('xmlns:r') === -1;
- }
- $.each(obj, function (name, val) {
- if ($.isPlainObject(val)) {
- var newDir = zip.folder(name);
- _addToZip(newDir, val);
- }
- else {
- if (_ieExcel) {
- // IE's XML serialiser will drop some name space attributes from
- // from the root node, so we need to save them. Do this by
- // replacing the namespace nodes with a regular attribute that
- // we convert back when serialised. Edge does not have this
- // issue
- var worksheet = val.childNodes[0];
- var i, ien;
- var attrs = [];
- for (i = worksheet.attributes.length - 1; i >= 0; i--) {
- var attrName = worksheet.attributes[i].nodeName;
- var attrValue = worksheet.attributes[i].nodeValue;
- if (attrName.indexOf(':') !== -1) {
- attrs.push({ name: attrName, value: attrValue });
- worksheet.removeAttribute(attrName);
- }
- }
- for (i = 0, ien = attrs.length; i < ien; i++) {
- var attr = val.createAttribute(
- attrs[i].name.replace(':', '_dt_b_namespace_token_')
- );
- attr.value = attrs[i].value;
- worksheet.setAttributeNode(attr);
- }
- }
- var str = _serialiser.serializeToString(val);
- // Fix IE's XML
- if (_ieExcel) {
- // IE doesn't include the XML declaration
- if (str.indexOf('<?xml') === -1) {
- str =
- '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
- str;
- }
- // Return namespace attributes to being as such
- str = str.replace(/_dt_b_namespace_token_/g, ':');
- // Remove testing name space that IE puts into the space preserve attr
- str = str.replace(/xmlns:NS[\d]+="" NS[\d]+:/g, '');
- }
- // Safari, IE and Edge will put empty name space attributes onto
- // various elements making them useless. This strips them out
- str = str.replace(/<([^<>]*?) xmlns=""([^<>]*?)>/g, '<$1 $2>');
- zip.file(name, str);
- }
- });
- }
- /**
- * Create an XML node and add any children, attributes, etc without needing to
- * be verbose in the DOM.
- *
- * @param {object} doc XML document
- * @param {string} nodeName Node name
- * @param {object} opts Options - can be `attr` (attributes), `children`
- * (child nodes) and `text` (text content)
- * @return {node} Created node
- */
- function _createNode(doc, nodeName, opts) {
- var tempNode = doc.createElement(nodeName);
- if (opts) {
- if (opts.attr) {
- $(tempNode).attr(opts.attr);
- }
- if (opts.children) {
- $.each(opts.children, function (key, value) {
- tempNode.appendChild(value);
- });
- }
- if (opts.text !== null && opts.text !== undefined) {
- tempNode.appendChild(doc.createTextNode(opts.text));
- }
- }
- return tempNode;
- }
- /**
- * Get the width for an Excel column based on the contents of that column
- * @param {object} data Data for export
- * @param {int} col Column index
- * @return {int} Column width
- */
- function _excelColWidth(data, col) {
- var max = data.header[col].length;
- var len, lineSplit, str;
- if (data.footer && data.footer[col] && data.footer[col].length > max) {
- max = data.footer[col].length;
- }
- for (var i = 0, ien = data.body.length; i < ien; i++) {
- var point = data.body[i][col];
- str = point !== null && point !== undefined ? point.toString() : '';
- // If there is a newline character, workout the width of the column
- // based on the longest line in the string
- if (str.indexOf('\n') !== -1) {
- lineSplit = str.split('\n');
- lineSplit.sort(function (a, b) {
- return b.length - a.length;
- });
- len = lineSplit[0].length;
- }
- else {
- len = str.length;
- }
- if (len > max) {
- max = len;
- }
- // Max width rather than having potentially massive column widths
- if (max > 40) {
- return 54; // 40 * 1.35
- }
- }
- max *= 1.35;
- // And a min width
- return max > 6 ? max : 6;
- }
- // Excel - Pre-defined strings to build a basic XLSX file
- var excelStrings = {
- '_rels/.rels':
- '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
- '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' +
- '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>' +
- '</Relationships>',
- 'xl/_rels/workbook.xml.rels':
- '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
- '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' +
- '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>' +
- '<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>' +
- '</Relationships>',
- '[Content_Types].xml':
- '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
- '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">' +
- '<Default Extension="xml" ContentType="application/xml" />' +
- '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml" />' +
- '<Default Extension="jpeg" ContentType="image/jpeg" />' +
- '<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" />' +
- '<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" />' +
- '<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml" />' +
- '</Types>',
- 'xl/workbook.xml':
- '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
- '<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">' +
- '<fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="24816"/>' +
- '<workbookPr showInkAnnotation="0" autoCompressPictures="0"/>' +
- '<bookViews>' +
- '<workbookView xWindow="0" yWindow="0" windowWidth="25600" windowHeight="19020" tabRatio="500"/>' +
- '</bookViews>' +
- '<sheets>' +
- '<sheet name="Sheet1" sheetId="1" r:id="rId1"/>' +
- '</sheets>' +
- '<definedNames/>' +
- '</workbook>',
- 'xl/worksheets/sheet1.xml':
- '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
- '<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">' +
- '<sheetData/>' +
- '<mergeCells count="0"/>' +
- '</worksheet>',
- 'xl/styles.xml':
- '<?xml version="1.0" encoding="UTF-8"?>' +
- '<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">' +
- '<numFmts count="6">' +
- '<numFmt numFmtId="164" formatCode="[$$-409]#,##0.00;-[$$-409]#,##0.00"/>' +
- '<numFmt numFmtId="165" formatCode=""£"#,##0.00"/>' +
- '<numFmt numFmtId="166" formatCode="[$€-2] #,##0.00"/>' +
- '<numFmt numFmtId="167" formatCode="0.0%"/>' +
- '<numFmt numFmtId="168" formatCode="#,##0;(#,##0)"/>' +
- '<numFmt numFmtId="169" formatCode="#,##0.00;(#,##0.00)"/>' +
- '</numFmts>' +
- '<fonts count="5" x14ac:knownFonts="1">' +
- '<font>' +
- '<sz val="11" />' +
- '<name val="Calibri" />' +
- '</font>' +
- '<font>' +
- '<sz val="11" />' +
- '<name val="Calibri" />' +
- '<color rgb="FFFFFFFF" />' +
- '</font>' +
- '<font>' +
- '<sz val="11" />' +
- '<name val="Calibri" />' +
- '<b />' +
- '</font>' +
- '<font>' +
- '<sz val="11" />' +
- '<name val="Calibri" />' +
- '<i />' +
- '</font>' +
- '<font>' +
- '<sz val="11" />' +
- '<name val="Calibri" />' +
- '<u />' +
- '</font>' +
- '</fonts>' +
- '<fills count="6">' +
- '<fill>' +
- '<patternFill patternType="none" />' +
- '</fill>' +
- '<fill>' + // Excel appears to use this as a dotted background regardless of values but
- '<patternFill patternType="none" />' + // to be valid to the schema, use a patternFill
- '</fill>' +
- '<fill>' +
- '<patternFill patternType="solid">' +
- '<fgColor rgb="FFD9D9D9" />' +
- '<bgColor indexed="64" />' +
- '</patternFill>' +
- '</fill>' +
- '<fill>' +
- '<patternFill patternType="solid">' +
- '<fgColor rgb="FFD99795" />' +
- '<bgColor indexed="64" />' +
- '</patternFill>' +
- '</fill>' +
- '<fill>' +
- '<patternFill patternType="solid">' +
- '<fgColor rgb="ffc6efce" />' +
- '<bgColor indexed="64" />' +
- '</patternFill>' +
- '</fill>' +
- '<fill>' +
- '<patternFill patternType="solid">' +
- '<fgColor rgb="ffc6cfef" />' +
- '<bgColor indexed="64" />' +
- '</patternFill>' +
- '</fill>' +
- '</fills>' +
- '<borders count="2">' +
- '<border>' +
- '<left />' +
- '<right />' +
- '<top />' +
- '<bottom />' +
- '<diagonal />' +
- '</border>' +
- '<border diagonalUp="false" diagonalDown="false">' +
- '<left style="thin">' +
- '<color auto="1" />' +
- '</left>' +
- '<right style="thin">' +
- '<color auto="1" />' +
- '</right>' +
- '<top style="thin">' +
- '<color auto="1" />' +
- '</top>' +
- '<bottom style="thin">' +
- '<color auto="1" />' +
- '</bottom>' +
- '<diagonal />' +
- '</border>' +
- '</borders>' +
- '<cellStyleXfs count="1">' +
- '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" />' +
- '</cellStyleXfs>' +
- '<cellXfs count="68">' +
- '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="1" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="2" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="3" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="4" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="0" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="1" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="2" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="3" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="4" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="0" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="1" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="2" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="3" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="4" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="0" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="1" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="2" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="3" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="4" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="0" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="1" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="2" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="3" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="4" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="0" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="1" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="2" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="3" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="4" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="0" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="1" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="2" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="3" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="4" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="0" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="1" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="2" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="3" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="4" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="0" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="1" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="2" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="3" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="4" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="0" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="1" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="2" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="3" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="4" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
- '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
- '<alignment horizontal="left"/>' +
- '</xf>' +
- '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
- '<alignment horizontal="center"/>' +
- '</xf>' +
- '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
- '<alignment horizontal="right"/>' +
- '</xf>' +
- '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
- '<alignment horizontal="fill"/>' +
- '</xf>' +
- '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
- '<alignment textRotation="90"/>' +
- '</xf>' +
- '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
- '<alignment wrapText="1"/>' +
- '</xf>' +
- '<xf numFmtId="9" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
- '<xf numFmtId="164" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
- '<xf numFmtId="165" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
- '<xf numFmtId="166" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
- '<xf numFmtId="167" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
- '<xf numFmtId="168" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
- '<xf numFmtId="169" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
- '<xf numFmtId="3" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
- '<xf numFmtId="4" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
- '<xf numFmtId="1" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
- '<xf numFmtId="2" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
- '<xf numFmtId="14" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
- '</cellXfs>' +
- '<cellStyles count="1">' +
- '<cellStyle name="Normal" xfId="0" builtinId="0" />' +
- '</cellStyles>' +
- '<dxfs count="0" />' +
- '<tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleMedium4" />' +
- '</styleSheet>'
- };
- // Note we could use 3 `for` loops for the styles, but when gzipped there is
- // virtually no difference in size, since the above can be easily compressed
- // Pattern matching for special number formats. Perhaps this should be exposed
- // via an API in future?
- // Ref: section 3.8.30 - built in formatters in open spreadsheet
- // https://www.ecma-international.org/news/TC45_current_work/Office%20Open%20XML%20Part%204%20-%20Markup%20Language%20Reference.pdf
- var _excelSpecials = [
- {
- match: /^\-?\d+\.\d%$/,
- style: 60,
- fmt: function (d) {
- return d / 100;
- }
- }, // Percent with d.p.
- {
- match: /^\-?\d+\.?\d*%$/,
- style: 56,
- fmt: function (d) {
- return d / 100;
- }
- }, // Percent
- { match: /^\-?\$[\d,]+.?\d*$/, style: 57 }, // Dollars
- { match: /^\-?£[\d,]+.?\d*$/, style: 58 }, // Pounds
- { match: /^\-?€[\d,]+.?\d*$/, style: 59 }, // Euros
- { match: /^\-?\d+$/, style: 65 }, // Numbers without thousand separators
- { match: /^\-?\d+\.\d{2}$/, style: 66 }, // Numbers 2 d.p. without thousands separators
- {
- match: /^\([\d,]+\)$/,
- style: 61,
- fmt: function (d) {
- return -1 * d.replace(/[\(\)]/g, '');
- }
- }, // Negative numbers indicated by brackets
- {
- match: /^\([\d,]+\.\d{2}\)$/,
- style: 62,
- fmt: function (d) {
- return -1 * d.replace(/[\(\)]/g, '');
- }
- }, // Negative numbers indicated by brackets - 2d.p.
- { match: /^\-?[\d,]+$/, style: 63 }, // Numbers with thousand separators
- { match: /^\-?[\d,]+\.\d{2}$/, style: 64 },
- {
- match: /^[\d]{4}\-[01][\d]\-[0123][\d]$/,
- style: 67,
- fmt: function (d) {
- return Math.round(25569 + Date.parse(d) / (86400 * 1000));
- }
- } //Date yyyy-mm-dd
- ];
- var _excelMergeCells = function (rels, row, column, rowspan, colspan) {
- var mergeCells = $('mergeCells', rels);
- mergeCells[0].appendChild(
- _createNode(rels, 'mergeCell', {
- attr: {
- ref:
- createCellPos(column) +
- row +
- ':' +
- createCellPos(column + colspan - 1) +
- (row + rowspan - 1)
- }
- })
- );
- mergeCells.attr('count', parseFloat(mergeCells.attr('count')) + 1);
- };
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Buttons
- */
- //
- // Copy to clipboard
- //
- DataTable.ext.buttons.copyHtml5 = {
- className: 'buttons-copy buttons-html5',
- text: function (dt) {
- return dt.i18n('buttons.copy', 'Copy');
- },
- action: function (e, dt, button, config, cb) {
- var exportData = _exportData(dt, config);
- var info = dt.buttons.exportInfo(config);
- var newline = _newLine(config);
- var output = exportData.str;
- var hiddenDiv = $('<div/>').css({
- height: 1,
- width: 1,
- overflow: 'hidden',
- position: 'fixed',
- top: 0,
- left: 0
- });
- if (info.title) {
- output = info.title + newline + newline + output;
- }
- if (info.messageTop) {
- output = info.messageTop + newline + newline + output;
- }
- if (info.messageBottom) {
- output = output + newline + newline + info.messageBottom;
- }
- if (config.customize) {
- output = config.customize(output, config, dt);
- }
- var textarea = $('<textarea readonly/>')
- .val(output)
- .appendTo(hiddenDiv);
- // For browsers that support the copy execCommand, try to use it
- if (document.queryCommandSupported('copy')) {
- hiddenDiv.appendTo(dt.table().container());
- textarea[0].focus();
- textarea[0].select();
- try {
- var successful = document.execCommand('copy');
- hiddenDiv.remove();
- if (successful) {
- dt.buttons.info(
- dt.i18n('buttons.copyTitle', 'Copy to clipboard'),
- dt.i18n(
- 'buttons.copySuccess',
- {
- 1: 'Copied one row to clipboard',
- _: 'Copied %d rows to clipboard'
- },
- exportData.rows
- ),
- 2000
- );
- cb();
- return;
- }
- } catch (t) {
- // noop
- }
- }
- // Otherwise we show the text box and instruct the user to use it
- var message = $(
- '<span>' +
- dt.i18n(
- 'buttons.copyKeys',
- 'Press <i>ctrl</i> or <i>\u2318</i> + <i>C</i> to copy the table data<br>to your system clipboard.<br><br>' +
- 'To cancel, click this message or press escape.'
- ) +
- '</span>'
- ).append(hiddenDiv);
- dt.buttons.info(
- dt.i18n('buttons.copyTitle', 'Copy to clipboard'),
- message,
- 0
- );
- // Select the text so when the user activates their system clipboard
- // it will copy that text
- textarea[0].focus();
- textarea[0].select();
- // Event to hide the message when the user is done
- var container = $(message).closest('.dt-button-info');
- var close = function () {
- container.off('click.buttons-copy');
- $(document).off('.buttons-copy');
- dt.buttons.info(false);
- };
- container.on('click.buttons-copy', close);
- $(document)
- .on('keydown.buttons-copy', function (e) {
- if (e.keyCode === 27) {
- // esc
- close();
- cb();
- }
- })
- .on('copy.buttons-copy cut.buttons-copy', function () {
- close();
- cb();
- });
- },
- async: 100,
- exportOptions: {},
- fieldSeparator: '\t',
- fieldBoundary: '',
- header: true,
- footer: true,
- title: '*',
- messageTop: '*',
- messageBottom: '*'
- };
- //
- // CSV export
- //
- DataTable.ext.buttons.csvHtml5 = {
- bom: false,
- className: 'buttons-csv buttons-html5',
- available: function () {
- return window.FileReader !== undefined && window.Blob;
- },
- text: function (dt) {
- return dt.i18n('buttons.csv', 'CSV');
- },
- action: function (e, dt, button, config, cb) {
- // Set the text
- var output = _exportData(dt, config).str;
- var info = dt.buttons.exportInfo(config);
- var charset = config.charset;
- if (config.customize) {
- output = config.customize(output, config, dt);
- }
- if (charset !== false) {
- if (!charset) {
- charset = document.characterSet || document.charset;
- }
- if (charset) {
- charset = ';charset=' + charset;
- }
- }
- else {
- charset = '';
- }
- if (config.bom) {
- output = String.fromCharCode(0xfeff) + output;
- }
- _saveAs(
- new Blob([output], { type: 'text/csv' + charset }),
- info.filename,
- true
- );
- cb();
- },
- async: 100,
- filename: '*',
- extension: '.csv',
- exportOptions: {},
- fieldSeparator: ',',
- fieldBoundary: '"',
- escapeChar: '"',
- charset: null,
- header: true,
- footer: true
- };
- //
- // Excel (xlsx) export
- //
- DataTable.ext.buttons.excelHtml5 = {
- className: 'buttons-excel buttons-html5',
- available: function () {
- return (
- window.FileReader !== undefined &&
- _jsZip() !== undefined &&
- !_isDuffSafari() &&
- _serialiser
- );
- },
- text: function (dt) {
- return dt.i18n('buttons.excel', 'Excel');
- },
- action: function (e, dt, button, config, cb) {
- var rowPos = 0;
- var dataStartRow, dataEndRow;
- var getXml = function (type) {
- var str = excelStrings[type];
- //str = str.replace( /xmlns:/g, 'xmlns_' ).replace( /mc:/g, 'mc_' );
- return $.parseXML(str);
- };
- var rels = getXml('xl/worksheets/sheet1.xml');
- var relsGet = rels.getElementsByTagName('sheetData')[0];
- var xlsx = {
- _rels: {
- '.rels': getXml('_rels/.rels')
- },
- xl: {
- _rels: {
- 'workbook.xml.rels': getXml('xl/_rels/workbook.xml.rels')
- },
- 'workbook.xml': getXml('xl/workbook.xml'),
- 'styles.xml': getXml('xl/styles.xml'),
- worksheets: {
- 'sheet1.xml': rels
- }
- },
- '[Content_Types].xml': getXml('[Content_Types].xml')
- };
- var data = dt.buttons.exportData(config.exportOptions);
- var currentRow, rowNode;
- var addRow = function (row) {
- currentRow = rowPos + 1;
- rowNode = _createNode(rels, 'row', { attr: { r: currentRow } });
- for (var i = 0, ien = row.length; i < ien; i++) {
- // Concat both the Cell Columns as a letter and the Row of the cell.
- var cellId = createCellPos(i) + '' + currentRow;
- var cell = null;
- // For null, undefined of blank cell, continue so it doesn't create the _createNode
- if (row[i] === null || row[i] === undefined || row[i] === '') {
- if (config.createEmptyCells === true) {
- row[i] = '';
- }
- else {
- continue;
- }
- }
- var originalContent = row[i];
- row[i] =
- typeof row[i].trim === 'function' ? row[i].trim() : row[i];
- // Special number formatting options
- for (var j = 0, jen = _excelSpecials.length; j < jen; j++) {
- var special = _excelSpecials[j];
- // TODO Need to provide the ability for the specials to say
- // if they are returning a string, since at the moment it is
- // assumed to be a number
- if (
- row[i].match &&
- !row[i].match(/^0\d+/) &&
- row[i].match(special.match)
- ) {
- var val = row[i].replace(/[^\d\.\-]/g, '');
- if (special.fmt) {
- val = special.fmt(val);
- }
- cell = _createNode(rels, 'c', {
- attr: {
- r: cellId,
- s: special.style
- },
- children: [_createNode(rels, 'v', { text: val })]
- });
- break;
- }
- }
- if (!cell) {
- if (
- typeof row[i] === 'number' ||
- (row[i].match &&
- row[i].match(/^-?\d+(\.\d+)?([eE]\-?\d+)?$/) && // Includes exponential format
- !row[i].match(/^0\d+/))
- ) {
- // Detect numbers - don't match numbers with leading zeros
- // or a negative anywhere but the start
- cell = _createNode(rels, 'c', {
- attr: {
- t: 'n',
- r: cellId
- },
- children: [_createNode(rels, 'v', { text: row[i] })]
- });
- }
- else {
- // String output - replace non standard characters for text output
- /*eslint no-control-regex: "off"*/
- var text = !originalContent.replace
- ? originalContent
- : originalContent.replace(
- /[\x00-\x09\x0B\x0C\x0E-\x1F\x7F-\x9F]/g,
- ''
- );
- cell = _createNode(rels, 'c', {
- attr: {
- t: 'inlineStr',
- r: cellId
- },
- children: {
- row: _createNode(rels, 'is', {
- children: {
- row: _createNode(rels, 't', {
- text: text,
- attr: {
- 'xml:space': 'preserve'
- }
- })
- }
- })
- }
- });
- }
- }
- rowNode.appendChild(cell);
- }
- relsGet.appendChild(rowNode);
- rowPos++;
- };
- var addHeader = function (structure) {
- structure.forEach(function (row) {
- addRow(
- row.map(function (cell) {
- return cell ? cell.title : '';
- }),
- rowPos
- );
- $('row:last c', rels).attr('s', '2'); // bold
- // Add any merge cells
- row.forEach(function (cell, columnCounter) {
- if (cell && (cell.colSpan > 1 || cell.rowSpan > 1)) {
- _excelMergeCells(
- rels,
- rowPos,
- columnCounter,
- cell.rowSpan,
- cell.colSpan
- );
- }
- });
- });
- };
- if (config.customizeData) {
- config.customizeData(data);
- }
- // Title and top messages
- var exportInfo = dt.buttons.exportInfo(config);
- if (exportInfo.title) {
- addRow([exportInfo.title], rowPos);
- _excelMergeCells(rels, rowPos, 0, 1, data.header.length);
- $('row:last c', rels).attr('s', '51'); // centre
- }
- if (exportInfo.messageTop) {
- addRow([exportInfo.messageTop], rowPos);
- _excelMergeCells(rels, rowPos, 0, 1, data.header.length);
- }
- // Table header
- if (config.header) {
- addHeader(data.headerStructure);
- }
- dataStartRow = rowPos;
- // Table body
- for (var n = 0, ie = data.body.length; n < ie; n++) {
- addRow(data.body[n], rowPos);
- }
- dataEndRow = rowPos;
- // Table footer
- if (config.footer && data.footer) {
- addHeader(data.footerStructure);
- }
- // Below the table
- if (exportInfo.messageBottom) {
- addRow([exportInfo.messageBottom], rowPos);
- _excelMergeCells(rels, rowPos, 0, 1, data.header.length);
- }
- // Set column widths
- var cols = _createNode(rels, 'cols');
- $('worksheet', rels).prepend(cols);
- for (var i = 0, ien = data.header.length; i < ien; i++) {
- cols.appendChild(
- _createNode(rels, 'col', {
- attr: {
- min: i + 1,
- max: i + 1,
- width: _excelColWidth(data, i),
- customWidth: 1
- }
- })
- );
- }
- // Workbook modifications
- var workbook = xlsx.xl['workbook.xml'];
- $('sheets sheet', workbook).attr('name', _sheetname(config));
- // Auto filter for columns
- if (config.autoFilter) {
- $('mergeCells', rels).before(
- _createNode(rels, 'autoFilter', {
- attr: {
- ref:
- 'A' +
- dataStartRow +
- ':' +
- createCellPos(data.header.length - 1) +
- dataEndRow
- }
- })
- );
- $('definedNames', workbook).append(
- _createNode(workbook, 'definedName', {
- attr: {
- name: '_xlnm._FilterDatabase',
- localSheetId: '0',
- hidden: 1
- },
- text:
- '\'' +
- _sheetname(config).replace(/'/g, '\'\'') +
- '\'!$A$' +
- dataStartRow +
- ':' +
- createCellPos(data.header.length - 1) +
- dataEndRow
- })
- );
- }
- // Let the developer customise the document if they want to
- if (config.customize) {
- config.customize(xlsx, config, dt);
- }
- // Excel doesn't like an empty mergeCells tag
- if ($('mergeCells', rels).children().length === 0) {
- $('mergeCells', rels).remove();
- }
- var jszip = _jsZip();
- var zip = new jszip();
- var zipConfig = {
- compression: 'DEFLATE',
- type: 'blob',
- mimeType:
- 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
- };
- _addToZip(zip, xlsx);
- // Modern Excel has a 218 character limit on the file name + path of the file (why!?)
- // https://support.microsoft.com/en-us/office/excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3
- // So we truncate to allow for this.
- var filename = exportInfo.filename;
- if (filename > 175) {
- filename = filename.substr(0, 175);
- }
- // Let the developer customize the final zip file if they want to before it is generated and sent to the browser
- if (config.customizeZip) {
- config.customizeZip(zip, data, filename);
- }
- if (zip.generateAsync) {
- // JSZip 3+
- zip.generateAsync(zipConfig).then(function (blob) {
- _saveAs(blob, filename);
- cb();
- });
- }
- else {
- // JSZip 2.5
- _saveAs(zip.generate(zipConfig), filename);
- cb();
- }
- },
- async: 100,
- filename: '*',
- extension: '.xlsx',
- exportOptions: {},
- header: true,
- footer: true,
- title: '*',
- messageTop: '*',
- messageBottom: '*',
- createEmptyCells: false,
- autoFilter: false,
- sheetName: ''
- };
- //
- // PDF export - using pdfMake - http://pdfmake.org
- //
- DataTable.ext.buttons.pdfHtml5 = {
- className: 'buttons-pdf buttons-html5',
- available: function () {
- return window.FileReader !== undefined && _pdfMake();
- },
- text: function (dt) {
- return dt.i18n('buttons.pdf', 'PDF');
- },
- action: function (e, dt, button, config, cb) {
- var data = dt.buttons.exportData(config.exportOptions);
- var info = dt.buttons.exportInfo(config);
- var rows = [];
- if (config.header) {
- data.headerStructure.forEach(function (row) {
- rows.push(
- row.map(function (cell) {
- return cell
- ? {
- text: cell.title,
- colSpan: cell.colspan,
- rowSpan: cell.rowspan,
- style: 'tableHeader'
- }
- : {};
- })
- );
- });
- }
- for (var i = 0, ien = data.body.length; i < ien; i++) {
- rows.push(
- data.body[i].map(function (d) {
- return {
- text:
- d === null || d === undefined
- ? ''
- : typeof d === 'string'
- ? d
- : d.toString()
- };
- })
- );
- }
- if (config.footer) {
- data.footerStructure.forEach(function (row) {
- rows.push(
- row.map(function (cell) {
- return cell
- ? {
- text: cell.title,
- colSpan: cell.colspan,
- rowSpan: cell.rowspan,
- style: 'tableHeader'
- }
- : {};
- })
- );
- });
- }
- var doc = {
- pageSize: config.pageSize,
- pageOrientation: config.orientation,
- content: [
- {
- style: 'table',
- table: {
- headerRows: data.headerStructure.length,
- footerRows: data.footerStructure.length, // Used for styling, doesn't do anything in pdfmake
- body: rows
- },
- layout: {
- hLineWidth: function (i, node) {
- if (i === 0 || i === node.table.body.length) {
- return 0;
- }
- return 0.5;
- },
- vLineWidth: function () {
- return 0;
- },
- hLineColor: function (i, node) {
- return i === node.table.headerRows ||
- i ===
- node.table.body.length -
- node.table.footerRows
- ? '#333'
- : '#ddd';
- },
- fillColor: function (rowIndex) {
- if (rowIndex < data.headerStructure.length) {
- return '#fff';
- }
- return rowIndex % 2 === 0 ? '#f3f3f3' : null;
- },
- paddingTop: function () {
- return 5;
- },
- paddingBottom: function () {
- return 5;
- }
- }
- }
- ],
- styles: {
- tableHeader: {
- bold: true,
- fontSize: 11,
- alignment: 'center'
- },
- tableFooter: {
- bold: true,
- fontSize: 11
- },
- table: {
- margin: [0, 5, 0, 5]
- },
- title: {
- alignment: 'center',
- fontSize: 13
- },
- message: {}
- },
- defaultStyle: {
- fontSize: 10
- }
- };
- if (info.messageTop) {
- doc.content.unshift({
- text: info.messageTop,
- style: 'message',
- margin: [0, 0, 0, 12]
- });
- }
- if (info.messageBottom) {
- doc.content.push({
- text: info.messageBottom,
- style: 'message',
- margin: [0, 0, 0, 12]
- });
- }
- if (info.title) {
- doc.content.unshift({
- text: info.title,
- style: 'title',
- margin: [0, 0, 0, 12]
- });
- }
- if (config.customize) {
- config.customize(doc, config, dt);
- }
- var pdf = _pdfMake().createPdf(doc);
- if (config.download === 'open' && !_isDuffSafari()) {
- pdf.open();
- }
- else {
- pdf.download(info.filename);
- }
- cb();
- },
- async: 100,
- title: '*',
- filename: '*',
- extension: '.pdf',
- exportOptions: {},
- orientation: 'portrait',
- // This isn't perfect, but it is close
- pageSize:
- navigator.language === 'en-US' || navigator.language === 'en-CA'
- ? 'LETTER'
- : 'A4',
- header: true,
- footer: true,
- messageTop: '*',
- messageBottom: '*',
- customize: null,
- download: 'download'
- };
- return DataTable;
- }));
- /*!
- * Print button for Buttons and DataTables.
- * © SpryMedia Ltd - datatables.net/license
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net', 'datatables.net-buttons'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- var jq = require('jquery');
- var cjsRequires = function (root, $) {
- if ( ! $.fn.dataTable ) {
- require('datatables.net')(root, $);
- }
- if ( ! $.fn.dataTable.Buttons ) {
- require('datatables.net-buttons')(root, $);
- }
- };
- if (typeof window === 'undefined') {
- 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 ( ! $ ) {
- $ = jq( root );
- }
- cjsRequires( root, $ );
- return factory( $, root, root.document );
- };
- }
- else {
- cjsRequires( window, jq );
- module.exports = factory( jq, window, window.document );
- }
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- var _link = document.createElement('a');
- /**
- * Clone link and style tags, taking into account the need to change the source
- * path.
- *
- * @param {node} el Element to convert
- */
- var _styleToAbs = function (el) {
- var clone = $(el).clone()[0];
- if (clone.nodeName.toLowerCase() === 'link') {
- clone.href = _relToAbs(clone.href);
- }
- return clone.outerHTML;
- };
- /**
- * Convert a URL from a relative to an absolute address so it will work
- * correctly in the popup window which has no base URL.
- *
- * @param {string} href URL
- */
- var _relToAbs = function (href) {
- // Assign to a link on the original page so the browser will do all the
- // hard work of figuring out where the file actually is
- _link.href = href;
- var linkHost = _link.host;
- // IE doesn't have a trailing slash on the host
- // Chrome has it on the pathname
- if (linkHost.indexOf('/') === -1 && _link.pathname.indexOf('/') !== 0) {
- linkHost += '/';
- }
- return _link.protocol + '//' + linkHost + _link.pathname + _link.search;
- };
- DataTable.ext.buttons.print = {
- className: 'buttons-print',
- text: function (dt) {
- return dt.i18n('buttons.print', 'Print');
- },
- action: function (e, dt, button, config, cb) {
- var data = dt.buttons.exportData(
- $.extend({ decodeEntities: false }, config.exportOptions) // XSS protection
- );
- var exportInfo = dt.buttons.exportInfo(config);
- // Get the classes for the columns from the header cells
- var columnClasses = dt
- .columns(config.exportOptions.columns)
- .nodes()
- .map(function (n) {
- return n.className;
- })
- .toArray();
- var addRow = function (d, tag) {
- var str = '<tr>';
- for (var i = 0, ien = d.length; i < ien; i++) {
- // null and undefined aren't useful in the print output
- var dataOut = d[i] === null || d[i] === undefined ? '' : d[i];
- var classAttr = columnClasses[i]
- ? 'class="' + columnClasses[i] + '"'
- : '';
- str +=
- '<' +
- tag +
- ' ' +
- classAttr +
- '>' +
- dataOut +
- '</' +
- tag +
- '>';
- }
- return str + '</tr>';
- };
- // Construct a table for printing
- var html = '<table class="' + dt.table().node().className + '">';
- if (config.header) {
- var headerRows = data.headerStructure.map(function (row) {
- return (
- '<tr>' +
- row
- .map(function (cell) {
- return cell
- ? '<th colspan="' +
- cell.colspan +
- '" rowspan="' +
- cell.rowspan +
- '">' +
- cell.title +
- '</th>'
- : '';
- })
- .join('') +
- '</tr>'
- );
- });
- html += '<thead>' + headerRows.join('') + '</thead>';
- }
- html += '<tbody>';
- for (var i = 0, ien = data.body.length; i < ien; i++) {
- html += addRow(data.body[i], 'td');
- }
- html += '</tbody>';
- if (config.footer && data.footer) {
- var footerRows = data.footerStructure.map(function (row) {
- return (
- '<tr>' +
- row
- .map(function (cell) {
- return cell
- ? '<th colspan="' +
- cell.colspan +
- '" rowspan="' +
- cell.rowspan +
- '">' +
- cell.title +
- '</th>'
- : '';
- })
- .join('') +
- '</tr>'
- );
- });
- html += '<tfoot>' + footerRows.join('') + '</tfoot>';
- }
- html += '</table>';
- // Open a new window for the printable table
- var win = window.open('', '');
- if (!win) {
- dt.buttons.info(
- dt.i18n('buttons.printErrorTitle', 'Unable to open print view'),
- dt.i18n(
- 'buttons.printErrorMsg',
- 'Please allow popups in your browser for this site to be able to view the print view.'
- ),
- 5000
- );
- return;
- }
- win.document.close();
- // Inject the title and also a copy of the style and link tags from this
- // document so the table can retain its base styling. Note that we have
- // to use string manipulation as IE won't allow elements to be created
- // in the host document and then appended to the new window.
- var head = '<title>' + exportInfo.title + '</title>';
- $('style, link').each(function () {
- head += _styleToAbs(this);
- });
- try {
- win.document.head.innerHTML = head; // Work around for Edge
- } catch (e) {
- $(win.document.head).html(head); // Old IE
- }
- // Inject the table and other surrounding information
- win.document.body.innerHTML =
- '<h1>' +
- exportInfo.title +
- '</h1>' +
- '<div>' +
- (exportInfo.messageTop || '') +
- '</div>' +
- html +
- '<div>' +
- (exportInfo.messageBottom || '') +
- '</div>';
- $(win.document.body).addClass('dt-print-view');
- $('img', win.document.body).each(function (i, img) {
- img.setAttribute('src', _relToAbs(img.getAttribute('src')));
- });
- if (config.customize) {
- config.customize(win, config, dt);
- }
- // Allow stylesheets time to load
- var autoPrint = function () {
- if (config.autoPrint) {
- win.print(); // blocking - so close will not
- win.close(); // execute until this is done
- }
- };
- win.setTimeout(autoPrint, 1000);
- cb();
- },
- async: 100,
- title: '*',
- messageTop: '*',
- messageBottom: '*',
- exportOptions: {},
- header: true,
- footer: true,
- autoPrint: true,
- customize: null
- };
- return DataTable;
- }));
- /*! FixedHeader 4.0.1
- * © 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
- var jq = require('jquery');
- var cjsRequires = function (root, $) {
- if ( ! $.fn.dataTable ) {
- require('datatables.net')(root, $);
- }
- };
- if (typeof window === 'undefined') {
- 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 ( ! $ ) {
- $ = jq( root );
- }
- cjsRequires( root, $ );
- return factory( $, root, root.document );
- };
- }
- else {
- cjsRequires( window, jq );
- module.exports = factory( jq, window, window.document );
- }
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- /**
- * @summary FixedHeader
- * @description Fix a table's header or footer, so it is always visible while
- * scrolling
- * @version 4.0.1
- * @author SpryMedia Ltd
- * @contact datatables.net
- *
- * 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
- */
- var _instCounter = 0;
- var FixedHeader = function (dt, config) {
- if (!DataTable.versionCheck('2')) {
- throw 'Warning: FixedHeader requires DataTables 2 or newer';
- }
- // 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,
- autoDisable: false
- };
- this.dom = {
- floatingHeader: null,
- thead: $(dt.table().header()),
- tbody: $(dt.table().body()),
- tfoot: $(dt.table().footer()),
- header: {
- host: null,
- floating: null,
- floatingParent: $('<div class="dtfh-floatingparent"><div></div></div>'),
- placeholder: null
- },
- footer: {
- host: null,
- floating: null,
- floatingParent: $('<div class="dtfh-floatingparent"><div></div></div>'),
- 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
- */
- /**
- * Kill off FH and any events
- */
- destroy: function () {
- var dom = this.dom;
- this.s.dt.off('.dtfc');
- $(window).off(this.s.namespace);
- // Remove clones of FC blockers
- if (dom.header.rightBlocker) {
- dom.header.rightBlocker.remove();
- }
- if (dom.header.leftBlocker) {
- dom.header.leftBlocker.remove();
- }
- if (dom.footer.rightBlocker) {
- dom.footer.rightBlocker.remove();
- }
- if (dom.footer.leftBlocker) {
- dom.footer.leftBlocker.remove();
- }
- if (this.c.header) {
- this._modeChange('in-place', 'header', true);
- }
- if (this.c.footer && dom.tfoot.length) {
- this._modeChange('in-place', 'footer', true);
- }
- },
- /**
- * Enable / disable the fixed elements
- *
- * @param {boolean} enable `true` to enable, `false` to disable
- */
- enable: function (enable, update, type) {
- this.s.enable = enable;
- this.s.enableType = type;
- if (update || update === undefined) {
- this._positions();
- this._scroll(true);
- }
- },
- /**
- * Get enabled status
- */
- enabled: function () {
- return this.s.enable;
- },
- /**
- * 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 (force) {
- var table = this.s.dt.table().node();
- // Update should only do something if enabled by the dev.
- if (!this.s.enable && !this.s.autoDisable) {
- return;
- }
- if ($(table).is(':visible')) {
- this.s.autoDisable = false;
- this.enable(true, false);
- }
- else {
- this.s.autoDisable = true;
- this.enable(false, false);
- }
- // Don't update if header is not in the document atm (due to
- // async events)
- if ($(table).children('thead').length === 0) {
- return;
- }
- this._positions();
- this._scroll(force !== undefined ? force : true);
- this._widths(this.dom.header);
- this._widths(this.dom.footer);
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * 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,
- DataTable.util.throttle(function () {
- that.s.position.windowHeight = $(window).height();
- that.update();
- }, 50)
- );
- 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 column-sizing.dt.dtfc responsive-display.dt.dtfc',
- function (e, ctx) {
- that.update();
- }
- ).on('draw.dt.dtfc', function (e, ctx) {
- // For updates from our own table, don't reclone, but for all others, do
- that.update(ctx === dt.settings()[0] ? false : true);
- });
- dt.on('destroy.dtfc', function () {
- that.destroy();
- });
- 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 that = this;
- var dt = this.s.dt;
- var itemDom = this.dom[item];
- var itemElement = item === 'header' ? this.dom.thead : this.dom.tfoot;
- // If footer and scrolling is enabled then we don't clone
- // Instead the table's height is decreased accordingly - see `_scroll()`
- if (item === 'footer' && this._scrollEnabled()) {
- return;
- }
- if (!force && itemDom.floating) {
- // existing floating element - reuse it
- itemDom.floating.removeClass(
- 'fixedHeader-floating fixedHeader-locked'
- );
- }
- else {
- if (itemDom.floating) {
- if (itemDom.placeholder !== null) {
- itemDom.placeholder.remove();
- }
- itemDom.floating.children().detach();
- itemDom.floating.remove();
- }
- var tableNode = $(dt.table().node());
- var scrollBody = $(tableNode.parent());
- var scrollEnabled = this._scrollEnabled();
- itemDom.floating = $(dt.table().node().cloneNode(false))
- .attr('aria-hidden', 'true')
- .css({
- top: 0,
- left: 0
- })
- .removeAttr('id');
- itemDom.floatingParent
- .css({
- width: scrollBody[0].offsetWidth,
- overflow: 'hidden',
- height: 'fit-content',
- position: 'fixed',
- left: scrollEnabled
- ? tableNode.offset().left + scrollBody.scrollLeft()
- : 0
- })
- .css(
- item === 'header'
- ? {
- top: this.c.headerOffset,
- bottom: ''
- }
- : {
- top: '',
- bottom: this.c.footerOffset
- }
- )
- .addClass(
- item === 'footer'
- ? 'dtfh-floatingparent-foot'
- : 'dtfh-floatingparent-head'
- )
- .appendTo('body')
- .children()
- .eq(0)
- .append(itemDom.floating);
- this._stickyPosition(itemDom.floating, '-');
- var scrollLeftUpdate = function () {
- var scrollLeft = scrollBody.scrollLeft();
- that.s.scrollLeft = { footer: scrollLeft, header: scrollLeft };
- itemDom.floatingParent.scrollLeft(that.s.scrollLeft.header);
- };
- scrollLeftUpdate();
- scrollBody.off('scroll.dtfh').on('scroll.dtfh', scrollLeftUpdate);
- // Need padding on the header's container to allow for a scrollbar,
- // just like how DataTables handles it
- itemDom.floatingParent.children().css({
- width: 'fit-content',
- paddingRight: that.s.dt.settings()[0].oBrowser.barWidth
- });
- // Blocker to hide the table behind the scrollbar - this needs to use
- // fixed positioning in the container since we don't have an outer wrapper
- let blocker = $(
- item === 'footer'
- ? 'div.dtfc-bottom-blocker'
- : 'div.dtfc-top-blocker',
- dt.table().container()
- );
- if (blocker.length) {
- blocker
- .clone()
- .appendTo(itemDom.floatingParent)
- .css({
- position: 'fixed',
- right: blocker.width()
- });
- }
- // Insert a fake thead/tfoot into the DataTable to stop it jumping around
- itemDom.placeholder = itemElement.clone(false);
- itemDom.placeholder.find('*[id]').removeAttr('id');
- // Move the thead / tfoot elements around - original into the floating
- // element and clone into the original table
- itemDom.host.prepend(itemDom.placeholder);
- itemDom.floating.append(itemElement);
- this._widths(itemDom);
- }
- },
- /**
- * This method sets the sticky position of the header elements to match fixed columns
- * @param {JQuery<HTMLElement>} el
- * @param {string} sign
- */
- _stickyPosition: function (el, sign) {
- if (this._scrollEnabled()) {
- var that = this;
- var rtl = $(that.s.dt.table().node()).css('direction') === 'rtl';
- el.find('th').each(function () {
- // Find out if fixed header has previously set this column
- if ($(this).css('position') === 'sticky') {
- var right = $(this).css('right');
- var left = $(this).css('left');
- var potential;
- if (right !== 'auto' && !rtl) {
- potential = +right.replace(/px/g, '')
- $(this).css('right', potential > 0 ? potential : 0);
- }
- else if (left !== 'auto' && rtl) {
- potential = +left.replace(/px/g, '');
- $(this).css('left', potential > 0 ? potential : 0);
- }
- }
- });
- }
- },
- /**
- * 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 lastScrollLeft = this.s.scrollLeft;
- if (itemDom.floating && lastScrollLeft[item] !== scrollLeft) {
- // If scrolling is enabled we need to match the floating header to the body
- if (this._scrollEnabled()) {
- var newScrollLeft = $(
- $(this.s.dt.table().node()).parent()
- ).scrollLeft();
- itemDom.floating.scrollLeft(newScrollLeft);
- itemDom.floatingParent.scrollLeft(newScrollLeft);
- }
- 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 itemDom = this.dom[item];
- var position = this.s.position;
- // Just determine if scroll is enabled once
- var scrollEnabled = this._scrollEnabled();
- // If footer and scrolling is enabled then we don't clone
- // Instead the table's height is decreased accordingly - see `_scroll()`
- if (item === 'footer' && scrollEnabled) {
- return;
- }
- // It isn't trivial to add a !important css attribute...
- var importantWidth = function (w) {
- itemDom.floating[0].style.setProperty('width', w + 'px', 'important');
- // If not scrolling also have to update the floatingParent
- if (!scrollEnabled) {
- itemDom.floatingParent[0].style.setProperty('width', w + 'px', 'important');
- }
- };
- // 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;
- var scrollBody = $($(this.s.dt.table().node()).parent());
- if (mode === 'in-place') {
- // Insert the header back into the table's real header
- if (itemDom.placeholder) {
- itemDom.placeholder.remove();
- itemDom.placeholder = null;
- }
- if (item === 'header') {
- itemDom.host.prepend(tablePart);
- }
- else {
- itemDom.host.append(tablePart);
- }
- if (itemDom.floating) {
- itemDom.floating.remove();
- itemDom.floating = null;
- this._stickyPosition(itemDom.host, '+');
- }
- if (itemDom.floatingParent) {
- itemDom.floatingParent.find('div.dtfc-top-blocker').remove();
- itemDom.floatingParent.remove();
- }
- $($(itemDom.host.parent()).parent()).scrollLeft(
- scrollBody.scrollLeft()
- );
- }
- else if (mode === 'in') {
- // Remove the header from the real table and insert into a fixed
- // positioned floating table clone
- this._clone(item, forceChange);
- // Get useful position values
- var scrollOffset = scrollBody.offset();
- var windowTop = $(document).scrollTop();
- var windowHeight = $(window).height();
- var windowBottom = windowTop + windowHeight;
- var bodyTop = scrollEnabled ? scrollOffset.top : position.tbodyTop;
- var bodyBottom = scrollEnabled
- ? scrollOffset.top + scrollBody.outerHeight()
- : position.tfootTop;
- // Calculate the amount that the footer or header needs to be shuffled
- var shuffle;
- if (item === 'footer') {
- shuffle =
- bodyTop > windowBottom
- ? position.tfootHeight // Yes - push the footer below
- : bodyTop + position.tfootHeight - windowBottom; // No
- }
- else {
- // Otherwise must be a header so get the difference from the bottom of the
- // desired floating header and the bottom of the table body
- shuffle =
- windowTop +
- this.c.headerOffset +
- position.theadHeight -
- bodyBottom;
- }
- // Set the top or bottom based off of the offset and the shuffle value
- var prop = item === 'header' ? 'top' : 'bottom';
- var val = this.c[item + 'Offset'] - (shuffle > 0 ? shuffle : 0);
- itemDom.floating.addClass('fixedHeader-floating');
- itemDom.floatingParent
- .css(prop, val)
- .css({
- left: position.left,
- 'z-index': 3
- });
- importantWidth(position.width);
- 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');
- itemDom.floatingParent.css({
- position: 'absolute',
- top: position.tfootTop - position.theadHeight,
- left: position.left + 'px'
- });
- importantWidth(position.width);
- }
- 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');
- itemDom.floatingParent.css({
- position: 'absolute',
- top: position.tbodyTop,
- left: position.left + 'px'
- });
- importantWidth(position.width);
- }
- // Restore focus if it was lost
- if (focus && focus !== document.activeElement) {
- setTimeout(function () {
- focus.focus();
- }, 10);
- }
- 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());
- var scrollEnabled = this._scrollEnabled();
- // 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 = $(dt.table().header());
- var tfoot = $(dt.table().footer());
- var tbody = dom.tbody;
- var scrollBody = tableNode.parent();
- position.visible = tableNode.is(':visible');
- position.width = tableNode.outerWidth();
- position.left = tableNode.offset().left;
- position.theadTop = thead.offset().top;
- position.tbodyTop = scrollEnabled
- ? scrollBody.offset().top
- : tbody.offset().top;
- position.tbodyHeight = scrollEnabled
- ? scrollBody.outerHeight()
- : tbody.outerHeight();
- position.theadHeight = thead.outerHeight();
- position.theadBottom = position.theadTop + position.theadHeight;
- position.tfootTop = position.tbodyTop + position.tbodyHeight; //tfoot.offset().top;
- if (tfoot.length) {
- position.tfootBottom = position.tfootTop + tfoot.outerHeight();
- position.tfootHeight = tfoot.outerHeight();
- }
- else {
- position.tfootBottom = position.tfootTop;
- position.tfootHeight = 0;
- }
- },
- /**
- * 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) {
- if (this.s.dt.settings()[0].bDestroying) {
- return;
- }
- // ScrollBody details
- var scrollEnabled = this._scrollEnabled();
- var scrollBody = $(this.s.dt.table().node()).parent();
- var scrollOffset = scrollBody.offset();
- var scrollHeight = scrollBody.outerHeight();
- // Window details
- var windowLeft = $(document).scrollLeft();
- var windowTop = $(document).scrollTop();
- var windowHeight = $(window).height();
- var windowBottom = windowHeight + windowTop;
- var position = this.s.position;
- var headerMode, footerMode;
- // Body Details
- var bodyTop = scrollEnabled ? scrollOffset.top : position.tbodyTop;
- var bodyLeft = scrollEnabled ? scrollOffset.left : position.left;
- var bodyBottom = scrollEnabled
- ? scrollOffset.top + scrollHeight
- : position.tfootTop;
- var bodyWidth = scrollEnabled
- ? scrollBody.outerWidth()
- : position.tbodyWidth;
- if (this.c.header) {
- if (!this.s.enable) {
- headerMode = 'in-place';
- }
- // The header is in it's normal place if the body top is lower than
- // the scroll of the window plus the headerOffset and the height of the header
- else if (
- !position.visible ||
- windowTop + this.c.headerOffset + position.theadHeight <=
- bodyTop
- ) {
- headerMode = 'in-place';
- }
- // The header should be floated if
- else if (
- // The scrolling plus the header offset plus the height of the header is lower than the top of the body
- windowTop + this.c.headerOffset + position.theadHeight >
- bodyTop &&
- // And the scrolling at the top plus the header offset is above the bottom of the body
- windowTop + this.c.headerOffset + position.theadHeight <
- bodyBottom
- ) {
- headerMode = 'in';
- // Further to the above, If the scrolling plus the header offset plus the header height is lower
- // than the bottom of the table a shuffle is required so have to force the calculation
- if (
- windowTop + this.c.headerOffset + position.theadHeight >
- bodyBottom ||
- this.dom.header.floatingParent === undefined
- ) {
- forceChange = true;
- }
- else {
- this.dom.header.floatingParent
- .css({
- top: this.c.headerOffset,
- position: 'fixed'
- })
- .children()
- .eq(0)
- .append(this.dom.header.floating);
- }
- }
- // Anything else and the view is below the table
- else {
- headerMode = 'below';
- }
- if (forceChange || headerMode !== this.s.headerMode) {
- this._modeChange(headerMode, 'header', forceChange);
- }
- this._horizontal('header', windowLeft);
- }
- var header = {
- offset: { top: 0, left: 0 },
- height: 0
- };
- var footer = {
- offset: { top: 0, left: 0 },
- height: 0
- };
- if (
- this.c.footer &&
- this.dom.tfoot.length &&
- this.dom.tfoot.find('th, td').length
- ) {
- if (!this.s.enable) {
- footerMode = 'in-place';
- }
- else if (
- !position.visible ||
- position.tfootBottom + this.c.footerOffset <= windowBottom
- ) {
- footerMode = 'in-place';
- }
- else if (
- bodyBottom + position.tfootHeight + this.c.footerOffset >
- windowBottom &&
- bodyTop + this.c.footerOffset < windowBottom
- ) {
- footerMode = 'in';
- forceChange = true;
- }
- else {
- footerMode = 'above';
- }
- if (forceChange || footerMode !== this.s.footerMode) {
- this._modeChange(footerMode, 'footer', forceChange);
- }
- this._horizontal('footer', windowLeft);
- var getOffsetHeight = function (el) {
- return {
- offset: el.offset(),
- height: el.outerHeight()
- };
- };
- header = this.dom.header.floating
- ? getOffsetHeight(this.dom.header.floating)
- : getOffsetHeight(this.dom.thead);
- footer = this.dom.footer.floating
- ? getOffsetHeight(this.dom.footer.floating)
- : getOffsetHeight(this.dom.tfoot);
- // If scrolling is enabled and the footer is off the screen
- if (scrollEnabled && footer.offset.top > windowTop) {
- // && footer.offset.top >= windowBottom) {
- // Calculate the gap between the top of the scrollBody and the top of the window
- var overlap = windowTop - scrollOffset.top;
- // The new height is the bottom of the window
- var newHeight =
- windowBottom +
- // If the gap between the top of the scrollbody and the window is more than
- // the height of the header then the top of the table is still visible so add that gap
- // Doing this has effectively calculated the height from the top of the table to the bottom of the current page
- (overlap > -header.height ? overlap : 0) -
- // Take from that
- // The top of the header plus
- (header.offset.top +
- // The header height if the standard header is present
- (overlap < -header.height ? header.height : 0) +
- // And the height of the footer
- footer.height);
- // Don't want a negative height
- if (newHeight < 0) {
- newHeight = 0;
- }
- // At the end of the above calculation the space between the header (top of the page if floating)
- // and the point just above the footer should be the new value for the height of the table.
- scrollBody.outerHeight(newHeight);
- // Need some rounding here as sometimes very small decimal places are encountered
- // If the actual height is bigger or equal to the height we just applied then the footer is "Floating"
- if (
- Math.round(scrollBody.outerHeight()) >=
- Math.round(newHeight)
- ) {
- $(this.dom.tfoot.parent()).addClass('fixedHeader-floating');
- }
- // Otherwise max-width has kicked in so it is not floating
- else {
- $(this.dom.tfoot.parent()).removeClass(
- 'fixedHeader-floating'
- );
- }
- }
- }
- if (this.dom.header.floating) {
- this.dom.header.floatingParent.css('left', bodyLeft - windowLeft);
- }
- if (this.dom.footer.floating) {
- this.dom.footer.floatingParent.css('left', bodyLeft - windowLeft);
- }
- // If fixed columns is being used on this table then the blockers need to be copied across
- // Cloning these is cleaner than creating as our own as it will keep consistency with fixedColumns automatically
- // ASSUMING that the class remains the same
- if (this.s.dt.settings()[0]._fixedColumns !== undefined) {
- var adjustBlocker = function (side, end, el) {
- if (el === undefined) {
- var blocker = $(
- 'div.dtfc-' + side + '-' + end + '-blocker'
- );
- el =
- blocker.length === 0
- ? null
- : blocker.clone().css('z-index', 1);
- }
- if (el !== null) {
- if (headerMode === 'in' || headerMode === 'below') {
- el.appendTo('body').css({
- top:
- end === 'top'
- ? header.offset.top
- : footer.offset.top,
- left:
- side === 'right'
- ? bodyLeft + bodyWidth - el.width()
- : bodyLeft
- });
- }
- else {
- el.detach();
- }
- }
- return el;
- };
- // Adjust all blockers
- this.dom.header.rightBlocker = adjustBlocker(
- 'right',
- 'top',
- this.dom.header.rightBlocker
- );
- this.dom.header.leftBlocker = adjustBlocker(
- 'left',
- 'top',
- this.dom.header.leftBlocker
- );
- this.dom.footer.rightBlocker = adjustBlocker(
- 'right',
- 'bottom',
- this.dom.footer.rightBlocker
- );
- this.dom.footer.leftBlocker = adjustBlocker(
- 'left',
- 'bottom',
- this.dom.footer.leftBlocker
- );
- }
- },
- /**
- * Function to check if scrolling is enabled on the table or not
- * @returns Boolean value indicating if scrolling on the table is enabled or not
- */
- _scrollEnabled: function () {
- var oScroll = this.s.dt.settings()[0].oScroll;
- if (oScroll.sY !== '' || oScroll.sX !== '') {
- return true;
- }
- return false;
- },
- /**
- * Realign columns by using the colgroup tag and
- * checking column widths
- */
- _widths: function (itemDom) {
- if (! itemDom || ! itemDom.placeholder) {
- return;
- }
- // Match the table overall width
- var tableNode = $(this.s.dt.table().node());
- var scrollBody = $(tableNode.parent());
- itemDom.floatingParent.css('width', scrollBody[0].offsetWidth);
- itemDom.floating.css('width', tableNode[0].offsetWidth);
- // Strip out the old colgroup
- $('colgroup', itemDom.floating).remove();
- // Copy the `colgroup` element to define the number of columns - needed
- // for complex header cases where a column might not have a unique
- // header
- var cols = itemDom.placeholder
- .parent()
- .find('colgroup')
- .clone()
- .appendTo(itemDom.floating)
- .find('col');
- // However, the widths defined in the colgroup from the DataTable might
- // not exactly reflect the actual widths of the columns (content can
- // force it to stretch). So we need to copy the actual widths into the
- // colgroup / col's used for the floating header.
- var widths = this.s.dt.columns(':visible').widths();
- for (var i=0 ; i<widths.length ; i++) {
- cols.eq(i).css('width', widths[i]);
- }
- }
- });
- /**
- * Version
- * @type {String}
- * @static
- */
- FixedHeader.version = '4.0.1';
- /**
- * 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.enabled()) {
- fh.enable(flag);
- }
- });
- });
- DataTable.Api.register('fixedHeader.enabled()', function () {
- if (this.context.length) {
- var fh = this.context[0]._fixedHeader;
- if (fh) {
- return fh.enabled();
- }
- }
- return false;
- });
- DataTable.Api.register('fixedHeader.disable()', function () {
- return this.iterator('table', function (ctx) {
- var fh = ctx._fixedHeader;
- if (fh && fh.enabled()) {
- 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 DataTable;
- }));
- /*! SearchBuilder 1.7.1
- * ©SpryMedia Ltd - datatables.net/license/mit
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- var jq = require('jquery');
- var cjsRequires = function (root, $) {
- if ( ! $.fn.dataTable ) {
- require('datatables.net')(root, $);
- }
- };
- if (typeof window === 'undefined') {
- 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 ( ! $ ) {
- $ = jq( root );
- }
- cjsRequires( root, $ );
- return factory( $, root, root.document );
- };
- }
- else {
- cjsRequires( window, jq );
- module.exports = factory( jq, window, window.document );
- }
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- (function () {
- 'use strict';
- var $$3;
- var dataTable$3;
- function moment() {
- return window.moment;
- }
- function luxon() {
- return window.luxon;
- }
- /**
- * Sets the value of jQuery for use in the file
- *
- * @param jq the instance of jQuery to be set
- */
- function setJQuery$2(jq) {
- $$3 = jq;
- dataTable$3 = jq.fn.dataTable;
- }
- /**
- * The Criteria class is used within SearchBuilder to represent a search criteria
- */
- var Criteria = /** @class */ (function () {
- function Criteria(table, opts, topGroup, index, depth, serverData, liveSearch) {
- if (index === void 0) { index = 0; }
- if (depth === void 0) { depth = 1; }
- if (serverData === void 0) { serverData = undefined; }
- if (liveSearch === void 0) { liveSearch = false; }
- var _this = this;
- // Check that the required version of DataTables is included
- if (!dataTable$3 || !dataTable$3.versionCheck || !dataTable$3.versionCheck('1.10.0')) {
- throw new Error('SearchPane requires DataTables 1.10 or newer');
- }
- this.classes = $$3.extend(true, {}, Criteria.classes);
- // Get options from user and any extra conditions/column types defined by plug-ins
- this.c = $$3.extend(true, {}, Criteria.defaults, $$3.fn.dataTable.ext.searchBuilder, opts);
- var i18n = this.c.i18n;
- this.s = {
- condition: undefined,
- conditions: {},
- data: undefined,
- dataIdx: -1,
- dataPoints: [],
- dateFormat: false,
- depth: depth,
- dt: table,
- filled: false,
- index: index,
- liveSearch: liveSearch,
- origData: undefined,
- preventRedraw: false,
- serverData: serverData,
- topGroup: topGroup,
- type: '',
- value: []
- };
- this.dom = {
- buttons: $$3('<div/>')
- .addClass(this.classes.buttonContainer),
- condition: $$3('<select disabled/>')
- .addClass(this.classes.condition)
- .addClass(this.classes.dropDown)
- .addClass(this.classes.italic)
- .attr('autocomplete', 'hacking'),
- conditionTitle: $$3('<option value="" disabled selected hidden/>')
- .html(this.s.dt.i18n('searchBuilder.condition', i18n.condition)),
- container: $$3('<div/>')
- .addClass(this.classes.container),
- data: $$3('<select/>')
- .addClass(this.classes.data)
- .addClass(this.classes.dropDown)
- .addClass(this.classes.italic),
- dataTitle: $$3('<option value="" disabled selected hidden/>')
- .html(this.s.dt.i18n('searchBuilder.data', i18n.data)),
- defaultValue: $$3('<select disabled/>')
- .addClass(this.classes.value)
- .addClass(this.classes.dropDown)
- .addClass(this.classes.select)
- .addClass(this.classes.italic),
- "delete": $$3('<button/>')
- .html(this.s.dt.i18n('searchBuilder.delete', i18n["delete"]))
- .addClass(this.classes["delete"])
- .addClass(this.classes.button)
- .attr('title', this.s.dt.i18n('searchBuilder.deleteTitle', i18n.deleteTitle))
- .attr('type', 'button'),
- inputCont: $$3('<div/>')
- .addClass(this.classes.inputCont),
- // eslint-disable-next-line no-useless-escape
- left: $$3('<button/>')
- .html(this.s.dt.i18n('searchBuilder.left', i18n.left))
- .addClass(this.classes.left)
- .addClass(this.classes.button)
- .attr('title', this.s.dt.i18n('searchBuilder.leftTitle', i18n.leftTitle))
- .attr('type', 'button'),
- // eslint-disable-next-line no-useless-escape
- right: $$3('<button/>')
- .html(this.s.dt.i18n('searchBuilder.right', i18n.right))
- .addClass(this.classes.right)
- .addClass(this.classes.button)
- .attr('title', this.s.dt.i18n('searchBuilder.rightTitle', i18n.rightTitle))
- .attr('type', 'button'),
- value: [
- $$3('<select disabled/>')
- .addClass(this.classes.value)
- .addClass(this.classes.dropDown)
- .addClass(this.classes.italic)
- .addClass(this.classes.select)
- ],
- valueTitle: $$3('<option value="--valueTitle--" disabled selected hidden/>')
- .html(this.s.dt.i18n('searchBuilder.value', i18n.value))
- };
- // If the greyscale option is selected then add the class to add the grey colour to SearchBuilder
- if (this.c.greyscale) {
- this.dom.data.addClass(this.classes.greyscale);
- this.dom.condition.addClass(this.classes.greyscale);
- this.dom.defaultValue.addClass(this.classes.greyscale);
- for (var _i = 0, _a = this.dom.value; _i < _a.length; _i++) {
- var val = _a[_i];
- val.addClass(this.classes.greyscale);
- }
- }
- $$3(window).on('resize.dtsb', dataTable$3.util.throttle(function () {
- _this.s.topGroup.trigger('dtsb-redrawLogic');
- }));
- this._buildCriteria();
- return this;
- }
- /**
- * Escape html characters within a string
- *
- * @param txt the string to be escaped
- * @returns the escaped string
- */
- Criteria._escapeHTML = function (txt) {
- return txt
- .toString()
- .replace(/</g, '<')
- .replace(/>/g, '>')
- .replace(/"/g, '"')
- .replace(/&/g, '&');
- };
- /**
- * Redraw the DataTable with the current search parameters
- */
- Criteria.prototype.doSearch = function () {
- // Only do the search if live search is disabled, otherwise the search
- // is triggered by the button at the top level group.
- if (this.c.liveSearch) {
- this.s.dt.draw();
- }
- };
- /**
- * Parses formatted numbers down to a form where they can be compared.
- * Note that this does not account for different decimal characters. Use
- * parseNumber instead on the instance.
- *
- * @param val the value to convert
- * @returns the converted value
- */
- Criteria.parseNumFmt = function (val) {
- return +val.replace(/(?!^-)[^0-9.]/g, '');
- };
- /**
- * Adds the left button to the criteria
- */
- Criteria.prototype.updateArrows = function (hasSiblings) {
- if (hasSiblings === void 0) { hasSiblings = false; }
- // Empty the container and append all of the elements in the correct order
- this.dom.container.children().detach();
- this.dom.container
- .append(this.dom.data)
- .append(this.dom.condition)
- .append(this.dom.inputCont);
- this.setListeners();
- // Trigger the inserted events for the value elements as they are inserted
- if (this.dom.value[0] !== undefined) {
- $$3(this.dom.value[0]).trigger('dtsb-inserted');
- }
- for (var i = 1; i < this.dom.value.length; i++) {
- this.dom.inputCont.append(this.dom.value[i]);
- $$3(this.dom.value[i]).trigger('dtsb-inserted');
- }
- // If this is a top level criteria then don't let it move left
- if (this.s.depth > 1) {
- this.dom.buttons.append(this.dom.left);
- }
- // If the depthLimit of the query has been hit then don't add the right button
- if ((this.c.depthLimit === false || this.s.depth < this.c.depthLimit) && hasSiblings) {
- this.dom.buttons.append(this.dom.right);
- }
- else {
- this.dom.right.remove();
- }
- this.dom.buttons.append(this.dom["delete"]);
- this.dom.container.append(this.dom.buttons);
- };
- /**
- * Destroys the criteria, removing listeners and container from the dom
- */
- Criteria.prototype.destroy = function () {
- // Turn off listeners
- this.dom.data.off('.dtsb');
- this.dom.condition.off('.dtsb');
- this.dom["delete"].off('.dtsb');
- for (var _i = 0, _a = this.dom.value; _i < _a.length; _i++) {
- var val = _a[_i];
- val.off('.dtsb');
- }
- // Remove container from the dom
- this.dom.container.remove();
- };
- /**
- * Passes in the data for the row and compares it against this single criteria
- *
- * @param rowData The data for the row to be compared
- * @returns boolean Whether the criteria has passed
- */
- Criteria.prototype.search = function (rowData, rowIdx) {
- var settings = this.s.dt.settings()[0];
- var condition = this.s.conditions[this.s.condition];
- if (this.s.condition !== undefined && condition !== undefined) {
- var filter = rowData[this.s.dataIdx];
- // This check is in place for if a custom decimal character is in place
- if (this.s.type.includes('num') &&
- (settings.oLanguage.sDecimal !== '' ||
- settings.oLanguage.sThousands !== '')) {
- var splitRD = [rowData[this.s.dataIdx]];
- if (settings.oLanguage.sDecimal !== '') {
- splitRD = rowData[this.s.dataIdx].split(settings.oLanguage.sDecimal);
- }
- if (settings.oLanguage.sThousands !== '') {
- for (var i = 0; i < splitRD.length; i++) {
- splitRD[i] = splitRD[i].replace(settings.oLanguage.sThousands, ',');
- }
- }
- filter = splitRD.join('.');
- }
- // If orthogonal data is in place we need to get it's values for searching
- if (this.c.orthogonal.search !== 'filter') {
- filter = settings.fastData(rowIdx, this.s.dataIdx, typeof this.c.orthogonal === 'string' ?
- this.c.orthogonal :
- this.c.orthogonal.search);
- }
- if (this.s.type === 'array') {
- // Make sure we are working with an array
- if (!Array.isArray(filter)) {
- filter = [filter];
- }
- filter.sort();
- for (var _i = 0, filter_1 = filter; _i < filter_1.length; _i++) {
- var filt = filter_1[_i];
- if (filt && typeof filt === 'string') {
- filt = filt.replace(/[\r\n\u2028]/g, ' ');
- }
- }
- }
- else if (filter !== null && typeof filter === 'string') {
- filter = filter.replace(/[\r\n\u2028]/g, ' ');
- }
- if (this.s.type.includes('html') && typeof filter === 'string') {
- filter = filter.replace(/(<([^>]+)>)/ig, '');
- }
- // Not ideal, but jqueries .val() returns an empty string even
- // when the value set is null, so we shall assume the two are equal
- if (filter === null) {
- filter = '';
- }
- return condition.search(filter, this.s.value, this);
- }
- };
- /**
- * Gets the details required to rebuild the criteria
- */
- Criteria.prototype.getDetails = function (deFormatDates) {
- if (deFormatDates === void 0) { deFormatDates = false; }
- var i;
- var settings = this.s.dt.settings()[0];
- // This check is in place for if a custom decimal character is in place
- if (this.s.type !== null &&
- this.s.type.includes('num') &&
- (settings.oLanguage.sDecimal !== '' || settings.oLanguage.sThousands !== '')) {
- for (i = 0; i < this.s.value.length; i++) {
- var splitRD = [this.s.value[i].toString()];
- if (settings.oLanguage.sDecimal !== '') {
- splitRD = this.s.value[i].split(settings.oLanguage.sDecimal);
- }
- if (settings.oLanguage.sThousands !== '') {
- for (var j = 0; j < splitRD.length; j++) {
- splitRD[j] = splitRD[j].replace(settings.oLanguage.sThousands, ',');
- }
- }
- this.s.value[i] = splitRD.join('.');
- }
- }
- else if (this.s.type !== null && deFormatDates) {
- if (this.s.type.includes('date') ||
- this.s.type.includes('time')) {
- for (i = 0; i < this.s.value.length; i++) {
- if (this.s.value[i].match(/^\d{4}-([0]\d|1[0-2])-([0-2]\d|3[01])$/g) === null) {
- this.s.value[i] = '';
- }
- }
- }
- else if (this.s.type.includes('moment')) {
- for (i = 0; i < this.s.value.length; i++) {
- if (this.s.value[i] &&
- this.s.value[i].length > 0 &&
- moment()(this.s.value[i], this.s.dateFormat, true).isValid()) {
- this.s.value[i] = moment()(this.s.value[i], this.s.dateFormat).format('YYYY-MM-DD HH:mm:ss');
- }
- }
- }
- else if (this.s.type.includes('luxon')) {
- for (i = 0; i < this.s.value.length; i++) {
- if (this.s.value[i] &&
- this.s.value[i].length > 0 &&
- luxon().DateTime.fromFormat(this.s.value[i], this.s.dateFormat).invalid === null) {
- this.s.value[i] = luxon().DateTime.fromFormat(this.s.value[i], this.s.dateFormat).toFormat('yyyy-MM-dd HH:mm:ss');
- }
- }
- }
- }
- if (this.s.type.includes('num') && this.s.dt.page.info().serverSide) {
- for (i = 0; i < this.s.value.length; i++) {
- this.s.value[i] = this.s.value[i].replace(/[^0-9.\-]/g, '');
- }
- }
- return {
- condition: this.s.condition,
- data: this.s.data,
- origData: this.s.origData,
- type: this.s.type,
- value: this.s.value.map(function (a) { return a !== null && a !== undefined ? a.toString() : a; })
- };
- };
- /**
- * Getter for the node for the container of the criteria
- *
- * @returns JQuery<HTMLElement> the node for the container
- */
- Criteria.prototype.getNode = function () {
- return this.dom.container;
- };
- /**
- * Parses formatted numbers down to a form where they can be compared
- *
- * @param val the value to convert
- * @returns the converted value
- */
- Criteria.prototype.parseNumber = function (val) {
- var decimal = this.s.dt.i18n('decimal');
- // Remove any periods and then replace the decimal with a period
- if (decimal && decimal !== '.') {
- val = val.replace(/\./g, '').replace(decimal, '.');
- }
- return +val.replace(/(?!^-)[^0-9.]/g, '');
- };
- /**
- * Populates the criteria data, condition and value(s) as far as has been selected
- */
- Criteria.prototype.populate = function () {
- this._populateData();
- // If the column index has been found attempt to select a condition
- if (this.s.dataIdx !== -1) {
- this._populateCondition();
- // If the condittion has been found attempt to select the values
- if (this.s.condition !== undefined) {
- this._populateValue();
- }
- }
- };
- /**
- * Rebuilds the criteria based upon the details passed in
- *
- * @param loadedCriteria the details required to rebuild the criteria
- */
- Criteria.prototype.rebuild = function (loadedCriteria) {
- // Check to see if the previously selected data exists, if so select it
- var foundData = false;
- var dataIdx, i;
- this._populateData();
- // If a data selection has previously been made attempt to find and select it
- if (loadedCriteria.data !== undefined) {
- var italic_1 = this.classes.italic;
- var data_1 = this.dom.data;
- this.dom.data.children('option').each(function () {
- if (!foundData &&
- ($$3(this).text() === loadedCriteria.data ||
- loadedCriteria.origData && $$3(this).prop('origData') === loadedCriteria.origData)) {
- $$3(this).prop('selected', true);
- data_1.removeClass(italic_1);
- foundData = true;
- dataIdx = parseInt($$3(this).val(), 10);
- }
- else {
- $$3(this).removeProp('selected');
- }
- });
- }
- // If the data has been found and selected then the condition can be populated and searched
- if (foundData) {
- this.s.data = loadedCriteria.data;
- this.s.origData = loadedCriteria.origData;
- this.s.dataIdx = dataIdx;
- this.c.orthogonal = this._getOptions().orthogonal;
- this.dom.dataTitle.remove();
- this._populateCondition();
- this.dom.conditionTitle.remove();
- var condition = void 0;
- // Check to see if the previously selected condition exists, if so select it
- var options = this.dom.condition.children('option');
- for (i = 0; i < options.length; i++) {
- var option = $$3(options[i]);
- if (loadedCriteria.condition !== undefined &&
- option.val() === loadedCriteria.condition &&
- typeof loadedCriteria.condition === 'string') {
- option.prop('selected', true);
- condition = option.val();
- }
- else {
- option.removeProp('selected');
- }
- }
- this.s.condition = condition;
- // If the condition has been found and selected then the value can be populated and searched
- if (this.s.condition !== undefined) {
- this.dom.conditionTitle.removeProp('selected');
- this.dom.conditionTitle.remove();
- this.dom.condition.removeClass(this.classes.italic);
- for (i = 0; i < options.length; i++) {
- var opt = $$3(options[i]);
- if (opt.val() !== this.s.condition) {
- opt.removeProp('selected');
- }
- }
- this._populateValue(loadedCriteria);
- }
- else {
- this.dom.conditionTitle.prependTo(this.dom.condition).prop('selected', true);
- }
- }
- };
- /**
- * Sets the listeners for the criteria
- */
- Criteria.prototype.setListeners = function () {
- var _this = this;
- this.dom.data
- .unbind('change')
- .on('change.dtsb', function () {
- _this.dom.dataTitle.removeProp('selected');
- // Need to go over every option to identify the correct selection
- var options = _this.dom.data.children('option.' + _this.classes.option);
- for (var i = 0; i < options.length; i++) {
- var option = $$3(options[i]);
- if (option.val() === _this.dom.data.val()) {
- _this.dom.data.removeClass(_this.classes.italic);
- option.prop('selected', true);
- _this.s.dataIdx = +option.val();
- _this.s.data = option.text();
- _this.s.origData = option.prop('origData');
- _this.c.orthogonal = _this._getOptions().orthogonal;
- // When the data is changed, the values in condition and
- // value may also change so need to renew them
- _this._clearCondition();
- _this._clearValue();
- _this._populateCondition();
- // If this criteria was previously active in the search then
- // remove it from the search and trigger a new search
- if (_this.s.filled) {
- _this.s.filled = false;
- _this.doSearch();
- _this.setListeners();
- }
- _this.s.dt.state.save();
- }
- else {
- option.removeProp('selected');
- }
- }
- });
- this.dom.condition
- .unbind('change')
- .on('change.dtsb', function () {
- _this.dom.conditionTitle.removeProp('selected');
- // Need to go over every option to identify the correct selection
- var options = _this.dom.condition.children('option.' + _this.classes.option);
- for (var i = 0; i < options.length; i++) {
- var option = $$3(options[i]);
- if (option.val() === _this.dom.condition.val()) {
- _this.dom.condition.removeClass(_this.classes.italic);
- option.prop('selected', true);
- var condDisp = option.val();
- // Find the condition that has been selected and store it internally
- for (var _i = 0, _a = Object.keys(_this.s.conditions); _i < _a.length; _i++) {
- var cond = _a[_i];
- if (cond === condDisp) {
- _this.s.condition = condDisp;
- break;
- }
- }
- // When the condition is changed, the value selector may switch between
- // a select element and an input element
- _this._clearValue();
- _this._populateValue();
- for (var _b = 0, _c = _this.dom.value; _b < _c.length; _b++) {
- var val = _c[_b];
- // If this criteria was previously active in the search then remove
- // it from the search and trigger a new search
- if (_this.s.filled && val !== undefined && _this.dom.inputCont.has(val[0]).length !== 0) {
- _this.s.filled = false;
- _this.doSearch();
- _this.setListeners();
- }
- }
- if (_this.dom.value.length === 0 ||
- _this.dom.value.length === 1 && _this.dom.value[0] === undefined) {
- _this.doSearch();
- }
- }
- else {
- option.removeProp('selected');
- }
- }
- });
- };
- Criteria.prototype.setupButtons = function () {
- if (window.innerWidth > 550) {
- this.dom.container.removeClass(this.classes.vertical);
- this.dom.buttons.css('left', null);
- this.dom.buttons.css('top', null);
- return;
- }
- this.dom.container.addClass(this.classes.vertical);
- this.dom.buttons.css('left', this.dom.data.innerWidth());
- this.dom.buttons.css('top', this.dom.data.position().top);
- };
- /**
- * Builds the elements of the dom together
- */
- Criteria.prototype._buildCriteria = function () {
- // Append Titles for select elements
- this.dom.data.append(this.dom.dataTitle);
- this.dom.condition.append(this.dom.conditionTitle);
- // Add elements to container
- this.dom.container
- .append(this.dom.data)
- .append(this.dom.condition);
- this.dom.inputCont.empty();
- for (var _i = 0, _a = this.dom.value; _i < _a.length; _i++) {
- var val = _a[_i];
- val.append(this.dom.valueTitle);
- this.dom.inputCont.append(val);
- }
- // Add buttons to container
- this.dom.buttons
- .append(this.dom["delete"])
- .append(this.dom.right);
- this.dom.container.append(this.dom.inputCont).append(this.dom.buttons);
- this.setListeners();
- };
- /**
- * Clears the condition select element
- */
- Criteria.prototype._clearCondition = function () {
- this.dom.condition.empty();
- this.dom.conditionTitle.prop('selected', true).attr('disabled', 'true');
- this.dom.condition.prepend(this.dom.conditionTitle).prop('selectedIndex', 0);
- this.s.conditions = {};
- this.s.condition = undefined;
- };
- /**
- * Clears the value elements
- */
- Criteria.prototype._clearValue = function () {
- var val;
- if (this.s.condition !== undefined) {
- if (this.dom.value.length > 0 && this.dom.value[0] !== undefined) {
- // Remove all of the value elements
- for (var _i = 0, _a = this.dom.value; _i < _a.length; _i++) {
- val = _a[_i];
- if (val !== undefined) {
- // Timeout is annoying but because of IOS
- setTimeout(function () {
- val.remove();
- }, 50);
- }
- }
- }
- // Call the init function to get the value elements for this condition
- this.dom.value = [].concat(this.s.conditions[this.s.condition].init(this, Criteria.updateListener));
- if (this.dom.value.length > 0 && this.dom.value[0] !== undefined) {
- this.dom.inputCont
- .empty()
- .append(this.dom.value[0])
- .insertAfter(this.dom.condition);
- $$3(this.dom.value[0]).trigger('dtsb-inserted');
- // Insert all of the value elements
- for (var i = 1; i < this.dom.value.length; i++) {
- this.dom.inputCont.append(this.dom.value[i]);
- $$3(this.dom.value[i]).trigger('dtsb-inserted');
- }
- }
- }
- else {
- // Remove all of the value elements
- for (var _b = 0, _c = this.dom.value; _b < _c.length; _b++) {
- val = _c[_b];
- if (val !== undefined) {
- // Timeout is annoying but because of IOS
- setTimeout(function () {
- val.remove();
- }, 50);
- }
- }
- // Append the default valueTitle to the default select element
- this.dom.valueTitle
- .prop('selected', true);
- this.dom.defaultValue
- .append(this.dom.valueTitle)
- .insertAfter(this.dom.condition);
- }
- this.s.value = [];
- this.dom.value = [
- $$3('<select disabled/>')
- .addClass(this.classes.value)
- .addClass(this.classes.dropDown)
- .addClass(this.classes.italic)
- .addClass(this.classes.select)
- .append(this.dom.valueTitle.clone())
- ];
- };
- /**
- * Gets the options for the column
- *
- * @returns {object} The options for the column
- */
- Criteria.prototype._getOptions = function () {
- var table = this.s.dt;
- return $$3.extend(true, {}, Criteria.defaults, table.settings()[0].aoColumns[this.s.dataIdx].searchBuilder);
- };
- /**
- * Populates the condition dropdown
- */
- Criteria.prototype._populateCondition = function () {
- var conditionOpts = [];
- var conditionsLength = Object.keys(this.s.conditions).length;
- var colInits = this.s.dt.settings()[0].aoColumns;
- var column = +this.dom.data.children('option:selected').val();
- var condition, condName;
- // If there are no conditions stored then we need to get them from the appropriate type
- if (conditionsLength === 0) {
- this.s.type = this.s.dt.column(column).type();
- if (colInits !== undefined) {
- var colInit = colInits[column];
- if (colInit.searchBuilderType !== undefined && colInit.searchBuilderType !== null) {
- this.s.type = colInit.searchBuilderType;
- }
- else if (this.s.type === undefined || this.s.type === null) {
- this.s.type = colInit.sType;
- }
- }
- // If the column type is still unknown use the internal API to detect type
- if (this.s.type === null || this.s.type === undefined) {
- // This can only happen in DT1 - DT2 will do the invalidation of the type itself
- if ($$3.fn.dataTable.ext.oApi) {
- $$3.fn.dataTable.ext.oApi._fnColumnTypes(this.s.dt.settings()[0]);
- }
- this.s.type = this.s.dt.column(column).type();
- }
- // Enable the condition element
- this.dom.condition
- .removeAttr('disabled')
- .empty()
- .append(this.dom.conditionTitle)
- .addClass(this.classes.italic);
- this.dom.conditionTitle
- .prop('selected', true);
- var decimal = this.s.dt.settings()[0].oLanguage.sDecimal;
- // This check is in place for if a custom decimal character is in place
- if (decimal !== '' && this.s.type.indexOf(decimal) === this.s.type.length - decimal.length) {
- if (this.s.type.includes('num-fmt')) {
- this.s.type = this.s.type.replace(decimal, '');
- }
- else if (this.s.type.includes('num')) {
- this.s.type = this.s.type.replace(decimal, '');
- }
- }
- // Select which conditions are going to be used based on the column type
- var conditionObj = this.c.conditions[this.s.type] !== undefined ?
- this.c.conditions[this.s.type] :
- this.s.type.includes('moment') ?
- this.c.conditions.moment :
- this.s.type.includes('luxon') ?
- this.c.conditions.luxon :
- this.c.conditions.string;
- // If it is a moment format then extract the date format
- if (this.s.type.includes('moment')) {
- this.s.dateFormat = this.s.type.replace(/moment-/g, '');
- }
- else if (this.s.type.includes('luxon')) {
- this.s.dateFormat = this.s.type.replace(/luxon-/g, '');
- }
- // Add all of the conditions to the select element
- for (var _i = 0, _a = Object.keys(conditionObj); _i < _a.length; _i++) {
- condition = _a[_i];
- if (conditionObj[condition] !== null) {
- // Serverside processing does not supply the options for the select elements
- // Instead input elements need to be used for these instead
- if (this.s.dt.page.info().serverSide && conditionObj[condition].init === Criteria.initSelect) {
- var col = colInits[column];
- if (this.s.serverData && this.s.serverData[col.data]) {
- conditionObj[condition].init = Criteria.initSelectSSP;
- conditionObj[condition].inputValue = Criteria.inputValueSelect;
- conditionObj[condition].isInputValid = Criteria.isInputValidSelect;
- }
- else {
- conditionObj[condition].init = Criteria.initInput;
- conditionObj[condition].inputValue = Criteria.inputValueInput;
- conditionObj[condition].isInputValid = Criteria.isInputValidInput;
- }
- }
- this.s.conditions[condition] = conditionObj[condition];
- condName = conditionObj[condition].conditionName;
- if (typeof condName === 'function') {
- condName = condName(this.s.dt, this.c.i18n);
- }
- conditionOpts.push($$3('<option>', {
- text: condName,
- value: condition
- })
- .addClass(this.classes.option)
- .addClass(this.classes.notItalic));
- }
- }
- }
- // Otherwise we can just load them in
- else if (conditionsLength > 0) {
- this.dom.condition.empty().removeAttr('disabled').addClass(this.classes.italic);
- for (var _b = 0, _c = Object.keys(this.s.conditions); _b < _c.length; _b++) {
- condition = _c[_b];
- var name_1 = this.s.conditions[condition].conditionName;
- if (typeof name_1 === 'function') {
- name_1 = name_1(this.s.dt, this.c.i18n);
- }
- var newOpt = $$3('<option>', {
- text: name_1,
- value: condition
- })
- .addClass(this.classes.option)
- .addClass(this.classes.notItalic);
- if (this.s.condition !== undefined && this.s.condition === name_1) {
- newOpt.prop('selected', true);
- this.dom.condition.removeClass(this.classes.italic);
- }
- conditionOpts.push(newOpt);
- }
- }
- else {
- this.dom.condition
- .attr('disabled', 'true')
- .addClass(this.classes.italic);
- return;
- }
- for (var _d = 0, conditionOpts_1 = conditionOpts; _d < conditionOpts_1.length; _d++) {
- var opt = conditionOpts_1[_d];
- this.dom.condition.append(opt);
- }
- // Selecting a default condition if one is set
- if (colInits[column].searchBuilder && colInits[column].searchBuilder.defaultCondition) {
- var defaultCondition = colInits[column].searchBuilder.defaultCondition;
- // If it is a number just use it as an index
- if (typeof defaultCondition === 'number') {
- this.dom.condition.prop('selectedIndex', defaultCondition);
- this.dom.condition.trigger('change');
- }
- // If it is a string then things get slightly more tricly
- else if (typeof defaultCondition === 'string') {
- // We need to check each condition option to see if any will match
- for (var i = 0; i < conditionOpts.length; i++) {
- // Need to check against the stored conditions so we can match the token "cond" to the option
- for (var _e = 0, _f = Object.keys(this.s.conditions); _e < _f.length; _e++) {
- var cond = _f[_e];
- condName = this.s.conditions[cond].conditionName;
- if (
- // If the conditionName matches the text of the option
- (typeof condName === 'string' ? condName : condName(this.s.dt, this.c.i18n)) ===
- conditionOpts[i].text() &&
- // and the tokens match
- cond === defaultCondition) {
- // Select that option
- this.dom.condition
- .prop('selectedIndex', this.dom.condition.children().toArray().indexOf(conditionOpts[i][0]))
- .removeClass(this.classes.italic);
- this.dom.condition.trigger('change');
- i = conditionOpts.length;
- break;
- }
- }
- }
- }
- }
- // If not default set then default to 0, the title
- else {
- this.dom.condition.prop('selectedIndex', 0);
- }
- };
- /**
- * Populates the data / column select element
- */
- Criteria.prototype._populateData = function () {
- var columns = this.s.dt.settings()[0].aoColumns;
- var includeColumns = this.s.dt.columns(this.c.columns).indexes().toArray();
- this.dom.data.empty().append(this.dom.dataTitle);
- for (var index = 0; index < columns.length; index++) {
- // Need to check that the column can be filtered on before adding it
- if (this.c.columns === true || includeColumns.includes(index)) {
- var col = columns[index];
- var opt = {
- index: index,
- origData: col.data,
- text: (col.searchBuilderTitle || col.sTitle)
- .replace(/(<([^>]+)>)/ig, '')
- };
- this.dom.data.append($$3('<option>', {
- text: opt.text,
- value: opt.index
- })
- .addClass(this.classes.option)
- .addClass(this.classes.notItalic)
- .prop('origData', col.data)
- .prop('selected', this.s.dataIdx === opt.index ? true : false));
- if (this.s.dataIdx === opt.index) {
- this.dom.dataTitle.removeProp('selected');
- }
- }
- }
- };
- /**
- * Populates the Value select element
- *
- * @param loadedCriteria optional, used to reload criteria from predefined filters
- */
- Criteria.prototype._populateValue = function (loadedCriteria) {
- var _this = this;
- var prevFilled = this.s.filled;
- var i;
- this.s.filled = false;
- // Remove any previous value elements
- // Timeout is annoying but because of IOS
- setTimeout(function () {
- _this.dom.defaultValue.remove();
- }, 50);
- var _loop_1 = function (val) {
- // Timeout is annoying but because of IOS
- setTimeout(function () {
- if (val !== undefined) {
- val.remove();
- }
- }, 50);
- };
- for (var _i = 0, _a = this.dom.value; _i < _a.length; _i++) {
- var val = _a[_i];
- _loop_1(val);
- }
- var children = this.dom.inputCont.children();
- if (children.length > 1) {
- for (i = 0; i < children.length; i++) {
- $$3(children[i]).remove();
- }
- }
- // Find the column with the title matching the data for the criteria and take note of the index
- if (loadedCriteria !== undefined) {
- this.s.dt.columns().every(function (index) {
- if (_this.s.dt.settings()[0].aoColumns[index].sTitle === loadedCriteria.data) {
- _this.s.dataIdx = index;
- }
- });
- }
- // Initialise the value elements based on the condition
- this.dom.value = [].concat(this.s.conditions[this.s.condition].init(this, Criteria.updateListener, loadedCriteria !== undefined ? loadedCriteria.value : undefined));
- if (loadedCriteria !== undefined && loadedCriteria.value !== undefined) {
- this.s.value = loadedCriteria.value;
- }
- this.dom.inputCont.empty();
- // Insert value elements and trigger the inserted event
- if (this.dom.value[0] !== undefined) {
- $$3(this.dom.value[0])
- .appendTo(this.dom.inputCont)
- .trigger('dtsb-inserted');
- }
- for (i = 1; i < this.dom.value.length; i++) {
- $$3(this.dom.value[i])
- .insertAfter(this.dom.value[i - 1])
- .trigger('dtsb-inserted');
- }
- // Check if the criteria can be used in a search
- this.s.filled = this.s.conditions[this.s.condition].isInputValid(this.dom.value, this);
- this.setListeners();
- // If it can and this is different to before then trigger a draw
- if (!this.s.preventRedraw && prevFilled !== this.s.filled) {
- // If using SSP we want to restrict the amount of server calls that take place
- // and this will already have taken place
- if (!this.s.dt.page.info().serverSide) {
- this.doSearch();
- }
- this.setListeners();
- }
- };
- /**
- * Provides throttling capabilities to SearchBuilder without having to use dt's _fnThrottle function
- * This is because that function is not quite suitable for our needs as it runs initially rather than waiting
- *
- * @param args arguments supplied to the throttle function
- * @returns Function that is to be run that implements the throttling
- */
- Criteria.prototype._throttle = function (fn, frequency) {
- if (frequency === void 0) { frequency = 200; }
- var last = null;
- var timer = null;
- var that = this;
- if (frequency === null) {
- frequency = 200;
- }
- return function () {
- var args = [];
- for (var _i = 0; _i < arguments.length; _i++) {
- args[_i] = arguments[_i];
- }
- var now = +new Date();
- if (last !== null && now < last + frequency) {
- clearTimeout(timer);
- }
- else {
- last = now;
- }
- timer = setTimeout(function () {
- last = null;
- fn.apply(that, args);
- }, frequency);
- };
- };
- Criteria.version = '1.1.0';
- Criteria.classes = {
- button: 'dtsb-button',
- buttonContainer: 'dtsb-buttonContainer',
- condition: 'dtsb-condition',
- container: 'dtsb-criteria',
- data: 'dtsb-data',
- "delete": 'dtsb-delete',
- dropDown: 'dtsb-dropDown',
- greyscale: 'dtsb-greyscale',
- input: 'dtsb-input',
- inputCont: 'dtsb-inputCont',
- italic: 'dtsb-italic',
- joiner: 'dtsb-joiner',
- left: 'dtsb-left',
- notItalic: 'dtsb-notItalic',
- option: 'dtsb-option',
- right: 'dtsb-right',
- select: 'dtsb-select',
- value: 'dtsb-value',
- vertical: 'dtsb-vertical'
- };
- /**
- * Default initialisation function for select conditions
- */
- Criteria.initSelect = function (that, fn, preDefined, array) {
- if (preDefined === void 0) { preDefined = null; }
- if (array === void 0) { array = false; }
- var column = that.dom.data.children('option:selected').val();
- var indexArray = that.s.dt.rows().indexes().toArray();
- var fastData = that.s.dt.settings()[0].fastData;
- that.dom.valueTitle.prop('selected', true);
- // Declare select element to be used with all of the default classes and listeners.
- var el = $$3('<select/>')
- .addClass(Criteria.classes.value)
- .addClass(Criteria.classes.dropDown)
- .addClass(Criteria.classes.italic)
- .addClass(Criteria.classes.select)
- .append(that.dom.valueTitle)
- .on('change.dtsb', function () {
- $$3(this).removeClass(Criteria.classes.italic);
- fn(that, this);
- });
- if (that.c.greyscale) {
- el.addClass(Criteria.classes.greyscale);
- }
- var added = [];
- var options = [];
- // Add all of the options from the table to the select element.
- // Only add one option for each possible value
- for (var _i = 0, indexArray_1 = indexArray; _i < indexArray_1.length; _i++) {
- var index = indexArray_1[_i];
- var filter = fastData(index, column, typeof that.c.orthogonal === 'string' ?
- that.c.orthogonal :
- that.c.orthogonal.search);
- var value = {
- filter: typeof filter === 'string' ?
- filter.replace(/[\r\n\u2028]/g, ' ') : // Need to replace certain characters to match search values
- filter,
- index: index,
- text: fastData(index, column, typeof that.c.orthogonal === 'string' ?
- that.c.orthogonal :
- that.c.orthogonal.display)
- };
- // If we are dealing with an array type, either make sure we are working with arrays, or sort them
- if (that.s.type === 'array') {
- value.filter = !Array.isArray(value.filter) ? [value.filter] : value.filter;
- value.text = !Array.isArray(value.text) ? [value.text] : value.text;
- }
- // Function to add an option to the select element
- var addOption = function (filt, text) {
- if (that.s.type.includes('html') && filt !== null && typeof filt === 'string') {
- filt.replace(/(<([^>]+)>)/ig, '');
- }
- // Add text and value, stripping out any html if that is the column type
- var opt = $$3('<option>', {
- type: Array.isArray(filt) ? 'Array' : 'String',
- value: filt
- })
- .data('sbv', filt)
- .addClass(that.classes.option)
- .addClass(that.classes.notItalic)
- // Have to add the text this way so that special html characters are not escaped - & etc.
- .html(typeof text === 'string' ?
- text.replace(/(<([^>]+)>)/ig, '') :
- text);
- var val = opt.val();
- // Check that this value has not already been added
- if (added.indexOf(val) === -1) {
- added.push(val);
- options.push(opt);
- if (preDefined !== null && Array.isArray(preDefined[0])) {
- preDefined[0] = preDefined[0].sort().join(',');
- }
- // If this value was previously selected as indicated by preDefined, then select it again
- if (preDefined !== null && opt.val() === preDefined[0]) {
- opt.prop('selected', true);
- el.removeClass(Criteria.classes.italic);
- that.dom.valueTitle.removeProp('selected');
- }
- }
- };
- // If this is to add the individual values within the array we need to loop over the array
- if (array) {
- for (var i = 0; i < value.filter.length; i++) {
- addOption(value.filter[i], value.text[i]);
- }
- }
- // Otherwise the value that is in the cell is to be added
- else {
- addOption(value.filter, Array.isArray(value.text) ? value.text.join(', ') : value.text);
- }
- }
- options.sort(function (a, b) {
- if (that.s.type === 'array' ||
- that.s.type === 'string' ||
- that.s.type === 'html') {
- if (a.val() < b.val()) {
- return -1;
- }
- else if (a.val() > b.val()) {
- return 1;
- }
- else {
- return 0;
- }
- }
- else if (that.s.type === 'num' ||
- that.s.type === 'html-num') {
- if (+a.val().replace(/(<([^>]+)>)/ig, '') < +b.val().replace(/(<([^>]+)>)/ig, '')) {
- return -1;
- }
- else if (+a.val().replace(/(<([^>]+)>)/ig, '') > +b.val().replace(/(<([^>]+)>)/ig, '')) {
- return 1;
- }
- else {
- return 0;
- }
- }
- else if (that.s.type === 'num-fmt' || that.s.type === 'html-num-fmt') {
- if (+a.val().replace(/[^0-9.]/g, '') < +b.val().replace(/[^0-9.]/g, '')) {
- return -1;
- }
- else if (+a.val().replace(/[^0-9.]/g, '') > +b.val().replace(/[^0-9.]/g, '')) {
- return 1;
- }
- else {
- return 0;
- }
- }
- });
- for (var _a = 0, options_1 = options; _a < options_1.length; _a++) {
- var opt = options_1[_a];
- el.append(opt);
- }
- return el;
- };
- /**
- * Default initialisation function for select conditions
- */
- Criteria.initSelectSSP = function (that, fn, preDefined) {
- if (preDefined === void 0) { preDefined = null; }
- that.dom.valueTitle.prop('selected', true);
- // Declare select element to be used with all of the default classes and listeners.
- var el = $$3('<select/>')
- .addClass(Criteria.classes.value)
- .addClass(Criteria.classes.dropDown)
- .addClass(Criteria.classes.italic)
- .addClass(Criteria.classes.select)
- .append(that.dom.valueTitle)
- .on('change.dtsb', function () {
- $$3(this).removeClass(Criteria.classes.italic);
- fn(that, this);
- });
- if (that.c.greyscale) {
- el.addClass(Criteria.classes.greyscale);
- }
- var options = [];
- for (var _i = 0, _a = that.s.serverData[that.s.origData]; _i < _a.length; _i++) {
- var option = _a[_i];
- var value = option.value;
- var label = option.label;
- // Function to add an option to the select element
- var addOption = function (filt, text) {
- if (that.s.type.includes('html') && filt !== null && typeof filt === 'string') {
- filt.replace(/(<([^>]+)>)/ig, '');
- }
- // Add text and value, stripping out any html if that is the column type
- var opt = $$3('<option>', {
- type: Array.isArray(filt) ? 'Array' : 'String',
- value: filt
- })
- .data('sbv', filt)
- .addClass(that.classes.option)
- .addClass(that.classes.notItalic)
- // Have to add the text this way so that special html characters are not escaped - & etc.
- .html(typeof text === 'string' ?
- text.replace(/(<([^>]+)>)/ig, '') :
- text);
- options.push(opt);
- // If this value was previously selected as indicated by preDefined, then select it again
- if (preDefined !== null && opt.val() === preDefined[0]) {
- opt.prop('selected', true);
- el.removeClass(Criteria.classes.italic);
- that.dom.valueTitle.removeProp('selected');
- }
- };
- addOption(value, label);
- }
- for (var _b = 0, options_2 = options; _b < options_2.length; _b++) {
- var opt = options_2[_b];
- el.append(opt);
- }
- return el;
- };
- /**
- * Default initialisation function for select array conditions
- *
- * This exists because there needs to be different select functionality for contains/without and equals/not
- */
- Criteria.initSelectArray = function (that, fn, preDefined) {
- if (preDefined === void 0) { preDefined = null; }
- return Criteria.initSelect(that, fn, preDefined, true);
- };
- /**
- * Default initialisation function for input conditions
- */
- Criteria.initInput = function (that, fn, preDefined) {
- if (preDefined === void 0) { preDefined = null; }
- // Declare the input element
- var searchDelay = that.s.dt.settings()[0].searchDelay;
- var el = $$3('<input/>')
- .addClass(Criteria.classes.value)
- .addClass(Criteria.classes.input)
- .on('input.dtsb keypress.dtsb', that._throttle(function (e) {
- var code = e.keyCode || e.which;
- return fn(that, this, code);
- }, searchDelay === null ? 100 : searchDelay));
- if (that.c.greyscale) {
- el.addClass(Criteria.classes.greyscale);
- }
- // If there is a preDefined value then add it
- if (preDefined !== null) {
- el.val(preDefined[0]);
- }
- // This is add responsive functionality to the logic button without redrawing everything else
- that.s.dt.one('draw.dtsb', function () {
- that.s.topGroup.trigger('dtsb-redrawLogic');
- });
- return el;
- };
- /**
- * Default initialisation function for conditions requiring 2 inputs
- */
- Criteria.init2Input = function (that, fn, preDefined) {
- if (preDefined === void 0) { preDefined = null; }
- // Declare all of the necessary jQuery elements
- var searchDelay = that.s.dt.settings()[0].searchDelay;
- var els = [
- $$3('<input/>')
- .addClass(Criteria.classes.value)
- .addClass(Criteria.classes.input)
- .on('input.dtsb keypress.dtsb', that._throttle(function (e) {
- var code = e.keyCode || e.which;
- return fn(that, this, code);
- }, searchDelay === null ? 100 : searchDelay)),
- $$3('<span>')
- .addClass(that.classes.joiner)
- .html(that.s.dt.i18n('searchBuilder.valueJoiner', that.c.i18n.valueJoiner)),
- $$3('<input/>')
- .addClass(Criteria.classes.value)
- .addClass(Criteria.classes.input)
- .on('input.dtsb keypress.dtsb', that._throttle(function (e) {
- var code = e.keyCode || e.which;
- return fn(that, this, code);
- }, searchDelay === null ? 100 : searchDelay))
- ];
- if (that.c.greyscale) {
- els[0].addClass(Criteria.classes.greyscale);
- els[2].addClass(Criteria.classes.greyscale);
- }
- // If there is a preDefined value then add it
- if (preDefined !== null) {
- els[0].val(preDefined[0]);
- els[2].val(preDefined[1]);
- }
- // This is add responsive functionality to the logic button without redrawing everything else
- that.s.dt.one('draw.dtsb', function () {
- that.s.topGroup.trigger('dtsb-redrawLogic');
- });
- return els;
- };
- /**
- * Default initialisation function for date conditions
- */
- Criteria.initDate = function (that, fn, preDefined) {
- if (preDefined === void 0) { preDefined = null; }
- var searchDelay = that.s.dt.settings()[0].searchDelay;
- var i18n = that.s.dt.i18n('datetime', {});
- // Declare date element using DataTables dateTime plugin
- var el = $$3('<input/>')
- .addClass(Criteria.classes.value)
- .addClass(Criteria.classes.input)
- .dtDateTime({
- attachTo: 'input',
- format: that.s.dateFormat ? that.s.dateFormat : undefined,
- i18n: i18n
- })
- .on('change.dtsb', that._throttle(function () {
- return fn(that, this);
- }, searchDelay === null ? 100 : searchDelay))
- .on('input.dtsb keypress.dtsb', function (e) {
- that._throttle(function () {
- var code = e.keyCode || e.which;
- return fn(that, this, code);
- }, searchDelay === null ? 100 : searchDelay);
- });
- if (that.c.greyscale) {
- el.addClass(Criteria.classes.greyscale);
- }
- // If there is a preDefined value then add it
- if (preDefined !== null) {
- el.val(preDefined[0]);
- }
- // This is add responsive functionality to the logic button without redrawing everything else
- that.s.dt.one('draw.dtsb', function () {
- that.s.topGroup.trigger('dtsb-redrawLogic');
- });
- return el;
- };
- Criteria.initNoValue = function (that) {
- // This is add responsive functionality to the logic button without redrawing everything else
- that.s.dt.one('draw.dtsb', function () {
- that.s.topGroup.trigger('dtsb-redrawLogic');
- });
- return [];
- };
- Criteria.init2Date = function (that, fn, preDefined) {
- var _this = this;
- if (preDefined === void 0) { preDefined = null; }
- var searchDelay = that.s.dt.settings()[0].searchDelay;
- var i18n = that.s.dt.i18n('datetime', {});
- // Declare all of the date elements that are required using DataTables dateTime plugin
- var els = [
- $$3('<input/>')
- .addClass(Criteria.classes.value)
- .addClass(Criteria.classes.input)
- .dtDateTime({
- attachTo: 'input',
- format: that.s.dateFormat ? that.s.dateFormat : undefined,
- i18n: i18n
- })
- .on('change.dtsb', searchDelay !== null ?
- DataTable.util.throttle(function () {
- return fn(that, this);
- }, searchDelay) :
- function () {
- fn(that, _this);
- })
- .on('input.dtsb keypress.dtsb', function (e) {
- DataTable.util.throttle(function () {
- var code = e.keyCode || e.which;
- return fn(that, this, code);
- }, searchDelay === null ? 0 : searchDelay);
- }),
- $$3('<span>')
- .addClass(that.classes.joiner)
- .html(that.s.dt.i18n('searchBuilder.valueJoiner', that.c.i18n.valueJoiner)),
- $$3('<input/>')
- .addClass(Criteria.classes.value)
- .addClass(Criteria.classes.input)
- .dtDateTime({
- attachTo: 'input',
- format: that.s.dateFormat ? that.s.dateFormat : undefined,
- i18n: i18n
- })
- .on('change.dtsb', searchDelay !== null ?
- DataTable.util.throttle(function () {
- return fn(that, this);
- }, searchDelay) :
- function () {
- fn(that, _this);
- })
- .on('input.dtsb keypress.dtsb', !that.c.enterSearch &&
- !(that.s.dt.settings()[0].oInit.search !== undefined &&
- that.s.dt.settings()[0].oInit.search["return"]) &&
- searchDelay !== null ?
- DataTable.util.throttle(function () {
- return fn(that, this);
- }, searchDelay) :
- function (e) {
- var code = e.keyCode || e.which;
- fn(that, _this, code);
- })
- ];
- if (that.c.greyscale) {
- els[0].addClass(Criteria.classes.greyscale);
- els[2].addClass(Criteria.classes.greyscale);
- }
- // If there are and preDefined values then add them
- if (preDefined !== null && preDefined.length > 0) {
- els[0].val(preDefined[0]);
- els[2].val(preDefined[1]);
- }
- // This is add responsive functionality to the logic button without redrawing everything else
- that.s.dt.one('draw.dtsb', function () {
- that.s.topGroup.trigger('dtsb-redrawLogic');
- });
- return els;
- };
- /**
- * Default function for select elements to validate condition
- */
- Criteria.isInputValidSelect = function (el) {
- var allFilled = true;
- // Check each element to make sure that the selections are valid
- for (var _i = 0, el_1 = el; _i < el_1.length; _i++) {
- var element = el_1[_i];
- if (element.children('option:selected').length ===
- element.children('option').length -
- element.children('option.' + Criteria.classes.notItalic).length &&
- element.children('option:selected').length === 1 &&
- element.children('option:selected')[0] === element.children('option')[0]) {
- allFilled = false;
- }
- }
- return allFilled;
- };
- /**
- * Default function for input and date elements to validate condition
- */
- Criteria.isInputValidInput = function (el) {
- var allFilled = true;
- // Check each element to make sure that the inputs are valid
- for (var _i = 0, el_2 = el; _i < el_2.length; _i++) {
- var element = el_2[_i];
- if (element.is('input') && element.val().length === 0) {
- allFilled = false;
- }
- }
- return allFilled;
- };
- /**
- * Default function for getting select conditions
- */
- Criteria.inputValueSelect = function (el) {
- var values = [];
- // Go through the select elements and push each selected option to the return array
- for (var _i = 0, el_3 = el; _i < el_3.length; _i++) {
- var element = el_3[_i];
- if (element.is('select')) {
- values.push(Criteria._escapeHTML(element.children('option:selected').data('sbv')));
- }
- }
- return values;
- };
- /**
- * Default function for getting input conditions
- */
- Criteria.inputValueInput = function (el) {
- var values = [];
- // Go through the input elements and push each value to the return array
- for (var _i = 0, el_4 = el; _i < el_4.length; _i++) {
- var element = el_4[_i];
- if (element.is('input')) {
- values.push(Criteria._escapeHTML(element.val()));
- }
- }
- return values;
- };
- /**
- * Function that is run on each element as a call back when a search should be triggered
- */
- Criteria.updateListener = function (that, el, code) {
- // When the value is changed the criteria is now complete so can be included in searches
- // Get the condition from the map based on the key that has been selected for the condition
- var condition = that.s.conditions[that.s.condition];
- var i;
- that.s.filled = condition.isInputValid(that.dom.value, that);
- that.s.value = condition.inputValue(that.dom.value, that);
- if (!that.s.filled) {
- if (!that.c.enterSearch &&
- !(that.s.dt.settings()[0].oInit.search !== undefined &&
- that.s.dt.settings()[0].oInit.search["return"]) ||
- code === 13) {
- that.doSearch();
- }
- return;
- }
- if (!Array.isArray(that.s.value)) {
- that.s.value = [that.s.value];
- }
- for (i = 0; i < that.s.value.length; i++) {
- // If the value is an array we need to sort it
- if (Array.isArray(that.s.value[i])) {
- that.s.value[i].sort();
- }
- }
- // Take note of the cursor position so that we can refocus there later
- var idx = null;
- var cursorPos = null;
- for (i = 0; i < that.dom.value.length; i++) {
- if (el === that.dom.value[i][0]) {
- idx = i;
- if (el.selectionStart !== undefined) {
- cursorPos = el.selectionStart;
- }
- }
- }
- if (!that.c.enterSearch &&
- !(that.s.dt.settings()[0].oInit.search !== undefined &&
- that.s.dt.settings()[0].oInit.search["return"]) ||
- code === 13) {
- // Trigger a search
- that.doSearch();
- }
- // Refocus the element and set the correct cursor position
- if (idx !== null) {
- that.dom.value[idx].removeClass(that.classes.italic);
- that.dom.value[idx].focus();
- if (cursorPos !== null) {
- that.dom.value[idx][0].setSelectionRange(cursorPos, cursorPos);
- }
- }
- };
- // The order of the conditions will make eslint sad :(
- // Has to be in this order so that they are displayed correctly in select elements
- // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
- Criteria.dateConditions = {
- '=': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.equals', i18n.conditions.date.equals);
- },
- init: Criteria.initDate,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison) {
- value = value.replace(/(\/|-|,)/g, '-');
- return value === comparison[0];
- }
- },
- '!=': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.not', i18n.conditions.date.not);
- },
- init: Criteria.initDate,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison) {
- value = value.replace(/(\/|-|,)/g, '-');
- return value !== comparison[0];
- }
- },
- '<': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.before', i18n.conditions.date.before);
- },
- init: Criteria.initDate,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison) {
- value = value.replace(/(\/|-|,)/g, '-');
- return value < comparison[0];
- }
- },
- '>': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.after', i18n.conditions.date.after);
- },
- init: Criteria.initDate,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison) {
- value = value.replace(/(\/|-|,)/g, '-');
- return value > comparison[0];
- }
- },
- 'between': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.between', i18n.conditions.date.between);
- },
- init: Criteria.init2Date,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison) {
- value = value.replace(/(\/|-|,)/g, '-');
- if (comparison[0] < comparison[1]) {
- return comparison[0] <= value && value <= comparison[1];
- }
- else {
- return comparison[1] <= value && value <= comparison[0];
- }
- }
- },
- '!between': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.notBetween', i18n.conditions.date.notBetween);
- },
- init: Criteria.init2Date,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison) {
- value = value.replace(/(\/|-|,)/g, '-');
- if (comparison[0] < comparison[1]) {
- return !(comparison[0] <= value && value <= comparison[1]);
- }
- else {
- return !(comparison[1] <= value && value <= comparison[0]);
- }
- }
- },
- 'null': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.empty', i18n.conditions.date.empty);
- },
- init: Criteria.initNoValue,
- inputValue: function () {
- return;
- },
- isInputValid: function () {
- return true;
- },
- search: function (value) {
- return value === null || value === undefined || value.length === 0;
- }
- },
- '!null': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.notEmpty', i18n.conditions.date.notEmpty);
- },
- init: Criteria.initNoValue,
- inputValue: function () {
- return;
- },
- isInputValid: function () {
- return true;
- },
- search: function (value) {
- return !(value === null || value === undefined || value.length === 0);
- }
- }
- };
- // The order of the conditions will make eslint sad :(
- // Has to be in this order so that they are displayed correctly in select elements
- // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
- Criteria.momentDateConditions = {
- '=': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.equals', i18n.conditions.date.equals);
- },
- init: Criteria.initDate,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison, that) {
- return moment()(value, that.s.dateFormat).valueOf() ===
- moment()(comparison[0], that.s.dateFormat).valueOf();
- }
- },
- '!=': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.not', i18n.conditions.date.not);
- },
- init: Criteria.initDate,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison, that) {
- return moment()(value, that.s.dateFormat).valueOf() !==
- moment()(comparison[0], that.s.dateFormat).valueOf();
- }
- },
- '<': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.before', i18n.conditions.date.before);
- },
- init: Criteria.initDate,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison, that) {
- return moment()(value, that.s.dateFormat).valueOf() < moment()(comparison[0], that.s.dateFormat).valueOf();
- }
- },
- '>': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.after', i18n.conditions.date.after);
- },
- init: Criteria.initDate,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison, that) {
- return moment()(value, that.s.dateFormat).valueOf() > moment()(comparison[0], that.s.dateFormat).valueOf();
- }
- },
- 'between': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.between', i18n.conditions.date.between);
- },
- init: Criteria.init2Date,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison, that) {
- var val = moment()(value, that.s.dateFormat).valueOf();
- var comp0 = moment()(comparison[0], that.s.dateFormat).valueOf();
- var comp1 = moment()(comparison[1], that.s.dateFormat).valueOf();
- if (comp0 < comp1) {
- return comp0 <= val && val <= comp1;
- }
- else {
- return comp1 <= val && val <= comp0;
- }
- }
- },
- '!between': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.notBetween', i18n.conditions.date.notBetween);
- },
- init: Criteria.init2Date,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison, that) {
- var val = moment()(value, that.s.dateFormat).valueOf();
- var comp0 = moment()(comparison[0], that.s.dateFormat).valueOf();
- var comp1 = moment()(comparison[1], that.s.dateFormat).valueOf();
- if (comp0 < comp1) {
- return !(+comp0 <= +val && +val <= +comp1);
- }
- else {
- return !(+comp1 <= +val && +val <= +comp0);
- }
- }
- },
- 'null': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.empty', i18n.conditions.date.empty);
- },
- init: Criteria.initNoValue,
- inputValue: function () {
- return;
- },
- isInputValid: function () {
- return true;
- },
- search: function (value) {
- return value === null || value === undefined || value.length === 0;
- }
- },
- '!null': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.notEmpty', i18n.conditions.date.notEmpty);
- },
- init: Criteria.initNoValue,
- inputValue: function () {
- return;
- },
- isInputValid: function () {
- return true;
- },
- search: function (value) {
- return !(value === null || value === undefined || value.length === 0);
- }
- }
- };
- // The order of the conditions will make eslint sad :(
- // Has to be in this order so that they are displayed correctly in select elements
- // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
- Criteria.luxonDateConditions = {
- '=': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.equals', i18n.conditions.date.equals);
- },
- init: Criteria.initDate,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison, that) {
- return luxon().DateTime.fromFormat(value, that.s.dateFormat).ts
- === luxon().DateTime.fromFormat(comparison[0], that.s.dateFormat).ts;
- }
- },
- '!=': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.not', i18n.conditions.date.not);
- },
- init: Criteria.initDate,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison, that) {
- return luxon().DateTime.fromFormat(value, that.s.dateFormat).ts
- !== luxon().DateTime.fromFormat(comparison[0], that.s.dateFormat).ts;
- }
- },
- '<': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.before', i18n.conditions.date.before);
- },
- init: Criteria.initDate,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison, that) {
- return luxon().DateTime.fromFormat(value, that.s.dateFormat).ts
- < luxon().DateTime.fromFormat(comparison[0], that.s.dateFormat).ts;
- }
- },
- '>': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.after', i18n.conditions.date.after);
- },
- init: Criteria.initDate,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison, that) {
- return luxon().DateTime.fromFormat(value, that.s.dateFormat).ts
- > luxon().DateTime.fromFormat(comparison[0], that.s.dateFormat).ts;
- }
- },
- 'between': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.between', i18n.conditions.date.between);
- },
- init: Criteria.init2Date,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison, that) {
- var val = luxon().DateTime.fromFormat(value, that.s.dateFormat).ts;
- var comp0 = luxon().DateTime.fromFormat(comparison[0], that.s.dateFormat).ts;
- var comp1 = luxon().DateTime.fromFormat(comparison[1], that.s.dateFormat).ts;
- if (comp0 < comp1) {
- return comp0 <= val && val <= comp1;
- }
- else {
- return comp1 <= val && val <= comp0;
- }
- }
- },
- '!between': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.notBetween', i18n.conditions.date.notBetween);
- },
- init: Criteria.init2Date,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison, that) {
- var val = luxon().DateTime.fromFormat(value, that.s.dateFormat).ts;
- var comp0 = luxon().DateTime.fromFormat(comparison[0], that.s.dateFormat).ts;
- var comp1 = luxon().DateTime.fromFormat(comparison[1], that.s.dateFormat).ts;
- if (comp0 < comp1) {
- return !(+comp0 <= +val && +val <= +comp1);
- }
- else {
- return !(+comp1 <= +val && +val <= +comp0);
- }
- }
- },
- 'null': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.empty', i18n.conditions.date.empty);
- },
- init: Criteria.initNoValue,
- inputValue: function () {
- return;
- },
- isInputValid: function () {
- return true;
- },
- search: function (value) {
- return value === null || value === undefined || value.length === 0;
- }
- },
- '!null': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.date.notEmpty', i18n.conditions.date.notEmpty);
- },
- init: Criteria.initNoValue,
- inputValue: function () {
- return;
- },
- isInputValid: function () {
- return true;
- },
- search: function (value) {
- return !(value === null || value === undefined || value.length === 0);
- }
- }
- };
- // The order of the conditions will make eslint sad :(
- // Has to be in this order so that they are displayed correctly in select elements
- // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
- Criteria.numConditions = {
- '=': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.number.equals', i18n.conditions.number.equals);
- },
- init: Criteria.initSelect,
- inputValue: Criteria.inputValueSelect,
- isInputValid: Criteria.isInputValidSelect,
- search: function (value, comparison) {
- return +value === +comparison[0];
- }
- },
- '!=': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.number.not', i18n.conditions.number.not);
- },
- init: Criteria.initSelect,
- inputValue: Criteria.inputValueSelect,
- isInputValid: Criteria.isInputValidSelect,
- search: function (value, comparison) {
- return +value !== +comparison[0];
- }
- },
- '<': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.number.lt', i18n.conditions.number.lt);
- },
- init: Criteria.initInput,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison) {
- return +value < +comparison[0];
- }
- },
- '<=': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.number.lte', i18n.conditions.number.lte);
- },
- init: Criteria.initInput,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison) {
- return +value <= +comparison[0];
- }
- },
- '>=': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.number.gte', i18n.conditions.number.gte);
- },
- init: Criteria.initInput,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison) {
- return +value >= +comparison[0];
- }
- },
- '>': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.number.gt', i18n.conditions.number.gt);
- },
- init: Criteria.initInput,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison) {
- return +value > +comparison[0];
- }
- },
- 'between': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.number.between', i18n.conditions.number.between);
- },
- init: Criteria.init2Input,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison) {
- if (+comparison[0] < +comparison[1]) {
- return +comparison[0] <= +value && +value <= +comparison[1];
- }
- else {
- return +comparison[1] <= +value && +value <= +comparison[0];
- }
- }
- },
- '!between': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.number.notBetween', i18n.conditions.number.notBetween);
- },
- init: Criteria.init2Input,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison) {
- if (+comparison[0] < +comparison[1]) {
- return !(+comparison[0] <= +value && +value <= +comparison[1]);
- }
- else {
- return !(+comparison[1] <= +value && +value <= +comparison[0]);
- }
- }
- },
- 'null': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.number.empty', i18n.conditions.number.empty);
- },
- init: Criteria.initNoValue,
- inputValue: function () {
- return;
- },
- isInputValid: function () {
- return true;
- },
- search: function (value) {
- return value === null || value === undefined || value.length === 0;
- }
- },
- '!null': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.number.notEmpty', i18n.conditions.number.notEmpty);
- },
- init: Criteria.initNoValue,
- inputValue: function () {
- return;
- },
- isInputValid: function () {
- return true;
- },
- search: function (value) {
- return !(value === null || value === undefined || value.length === 0);
- }
- }
- };
- // The order of the conditions will make eslint sad :(
- // Has to be in this order so that they are displayed correctly in select elements
- // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
- Criteria.numFmtConditions = {
- '=': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.number.equals', i18n.conditions.number.equals);
- },
- init: Criteria.initSelect,
- inputValue: Criteria.inputValueSelect,
- isInputValid: Criteria.isInputValidSelect,
- search: function (value, comparison, criteria) {
- return criteria.parseNumber(value) === criteria.parseNumber(comparison[0]);
- }
- },
- '!=': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.number.not', i18n.conditions.number.not);
- },
- init: Criteria.initSelect,
- inputValue: Criteria.inputValueSelect,
- isInputValid: Criteria.isInputValidSelect,
- search: function (value, comparison, criteria) {
- return criteria.parseNumber(value) !== criteria.parseNumber(comparison[0]);
- }
- },
- '<': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.number.lt', i18n.conditions.number.lt);
- },
- init: Criteria.initInput,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison, criteria) {
- return criteria.parseNumber(value) < criteria.parseNumber(comparison[0]);
- }
- },
- '<=': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.number.lte', i18n.conditions.number.lte);
- },
- init: Criteria.initInput,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison, criteria) {
- return criteria.parseNumber(value) <= criteria.parseNumber(comparison[0]);
- }
- },
- '>=': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.number.gte', i18n.conditions.number.gte);
- },
- init: Criteria.initInput,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison, criteria) {
- return criteria.parseNumber(value) >= criteria.parseNumber(comparison[0]);
- }
- },
- '>': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.number.gt', i18n.conditions.number.gt);
- },
- init: Criteria.initInput,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison, criteria) {
- return criteria.parseNumber(value) > criteria.parseNumber(comparison[0]);
- }
- },
- 'between': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.number.between', i18n.conditions.number.between);
- },
- init: Criteria.init2Input,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison, criteria) {
- var val = criteria.parseNumber(value);
- var comp0 = criteria.parseNumber(comparison[0]);
- var comp1 = criteria.parseNumber(comparison[1]);
- if (+comp0 < +comp1) {
- return +comp0 <= +val && +val <= +comp1;
- }
- else {
- return +comp1 <= +val && +val <= +comp0;
- }
- }
- },
- '!between': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.number.notBetween', i18n.conditions.number.notBetween);
- },
- init: Criteria.init2Input,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison, criteria) {
- var val = criteria.parseNumber(value);
- var comp0 = criteria.parseNumber(comparison[0]);
- var comp1 = criteria.parseNumber(comparison[1]);
- if (+comp0 < +comp1) {
- return !(+comp0 <= +val && +val <= +comp1);
- }
- else {
- return !(+comp1 <= +val && +val <= +comp0);
- }
- }
- },
- 'null': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.number.empty', i18n.conditions.number.empty);
- },
- init: Criteria.initNoValue,
- inputValue: function () {
- return;
- },
- isInputValid: function () {
- return true;
- },
- search: function (value) {
- return value === null || value === undefined || value.length === 0;
- }
- },
- '!null': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.number.notEmpty', i18n.conditions.number.notEmpty);
- },
- init: Criteria.initNoValue,
- inputValue: function () {
- return;
- },
- isInputValid: function () {
- return true;
- },
- search: function (value) {
- return !(value === null || value === undefined || value.length === 0);
- }
- }
- };
- // The order of the conditions will make eslint sad :(
- // Has to be in this order so that they are displayed correctly in select elements
- // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
- Criteria.stringConditions = {
- '=': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.string.equals', i18n.conditions.string.equals);
- },
- init: Criteria.initSelect,
- inputValue: Criteria.inputValueSelect,
- isInputValid: Criteria.isInputValidSelect,
- search: function (value, comparison) {
- return value === comparison[0];
- }
- },
- '!=': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.string.not', i18n.conditions.string.not);
- },
- init: Criteria.initSelect,
- inputValue: Criteria.inputValueSelect,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison) {
- return value !== comparison[0];
- }
- },
- 'starts': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.string.startsWith', i18n.conditions.string.startsWith);
- },
- init: Criteria.initInput,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison) {
- return value.toLowerCase().indexOf(comparison[0].toLowerCase()) === 0;
- }
- },
- '!starts': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.string.notStartsWith', i18n.conditions.string.notStartsWith);
- },
- init: Criteria.initInput,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison) {
- return value.toLowerCase().indexOf(comparison[0].toLowerCase()) !== 0;
- }
- },
- 'contains': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.string.contains', i18n.conditions.string.contains);
- },
- init: Criteria.initInput,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison) {
- return value.toLowerCase().includes(comparison[0].toLowerCase());
- }
- },
- '!contains': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.string.notContains', i18n.conditions.string.notContains);
- },
- init: Criteria.initInput,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison) {
- return !value.toLowerCase().includes(comparison[0].toLowerCase());
- }
- },
- 'ends': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.string.endsWith', i18n.conditions.string.endsWith);
- },
- init: Criteria.initInput,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison) {
- return value.toLowerCase().endsWith(comparison[0].toLowerCase());
- }
- },
- '!ends': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.string.notEndsWith', i18n.conditions.string.notEndsWith);
- },
- init: Criteria.initInput,
- inputValue: Criteria.inputValueInput,
- isInputValid: Criteria.isInputValidInput,
- search: function (value, comparison) {
- return !value.toLowerCase().endsWith(comparison[0].toLowerCase());
- }
- },
- 'null': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.string.empty', i18n.conditions.string.empty);
- },
- init: Criteria.initNoValue,
- inputValue: function () {
- return;
- },
- isInputValid: function () {
- return true;
- },
- search: function (value) {
- return value === null || value === undefined || value.length === 0;
- }
- },
- '!null': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.string.notEmpty', i18n.conditions.string.notEmpty);
- },
- init: Criteria.initNoValue,
- inputValue: function () {
- return;
- },
- isInputValid: function () {
- return true;
- },
- search: function (value) {
- return !(value === null || value === undefined || value.length === 0);
- }
- }
- };
- // The order of the conditions will make eslint sad :(
- // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
- Criteria.arrayConditions = {
- 'contains': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.array.contains', i18n.conditions.array.contains);
- },
- init: Criteria.initSelectArray,
- inputValue: Criteria.inputValueSelect,
- isInputValid: Criteria.isInputValidSelect,
- search: function (value, comparison) {
- return value.includes(comparison[0]);
- }
- },
- 'without': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.array.without', i18n.conditions.array.without);
- },
- init: Criteria.initSelectArray,
- inputValue: Criteria.inputValueSelect,
- isInputValid: Criteria.isInputValidSelect,
- search: function (value, comparison) {
- return value.indexOf(comparison[0]) === -1;
- }
- },
- '=': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.array.equals', i18n.conditions.array.equals);
- },
- init: Criteria.initSelect,
- inputValue: Criteria.inputValueSelect,
- isInputValid: Criteria.isInputValidSelect,
- search: function (value, comparison) {
- if (value.length === comparison[0].length) {
- for (var i = 0; i < value.length; i++) {
- if (value[i] !== comparison[0][i]) {
- return false;
- }
- }
- return true;
- }
- return false;
- }
- },
- '!=': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.array.not', i18n.conditions.array.not);
- },
- init: Criteria.initSelect,
- inputValue: Criteria.inputValueSelect,
- isInputValid: Criteria.isInputValidSelect,
- search: function (value, comparison) {
- if (value.length === comparison[0].length) {
- for (var i = 0; i < value.length; i++) {
- if (value[i] !== comparison[0][i]) {
- return true;
- }
- }
- return false;
- }
- return true;
- }
- },
- 'null': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.array.empty', i18n.conditions.array.empty);
- },
- init: Criteria.initNoValue,
- inputValue: function () {
- return;
- },
- isInputValid: function () {
- return true;
- },
- search: function (value) {
- return value === null || value === undefined || value.length === 0;
- }
- },
- '!null': {
- conditionName: function (dt, i18n) {
- return dt.i18n('searchBuilder.conditions.array.notEmpty', i18n.conditions.array.notEmpty);
- },
- init: Criteria.initNoValue,
- inputValue: function () {
- return;
- },
- isInputValid: function () {
- return true;
- },
- search: function (value) {
- return value !== null && value !== undefined && value.length !== 0;
- }
- }
- };
- // eslint will be sad because we have to disable member ordering for this as the
- // private static properties used are not yet declared otherwise
- Criteria.defaults = {
- columns: true,
- conditions: {
- 'array': Criteria.arrayConditions,
- 'date': Criteria.dateConditions,
- 'html': Criteria.stringConditions,
- 'html-num': Criteria.numConditions,
- 'html-num-fmt': Criteria.numFmtConditions,
- 'luxon': Criteria.luxonDateConditions,
- 'moment': Criteria.momentDateConditions,
- 'num': Criteria.numConditions,
- 'num-fmt': Criteria.numFmtConditions,
- 'string': Criteria.stringConditions
- },
- depthLimit: false,
- enterSearch: false,
- filterChanged: undefined,
- greyscale: false,
- i18n: {
- add: 'Add Condition',
- button: {
- 0: 'Search Builder',
- _: 'Search Builder (%d)'
- },
- clearAll: 'Clear All',
- condition: 'Condition',
- data: 'Data',
- "delete": '×',
- deleteTitle: 'Delete filtering rule',
- left: '<',
- leftTitle: 'Outdent criteria',
- logicAnd: 'And',
- logicOr: 'Or',
- right: '>',
- rightTitle: 'Indent criteria',
- search: 'Search',
- title: {
- 0: 'Custom Search Builder',
- _: 'Custom Search Builder (%d)'
- },
- value: 'Value',
- valueJoiner: 'and'
- },
- liveSearch: true,
- logic: 'AND',
- orthogonal: {
- display: 'display',
- search: 'filter'
- },
- preDefined: false
- };
- return Criteria;
- }());
- var $$2;
- var dataTable$2;
- /**
- * Sets the value of jQuery for use in the file
- *
- * @param jq the instance of jQuery to be set
- */
- function setJQuery$1(jq) {
- $$2 = jq;
- dataTable$2 = jq.fn.dataTable;
- }
- /**
- * The Group class is used within SearchBuilder to represent a group of criteria
- */
- var Group = /** @class */ (function () {
- function Group(table, opts, topGroup, index, isChild, depth, serverData) {
- if (index === void 0) { index = 0; }
- if (isChild === void 0) { isChild = false; }
- if (depth === void 0) { depth = 1; }
- if (serverData === void 0) { serverData = undefined; }
- // Check that the required version of DataTables is included
- if (!dataTable$2 || !dataTable$2.versionCheck || !dataTable$2.versionCheck('1.10.0')) {
- throw new Error('SearchBuilder requires DataTables 1.10 or newer');
- }
- this.classes = $$2.extend(true, {}, Group.classes);
- // Get options from user
- this.c = $$2.extend(true, {}, Group.defaults, opts);
- this.s = {
- criteria: [],
- depth: depth,
- dt: table,
- index: index,
- isChild: isChild,
- logic: undefined,
- opts: opts,
- preventRedraw: false,
- serverData: serverData,
- toDrop: undefined,
- topGroup: topGroup
- };
- this.dom = {
- add: $$2('<button/>')
- .addClass(this.classes.add)
- .addClass(this.classes.button)
- .attr('type', 'button'),
- clear: $$2('<button>×</button>')
- .addClass(this.classes.button)
- .addClass(this.classes.clearGroup)
- .attr('type', 'button'),
- container: $$2('<div/>')
- .addClass(this.classes.group),
- logic: $$2('<button><div/></button>')
- .addClass(this.classes.logic)
- .addClass(this.classes.button)
- .attr('type', 'button'),
- logicContainer: $$2('<div/>')
- .addClass(this.classes.logicContainer),
- search: $$2('<button/>')
- .addClass(this.classes.search)
- .addClass(this.classes.button)
- .attr('type', 'button')
- .css('display', 'none')
- };
- // A reference to the top level group is maintained throughout any subgroups and criteria that may be created
- if (this.s.topGroup === undefined) {
- this.s.topGroup = this.dom.container;
- }
- this._setup();
- return this;
- }
- /**
- * Destroys the groups buttons, clears the internal criteria and removes it from the dom
- */
- Group.prototype.destroy = function () {
- // Turn off listeners
- this.dom.add.off('.dtsb');
- this.dom.logic.off('.dtsb');
- this.dom.search.off('.dtsb');
- // Trigger event for groups at a higher level to pick up on
- this.dom.container
- .trigger('dtsb-destroy')
- .remove();
- this.s.criteria = [];
- };
- /**
- * Gets the details required to rebuild the group
- */
- // Eslint upset at empty object but needs to be done
- Group.prototype.getDetails = function (deFormatDates) {
- if (deFormatDates === void 0) { deFormatDates = false; }
- if (this.s.criteria.length === 0) {
- return {};
- }
- var details = {
- criteria: [],
- logic: this.s.logic
- };
- // NOTE here crit could be either a subgroup or a criteria
- for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
- var crit = _a[_i];
- details.criteria.push(crit.criteria.getDetails(deFormatDates));
- }
- return details;
- };
- /**
- * Getter for the node for the container of the group
- *
- * @returns Node for the container of the group
- */
- Group.prototype.getNode = function () {
- return this.dom.container;
- };
- /**
- * Rebuilds the group based upon the details passed in
- *
- * @param loadedDetails the details required to rebuild the group
- */
- Group.prototype.rebuild = function (loadedDetails) {
- var crit;
- // If no criteria are stored then just return
- if (loadedDetails.criteria === undefined ||
- loadedDetails.criteria === null ||
- Array.isArray(loadedDetails.criteria) && loadedDetails.criteria.length === 0) {
- return;
- }
- this.s.logic = loadedDetails.logic;
- this.dom.logic.children().first().html(this.s.logic === 'OR'
- ? this.s.dt.i18n('searchBuilder.logicOr', this.c.i18n.logicOr)
- : this.s.dt.i18n('searchBuilder.logicAnd', this.c.i18n.logicAnd));
- // Add all of the criteria, be it a sub group or a criteria
- if (Array.isArray(loadedDetails.criteria)) {
- for (var _i = 0, _a = loadedDetails.criteria; _i < _a.length; _i++) {
- crit = _a[_i];
- if (crit.logic !== undefined) {
- this._addPrevGroup(crit);
- }
- else if (crit.logic === undefined) {
- this._addPrevCriteria(crit);
- }
- }
- }
- // For all of the criteria children, update the arrows incase they require changing and set the listeners
- for (var _b = 0, _c = this.s.criteria; _b < _c.length; _b++) {
- crit = _c[_b];
- if (crit.criteria instanceof Criteria) {
- crit.criteria.updateArrows(this.s.criteria.length > 1);
- this._setCriteriaListeners(crit.criteria);
- }
- }
- };
- /**
- * Redraws the Contents of the searchBuilder Groups and Criteria
- */
- Group.prototype.redrawContents = function () {
- if (this.s.preventRedraw) {
- return;
- }
- // Clear the container out and add the basic elements
- this.dom.container.children().detach();
- this.dom.container
- .append(this.dom.logicContainer)
- .append(this.dom.add);
- if (!this.c.liveSearch) {
- this.dom.container.append(this.dom.search);
- }
- // Sort the criteria by index so that they appear in the correct order
- this.s.criteria.sort(function (a, b) {
- if (a.criteria.s.index < b.criteria.s.index) {
- return -1;
- }
- else if (a.criteria.s.index > b.criteria.s.index) {
- return 1;
- }
- return 0;
- });
- this.setListeners();
- for (var i = 0; i < this.s.criteria.length; i++) {
- var crit = this.s.criteria[i].criteria;
- if (crit instanceof Criteria) {
- // Reset the index to the new value
- this.s.criteria[i].index = i;
- this.s.criteria[i].criteria.s.index = i;
- // Add to the group
- this.s.criteria[i].criteria.dom.container.insertBefore(this.dom.add);
- // Set listeners for various points
- this._setCriteriaListeners(crit);
- this.s.criteria[i].criteria.s.preventRedraw = this.s.preventRedraw;
- this.s.criteria[i].criteria.rebuild(this.s.criteria[i].criteria.getDetails());
- this.s.criteria[i].criteria.s.preventRedraw = false;
- }
- else if (crit instanceof Group && crit.s.criteria.length > 0) {
- // Reset the index to the new value
- this.s.criteria[i].index = i;
- this.s.criteria[i].criteria.s.index = i;
- // Add the sub group to the group
- this.s.criteria[i].criteria.dom.container.insertBefore(this.dom.add);
- // Redraw the contents of the group
- crit.s.preventRedraw = this.s.preventRedraw;
- crit.redrawContents();
- crit.s.preventRedraw = false;
- this._setGroupListeners(crit);
- }
- else {
- // The group is empty so remove it
- this.s.criteria.splice(i, 1);
- i--;
- }
- }
- this.setupLogic();
- };
- /**
- * Resizes the logic button only rather than the entire dom.
- */
- Group.prototype.redrawLogic = function () {
- for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
- var crit = _a[_i];
- if (crit.criteria instanceof Group) {
- crit.criteria.redrawLogic();
- }
- }
- this.setupLogic();
- };
- /**
- * Search method, checking the row data against the criteria in the group
- *
- * @param rowData The row data to be compared
- * @returns boolean The result of the search
- */
- Group.prototype.search = function (rowData, rowIdx) {
- if (this.s.logic === 'AND') {
- return this._andSearch(rowData, rowIdx);
- }
- else if (this.s.logic === 'OR') {
- return this._orSearch(rowData, rowIdx);
- }
- return true;
- };
- /**
- * Locates the groups logic button to the correct location on the page
- */
- Group.prototype.setupLogic = function () {
- // Remove logic button
- this.dom.logicContainer.remove();
- this.dom.clear.remove();
- // If there are no criteria in the group then keep the logic removed and return
- if (this.s.criteria.length < 1) {
- if (!this.s.isChild) {
- this.dom.container.trigger('dtsb-destroy');
- // Set criteria left margin
- this.dom.container.css('margin-left', 0);
- }
- this.dom.search.css('display', 'none');
- return;
- }
- this.dom.clear.height('0px');
- this.dom.logicContainer.append(this.dom.clear);
- if (!this.s.isChild) {
- this.dom.search.css('display', 'inline-block');
- }
- // Prepend logic button
- this.dom.container.prepend(this.dom.logicContainer);
- for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
- var crit = _a[_i];
- if (crit.criteria instanceof Criteria) {
- crit.criteria.setupButtons();
- }
- }
- // Set width, take 2 for the border
- var height = this.dom.container.outerHeight() - 1;
- this.dom.logicContainer.width(height);
- this._setLogicListener();
- // Set criteria left margin
- this.dom.container.css('margin-left', this.dom.logicContainer.outerHeight(true));
- var logicOffset = this.dom.logicContainer.offset();
- // Set horizontal alignment
- var currentLeft = logicOffset.left;
- var groupLeft = this.dom.container.offset().left;
- var shuffleLeft = currentLeft - groupLeft;
- var newPos = currentLeft - shuffleLeft - this.dom.logicContainer.outerHeight(true);
- this.dom.logicContainer.offset({ left: newPos });
- // Set vertical alignment
- var firstCrit = this.dom.logicContainer.next();
- var currentTop = logicOffset.top;
- var firstTop = $$2(firstCrit).offset().top;
- var shuffleTop = currentTop - firstTop;
- var newTop = currentTop - shuffleTop;
- this.dom.logicContainer.offset({ top: newTop });
- this.dom.clear.outerHeight(this.dom.logicContainer.height());
- this._setClearListener();
- };
- /**
- * Sets listeners on the groups elements
- */
- Group.prototype.setListeners = function () {
- var _this = this;
- this.dom.add.unbind('click');
- this.dom.add.on('click.dtsb', function () {
- // If this is the parent group then the logic button has not been added yet
- if (!_this.s.isChild) {
- _this.dom.container.prepend(_this.dom.logicContainer);
- }
- _this.addCriteria();
- _this.dom.container.trigger('dtsb-add');
- _this.s.dt.state.save();
- return false;
- });
- this.dom.search
- .off('click.dtsb')
- .on('click.dtsb', function () {
- _this.s.dt.draw();
- });
- for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
- var crit = _a[_i];
- crit.criteria.setListeners();
- }
- this._setClearListener();
- this._setLogicListener();
- };
- /**
- * Adds a criteria to the group
- *
- * @param crit Instance of Criteria to be added to the group
- */
- Group.prototype.addCriteria = function (crit) {
- if (crit === void 0) { crit = null; }
- var index = crit === null ? this.s.criteria.length : crit.s.index;
- var criteria = new Criteria(this.s.dt, this.s.opts, this.s.topGroup, index, this.s.depth, this.s.serverData, this.c.liveSearch);
- // If a Criteria has been passed in then set the values to continue that
- if (crit !== null) {
- criteria.c = crit.c;
- criteria.s = crit.s;
- criteria.s.depth = this.s.depth;
- criteria.classes = crit.classes;
- }
- criteria.populate();
- var inserted = false;
- for (var i = 0; i < this.s.criteria.length; i++) {
- if (i === 0 && this.s.criteria[i].criteria.s.index > criteria.s.index) {
- // Add the node for the criteria at the start of the group
- criteria.getNode().insertBefore(this.s.criteria[i].criteria.dom.container);
- inserted = true;
- }
- else if (i < this.s.criteria.length - 1 &&
- this.s.criteria[i].criteria.s.index < criteria.s.index &&
- this.s.criteria[i + 1].criteria.s.index > criteria.s.index) {
- // Add the node for the criteria in the correct location
- criteria.getNode().insertAfter(this.s.criteria[i].criteria.dom.container);
- inserted = true;
- }
- }
- if (!inserted) {
- criteria.getNode().insertBefore(this.dom.add);
- }
- // Add the details for this criteria to the array
- this.s.criteria.push({
- criteria: criteria,
- index: index
- });
- this.s.criteria = this.s.criteria.sort(function (a, b) { return a.criteria.s.index - b.criteria.s.index; });
- for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
- var opt = _a[_i];
- if (opt.criteria instanceof Criteria) {
- opt.criteria.updateArrows(this.s.criteria.length > 1);
- }
- }
- this._setCriteriaListeners(criteria);
- criteria.setListeners();
- this.setupLogic();
- };
- /**
- * Checks the group to see if it has any filled criteria
- */
- Group.prototype.checkFilled = function () {
- for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
- var crit = _a[_i];
- if (crit.criteria instanceof Criteria && crit.criteria.s.filled ||
- crit.criteria instanceof Group && crit.criteria.checkFilled()) {
- return true;
- }
- }
- return false;
- };
- /**
- * Gets the count for the number of criteria in this group and any sub groups
- */
- Group.prototype.count = function () {
- var count = 0;
- for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
- var crit = _a[_i];
- if (crit.criteria instanceof Group) {
- count += crit.criteria.count();
- }
- else {
- count++;
- }
- }
- return count;
- };
- /**
- * Rebuilds a sub group that previously existed
- *
- * @param loadedGroup The details of a group within this group
- */
- Group.prototype._addPrevGroup = function (loadedGroup) {
- var idx = this.s.criteria.length;
- var group = new Group(this.s.dt, this.c, this.s.topGroup, idx, true, this.s.depth + 1, this.s.serverData);
- // Add the new group to the criteria array
- this.s.criteria.push({
- criteria: group,
- index: idx,
- logic: group.s.logic
- });
- // Rebuild it with the previous conditions for that group
- group.rebuild(loadedGroup);
- this.s.criteria[idx].criteria = group;
- this.s.topGroup.trigger('dtsb-redrawContents');
- this._setGroupListeners(group);
- };
- /**
- * Rebuilds a criteria of this group that previously existed
- *
- * @param loadedCriteria The details of a criteria within the group
- */
- Group.prototype._addPrevCriteria = function (loadedCriteria) {
- var idx = this.s.criteria.length;
- var criteria = new Criteria(this.s.dt, this.s.opts, this.s.topGroup, idx, this.s.depth, this.s.serverData);
- criteria.populate();
- // Add the new criteria to the criteria array
- this.s.criteria.push({
- criteria: criteria,
- index: idx
- });
- // Rebuild it with the previous conditions for that criteria
- criteria.s.preventRedraw = this.s.preventRedraw;
- criteria.rebuild(loadedCriteria);
- criteria.s.preventRedraw = false;
- this.s.criteria[idx].criteria = criteria;
- if (!this.s.preventRedraw) {
- this.s.topGroup.trigger('dtsb-redrawContents');
- }
- };
- /**
- * Checks And the criteria using AND logic
- *
- * @param rowData The row data to be checked against the search criteria
- * @returns boolean The result of the AND search
- */
- Group.prototype._andSearch = function (rowData, rowIdx) {
- // If there are no criteria then return true for this group
- if (this.s.criteria.length === 0) {
- return true;
- }
- for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
- var crit = _a[_i];
- // If the criteria is not complete then skip it
- if (crit.criteria instanceof Criteria && !crit.criteria.s.filled) {
- continue;
- }
- // Otherwise if a single one fails return false
- else if (!crit.criteria.search(rowData, rowIdx)) {
- return false;
- }
- }
- // If we get to here then everything has passed, so return true for the group
- return true;
- };
- /**
- * Checks And the criteria using OR logic
- *
- * @param rowData The row data to be checked against the search criteria
- * @returns boolean The result of the OR search
- */
- Group.prototype._orSearch = function (rowData, rowIdx) {
- // If there are no criteria in the group then return true
- if (this.s.criteria.length === 0) {
- return true;
- }
- // This will check to make sure that at least one criteria in the group is complete
- var filledfound = false;
- for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
- var crit = _a[_i];
- if (crit.criteria instanceof Criteria && crit.criteria.s.filled) {
- // A completed criteria has been found so set the flag
- filledfound = true;
- // If the search passes then return true
- if (crit.criteria.search(rowData, rowIdx)) {
- return true;
- }
- }
- else if (crit.criteria instanceof Group && crit.criteria.checkFilled()) {
- filledfound = true;
- if (crit.criteria.search(rowData, rowIdx)) {
- return true;
- }
- }
- }
- // If we get here we need to return the inverse of filledfound,
- // as if any have been found and we are here then none have passed
- return !filledfound;
- };
- /**
- * Removes a criteria from the group
- *
- * @param criteria The criteria instance to be removed
- */
- Group.prototype._removeCriteria = function (criteria, group) {
- if (group === void 0) { group = false; }
- var i;
- // If removing a criteria and there is only then then just destroy the group
- if (this.s.criteria.length <= 1 && this.s.isChild) {
- this.destroy();
- }
- else {
- // Otherwise splice the given criteria out and redo the indexes
- var last = void 0;
- for (i = 0; i < this.s.criteria.length; i++) {
- if (this.s.criteria[i].index === criteria.s.index &&
- (!group || this.s.criteria[i].criteria instanceof Group)) {
- last = i;
- }
- }
- // We want to remove the last element with the desired index, as its replacement will be inserted before it
- if (last !== undefined) {
- this.s.criteria.splice(last, 1);
- }
- for (i = 0; i < this.s.criteria.length; i++) {
- this.s.criteria[i].index = i;
- this.s.criteria[i].criteria.s.index = i;
- }
- }
- };
- /**
- * Sets the listeners in group for a criteria
- *
- * @param criteria The criteria for the listeners to be set on
- */
- Group.prototype._setCriteriaListeners = function (criteria) {
- var _this = this;
- criteria.dom["delete"]
- .unbind('click')
- .on('click.dtsb', function () {
- _this._removeCriteria(criteria);
- criteria.dom.container.remove();
- for (var _i = 0, _a = _this.s.criteria; _i < _a.length; _i++) {
- var crit = _a[_i];
- if (crit.criteria instanceof Criteria) {
- crit.criteria.updateArrows(_this.s.criteria.length > 1);
- }
- }
- criteria.destroy();
- _this.s.dt.draw();
- _this.s.topGroup.trigger('dtsb-redrawContents');
- return false;
- });
- criteria.dom.right
- .unbind('click')
- .on('click.dtsb', function () {
- var idx = criteria.s.index;
- var group = new Group(_this.s.dt, _this.s.opts, _this.s.topGroup, criteria.s.index, true, _this.s.depth + 1, _this.s.serverData);
- // Add the criteria that is to be moved to the new group
- group.addCriteria(criteria);
- // Update the details in the current groups criteria array
- _this.s.criteria[idx].criteria = group;
- _this.s.criteria[idx].logic = 'AND';
- _this.s.topGroup.trigger('dtsb-redrawContents');
- _this._setGroupListeners(group);
- return false;
- });
- criteria.dom.left
- .unbind('click')
- .on('click.dtsb', function () {
- _this.s.toDrop = new Criteria(_this.s.dt, _this.s.opts, _this.s.topGroup, criteria.s.index, undefined, _this.s.serverData);
- _this.s.toDrop.s = criteria.s;
- _this.s.toDrop.c = criteria.c;
- _this.s.toDrop.classes = criteria.classes;
- _this.s.toDrop.populate();
- // The dropCriteria event mutates the reference to the index so need to store it
- var index = _this.s.toDrop.s.index;
- _this.dom.container.trigger('dtsb-dropCriteria');
- criteria.s.index = index;
- _this._removeCriteria(criteria);
- // By tracking the top level group we can directly trigger a redraw on it,
- // bubbling is also possible, but that is slow with deep levelled groups
- _this.s.topGroup.trigger('dtsb-redrawContents');
- _this.s.dt.draw();
- return false;
- });
- };
- /**
- * Set's the listeners for the group clear button
- */
- Group.prototype._setClearListener = function () {
- var _this = this;
- this.dom.clear
- .unbind('click')
- .on('click.dtsb', function () {
- if (!_this.s.isChild) {
- _this.dom.container.trigger('dtsb-clearContents');
- return false;
- }
- _this.destroy();
- _this.s.topGroup.trigger('dtsb-redrawContents');
- return false;
- });
- };
- /**
- * Sets listeners for sub groups of this group
- *
- * @param group The sub group that the listeners are to be set on
- */
- Group.prototype._setGroupListeners = function (group) {
- var _this = this;
- // Set listeners for the new group
- group.dom.add
- .unbind('click')
- .on('click.dtsb', function () {
- _this.setupLogic();
- _this.dom.container.trigger('dtsb-add');
- return false;
- });
- group.dom.container
- .unbind('dtsb-add')
- .on('dtsb-add.dtsb', function () {
- _this.setupLogic();
- _this.dom.container.trigger('dtsb-add');
- return false;
- });
- group.dom.container
- .unbind('dtsb-destroy')
- .on('dtsb-destroy.dtsb', function () {
- _this._removeCriteria(group, true);
- group.dom.container.remove();
- _this.setupLogic();
- return false;
- });
- group.dom.container
- .unbind('dtsb-dropCriteria')
- .on('dtsb-dropCriteria.dtsb', function () {
- var toDrop = group.s.toDrop;
- toDrop.s.index = group.s.index;
- toDrop.updateArrows(_this.s.criteria.length > 1);
- _this.addCriteria(toDrop);
- return false;
- });
- group.setListeners();
- };
- /**
- * Sets up the Group instance, setting listeners and appending elements
- */
- Group.prototype._setup = function () {
- this.setListeners();
- this.dom.add.html(this.s.dt.i18n('searchBuilder.add', this.c.i18n.add));
- this.dom.search.html(this.s.dt.i18n('searchBuilder.search', this.c.i18n.search));
- this.dom.logic.children().first().html(this.c.logic === 'OR'
- ? this.s.dt.i18n('searchBuilder.logicOr', this.c.i18n.logicOr)
- : this.s.dt.i18n('searchBuilder.logicAnd', this.c.i18n.logicAnd));
- this.s.logic = this.c.logic === 'OR' ? 'OR' : 'AND';
- if (this.c.greyscale) {
- this.dom.logic.addClass(this.classes.greyscale);
- }
- this.dom.logicContainer.append(this.dom.logic).append(this.dom.clear);
- // Only append the logic button immediately if this is a sub group,
- // otherwise it will be prepended later when adding a criteria
- if (this.s.isChild) {
- this.dom.container.append(this.dom.logicContainer);
- }
- this.dom.container.append(this.dom.add);
- if (!this.c.liveSearch) {
- this.dom.container.append(this.dom.search);
- }
- };
- /**
- * Sets the listener for the logic button
- */
- Group.prototype._setLogicListener = function () {
- var _this = this;
- this.dom.logic
- .unbind('click')
- .on('click.dtsb', function () {
- _this._toggleLogic();
- _this.s.dt.draw();
- for (var _i = 0, _a = _this.s.criteria; _i < _a.length; _i++) {
- var crit = _a[_i];
- crit.criteria.setListeners();
- }
- });
- };
- /**
- * Toggles the logic for the group
- */
- Group.prototype._toggleLogic = function () {
- if (this.s.logic === 'OR') {
- this.s.logic = 'AND';
- this.dom.logic.children().first().html(this.s.dt.i18n('searchBuilder.logicAnd', this.c.i18n.logicAnd));
- }
- else if (this.s.logic === 'AND') {
- this.s.logic = 'OR';
- this.dom.logic.children().first().html(this.s.dt.i18n('searchBuilder.logicOr', this.c.i18n.logicOr));
- }
- };
- Group.version = '1.1.0';
- Group.classes = {
- add: 'dtsb-add',
- button: 'dtsb-button',
- clearGroup: 'dtsb-clearGroup',
- greyscale: 'dtsb-greyscale',
- group: 'dtsb-group',
- inputButton: 'dtsb-iptbtn',
- logic: 'dtsb-logic',
- logicContainer: 'dtsb-logicContainer',
- search: 'dtsb-search'
- };
- Group.defaults = {
- columns: true,
- conditions: {
- 'date': Criteria.dateConditions,
- 'html': Criteria.stringConditions,
- 'html-num': Criteria.numConditions,
- 'html-num-fmt': Criteria.numFmtConditions,
- 'luxon': Criteria.luxonDateConditions,
- 'moment': Criteria.momentDateConditions,
- 'num': Criteria.numConditions,
- 'num-fmt': Criteria.numFmtConditions,
- 'string': Criteria.stringConditions
- },
- depthLimit: false,
- enterSearch: false,
- filterChanged: undefined,
- greyscale: false,
- liveSearch: true,
- i18n: {
- add: 'Add Condition',
- button: {
- 0: 'Search Builder',
- _: 'Search Builder (%d)'
- },
- clearAll: 'Clear All',
- condition: 'Condition',
- data: 'Data',
- "delete": '×',
- deleteTitle: 'Delete filtering rule',
- left: '<',
- leftTitle: 'Outdent criteria',
- logicAnd: 'And',
- logicOr: 'Or',
- right: '>',
- rightTitle: 'Indent criteria',
- search: 'Search',
- title: {
- 0: 'Custom Search Builder',
- _: 'Custom Search Builder (%d)'
- },
- value: 'Value',
- valueJoiner: 'and'
- },
- logic: 'AND',
- orthogonal: {
- display: 'display',
- search: 'filter'
- },
- preDefined: false
- };
- return Group;
- }());
- var $$1;
- var dataTable$1;
- /**
- * Sets the value of jQuery for use in the file
- *
- * @param jq the instance of jQuery to be set
- */
- function setJQuery(jq) {
- $$1 = jq;
- dataTable$1 = jq.fn.DataTable;
- }
- /**
- * SearchBuilder class for DataTables.
- * Allows for complex search queries to be constructed and implemented on a DataTable
- */
- var SearchBuilder = /** @class */ (function () {
- function SearchBuilder(builderSettings, opts) {
- var _this = this;
- // Check that the required version of DataTables is included
- if (!dataTable$1 || !dataTable$1.versionCheck || !dataTable$1.versionCheck('1.10.0')) {
- throw new Error('SearchBuilder requires DataTables 1.10 or newer');
- }
- var table = new dataTable$1.Api(builderSettings);
- this.classes = $$1.extend(true, {}, SearchBuilder.classes);
- // Get options from user
- this.c = $$1.extend(true, {}, SearchBuilder.defaults, opts);
- this.dom = {
- clearAll: $$1('<button type="button">' + table.i18n('searchBuilder.clearAll', this.c.i18n.clearAll) + '</button>')
- .addClass(this.classes.clearAll)
- .addClass(this.classes.button)
- .attr('type', 'button'),
- container: $$1('<div/>')
- .addClass(this.classes.container),
- title: $$1('<div/>')
- .addClass(this.classes.title),
- titleRow: $$1('<div/>')
- .addClass(this.classes.titleRow),
- topGroup: undefined
- };
- this.s = {
- dt: table,
- opts: opts,
- search: undefined,
- serverData: undefined,
- topGroup: undefined
- };
- // If searchbuilder is already defined for this table then return
- if (table.settings()[0]._searchBuilder !== undefined) {
- return;
- }
- table.settings()[0]._searchBuilder = this;
- // If using SSP we want to include the previous state in the very first server call
- if (this.s.dt.page.info().serverSide) {
- this.s.dt.on('preXhr.dtsb', function (e, settings, data) {
- var loadedState = _this.s.dt.state.loaded();
- if (loadedState && loadedState.searchBuilder) {
- data.searchBuilder = _this._collapseArray(loadedState.searchBuilder);
- }
- });
- this.s.dt.on('xhr.dtsb', function (e, settings, json) {
- if (json && json.searchBuilder && json.searchBuilder.options) {
- _this.s.serverData = json.searchBuilder.options;
- }
- });
- }
- // Run the remaining setup when the table is initialised
- if (this.s.dt.settings()[0]._bInitComplete) {
- this._setUp();
- }
- else {
- table.one('init.dt', function () {
- _this._setUp();
- });
- }
- return this;
- }
- /**
- * Gets the details required to rebuild the SearchBuilder as it currently is
- */
- // eslint upset at empty object but that is what it is
- SearchBuilder.prototype.getDetails = function (deFormatDates) {
- if (deFormatDates === void 0) { deFormatDates = false; }
- return this.s.topGroup.getDetails(deFormatDates);
- };
- /**
- * Getter for the node of the container for the searchBuilder
- *
- * @returns JQuery<HTMLElement> the node of the container
- */
- SearchBuilder.prototype.getNode = function () {
- return this.dom.container;
- };
- /**
- * Rebuilds the SearchBuilder to a state that is provided
- *
- * @param details The details required to perform a rebuild
- */
- SearchBuilder.prototype.rebuild = function (details) {
- this.dom.clearAll.click();
- // If there are no details to rebuild then return
- if (details === undefined || details === null) {
- return this;
- }
- this.s.topGroup.s.preventRedraw = true;
- this.s.topGroup.rebuild(details);
- this.s.topGroup.s.preventRedraw = false;
- this._checkClear();
- this._updateTitle(this.s.topGroup.count());
- this.s.topGroup.redrawContents();
- this.s.dt.draw(false);
- this.s.topGroup.setListeners();
- return this;
- };
- /**
- * Applies the defaults to preDefined criteria
- *
- * @param preDef the array of criteria to be processed.
- */
- SearchBuilder.prototype._applyPreDefDefaults = function (preDef) {
- var _this = this;
- if (preDef.criteria !== undefined && preDef.logic === undefined) {
- preDef.logic = 'AND';
- }
- var _loop_1 = function (crit) {
- // Apply the defaults to any further criteria
- if (crit.criteria !== undefined) {
- crit = this_1._applyPreDefDefaults(crit);
- }
- else {
- this_1.s.dt.columns().every(function (index) {
- if (_this.s.dt.settings()[0].aoColumns[index].sTitle === crit.data) {
- crit.dataIdx = index;
- }
- });
- }
- };
- var this_1 = this;
- for (var _i = 0, _a = preDef.criteria; _i < _a.length; _i++) {
- var crit = _a[_i];
- _loop_1(crit);
- }
- return preDef;
- };
- /**
- * Set's up the SearchBuilder
- */
- SearchBuilder.prototype._setUp = function (loadState) {
- var _this = this;
- if (loadState === void 0) { loadState = true; }
- // Register an Api method for getting the column type. DataTables 2 has
- // this built in
- if (typeof this.s.dt.column().type !== 'function') {
- DataTable.Api.registerPlural('columns().types()', 'column().type()', function () {
- return this.iterator('column', function (settings, column) {
- return settings.aoColumns[column].sType;
- }, 1);
- });
- }
- // Check that DateTime is included, If not need to check if it could be used
- // eslint-disable-next-line no-extra-parens
- if (!dataTable$1.DateTime) {
- var types = this.s.dt.columns().types().toArray();
- if (types === undefined || types.includes(undefined) || types.includes(null)) {
- types = [];
- for (var _i = 0, _a = this.s.dt.settings()[0].aoColumns; _i < _a.length; _i++) {
- var colInit = _a[_i];
- types.push(colInit.searchBuilderType !== undefined ? colInit.searchBuilderType : colInit.sType);
- }
- }
- var columnIdxs = this.s.dt.columns().toArray();
- // If the column type is still unknown use the internal API to detect type
- if (types === undefined || types.includes(undefined) || types.includes(null)) {
- // This can only happen in DT1 - DT2 will do the invalidation of the type itself
- if ($$1.fn.dataTable.ext.oApi) {
- $$1.fn.dataTable.ext.oApi._fnColumnTypes(this.s.dt.settings()[0]);
- }
- types = this.s.dt.columns().types().toArray();
- }
- for (var i = 0; i < columnIdxs[0].length; i++) {
- var column = columnIdxs[0][i];
- var type = types[column];
- if (
- // Check if this column can be filtered
- (this.c.columns === true ||
- Array.isArray(this.c.columns) &&
- this.c.columns.includes(i)) &&
- // Check if the type is one of the restricted types
- (type.includes('date') ||
- type.includes('moment') ||
- type.includes('luxon'))) {
- alert('SearchBuilder Requires DateTime when used with dates.');
- throw new Error('SearchBuilder requires DateTime');
- }
- }
- }
- this.s.topGroup = new Group(this.s.dt, this.c, undefined, undefined, undefined, undefined, this.s.serverData);
- this._setClearListener();
- this.s.dt.on('stateSaveParams.dtsb', function (e, settings, data) {
- data.searchBuilder = _this.getDetails();
- if (!data.scroller) {
- data.page = _this.s.dt.page();
- }
- else {
- data.start = _this.s.dt.state().start;
- }
- });
- this.s.dt.on('stateLoadParams.dtsb', function (e, settings, data) {
- _this.rebuild(data.searchBuilder);
- });
- this._build();
- this.s.dt.on('preXhr.dtsb', function (e, settings, data) {
- if (_this.s.dt.page.info().serverSide) {
- data.searchBuilder = _this._collapseArray(_this.getDetails(true));
- }
- });
- this.s.dt.on(dataTable$1.versionCheck('2')
- ? 'columns-reordered'
- : 'column-reorder', function () {
- _this.rebuild(_this.getDetails());
- });
- if (loadState) {
- var loadedState = this.s.dt.state.loaded();
- // If the loaded State is not null rebuild based on it for statesave
- if (loadedState !== null && loadedState.searchBuilder !== undefined) {
- this.s.topGroup.rebuild(loadedState.searchBuilder);
- this.s.topGroup.dom.container.trigger('dtsb-redrawContents');
- // If using SSP we want to restrict the amount of server calls that take place
- // and this information will already have been processed
- if (!this.s.dt.page.info().serverSide) {
- if (loadedState.page) {
- this.s.dt.page(loadedState.page).draw('page');
- }
- else if (this.s.dt.scroller && loadedState.scroller) {
- this.s.dt.scroller().scrollToRow(loadedState.scroller.topRow);
- }
- }
- this.s.topGroup.setListeners();
- }
- // Otherwise load any predefined options
- else if (this.c.preDefined !== false) {
- this.c.preDefined = this._applyPreDefDefaults(this.c.preDefined);
- this.rebuild(this.c.preDefined);
- }
- }
- this._setEmptyListener();
- this.s.dt.state.save();
- };
- SearchBuilder.prototype._collapseArray = function (criteria) {
- if (criteria.logic === undefined) {
- if (criteria.value !== undefined) {
- criteria.value.sort(function (a, b) {
- if (!isNaN(+a)) {
- a = +a;
- b = +b;
- }
- if (a < b) {
- return -1;
- }
- else if (b < a) {
- return 1;
- }
- else {
- return 0;
- }
- });
- criteria.value1 = criteria.value[0];
- criteria.value2 = criteria.value[1];
- }
- }
- else {
- for (var i = 0; i < criteria.criteria.length; i++) {
- criteria.criteria[i] = this._collapseArray(criteria.criteria[i]);
- }
- }
- return criteria;
- };
- /**
- * Updates the title of the SearchBuilder
- *
- * @param count the number of filters in the SearchBuilder
- */
- SearchBuilder.prototype._updateTitle = function (count) {
- this.dom.title.html(this.s.dt.i18n('searchBuilder.title', this.c.i18n.title, count));
- };
- /**
- * Builds all of the dom elements together
- */
- SearchBuilder.prototype._build = function () {
- var _this = this;
- // Empty and setup the container
- this.dom.clearAll.remove();
- this.dom.container.empty();
- var count = this.s.topGroup.count();
- this._updateTitle(count);
- this.dom.titleRow.append(this.dom.title);
- this.dom.container.append(this.dom.titleRow);
- this.dom.topGroup = this.s.topGroup.getNode();
- this.dom.container.append(this.dom.topGroup);
- this._setRedrawListener();
- var tableNode = this.s.dt.table(0).node();
- if (!$$1.fn.dataTable.ext.search.includes(this.s.search)) {
- // Custom search function for SearchBuilder
- this.s.search = function (settings, searchData, dataIndex) {
- if (settings.nTable !== tableNode) {
- return true;
- }
- return _this.s.topGroup.search(searchData, dataIndex);
- };
- // Add SearchBuilder search function to the dataTables search array
- $$1.fn.dataTable.ext.search.push(this.s.search);
- }
- this.s.dt.on('destroy.dtsb', function () {
- _this.dom.container.remove();
- _this.dom.clearAll.remove();
- var searchIdx = $$1.fn.dataTable.ext.search.indexOf(_this.s.search);
- while (searchIdx !== -1) {
- $$1.fn.dataTable.ext.search.splice(searchIdx, 1);
- searchIdx = $$1.fn.dataTable.ext.search.indexOf(_this.s.search);
- }
- _this.s.dt.off('.dtsb');
- $$1(_this.s.dt.table().node()).off('.dtsb');
- });
- };
- /**
- * Checks if the clearAll button should be added or not
- */
- SearchBuilder.prototype._checkClear = function () {
- if (this.s.topGroup.s.criteria.length > 0) {
- this.dom.clearAll.insertAfter(this.dom.title);
- this._setClearListener();
- }
- else {
- this.dom.clearAll.remove();
- }
- };
- /**
- * Update the count in the title/button
- *
- * @param count Number of filters applied
- */
- SearchBuilder.prototype._filterChanged = function (count) {
- var fn = this.c.filterChanged;
- if (typeof fn === 'function') {
- fn(count, this.s.dt.i18n('searchBuilder.button', this.c.i18n.button, count));
- }
- };
- /**
- * Set the listener for the clear button
- */
- SearchBuilder.prototype._setClearListener = function () {
- var _this = this;
- this.dom.clearAll.unbind('click');
- this.dom.clearAll.on('click.dtsb', function () {
- _this.s.topGroup = new Group(_this.s.dt, _this.c, undefined, undefined, undefined, undefined, _this.s.serverData);
- _this._build();
- _this.s.dt.draw();
- _this.s.topGroup.setListeners();
- _this.dom.clearAll.remove();
- _this._setEmptyListener();
- _this._filterChanged(0);
- return false;
- });
- };
- /**
- * Set the listener for the Redraw event
- */
- SearchBuilder.prototype._setRedrawListener = function () {
- var _this = this;
- this.s.topGroup.dom.container.unbind('dtsb-redrawContents');
- this.s.topGroup.dom.container.on('dtsb-redrawContents.dtsb', function () {
- _this._checkClear();
- _this.s.topGroup.redrawContents();
- _this.s.topGroup.setupLogic();
- _this._setEmptyListener();
- var count = _this.s.topGroup.count();
- _this._updateTitle(count);
- _this._filterChanged(count);
- // If using SSP we want to restrict the amount of server calls that take place
- // and this information will already have been processed
- if (!_this.s.dt.page.info().serverSide) {
- _this.s.dt.draw();
- }
- _this.s.dt.state.save();
- });
- this.s.topGroup.dom.container.unbind('dtsb-redrawContents-noDraw');
- this.s.topGroup.dom.container.on('dtsb-redrawContents-noDraw.dtsb', function () {
- _this._checkClear();
- _this.s.topGroup.s.preventRedraw = true;
- _this.s.topGroup.redrawContents();
- _this.s.topGroup.s.preventRedraw = false;
- _this.s.topGroup.setupLogic();
- _this._setEmptyListener();
- var count = _this.s.topGroup.count();
- _this._updateTitle(count);
- _this._filterChanged(count);
- });
- this.s.topGroup.dom.container.unbind('dtsb-redrawLogic');
- this.s.topGroup.dom.container.on('dtsb-redrawLogic.dtsb', function () {
- _this.s.topGroup.redrawLogic();
- var count = _this.s.topGroup.count();
- _this._updateTitle(count);
- _this._filterChanged(count);
- });
- this.s.topGroup.dom.container.unbind('dtsb-add');
- this.s.topGroup.dom.container.on('dtsb-add.dtsb', function () {
- var count = _this.s.topGroup.count();
- _this._updateTitle(count);
- _this._filterChanged(count);
- _this._checkClear();
- });
- this.s.dt.on('postEdit.dtsb postCreate.dtsb postRemove.dtsb', function () {
- _this.s.topGroup.redrawContents();
- });
- this.s.topGroup.dom.container.unbind('dtsb-clearContents');
- this.s.topGroup.dom.container.on('dtsb-clearContents.dtsb', function () {
- _this._setUp(false);
- _this._filterChanged(0);
- _this.s.dt.draw();
- });
- };
- /**
- * Sets listeners to check whether clearAll should be added or removed
- */
- SearchBuilder.prototype._setEmptyListener = function () {
- var _this = this;
- this.s.topGroup.dom.add.on('click.dtsb', function () {
- _this._checkClear();
- });
- this.s.topGroup.dom.container.on('dtsb-destroy.dtsb', function () {
- _this.dom.clearAll.remove();
- });
- };
- SearchBuilder.version = '1.7.1';
- SearchBuilder.classes = {
- button: 'dtsb-button',
- clearAll: 'dtsb-clearAll',
- container: 'dtsb-searchBuilder',
- inputButton: 'dtsb-iptbtn',
- title: 'dtsb-title',
- titleRow: 'dtsb-titleRow'
- };
- SearchBuilder.defaults = {
- columns: true,
- conditions: {
- 'date': Criteria.dateConditions,
- 'html': Criteria.stringConditions,
- 'html-num': Criteria.numConditions,
- 'html-num-fmt': Criteria.numFmtConditions,
- 'luxon': Criteria.luxonDateConditions,
- 'moment': Criteria.momentDateConditions,
- 'num': Criteria.numConditions,
- 'num-fmt': Criteria.numFmtConditions,
- 'string': Criteria.stringConditions
- },
- depthLimit: false,
- enterSearch: false,
- filterChanged: undefined,
- greyscale: false,
- liveSearch: true,
- i18n: {
- add: 'Add Condition',
- button: {
- 0: 'Search Builder',
- _: 'Search Builder (%d)'
- },
- clearAll: 'Clear All',
- condition: 'Condition',
- conditions: {
- array: {
- contains: 'Contains',
- empty: 'Empty',
- equals: 'Equals',
- not: 'Not',
- notEmpty: 'Not Empty',
- without: 'Without'
- },
- date: {
- after: 'After',
- before: 'Before',
- between: 'Between',
- empty: 'Empty',
- equals: 'Equals',
- not: 'Not',
- notBetween: 'Not Between',
- notEmpty: 'Not Empty'
- },
- // eslint-disable-next-line id-blacklist
- number: {
- between: 'Between',
- empty: 'Empty',
- equals: 'Equals',
- gt: 'Greater Than',
- gte: 'Greater Than Equal To',
- lt: 'Less Than',
- lte: 'Less Than Equal To',
- not: 'Not',
- notBetween: 'Not Between',
- notEmpty: 'Not Empty'
- },
- // eslint-disable-next-line id-blacklist
- string: {
- contains: 'Contains',
- empty: 'Empty',
- endsWith: 'Ends With',
- equals: 'Equals',
- not: 'Not',
- notContains: 'Does Not Contain',
- notEmpty: 'Not Empty',
- notEndsWith: 'Does Not End With',
- notStartsWith: 'Does Not Start With',
- startsWith: 'Starts With'
- }
- },
- data: 'Data',
- "delete": '×',
- deleteTitle: 'Delete filtering rule',
- left: '<',
- leftTitle: 'Outdent criteria',
- logicAnd: 'And',
- logicOr: 'Or',
- right: '>',
- rightTitle: 'Indent criteria',
- search: 'Search',
- title: {
- 0: 'Custom Search Builder',
- _: 'Custom Search Builder (%d)'
- },
- value: 'Value',
- valueJoiner: 'and'
- },
- logic: 'AND',
- orthogonal: {
- display: 'display',
- search: 'filter'
- },
- preDefined: false
- };
- return SearchBuilder;
- }());
- /*! SearchBuilder 1.7.1
- * ©SpryMedia Ltd - datatables.net/license/mit
- */
- setJQuery($);
- setJQuery$1($);
- setJQuery$2($);
- var dataTable = $.fn.dataTable;
- // eslint-disable-next-line no-extra-parens
- DataTable.SearchBuilder = SearchBuilder;
- // eslint-disable-next-line no-extra-parens
- dataTable.SearchBuilder = SearchBuilder;
- // eslint-disable-next-line no-extra-parens
- DataTable.Group = Group;
- // eslint-disable-next-line no-extra-parens
- dataTable.Group = Group;
- // eslint-disable-next-line no-extra-parens
- DataTable.Criteria = Criteria;
- // eslint-disable-next-line no-extra-parens
- dataTable.Criteria = Criteria;
- // eslint-disable-next-line no-extra-parens
- var apiRegister = DataTable.Api.register;
- // Set up object for plugins
- DataTable.ext.searchBuilder = {
- conditions: {}
- };
- DataTable.ext.buttons.searchBuilder = {
- action: function (e, dt, node, config) {
- this.popover(config._searchBuilder.getNode(), {
- align: 'container',
- span: 'container'
- });
- var topGroup = config._searchBuilder.s.topGroup;
- // Need to redraw the contents to calculate the correct positions for the elements
- if (topGroup !== undefined) {
- topGroup.dom.container.trigger('dtsb-redrawContents-noDraw');
- }
- if (topGroup.s.criteria.length === 0) {
- $('.' + $.fn.dataTable.Group.classes.add.replace(/ /g, '.')).click();
- }
- },
- config: {},
- init: function (dt, node, config) {
- var sb = new DataTable.SearchBuilder(dt, $.extend({
- filterChanged: function (count, text) {
- dt.button(node).text(text);
- }
- }, config.config));
- dt.button(node).text(config.text || dt.i18n('searchBuilder.button', sb.c.i18n.button, 0));
- config._searchBuilder = sb;
- },
- text: null
- };
- apiRegister('searchBuilder.getDetails()', function (deFormatDates) {
- if (deFormatDates === void 0) { deFormatDates = false; }
- var ctx = this.context[0];
- // If SearchBuilder has not been initialised on this instance then return
- return ctx._searchBuilder ?
- ctx._searchBuilder.getDetails(deFormatDates) :
- null;
- });
- apiRegister('searchBuilder.rebuild()', function (details) {
- var ctx = this.context[0];
- // If SearchBuilder has not been initialised on this instance then return
- if (ctx._searchBuilder === undefined) {
- return null;
- }
- ctx._searchBuilder.rebuild(details);
- return this;
- });
- apiRegister('searchBuilder.container()', function () {
- var ctx = this.context[0];
- // If SearchBuilder has not been initialised on this instance then return
- return ctx._searchBuilder ?
- ctx._searchBuilder.getNode() :
- null;
- });
- /**
- * Init function for SearchBuilder
- *
- * @param settings the settings to be applied
- * @param options the options for SearchBuilder
- * @returns JQUERY<HTMLElement> Returns the node of the SearchBuilder
- */
- function _init(settings, options) {
- var api = new DataTable.Api(settings);
- var opts = options
- ? options
- : api.init().searchBuilder || DataTable.defaults.searchBuilder;
- var searchBuilder = new SearchBuilder(api, opts);
- var node = searchBuilder.getNode();
- return node;
- }
- // Attach a listener to the document which listens for DataTables initialisation
- // events so we can automatically initialise
- $(document).on('preInit.dt.dtsp', function (e, settings) {
- if (e.namespace !== 'dt') {
- return;
- }
- if (settings.oInit.searchBuilder ||
- DataTable.defaults.searchBuilder) {
- if (!settings._searchBuilder) {
- _init(settings);
- }
- }
- });
- // DataTables `dom` feature option
- DataTable.ext.feature.push({
- cFeature: 'Q',
- fnInit: _init
- });
- // DataTables 2 layout feature
- if (DataTable.feature) {
- DataTable.feature.register('searchBuilder', _init);
- }
- })();
- return DataTable;
- }));
- /*! Bootstrap 5 ui integration for DataTables' SearchBuilder
- * © SpryMedia Ltd - datatables.net/license
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net-bs5', 'datatables.net-searchbuilder'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- var jq = require('jquery');
- var cjsRequires = function (root, $) {
- if ( ! $.fn.dataTable ) {
- require('datatables.net-bs5')(root, $);
- }
- if ( ! $.fn.dataTable.SearchBuilder ) {
- require('datatables.net-searchbuilder')(root, $);
- }
- };
- if (typeof window === 'undefined') {
- 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 ( ! $ ) {
- $ = jq( root );
- }
- cjsRequires( root, $ );
- return factory( $, root, root.document );
- };
- }
- else {
- cjsRequires( window, jq );
- module.exports = factory( jq, window, window.document );
- }
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- $.extend(true, DataTable.SearchBuilder.classes, {
- clearAll: 'btn btn-secondary dtsb-clearAll'
- });
- $.extend(true, DataTable.Group.classes, {
- add: 'btn btn-secondary dtsb-add',
- clearGroup: 'btn btn-secondary dtsb-clearGroup',
- logic: 'btn btn-secondary dtsb-logic',
- search: 'btn btn-secondary dtsb-search'
- });
- $.extend(true, DataTable.Criteria.classes, {
- condition: 'form-select dtsb-condition',
- data: 'dtsb-data form-select',
- "delete": 'btn btn-secondary dtsb-delete',
- input: 'form-control dtsb-input',
- left: 'btn btn-secondary dtsb-left',
- right: 'btn btn-secondary dtsb-right',
- select: 'form-select',
- value: 'dtsb-value'
- });
- return DataTable;
- }));
- /*! SearchPanes 2.3.1
- * © 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
- var jq = require('jquery');
- var cjsRequires = function (root, $) {
- if ( ! $.fn.dataTable ) {
- require('datatables.net')(root, $);
- }
- };
- if (typeof window === 'undefined') {
- 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 ( ! $ ) {
- $ = jq( root );
- }
- cjsRequires( root, $ );
- return factory( $, root, root.document );
- };
- }
- else {
- cjsRequires( window, jq );
- module.exports = factory( jq, window, window.document );
- }
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- (function () {
- 'use strict';
- var $$5;
- var dataTable$2;
- function setJQuery$4(jq) {
- $$5 = jq;
- dataTable$2 = jq.fn.dataTable;
- }
- var SearchPane = /** @class */ (function () {
- /**
- * Creates the panes, sets up the search function
- *
- * @param paneSettings The settings for the searchPanes
- * @param opts The options for the default features
- * @param index the index of the column for this pane
- * @param panesContainer The overall container for SearchPanes that this pane will be attached to
- * @param panes The custom pane settings if this is a custom pane
- * @returns {object} the pane that has been created, including the table and the index of the pane
- */
- function SearchPane(paneSettings, opts, index, panesContainer, panes) {
- var _this = this;
- if (panes === void 0) { panes = null; }
- // Check that the required version of DataTables is included
- if (!dataTable$2 || !dataTable$2.versionCheck || !dataTable$2.versionCheck('1.10.0')) {
- throw new Error('SearchPane requires DataTables 1.10 or newer');
- }
- // Check that Select is included
- // eslint-disable-next-line no-extra-parens
- if (!dataTable$2.select) {
- throw new Error('SearchPane requires Select');
- }
- var table = new dataTable$2.Api(paneSettings);
- this.classes = $$5.extend(true, {}, SearchPane.classes);
- // Get options from user
- this.c = $$5.extend(true, {}, SearchPane.defaults, opts, panes);
- if (opts && opts.hideCount && opts.viewCount === undefined) {
- this.c.viewCount = !this.c.hideCount;
- }
- var rowLength = table.columns().eq(0).toArray().length;
- this.s = {
- colExists: index < rowLength,
- colOpts: undefined,
- customPaneSettings: panes,
- displayed: false,
- dt: table,
- dtPane: undefined,
- firstSet: true,
- index: index,
- indexes: [],
- listSet: false,
- name: undefined,
- rowData: {
- arrayFilter: [],
- arrayOriginal: [],
- bins: {},
- binsOriginal: {},
- filterMap: new Map(),
- totalOptions: 0
- },
- scrollTop: 0,
- searchFunction: undefined,
- selections: [],
- serverSelect: [],
- serverSelecting: false,
- tableLength: null,
- updating: false
- };
- this.s.colOpts = this.s.colExists ? this._getOptions() : this._getBonusOptions();
- this.dom = {
- buttonGroup: $$5('<div/>').addClass(this.classes.buttonGroup),
- clear: $$5('<button type="button">×</button>')
- .attr('disabled', 'true')
- .addClass(this.classes.disabledButton)
- .addClass(this.classes.paneButton)
- .addClass(this.classes.clearButton)
- .html(this.s.dt.i18n('searchPanes.clearPane', this.c.i18n.clearPane)),
- collapseButton: $$5('<button type="button"><span class="' + this.classes.caret + '">^</span></button>')
- .addClass(this.classes.paneButton)
- .addClass(this.classes.collapseButton),
- container: $$5('<div/>')
- .addClass(this.classes.container)
- .addClass(this.s.colOpts.className)
- .addClass(this.classes.layout +
- (parseInt(this.c.layout.split('-')[1], 10) < 10 ?
- this.c.layout :
- this.c.layout.split('-')[0] + '-9'))
- .addClass(this.s.customPaneSettings && this.s.customPaneSettings.className
- ? this.s.customPaneSettings.className
- : ''),
- countButton: $$5('<button type="button"><span></span></button>')
- .addClass(this.classes.paneButton)
- .addClass(this.classes.countButton),
- dtP: $$5('<table width="100%"><thead><tr><th></th><th></th></tr></thead></table>'),
- lower: $$5('<div/>').addClass(this.classes.subRow2).addClass(this.classes.narrowButton),
- nameButton: $$5('<button type="button"><span></span></button>')
- .addClass(this.classes.paneButton)
- .addClass(this.classes.nameButton),
- panesContainer: $$5(panesContainer),
- searchBox: $$5('<input/>').addClass(this.classes.paneInputButton).addClass(this.classes.search),
- searchButton: $$5('<button type="button"><span></span></button>')
- .addClass(this.classes.searchIcon)
- .addClass(this.classes.paneButton),
- searchCont: $$5('<div/>').addClass(this.classes.searchCont),
- searchLabelCont: $$5('<div/>').addClass(this.classes.searchLabelCont),
- topRow: $$5('<div/>').addClass(this.classes.topRow),
- upper: $$5('<div/>').addClass(this.classes.subRow1).addClass(this.classes.narrowSearch)
- };
- var title = '';
- if (this.s.colExists) {
- title = $$5(this.s.dt.column(this.s.index).header()).text();
- this.dom.dtP.find('th').eq(0).text(title);
- }
- else {
- title = this.s.customPaneSettings.header || 'Custom Pane';
- this.dom.dtP.find('th').eq(0).html(title);
- }
- // Set the value of name incase ordering is desired
- if (this.s.colOpts.name) {
- this.s.name = this.s.colOpts.name;
- }
- else if (this.s.customPaneSettings && this.s.customPaneSettings.name) {
- this.s.name = this.s.customPaneSettings.name;
- }
- else {
- this.s.name = title;
- }
- var tableNode = this.s.dt.table(0).node();
- // Custom search function for table
- this.s.searchFunction = function (settings, searchData, dataIndex) {
- // If no data has been selected then show all
- if (_this.s.selections.length === 0) {
- return true;
- }
- if (settings.nTable !== tableNode) {
- return true;
- }
- var filter = null;
- if (_this.s.colExists) {
- // Get the current filtered data
- filter = searchData[_this.s.index];
- if (_this.s.colOpts.orthogonal.filter !== 'filter') {
- // get the filter value from the map
- filter = _this.s.rowData.filterMap.get(dataIndex);
- if (filter instanceof $$5.fn.dataTable.Api) {
- // eslint-disable-next-line no-extra-parens
- filter = filter.toArray();
- }
- }
- }
- return _this._search(filter, dataIndex);
- };
- $$5.fn.dataTable.ext.search.push(this.s.searchFunction);
- // If the clear button for this pane is clicked clear the selections
- if (this.c.clear) {
- this.dom.clear.on('click.dtsp', function () {
- var searches = _this.dom.container.find('.' + _this.classes.search.replace(/\s+/g, '.'));
- searches.each(function () {
- $$5(this).val('').trigger('input');
- });
- _this.clearPane();
- });
- }
- // Sometimes the top row of the panes containing the search box and ordering buttons appears
- // weird if the width of the panes is lower than expected, this fixes the design.
- // Equally this may occur when the table is resized.
- this.s.dt.on('draw.dtsp', function () { return _this.adjustTopRow(); });
- this.s.dt.on('buttons-action.dtsp', function () { return _this.adjustTopRow(); });
- // When column-reorder is present and the columns are moved, it is necessary to
- // reassign all of the panes indexes to the new index of the column.
- this.s.dt.on('column-reorder.dtsp', function (e, settings, details) {
- _this.s.index = details.mapping[_this.s.index];
- });
- return this;
- }
- /**
- * Adds a row to the panes table
- *
- * @param display the value to be displayed to the user
- * @param filter the value to be filtered on when searchpanes is implemented
- * @param shown the number of rows in the table that are currently visible matching this criteria
- * @param total the total number of rows in the table that match this criteria
- * @param sort the value to be sorted in the pane table
- * @param type the value of which the type is to be derived from
- */
- SearchPane.prototype.addRow = function (display, filter, sort, type, className, total, shown) {
- if (!total) {
- total = this.s.rowData.bins[filter] ?
- this.s.rowData.bins[filter] :
- 0;
- }
- if (!shown) {
- shown = this._getShown(filter);
- }
- var index;
- for (var _i = 0, _a = this.s.indexes; _i < _a.length; _i++) {
- var entry = _a[_i];
- if (entry.filter === filter) {
- index = entry.index;
- }
- }
- if (index === undefined) {
- index = this.s.indexes.length;
- this.s.indexes.push({ filter: filter, index: index });
- }
- return this.s.dtPane.row.add({
- className: className,
- display: display !== '' ?
- display :
- this.emptyMessage(),
- filter: filter,
- index: index,
- shown: shown,
- sort: sort,
- total: total,
- type: type
- });
- };
- /**
- * Adjusts the layout of the top row when the screen is resized
- */
- SearchPane.prototype.adjustTopRow = function () {
- var subContainers = this.dom.container.find('.' + this.classes.subRowsContainer.replace(/\s+/g, '.'));
- var subRow1 = this.dom.container.find('.' + this.classes.subRow1.replace(/\s+/g, '.'));
- var subRow2 = this.dom.container.find('.' + this.classes.subRow2.replace(/\s+/g, '.'));
- var topRow = this.dom.container.find('.' + this.classes.topRow.replace(/\s+/g, '.'));
- // If the width is 0 then it is safe to assume that the pane has not yet been displayed.
- // Even if it has, if the width is 0 it won't make a difference if it has the narrow class or not
- if (($$5(subContainers[0]).width() < 252 || $$5(topRow[0]).width() < 252) && $$5(subContainers[0]).width() !== 0) {
- $$5(subContainers[0]).addClass(this.classes.narrow);
- $$5(subRow1[0]).addClass(this.classes.narrowSub).removeClass(this.classes.narrowSearch);
- $$5(subRow2[0]).addClass(this.classes.narrowSub).removeClass(this.classes.narrowButton);
- }
- else {
- $$5(subContainers[0]).removeClass(this.classes.narrow);
- $$5(subRow1[0]).removeClass(this.classes.narrowSub).addClass(this.classes.narrowSearch);
- $$5(subRow2[0]).removeClass(this.classes.narrowSub).addClass(this.classes.narrowButton);
- }
- };
- /**
- * In the case of a rebuild there is potential for new data to have been included or removed
- * so all of the rowData must be reset as a precaution.
- */
- SearchPane.prototype.clearData = function () {
- this.s.rowData = {
- arrayFilter: [],
- arrayOriginal: [],
- bins: {},
- binsOriginal: {},
- filterMap: new Map(),
- totalOptions: 0
- };
- };
- /**
- * Clear the selections in the pane
- */
- SearchPane.prototype.clearPane = function () {
- // Deselect all rows which are selected and update the table and filter count.
- this.s.dtPane.rows({ selected: true }).deselect();
- this.updateTable();
- return this;
- };
- /**
- * Collapses the pane so that only the header is displayed
- */
- SearchPane.prototype.collapse = function () {
- var _this = this;
- if (!this.s.displayed ||
- (
- // If collapsing is disabled globally, and not enabled specifically for this column
- !this.c.collapse && this.s.colOpts.collapse !== true ||
- // OR, collapsing could be enabled globally and this column specifically
- // is not to be collapsed.
- // We can't just take !this.s.colOpts.collapse here as if it is undefined
- // then the global should be used
- this.s.colOpts.collapse === false)) {
- return;
- }
- $$5(this.s.dtPane.table().container()).addClass(this.classes.hidden);
- this.dom.topRow.addClass(this.classes.bordered);
- this.dom.nameButton.addClass(this.classes.disabledButton);
- this.dom.countButton.addClass(this.classes.disabledButton);
- this.dom.searchButton.addClass(this.classes.disabledButton);
- this.dom.collapseButton.addClass(this.classes.rotated);
- this.dom.topRow.one('click.dtsp', function () { return _this.show(); });
- this.dom.topRow.trigger('collapse.dtsps');
- };
- /**
- * Strips all of the SearchPanes elements from the document and turns all of the listeners for the buttons off
- */
- SearchPane.prototype.destroy = function () {
- if (this.s.dtPane) {
- this.s.dtPane.off('.dtsp');
- }
- this.s.dt.off('.dtsp');
- this.dom.clear.off('.dtsp');
- this.dom.nameButton.off('.dtsp');
- this.dom.countButton.off('.dtsp');
- this.dom.searchButton.off('.dtsp');
- this.dom.collapseButton.off('.dtsp');
- $$5(this.s.dt.table().node()).off('.dtsp');
- this.dom.container.detach();
- var searchIdx = $$5.fn.dataTable.ext.search.indexOf(this.s.searchFunction);
- while (searchIdx !== -1) {
- $$5.fn.dataTable.ext.search.splice(searchIdx, 1);
- searchIdx = $$5.fn.dataTable.ext.search.indexOf(this.s.searchFunction);
- }
- // If the datatables have been defined for the panes then also destroy these
- if (this.s.dtPane) {
- this.s.dtPane.destroy();
- }
- this.s.listSet = false;
- };
- /**
- * Getting the legacy message is a little complex due a legacy parameter
- */
- SearchPane.prototype.emptyMessage = function () {
- var def = this.c.i18n.emptyMessage;
- // Legacy parameter support
- if (this.c.emptyMessage) {
- def = this.c.emptyMessage;
- }
- // Override per column
- if (this.s.colOpts.emptyMessage !== false && this.s.colOpts.emptyMessage !== null) {
- def = this.s.colOpts.emptyMessage;
- }
- return this.s.dt.i18n('searchPanes.emptyMessage', def);
- };
- /**
- * Updates the number of filters that have been applied in the title
- */
- SearchPane.prototype.getPaneCount = function () {
- return this.s.dtPane ?
- this.s.dtPane.rows({ selected: true }).data().toArray().length :
- 0;
- };
- /**
- * Rebuilds the panes from the start having deleted the old ones
- *
- * @param? dataIn data to be used in buildPane
- * @param? maintainSelection Whether the current selections are to be maintained over rebuild
- */
- SearchPane.prototype.rebuildPane = function (dataIn, maintainSelection) {
- if (dataIn === void 0) { dataIn = null; }
- if (maintainSelection === void 0) { maintainSelection = false; }
- this.clearData();
- var selectedRows = [];
- this.s.serverSelect = [];
- var prevEl = null;
- // When rebuilding strip all of the HTML Elements out of the container and start from scratch
- if (this.s.dtPane) {
- if (maintainSelection) {
- if (!this.s.dt.page.info().serverSide) {
- selectedRows = this.s.dtPane.rows({ selected: true }).data().toArray();
- }
- else {
- this.s.serverSelect = this.s.dtPane.rows({ selected: true }).data().toArray();
- }
- }
- this.s.dtPane.clear().destroy();
- prevEl = this.dom.container.prev();
- this.destroy();
- this.s.dtPane = undefined;
- $$5.fn.dataTable.ext.search.push(this.s.searchFunction);
- }
- this.dom.container.removeClass(this.classes.hidden);
- this.s.displayed = false;
- this._buildPane(!this.s.dt.page.info().serverSide ?
- selectedRows :
- this.s.serverSelect, dataIn, prevEl);
- return this;
- };
- /**
- * Resizes the pane based on the layout that is passed in
- *
- * @param layout the layout to be applied to this pane
- */
- SearchPane.prototype.resize = function (layout) {
- this.c.layout = layout;
- this.dom.container
- .removeClass()
- .addClass(this.classes.show)
- .addClass(this.classes.container)
- .addClass(this.s.colOpts.className)
- .addClass(this.classes.layout +
- (parseInt(layout.split('-')[1], 10) < 10 ?
- layout :
- layout.split('-')[0] + '-9'))
- .addClass(this.s.customPaneSettings !== null && this.s.customPaneSettings.className
- ? this.s.customPaneSettings.className
- : '');
- this.adjustTopRow();
- };
- /**
- * Sets the listeners for the pane.
- *
- * Having it in it's own function makes it easier to only set them once
- */
- SearchPane.prototype.setListeners = function () {
- var _this = this;
- if (!this.s.dtPane) {
- return;
- }
- // When an item is selected on the pane, add these to the array which holds selected items.
- // Custom search will perform.
- this.s.dtPane.off('select.dtsp').on('select.dtsp', function () {
- clearTimeout(_this.s.deselectTimeout);
- _this._updateSelection(!_this.s.updating);
- _this.dom.clear.removeClass(_this.classes.disabledButton).removeAttr('disabled');
- });
- // When an item is deselected on the pane, re add the currently selected items to the array
- // which holds selected items. Custom search will be performed.
- this.s.dtPane.off('deselect.dtsp').on('deselect.dtsp', function () {
- _this.s.deselectTimeout = setTimeout(function () {
- _this._updateSelection(true);
- if (_this.s.dtPane.rows({ selected: true }).data().toArray().length === 0) {
- _this.dom.clear.addClass(_this.classes.disabledButton).attr('disabled', 'true');
- }
- }, 50);
- });
- // If we attempty to turn off this event then it will ruin behaviour in other panes
- // so need to make sure that it is only done once
- if (this.s.firstSet) {
- this.s.firstSet = false;
- // When saving the state store all of the selected rows for preselection next time around
- this.s.dt.on('stateSaveParams.dtsp', function (e, settings, data) {
- // If the data being passed in is empty then state clear must have occured
- // so clear the panes state as well
- if ($$5.isEmptyObject(data)) {
- _this.s.dtPane.state.clear();
- return;
- }
- var bins;
- var order;
- var selected = [];
- var collapsed;
- var searchTerm;
- var arrayFilter;
- // Get all of the data needed for the state save from the pane
- if (_this.s.dtPane) {
- selected = _this.s.dtPane
- .rows({ selected: true })
- .data()
- .map(function (item) { return item.filter !== null ? item.filter.toString() : null; })
- .toArray();
- searchTerm = _this.dom.searchBox.val();
- order = _this.s.dtPane.order();
- bins = _this.s.rowData.binsOriginal;
- arrayFilter = _this.s.rowData.arrayOriginal;
- collapsed = _this.dom.collapseButton.hasClass(_this.classes.rotated);
- }
- if (data.searchPanes === undefined) {
- data.searchPanes = {};
- }
- if (data.searchPanes.panes === undefined) {
- data.searchPanes.panes = [];
- }
- for (var i = 0; i < data.searchPanes.panes.length; i++) {
- if (data.searchPanes.panes[i].id === _this.s.index) {
- data.searchPanes.panes.splice(i, 1);
- i--;
- }
- }
- // Add the panes data to the state object
- data.searchPanes.panes.push({
- arrayFilter: arrayFilter,
- bins: bins,
- collapsed: collapsed,
- id: _this.s.index,
- order: order,
- searchTerm: searchTerm,
- selected: selected
- });
- });
- }
- this.s.dtPane.off('user-select.dtsp').on('user-select.dtsp', function (e, _dt, type, cell, originalEvent) {
- originalEvent.stopPropagation();
- });
- this.s.dtPane.off('draw.dtsp').on('draw.dtsp', function () { return _this.adjustTopRow(); });
- // When the button to order by the name of the options is clicked then
- // change the ordering to whatever it isn't currently
- this.dom.nameButton.off('click.dtsp').on('click.dtsp', function () {
- var currentOrder = _this.s.dtPane.order()[0][1];
- _this.s.dtPane.order([0, currentOrder === 'asc' ? 'desc' : 'asc']).draw();
- // This state save is required so that the ordering of the panes is maintained
- _this.s.dt.state.save();
- });
- // When the button to order by the number of entries in the column is clicked then
- // change the ordering to whatever it isn't currently
- this.dom.countButton.off('click.dtsp').on('click.dtsp', function () {
- var currentOrder = _this.s.dtPane.order()[0][1];
- _this.s.dtPane.order([1, currentOrder === 'asc' ? 'desc' : 'asc']).draw();
- // This state save is required so that the ordering of the panes is maintained
- _this.s.dt.state.save();
- });
- // When the button to order by the number of entries in the column is clicked then
- // change the ordering to whatever it isn't currently
- this.dom.collapseButton.off('click.dtsp').on('click.dtsp', function (e) {
- e.stopPropagation();
- var container = $$5(_this.s.dtPane.table().container());
- // Toggle the classes
- container.toggleClass(_this.classes.hidden);
- _this.dom.topRow.toggleClass(_this.classes.bordered);
- _this.dom.nameButton.toggleClass(_this.classes.disabledButton);
- _this.dom.countButton.toggleClass(_this.classes.disabledButton);
- _this.dom.searchButton.toggleClass(_this.classes.disabledButton);
- _this.dom.collapseButton.toggleClass(_this.classes.rotated);
- if (container.hasClass(_this.classes.hidden)) {
- _this.dom.topRow.on('click.dtsp', function () { return _this.dom.collapseButton.click(); });
- }
- else {
- _this.dom.topRow.off('click.dtsp');
- }
- _this.s.dt.state.save();
- _this.dom.topRow.trigger('collapse.dtsps');
- });
- // When the clear button is clicked reset the pane
- this.dom.clear.off('click.dtsp').on('click.dtsp', function () {
- var searches = _this.dom.container.find('.' + _this.classes.search.replace(/ /g, '.'));
- searches.each(function () {
- // set the value of the search box to be an empty string and then search on that, effectively reseting
- $$5(this).val('').trigger('input');
- });
- _this.clearPane();
- });
- // When the search button is clicked then draw focus to the search box
- this.dom.searchButton.off('click.dtsp').on('click.dtsp', function () { return _this.dom.searchBox.focus(); });
- // When a character is inputted into the searchbox search the pane for matching values.
- // Doing it this way means that no button has to be clicked to trigger a search, it is done asynchronously
- this.dom.searchBox.off('click.dtsp').on('input.dtsp', function () {
- var searchval = _this.dom.searchBox.val();
- _this.s.dtPane.search(searchval).draw();
- if (typeof searchval === 'string' &&
- (searchval.length > 0 ||
- searchval.length === 0 && _this.s.dtPane.rows({ selected: true }).data().toArray().length > 0)) {
- _this.dom.clear.removeClass(_this.classes.disabledButton).removeAttr('disabled');
- }
- else {
- _this.dom.clear.addClass(_this.classes.disabledButton).attr('disabled', 'true');
- }
- // This state save is required so that the searching on the panes is maintained
- _this.s.dt.state.save();
- });
- this.s.dtPane.select.style(this.s.colOpts.dtOpts && this.s.colOpts.dtOpts.select && this.s.colOpts.dtOpts.select.style
- ? this.s.colOpts.dtOpts.select.style
- : this.c.dtOpts && this.c.dtOpts.select && this.c.dtOpts.select.style
- ? this.c.dtOpts.select.style
- : 'os');
- };
- /**
- * Populates the SearchPane based off of the data that has been recieved from the server
- *
- * This method is overriden by SearchPaneST
- *
- * @param dataIn The data that has been sent from the server
- */
- SearchPane.prototype._serverPopulate = function (dataIn) {
- if (dataIn.tableLength) {
- this.s.tableLength = dataIn.tableLength;
- this.s.rowData.totalOptions = this.s.tableLength;
- }
- else if (this.s.tableLength === null || this.s.dt.rows()[0].length > this.s.tableLength) {
- this.s.tableLength = this.s.dt.rows()[0].length;
- this.s.rowData.totalOptions = this.s.tableLength;
- }
- var colTitle = this.s.dt.column(this.s.index).dataSrc();
- // If there is SP data for this column add it to the data array and bin
- if (dataIn.searchPanes.options[colTitle]) {
- for (var _i = 0, _a = dataIn.searchPanes.options[colTitle]; _i < _a.length; _i++) {
- var dataPoint = _a[_i];
- this.s.rowData.arrayFilter.push({
- display: dataPoint.label,
- filter: dataPoint.value,
- sort: dataPoint.label,
- type: dataPoint.label
- });
- this.s.rowData.bins[dataPoint.value] = dataPoint.total;
- }
- }
- var binLength = Object.keys(this.s.rowData.bins).length;
- var uniqueRatio = this._uniqueRatio(binLength, this.s.tableLength);
- // Don't show the pane if there isnt enough variance in the data, or there is only 1 entry for that pane
- if (this.s.displayed === false &&
- ((this.s.colOpts.show === undefined && this.s.colOpts.threshold === null ?
- uniqueRatio > this.c.threshold :
- uniqueRatio > this.s.colOpts.threshold) ||
- this.s.colOpts.show !== true && binLength <= 1)) {
- this.dom.container.addClass(this.classes.hidden);
- this.s.displayed = false;
- return;
- }
- // Store the original data
- this.s.rowData.arrayOriginal = this.s.rowData.arrayFilter;
- this.s.rowData.binsOriginal = this.s.rowData.bins;
- // Flag this pane as being displayed
- this.s.displayed = true;
- };
- /**
- * Expands the pane from the collapsed state
- */
- SearchPane.prototype.show = function () {
- if (!this.s.displayed) {
- return;
- }
- this.dom.topRow.removeClass(this.classes.bordered);
- this.dom.nameButton.removeClass(this.classes.disabledButton);
- this.dom.countButton.removeClass(this.classes.disabledButton);
- this.dom.searchButton.removeClass(this.classes.disabledButton);
- this.dom.collapseButton.removeClass(this.classes.rotated);
- $$5(this.s.dtPane.table().container()).removeClass(this.classes.hidden);
- this.dom.topRow.trigger('collapse.dtsps');
- };
- /**
- * Finds the ratio of the number of different options in the table to the number of rows
- *
- * @param bins the number of different options in the table
- * @param rowCount the total number of rows in the table
- * @returns {number} returns the ratio
- */
- SearchPane.prototype._uniqueRatio = function (bins, rowCount) {
- if (rowCount > 0 &&
- (this.s.rowData.totalOptions > 0 && !this.s.dt.page.info().serverSide ||
- this.s.dt.page.info().serverSide && this.s.tableLength > 0)) {
- return bins / this.s.rowData.totalOptions;
- }
- return 1;
- };
- /**
- * Updates the panes if one of the options to do so has been set to true
- * rather than the filtered message when using viewTotal.
- */
- SearchPane.prototype.updateTable = function () {
- var selectedRows = this.s.dtPane.rows({ selected: true }).data().toArray().map(function (el) { return el.filter; });
- this.s.selections = selectedRows;
- this._searchExtras();
- };
- /**
- * Adds the custom options to the pane
- *
- * @returns {Array} Returns the array of rows which have been added to the pane
- */
- SearchPane.prototype._getComparisonRows = function () {
- // Find the appropriate options depending on whether this is a pane for a specific column or a custom pane
- var options = this.s.colOpts.options
- ? this.s.colOpts.options
- : this.s.customPaneSettings && this.s.customPaneSettings.options
- ? this.s.customPaneSettings.options
- : undefined;
- if (options === undefined) {
- return;
- }
- var allRows = this.s.dt.rows();
- var tableValsTotal = allRows.data().toArray();
- var rows = [];
- // Clear all of the other rows from the pane, only custom options are to be displayed when they are defined
- this.s.dtPane.clear();
- this.s.indexes = [];
- for (var _i = 0, options_1 = options; _i < options_1.length; _i++) {
- var comp = options_1[_i];
- // Initialise the object which is to be placed in the row
- var insert = comp.label !== '' ?
- comp.label :
- this.emptyMessage();
- var comparisonObj = {
- className: comp.className,
- display: insert,
- filter: typeof comp.value === 'function' ? comp.value : [],
- sort: comp.order !== undefined
- ? comp.order
- : insert,
- total: 0,
- type: insert
- };
- // If a custom function is in place
- if (typeof comp.value === 'function') {
- // Count the number of times the function evaluates to true for the original data in the Table
- for (var i = 0; i < tableValsTotal.length; i++) {
- if (comp.value.call(this.s.dt, tableValsTotal[i], allRows[0][i])) {
- comparisonObj.total++;
- }
- }
- // Update the comparisonObj
- if (typeof comparisonObj.filter !== 'function') {
- comparisonObj.filter.push(comp.filter);
- }
- }
- rows.push(this.addRow(comparisonObj.display, comparisonObj.filter, comparisonObj.sort, comparisonObj.type, comparisonObj.className, comparisonObj.total));
- }
- return rows;
- };
- SearchPane.prototype._getMessage = function (row) {
- return this.s.dt.i18n('searchPanes.count', this.c.i18n.count).replace(/{total}/g, row.total);
- };
- /**
- * Overridden in SearchPaneViewTotal and SearchPaneCascade to get the number of times a specific value is shown
- *
- * Here it is blanked so that it takes no action
- *
- * @param filter The filter value
- * @returns undefined
- */
- SearchPane.prototype._getShown = function (filter) {
- return undefined;
- };
- /**
- * Get's the pane config appropriate to this class
- *
- * @returns The config needed to create a pane of this type
- */
- SearchPane.prototype._getPaneConfig = function () {
- var _this = this;
- // eslint-disable-next-line no-extra-parens
- var haveScroller = dataTable$2.Scroller;
- var langOpts = this.s.dt.settings()[0].oLanguage;
- langOpts.url = undefined;
- langOpts.sUrl = undefined;
- return {
- columnDefs: [
- {
- className: 'dtsp-nameColumn',
- data: 'display',
- render: function (data, type, row) {
- if (type === 'sort') {
- return row.sort;
- }
- else if (type === 'type') {
- return row.type;
- }
- var message = _this._getMessage(row);
- // We are displaying the count in the same columne as the name of the search option.
- // This is so that there is not need to call columns.adjust()
- // which in turn speeds up the code
- var pill = '<span class="' + _this.classes.pill + '">' + message + '</span>';
- if (!_this.c.viewCount || !_this.s.colOpts.viewCount) {
- pill = '';
- }
- if (type === 'filter') {
- return typeof data === 'string' && data.match(/<[^>]*>/) !== null ?
- data.replace(/<[^>]*>/g, '') :
- data;
- }
- return '<div class="' + _this.classes.nameCont + '"><span title="' +
- (typeof data === 'string' && data.match(/<[^>]*>/) !== null ?
- data.replace(/<[^>]*>/g, '') :
- data) +
- '" class="' + _this.classes.name + '">' +
- data + '</span>' +
- pill + '</div>';
- },
- targets: 0,
- // Accessing the private datatables property to set type based on the original table.
- // This is null if not defined by the user, meaning that automatic type detection
- // would take place
- type: this.s.dt.settings()[0].aoColumns[this.s.index] ?
- this.s.dt.settings()[0].aoColumns[this.s.index]._sManualType :
- null
- },
- {
- className: 'dtsp-countColumn ' + this.classes.badgePill,
- data: 'total',
- searchable: false,
- targets: 1,
- visible: false
- }
- ],
- deferRender: true,
- info: false,
- language: langOpts,
- paging: haveScroller ? true : false,
- scrollX: false,
- scrollY: '200px',
- scroller: haveScroller ? true : false,
- select: true,
- stateSave: this.s.dt.settings()[0].oFeatures.bStateSave ? true : false
- };
- };
- /**
- * This method allows for changes to the panes and table to be made when a selection or a deselection occurs
- */
- SearchPane.prototype._makeSelection = function () {
- this.updateTable();
- this.s.updating = true;
- this.s.dt.draw();
- this.s.updating = false;
- };
- /**
- * Populates an array with all of the data for the table
- *
- * @param rowIdx The current row index to be compared
- * @param arrayFilter The array that is to be populated with row Details
- * @param settings The DataTable settings object
- * @param bins The bins object that is to be populated with the row counts
- */
- SearchPane.prototype._populatePaneArray = function (rowIdx, arrayFilter, settings, bins) {
- if (bins === void 0) { bins = this.s.rowData.bins; }
- // Retrieve the rendered data from the cell using the fastData function
- // rather than the cell().render API method for optimisation
- var fastData = settings.fastData
- ? settings.fastData
- : function (row, col, orth) {
- // Legacy DT1
- return settings.oApi._fnGetCellData(settings, row, col, orth);
- };
- if (typeof this.s.colOpts.orthogonal === 'string') {
- var rendered = fastData(rowIdx, this.s.index, this.s.colOpts.orthogonal);
- this.s.rowData.filterMap.set(rowIdx, rendered);
- this._addOption(rendered, rendered, rendered, rendered, arrayFilter, bins);
- this.s.rowData.totalOptions++;
- }
- else {
- var filter = fastData(rowIdx, this.s.index, this.s.colOpts.orthogonal.search);
- // Null and empty string are to be considered the same value
- if (filter === null) {
- filter = '';
- }
- if (typeof filter === 'string') {
- filter = filter.replace(/<[^>]*>/g, '');
- }
- this.s.rowData.filterMap.set(rowIdx, filter);
- if (!bins[filter]) {
- bins[filter] = 1;
- this._addOption(filter, fastData(rowIdx, this.s.index, this.s.colOpts.orthogonal.display), fastData(rowIdx, this.s.index, this.s.colOpts.orthogonal.sort), fastData(rowIdx, this.s.index, this.s.colOpts.orthogonal.type), arrayFilter, bins);
- this.s.rowData.totalOptions++;
- }
- else {
- bins[filter]++;
- this.s.rowData.totalOptions++;
- }
- }
- };
- /**
- * Reloads all of the previous selects into the panes
- *
- * @param loadedFilter The loaded filters from a previous state
- */
- SearchPane.prototype._reloadSelect = function (loadedFilter) {
- // If the state was not saved don't selected any
- if (loadedFilter === undefined) {
- return;
- }
- var idx;
- // For each pane, check that the loadedFilter list exists and is not null,
- // find the id of each search item and set it to be selected.
- for (var i = 0; i < loadedFilter.searchPanes.panes.length; i++) {
- if (loadedFilter.searchPanes.panes[i].id === this.s.index) {
- idx = i;
- break;
- }
- }
- if (idx) {
- var table = this.s.dtPane;
- var rows = table.rows({ order: 'index' }).data().map(function (item) { return item.filter !== null ?
- item.filter.toString() :
- null; }).toArray();
- for (var _i = 0, _a = loadedFilter.searchPanes.panes[idx].selected; _i < _a.length; _i++) {
- var filter = _a[_i];
- var id = -1;
- if (filter !== null) {
- id = rows.indexOf(filter.toString());
- }
- if (id > -1) {
- this.s.serverSelecting = true;
- table.row(id).select();
- this.s.serverSelecting = false;
- }
- }
- }
- };
- /**
- * Notes the rows that have been selected within this pane and stores them internally
- *
- * @param notUpdating Whether the panes are updating themselves or not
- */
- SearchPane.prototype._updateSelection = function (notUpdating) {
- var _this = this;
- var processing = function (state) {
- if (DataTable.versionCheck('2')) {
- _this.s.dt.processing(state);
- }
- else {
- // Legacy v1
- var settings = _this.s.dt.settings()[0];
- var oApi = settings.oApi;
- oApi._fnProcessingDisplay(settings, false);
- }
- };
- var run = function () {
- _this.s.scrollTop = $$5(_this.s.dtPane.table().node()).parent()[0].scrollTop;
- if (_this.s.dt.page.info().serverSide && !_this.s.updating) {
- if (!_this.s.serverSelecting) {
- _this.s.serverSelect = _this.s.dtPane.rows({ selected: true }).data().toArray();
- _this.s.dt.draw(false);
- }
- }
- else if (notUpdating) {
- _this._makeSelection();
- }
- processing(false);
- };
- processing(true);
- setTimeout(run, 1);
- };
- /**
- * Takes in potentially undetected rows and adds them to the array if they are not yet featured
- *
- * @param filter the filter value of the potential row
- * @param display the display value of the potential row
- * @param sort the sort value of the potential row
- * @param type the type value of the potential row
- * @param arrayFilter the array to be populated
- * @param bins the bins to be populated
- */
- SearchPane.prototype._addOption = function (filter, display, sort, type, arrayFilter, bins) {
- // If the filter is an array then take a note of this, and add the elements to the arrayFilter array
- if (Array.isArray(filter) || filter instanceof dataTable$2.Api) {
- // Convert to an array so that we can work with it
- if (filter instanceof dataTable$2.Api) {
- filter = filter.toArray();
- display = display.toArray();
- }
- if (filter.length === display.length) {
- for (var i = 0; i < filter.length; i++) {
- // If we haven't seen this row before add it
- if (!bins[filter[i]]) {
- bins[filter[i]] = 1;
- arrayFilter.push({
- display: display[i],
- filter: filter[i],
- sort: sort[i],
- type: type[i]
- });
- }
- // Otherwise just increment the count
- else {
- bins[filter[i]]++;
- }
- this.s.rowData.totalOptions++;
- }
- return;
- }
- throw new Error('display and filter not the same length');
- }
- // If the values were affected by othogonal data and are not an array then check if it is already present
- else if (typeof this.s.colOpts.orthogonal === 'string') {
- if (!bins[filter]) {
- bins[filter] = 1;
- arrayFilter.push({
- display: display,
- filter: filter,
- sort: sort,
- type: type
- });
- this.s.rowData.totalOptions++;
- }
- else {
- bins[filter]++;
- this.s.rowData.totalOptions++;
- }
- }
- // Otherwise we must just be adding an option
- else {
- arrayFilter.push({
- display: display,
- filter: filter,
- sort: sort,
- type: type
- });
- }
- };
- /**
- * Method to construct the actual pane.
- *
- * @param selectedRows previously selected Rows to be reselected
- * @param dataIn Data that should be used to populate this pane
- * @param prevEl Reference to the previous element, used to ensure insert is in the correct location
- * @returns boolean to indicate whether this pane was the last one to have a selection made
- */
- SearchPane.prototype._buildPane = function (selectedRows, dataIn, prevEl) {
- var _this = this;
- if (selectedRows === void 0) { selectedRows = []; }
- if (dataIn === void 0) { dataIn = null; }
- if (prevEl === void 0) { prevEl = null; }
- // Aliases
- this.s.selections = [];
- // Other Variables
- var loadedFilter = this.s.dt.state.loaded();
- var row;
- // If the listeners have not been set yet then using the latest state may result in funny errors
- if (this.s.listSet) {
- loadedFilter = this.s.dt.state();
- }
- // If it is not a custom pane in place
- if (this.s.colExists) {
- var idx = -1;
- if (loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.panes) {
- for (var i = 0; i < loadedFilter.searchPanes.panes.length; i++) {
- if (loadedFilter.searchPanes.panes[i].id === this.s.index) {
- idx = i;
- break;
- }
- }
- }
- // Perform checks that do not require populate pane to run
- if ((this.s.colOpts.show === false ||
- this.s.colOpts.show !== undefined && this.s.colOpts.show !== true) &&
- idx === -1) {
- this.dom.container.addClass(this.classes.hidden);
- this.s.displayed = false;
- return false;
- }
- else if (this.s.colOpts.show === true || idx !== -1) {
- this.s.displayed = true;
- }
- if (!this.s.dt.page.info().serverSide &&
- (!dataIn ||
- !dataIn.searchPanes ||
- !dataIn.searchPanes.options)) {
- // Only run populatePane if the data has not been collected yet
- if (this.s.rowData.arrayFilter.length === 0) {
- this.s.rowData.totalOptions = 0;
- this._populatePane();
- this.s.rowData.arrayOriginal = this.s.rowData.arrayFilter;
- this.s.rowData.binsOriginal = this.s.rowData.bins;
- }
- var binLength = Object.keys(this.s.rowData.binsOriginal).length;
- var uniqueRatio = this._uniqueRatio(binLength, this.s.dt.rows()[0].length);
- // Don't show the pane if there isn't enough variance in the data, or there is only 1 entry
- // for that pane
- if (this.s.displayed === false &&
- ((this.s.colOpts.show === undefined && this.s.colOpts.threshold === null ?
- uniqueRatio > this.c.threshold :
- uniqueRatio > this.s.colOpts.threshold) ||
- this.s.colOpts.show !== true && binLength <= 1)) {
- this.dom.container.addClass(this.classes.hidden);
- this.s.displayed = false;
- return;
- }
- this.dom.container.addClass(this.classes.show);
- this.s.displayed = true;
- }
- else if (dataIn && dataIn.searchPanes && dataIn.searchPanes.options) {
- this._serverPopulate(dataIn);
- }
- }
- else {
- this.s.displayed = true;
- }
- // If the variance is accceptable then display the search pane
- this._displayPane();
- if (!this.s.listSet) {
- // Here, when the state is loaded if the data object on the original table is empty,
- // then a state.clear() must have occurred, so delete all of the panes tables state objects too.
- this.dom.dtP.on('stateLoadParams.dtsp', function (e, settings, data) {
- if ($$5.isEmptyObject(_this.s.dt.state.loaded())) {
- $$5.each(data, function (index) {
- delete data[index];
- });
- }
- });
- }
- // Add the container to the document in its original location
- if (prevEl !== null && this.dom.panesContainer.has(prevEl).length > 0) {
- this.dom.container.insertAfter(prevEl);
- }
- else {
- this.dom.panesContainer.prepend(this.dom.container);
- }
- // Declare the datatable for the pane
- var errMode = $$5.fn.dataTable.ext.errMode;
- $$5.fn.dataTable.ext.errMode = 'none';
- // eslint-disable-next-line no-extra-parens
- // For async loading of a DataTable (e.g. language file)
- // we need to set the select style to make sure the event
- // handlers are added.
- this.dom.dtP.on('init.dt', function (e, s) {
- var dt = _this.dom.dtP.DataTable();
- var style = dt.select.style();
- dt.select.style(style);
- });
- this.s.dtPane = this.dom.dtP.DataTable($$5.extend(true, this._getPaneConfig(), this.c.dtOpts, this.s.colOpts ? this.s.colOpts.dtOpts : {}, this.s.colOpts.options || !this.s.colExists ?
- {
- createdRow: function (row, data) {
- $$5(row).addClass(data.className);
- }
- } :
- undefined, this.s.customPaneSettings !== null && this.s.customPaneSettings.dtOpts ?
- this.s.customPaneSettings.dtOpts :
- {}, $$5.fn.dataTable.versionCheck('2')
- ? {
- layout: {
- bottomStart: null,
- bottomEnd: null,
- topStart: null,
- topEnd: null
- }
- }
- : { dom: 't' }));
- this.dom.dtP.addClass(this.classes.table);
- // Getting column titles is a little messy
- var headerText = 'Custom Pane';
- if (this.s.customPaneSettings && this.s.customPaneSettings.header) {
- headerText = this.s.customPaneSettings.header;
- }
- else if (this.s.colOpts.header) {
- headerText = this.s.colOpts.header;
- }
- else if (this.s.colExists) {
- headerText = $$5.fn.dataTable.versionCheck('2')
- ? this.s.dt.column(this.s.index).title()
- : this.s.dt.settings()[0].aoColumns[this.s.index].sTitle;
- }
- headerText = this._escapeHTML(headerText);
- this.dom.searchBox.attr('placeholder', headerText);
- $$5.fn.dataTable.ext.errMode = errMode;
- // If it is not a custom pane
- if (this.s.colExists) {
- // Add all of the search options to the pane
- for (var j = 0, jen = this.s.rowData.arrayFilter.length; j < jen; j++) {
- if (this.s.dt.page.info().serverSide) {
- row = this.addRow(this.s.rowData.arrayFilter[j].display, this.s.rowData.arrayFilter[j].filter, this.s.rowData.arrayFilter[j].sort, this.s.rowData.arrayFilter[j].type);
- for (var _i = 0, _a = this.s.serverSelect; _i < _a.length; _i++) {
- var option = _a[_i];
- if (option.filter === this.s.rowData.arrayFilter[j].filter) {
- this.s.serverSelecting = true;
- row.select();
- this.s.serverSelecting = false;
- }
- }
- }
- else if (!this.s.dt.page.info().serverSide && this.s.rowData.arrayFilter[j]) {
- this.addRow(this.s.rowData.arrayFilter[j].display, this.s.rowData.arrayFilter[j].filter, this.s.rowData.arrayFilter[j].sort, this.s.rowData.arrayFilter[j].type);
- }
- else if (!this.s.dt.page.info().serverSide) {
- // Just pass an empty string as the message will be calculated based on that in addRow()
- this.addRow('', '', '', '');
- }
- }
- }
- // If there are custom options set or it is a custom pane then get them
- if (this.s.colOpts.options ||
- this.s.customPaneSettings && this.s.customPaneSettings.options) {
- this._getComparisonRows();
- }
- // Display the pane
- this.s.dtPane.draw();
- this.s.dtPane.table().node().parentNode.scrollTop = this.s.scrollTop;
- this.adjustTopRow();
- this.setListeners();
- this.s.listSet = true;
- for (var _b = 0, selectedRows_1 = selectedRows; _b < selectedRows_1.length; _b++) {
- var selection = selectedRows_1[_b];
- if (selection) {
- for (var _c = 0, _d = this.s.dtPane.rows().indexes().toArray(); _c < _d.length; _c++) {
- row = _d[_c];
- if (this.s.dtPane.row(row).data() &&
- selection.filter === this.s.dtPane.row(row).data().filter) {
- // If this is happening when serverSide processing is happening then
- // different behaviour is needed
- if (this.s.dt.page.info().serverSide) {
- this.s.serverSelecting = true;
- this.s.dtPane.row(row).select();
- this.s.serverSelecting = false;
- }
- else {
- this.s.dtPane.row(row).select();
- }
- }
- }
- }
- }
- // If SSP and the table is ready, apply the search for the pane
- if (this.s.dt.page.info().serverSide) {
- this.s.dtPane.search(this.dom.searchBox.val()).draw();
- }
- if ((this.c.initCollapsed && this.s.colOpts.initCollapsed !== false ||
- this.s.colOpts.initCollapsed) &&
- (this.c.collapse && this.s.colOpts.collapse !== false ||
- this.s.colOpts.collapse)) {
- // If the pane has not initialised yet then we need to wait for it to do so before collapsing
- // Otherwise the container that the class is added to does not exist
- if (this.s.dtPane.settings()[0]._bInitComplete) {
- this.collapse();
- }
- else {
- this.s.dtPane.one('init', function () { return _this.collapse(); });
- }
- }
- // Reload the selection, searchbox entry and ordering from the previous state
- // Need to check here if SSP that this is the first draw, otherwise it will infinite loop
- if (loadedFilter &&
- loadedFilter.searchPanes &&
- loadedFilter.searchPanes.panes &&
- (!dataIn ||
- dataIn.draw === 1)) {
- this._reloadSelect(loadedFilter);
- for (var _e = 0, _f = loadedFilter.searchPanes.panes; _e < _f.length; _e++) {
- var pane = _f[_e];
- if (pane.id === this.s.index) {
- // Save some time by only triggering an input if there is a value
- if (pane.searchTerm && pane.searchTerm.length > 0) {
- this.dom.searchBox.val(pane.searchTerm).trigger('input');
- }
- if (pane.order) {
- this.s.dtPane.order(pane.order).draw();
- }
- // Is the pane to be hidden or shown?
- if (pane.collapsed) {
- this.collapse();
- }
- else {
- this.show();
- }
- }
- }
- }
- return true;
- };
- /**
- * Appends all of the HTML elements to their relevant parent Elements
- */
- SearchPane.prototype._displayPane = function () {
- // Empty everything to start again
- this.dom.dtP.empty();
- this.dom.topRow.empty().addClass(this.classes.topRow);
- // If there are more than 3 columns defined then make there be a smaller gap between the panes
- if (parseInt(this.c.layout.split('-')[1], 10) > 3) {
- this.dom.container.addClass(this.classes.smallGap);
- }
- this.dom.topRow
- .addClass(this.classes.subRowsContainer)
- .append(this.dom.upper.append(this.dom.searchCont))
- .append(this.dom.lower.append(this.dom.buttonGroup));
- // If no selections have been made in the pane then disable the clear button
- if (this.c.dtOpts.searching === false ||
- this.s.colOpts.dtOpts && this.s.colOpts.dtOpts.searching === false ||
- (!this.c.controls || !this.s.colOpts.controls) ||
- this.s.customPaneSettings &&
- this.s.customPaneSettings.dtOpts &&
- this.s.customPaneSettings.dtOpts.searching !== undefined &&
- !this.s.customPaneSettings.dtOpts.searching) {
- this.dom.searchBox
- .removeClass(this.classes.paneInputButton)
- .addClass(this.classes.disabledButton)
- .attr('disabled', 'true');
- }
- this.dom.searchBox.appendTo(this.dom.searchCont);
- // Create the contents of the searchCont div. Worth noting that this function will change when using semantic ui
- this._searchContSetup();
- // If the clear button is allowed to show then display it
- if (this.c.clear && this.c.controls && this.s.colOpts.controls) {
- this.dom.clear.appendTo(this.dom.buttonGroup);
- }
- if (this.c.orderable && this.s.colOpts.orderable && this.c.controls && this.s.colOpts.controls) {
- this.dom.nameButton.appendTo(this.dom.buttonGroup);
- }
- // If the count column is hidden then don't display the ordering button for it
- if (this.c.viewCount &&
- this.s.colOpts.viewCount &&
- this.c.orderable &&
- this.s.colOpts.orderable &&
- this.c.controls &&
- this.s.colOpts.controls) {
- this.dom.countButton.appendTo(this.dom.buttonGroup);
- }
- if ((this.c.collapse && this.s.colOpts.collapse !== false ||
- this.s.colOpts.collapse) &&
- this.c.controls && this.s.colOpts.controls) {
- this.dom.collapseButton.appendTo(this.dom.buttonGroup);
- }
- this.dom.container.prepend(this.dom.topRow).append(this.dom.dtP).show();
- };
- /**
- * Escape html characters within a string
- *
- * @param txt the string to be escaped
- * @returns the escaped string
- */
- SearchPane.prototype._escapeHTML = function (txt) {
- return txt
- .toString()
- .replace(/</g, '<')
- .replace(/>/g, '>')
- .replace(/"/g, '"')
- .replace(/&/g, '&');
- };
- /**
- * Gets the options for the row for the customPanes
- *
- * @returns {object} The options for the row extended to include the options from the user.
- */
- SearchPane.prototype._getBonusOptions = function () {
- // We need to reset the thresholds as if they have a value in colOpts then that value will be used
- var defaultMutator = {
- threshold: null
- };
- return $$5.extend(true, {}, SearchPane.defaults, defaultMutator, this.c ? this.c : {});
- };
- /**
- * Gets the options for the row for the customPanes
- *
- * @returns {object} The options for the row extended to include the options from the user.
- */
- SearchPane.prototype._getOptions = function () {
- var table = this.s.dt;
- // We need to reset the thresholds as if they have a value in colOpts then that value will be used
- var defaultMutator = {
- collapse: null,
- emptyMessage: false,
- initCollapsed: null,
- threshold: null
- };
- var columnOptions = table.settings()[0].aoColumns[this.s.index].searchPanes;
- var colOpts = $$5.extend(true, {}, SearchPane.defaults, defaultMutator, columnOptions);
- if (columnOptions && columnOptions.hideCount && columnOptions.viewCount === undefined) {
- colOpts.viewCount = !columnOptions.hideCount;
- }
- return colOpts;
- };
- /**
- * Fill the array with the values that are currently being displayed in the table
- */
- SearchPane.prototype._populatePane = function () {
- this.s.rowData.arrayFilter = [];
- this.s.rowData.bins = {};
- var settings = this.s.dt.context[0];
- if (!this.s.dt.page.info().serverSide) {
- for (var _i = 0, _a = this.s.dt.rows().indexes().toArray(); _i < _a.length; _i++) {
- var index = _a[_i];
- this._populatePaneArray(index, this.s.rowData.arrayFilter, settings);
- }
- }
- };
- /**
- * This method decides whether a row should contribute to the pane or not
- *
- * @param filter the value that the row is to be filtered on
- * @param dataIndex the row index
- */
- SearchPane.prototype._search = function (filter, dataIndex) {
- var colOpts = this.s.colOpts;
- var table = this.s.dt;
- // For each item selected in the pane, check if it is available in the cell
- for (var _i = 0, _a = this.s.selections; _i < _a.length; _i++) {
- var colSelect = _a[_i];
- if (typeof colSelect === 'string' && typeof filter === 'string') {
- // The filter value will not have the & in place but a &,
- // so we need to do a replace to make sure that they will match
- colSelect = this._escapeHTML(colSelect);
- }
- // if the filter is an array then is the column present in it
- if (Array.isArray(filter)) {
- if (colOpts.combiner === 'and') {
- if (!filter.includes(colSelect)) {
- return false;
- }
- }
- else if (filter.includes(colSelect)) {
- return true;
- }
- }
- // if the filter is a function then does it meet the criteria of that function or not
- else if (typeof colSelect === 'function') {
- if (colSelect.call(table, table.row(dataIndex).data(), dataIndex)) {
- if (colOpts.combiner === 'or') {
- return true;
- }
- }
- // If the combiner is an "and" then we need to check against all possible selections
- // so if it fails here then the and is not met and return false
- else if (colOpts.combiner === 'and') {
- return false;
- }
- }
- // otherwise if the two filter values are equal then return true
- else if (filter === colSelect ||
- // Loose type checking incase number type in column comparing to a string
- // eslint-disable-next-line eqeqeq
- !(typeof filter === 'string' && filter.length === 0) && filter == colSelect ||
- colSelect === null && typeof filter === 'string' && filter === '') {
- return true;
- }
- }
- // If the combiner is an and then we need to check against all possible selections
- // so return true here if so because it would have returned false earlier if it had failed
- if (colOpts.combiner === 'and') {
- return true;
- }
- // Otherwise it hasn't matched with anything by this point so it must be false
- return false;
- };
- /**
- * Creates the contents of the searchCont div
- *
- * NOTE This is overridden when semantic ui styling in order to integrate the search button into the text box.
- */
- SearchPane.prototype._searchContSetup = function () {
- if (this.c.controls && this.s.colOpts.controls) {
- this.dom.searchButton.appendTo(this.dom.searchLabelCont);
- }
- if (!(this.c.dtOpts.searching === false ||
- this.s.colOpts.dtOpts.searching === false ||
- this.s.customPaneSettings &&
- this.s.customPaneSettings.dtOpts &&
- this.s.customPaneSettings.dtOpts.searching !== undefined &&
- !this.s.customPaneSettings.dtOpts.searching)) {
- this.dom.searchLabelCont.appendTo(this.dom.searchCont);
- }
- };
- /**
- * Adds outline to the pane when a selection has been made
- */
- SearchPane.prototype._searchExtras = function () {
- var updating = this.s.updating;
- this.s.updating = true;
- var filters = this.s.dtPane.rows({ selected: true }).data().pluck('filter').toArray();
- var nullIndex = filters.indexOf(this.emptyMessage());
- var container = $$5(this.s.dtPane.table().container());
- // If null index is found then search for empty cells as a filter.
- if (nullIndex > -1) {
- filters[nullIndex] = '';
- }
- // If a filter has been applied then outline the respective pane, remove it when it no longer is.
- if (filters.length > 0) {
- container.addClass(this.classes.selected);
- }
- else if (filters.length === 0) {
- container.removeClass(this.classes.selected);
- }
- this.s.updating = updating;
- };
- SearchPane.version = '2.1.2';
- SearchPane.classes = {
- bordered: 'dtsp-bordered',
- buttonGroup: 'dtsp-buttonGroup',
- buttonSub: 'dtsp-buttonSub',
- caret: 'dtsp-caret',
- clear: 'dtsp-clear',
- clearAll: 'dtsp-clearAll',
- clearButton: 'clearButton',
- collapseAll: 'dtsp-collapseAll',
- collapseButton: 'dtsp-collapseButton',
- container: 'dtsp-searchPane',
- countButton: 'dtsp-countButton',
- disabledButton: 'dtsp-disabledButton',
- hidden: 'dtsp-hidden',
- hide: 'dtsp-hide',
- layout: 'dtsp-',
- name: 'dtsp-name',
- nameButton: 'dtsp-nameButton',
- nameCont: 'dtsp-nameCont',
- narrow: 'dtsp-narrow',
- paneButton: 'dtsp-paneButton',
- paneInputButton: 'dtsp-paneInputButton',
- pill: 'dtsp-pill',
- rotated: 'dtsp-rotated',
- search: 'dtsp-search',
- searchCont: 'dtsp-searchCont',
- searchIcon: 'dtsp-searchIcon',
- searchLabelCont: 'dtsp-searchButtonCont',
- selected: 'dtsp-selected',
- smallGap: 'dtsp-smallGap',
- subRow1: 'dtsp-subRow1',
- subRow2: 'dtsp-subRow2',
- subRowsContainer: 'dtsp-subRowsContainer',
- title: 'dtsp-title',
- topRow: 'dtsp-topRow'
- };
- // Define SearchPanes default options
- SearchPane.defaults = {
- clear: true,
- collapse: true,
- combiner: 'or',
- container: function (dt) {
- return dt.table().container();
- },
- controls: true,
- dtOpts: {},
- emptyMessage: null,
- hideCount: false,
- i18n: {
- clearPane: '×',
- count: '{total}',
- emptyMessage: '<em>No data</em>'
- },
- initCollapsed: false,
- layout: 'auto',
- name: undefined,
- orderable: true,
- orthogonal: {
- display: 'display',
- filter: 'filter',
- hideCount: false,
- search: 'filter',
- show: undefined,
- sort: 'sort',
- threshold: 0.6,
- type: 'type',
- viewCount: true
- },
- preSelect: [],
- threshold: 0.6,
- viewCount: true
- };
- return SearchPane;
- }());
- var __extends$4 = (window && window.__extends) || (function () {
- var extendStatics = function (d, b) {
- extendStatics = Object.setPrototypeOf ||
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
- function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
- return extendStatics(d, b);
- };
- return function (d, b) {
- extendStatics(d, b);
- function __() { this.constructor = d; }
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
- };
- })();
- var SearchPaneST = /** @class */ (function (_super) {
- __extends$4(SearchPaneST, _super);
- function SearchPaneST(paneSettings, opts, index, panesContainer, panes) {
- return _super.call(this, paneSettings, opts, index, panesContainer, panes) || this;
- }
- /**
- * When server-side processing is enabled, SP will remove rows and then readd them,
- * resulting in Select's reference to the last selected cell being lost.
- * This function is provided to update that reference.
- *
- * @returns Function
- */
- SearchPaneST.prototype._emptyPane = function () {
- var dt = this.s.dtPane;
- if (DataTable.versionCheck('2')) {
- var last = dt.select.last();
- var selectedIndex_1;
- if (last && dt.row(last.row).any()) {
- selectedIndex_1 = dt.row(last.row).data().index;
- }
- dt.rows().remove();
- return function () {
- if (selectedIndex_1 !== undefined) {
- var idx = dt.row(function (i, data) { return data.index === selectedIndex_1; }).index();
- dt.select.last({ row: idx, column: 0 });
- }
- };
- }
- dt.rows().remove();
- return function () { };
- };
- /**
- * Populates the SearchPane based off of the data that has been recieved from the server
- *
- * This method overrides SearchPane's _serverPopulate() method
- *
- * @param dataIn The data that has been sent from the server
- */
- SearchPaneST.prototype._serverPopulate = function (dataIn) {
- var selection, row, data;
- this.s.rowData.binsShown = {};
- this.s.rowData.arrayFilter = [];
- if (dataIn.tableLength !== undefined) {
- this.s.tableLength = dataIn.tableLength;
- this.s.rowData.totalOptions = this.s.tableLength;
- }
- else if (this.s.tableLength === null || this.s.dt.rows()[0].length > this.s.tableLength) {
- this.s.tableLength = this.s.dt.rows()[0].length;
- this.s.rowData.totalOptions = this.s.tableLength;
- }
- var colTitle = this.s.dt.column(this.s.index).dataSrc();
- // If there is SP data for this column add it to the data array and bin
- if (dataIn.searchPanes.options[colTitle] !== undefined) {
- for (var _i = 0, _a = dataIn.searchPanes.options[colTitle]; _i < _a.length; _i++) {
- var dataPoint = _a[_i];
- this.s.rowData.arrayFilter.push({
- display: dataPoint.label,
- filter: dataPoint.value,
- shown: +dataPoint.count,
- sort: dataPoint.label,
- total: +dataPoint.total,
- type: dataPoint.label
- });
- this.s.rowData.binsShown[dataPoint.value] = +dataPoint.count;
- this.s.rowData.bins[dataPoint.value] = +dataPoint.total;
- }
- }
- var binLength = Object.keys(this.s.rowData.bins).length;
- var uniqueRatio = this._uniqueRatio(binLength, this.s.tableLength);
- // Don't show the pane if there isnt enough variance in the data, or there is only 1 entry for that pane
- if (!this.s.colOpts.show &&
- this.s.displayed === false &&
- ((this.s.colOpts.show === undefined && this.s.colOpts.threshold === null ?
- uniqueRatio > this.c.threshold :
- uniqueRatio > this.s.colOpts.threshold) ||
- this.s.colOpts.show !== true && binLength <= 1)) {
- this.dom.container.addClass(this.classes.hidden);
- this.s.displayed = false;
- return;
- }
- // Store the original data
- this.s.rowData.arrayOriginal = this.s.rowData.arrayFilter;
- this.s.rowData.binsOriginal = this.s.rowData.bins;
- // Flag this pane as being displayed
- this.s.displayed = true;
- // If the pane exists
- if (this.s.dtPane) {
- // Not the selections that have been made and remove all of the rows
- var selected = this.s.serverSelect;
- var reselect = this._emptyPane();
- // Add the rows that are to be shown into the pane
- for (var _b = 0, _c = this.s.rowData.arrayFilter; _b < _c.length; _b++) {
- data = _c[_b];
- if (this._shouldAddRow(data)) {
- row = this.addRow(data.display, data.filter, data.sort, data.type);
- // Select the row if it was selected before
- for (var i = 0; i < selected.length; i++) {
- selection = selected[i];
- if (selection.filter === data.filter) {
- // This flag stops another request being made to the server
- this.s.serverSelecting = true;
- row.select();
- this.s.serverSelecting = false;
- // Remove the selection from the to select list and add it to the selected list
- selected.splice(i, 1);
- this.s.selections.push(data.filter);
- break;
- }
- }
- }
- }
- // Remake any selections that are no longer present
- for (var _d = 0, selected_1 = selected; _d < selected_1.length; _d++) {
- selection = selected_1[_d];
- for (var _e = 0, _f = this.s.rowData.arrayOriginal; _e < _f.length; _e++) {
- data = _f[_e];
- if (data.filter === selection.filter) {
- row = this.addRow(data.display, data.filter, data.sort, data.type);
- this.s.serverSelecting = true;
- row.select();
- this.s.serverSelecting = false;
- this.s.selections.push(data.filter);
- }
- }
- }
- // Store the selected rows
- this.s.serverSelect = this.s.dtPane.rows({ selected: true }).data().toArray();
- // Update the pane
- this.s.dtPane.draw();
- reselect();
- }
- };
- /**
- * This method updates the rows and their data within the SearchPanes
- *
- * SearchPaneCascade overrides this method
- */
- SearchPaneST.prototype.updateRows = function () {
- if (!this.s.dt.page.info().serverSide) {
- // Get the latest count values from the table
- this.s.rowData.binsShown = {};
- for (var _i = 0, _a = this.s.dt.rows({ search: 'applied' }).indexes().toArray(); _i < _a.length; _i++) {
- var index = _a[_i];
- this._updateShown(index, this.s.dt.settings()[0], this.s.rowData.binsShown);
- }
- }
- var _loop_1 = function (row) {
- row.shown = typeof this_1.s.rowData.binsShown[row.filter] === 'number' ?
- this_1.s.rowData.binsShown[row.filter] :
- 0;
- this_1.s.dtPane
- .row(function (idx, data) {
- return data && (data.index === row.index);
- })
- .data(row);
- };
- var this_1 = this;
- // Update the rows data to show the current counts
- for (var _b = 0, _c = this.s.dtPane.rows().data().toArray(); _b < _c.length; _b++) {
- var row = _c[_b];
- _loop_1(row);
- }
- // Show updates in the pane
- this.s.dtPane.draw();
- this.s.dtPane.table().node().parentNode.scrollTop = this.s.scrollTop;
- };
- /**
- * Remove functionality from makeSelection - needs to be more advanced when tracking selections
- */
- SearchPaneST.prototype._makeSelection = function () {
- return;
- };
- /**
- * Blank method to remove reloading of selected rows - needs to be more advanced when tracking selections
- */
- SearchPaneST.prototype._reloadSelect = function () {
- return;
- };
- /**
- * Decides if a row should be added when being added from the server
- *
- * Overridden by SearchPaneCascade
- *
- * @param data the row data
- * @returns boolean indicating if the row should be added or not
- */
- SearchPaneST.prototype._shouldAddRow = function (data) {
- return true;
- };
- /**
- * Updates the server selection list where appropriate
- */
- SearchPaneST.prototype._updateSelection = function () {
- if (this.s.dt.page.info().serverSide && !this.s.updating && !this.s.serverSelecting) {
- this.s.serverSelect = this.s.dtPane.rows({ selected: true }).data().toArray();
- }
- };
- /**
- * Used when binning the data for a column
- *
- * @param rowIdx The current row that is to be added to the bins
- * @param settings The datatables settings object
- * @param bins The bins object that is to be incremented
- */
- SearchPaneST.prototype._updateShown = function (rowIdx, settings, bins) {
- if (bins === void 0) { bins = this.s.rowData.binsShown; }
- var orth = typeof this.s.colOpts.orthogonal === 'string'
- ? this.s.colOpts.orthogonal
- : this.s.colOpts.orthogonal.search;
- var fastData = settings.fastData
- ? settings.fastData
- : function (row, col, orth) {
- // Legacy DT1
- return settings.oApi._fnGetCellData(settings, row, col, orth);
- };
- var filter = fastData(rowIdx, this.s.index, orth);
- var add = function (f) {
- if (!bins[f]) {
- bins[f] = 1;
- }
- else {
- bins[f]++;
- }
- };
- if (Array.isArray(filter)) {
- for (var _i = 0, filter_1 = filter; _i < filter_1.length; _i++) {
- var f = filter_1[_i];
- add(f);
- }
- }
- else {
- add(filter);
- }
- };
- return SearchPaneST;
- }(SearchPane));
- var __extends$3 = (window && window.__extends) || (function () {
- var extendStatics = function (d, b) {
- extendStatics = Object.setPrototypeOf ||
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
- function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
- return extendStatics(d, b);
- };
- return function (d, b) {
- extendStatics(d, b);
- function __() { this.constructor = d; }
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
- };
- })();
- var $$4;
- function setJQuery$3(jq) {
- $$4 = jq;
- }
- var SearchPaneViewTotal = /** @class */ (function (_super) {
- __extends$3(SearchPaneViewTotal, _super);
- function SearchPaneViewTotal(paneSettings, opts, index, panesContainer, panes) {
- var _this = this;
- var override = {
- i18n: {
- countFiltered: '{shown} ({total})'
- }
- };
- _this = _super.call(this, paneSettings, $$4.extend(override, opts), index, panesContainer, panes) || this;
- return _this;
- }
- /**
- * Gets the message that is to be used to indicate the count for each SearchPane row
- *
- * This method overrides _getMessage() in SearchPane and is overridden by SearchPaneCascadeViewTotal
- *
- * @param row The row object that is being processed
- * @returns string - the message that is to be shown for the count of each entry
- */
- SearchPaneViewTotal.prototype._getMessage = function (row) {
- var countMessage = this.s.dt.i18n('searchPanes.count', this.c.i18n.count);
- var filteredMessage = this.s.dt.i18n('searchPanes.countFiltered', this.c.i18n.countFiltered);
- return (this.s.filteringActive ? filteredMessage : countMessage)
- .replace(/{total}/g, row.total)
- .replace(/{shown}/g, row.shown);
- };
- /**
- * Overrides the blank method in SearchPane to return the number of times a given value is currently being displayed
- *
- * @param filter The filter value
- * @returns number - The number of times the value is shown
- */
- SearchPaneViewTotal.prototype._getShown = function (filter) {
- return this.s.rowData.binsShown && this.s.rowData.binsShown[filter] ?
- this.s.rowData.binsShown[filter] :
- 0;
- };
- return SearchPaneViewTotal;
- }(SearchPaneST));
- var __extends$2 = (window && window.__extends) || (function () {
- var extendStatics = function (d, b) {
- extendStatics = Object.setPrototypeOf ||
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
- function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
- return extendStatics(d, b);
- };
- return function (d, b) {
- extendStatics(d, b);
- function __() { this.constructor = d; }
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
- };
- })();
- var $$3;
- function setJQuery$2(jq) {
- $$3 = jq;
- }
- var SearchPaneCascade = /** @class */ (function (_super) {
- __extends$2(SearchPaneCascade, _super);
- function SearchPaneCascade(paneSettings, opts, index, panesContainer, panes) {
- var _this = this;
- var override = {
- i18n: {
- count: '{shown}'
- }
- };
- _this = _super.call(this, paneSettings, $$3.extend(override, opts), index, panesContainer, panes) || this;
- return _this;
- }
- /**
- * This method updates the rows and their data within the SearchPanes
- *
- * This overrides the method in SearchPane
- */
- SearchPaneCascade.prototype.updateRows = function () {
- // Note the currently selected values in the pane and remove all of the rows
- var selected = this.s.dtPane.rows({ selected: true }).data().toArray();
- var selection;
- if (this.s.colOpts.options ||
- this.s.customPaneSettings && this.s.customPaneSettings.options) {
- // If there are custom options set or it is a custom pane then get them
- this._getComparisonRows();
- var rows = this.s.dtPane.rows().toArray()[0];
- for (var i = 0; i < rows.length; i++) {
- var row = this.s.dtPane.row(rows[i]);
- var rowData = row.data();
- if (rowData === undefined) {
- continue;
- }
- if (rowData.shown === 0) {
- row.remove();
- rows = this.s.dtPane.rows().toArray()[0];
- i--;
- continue;
- }
- for (var _i = 0, selected_1 = selected; _i < selected_1.length; _i++) {
- selection = selected_1[_i];
- if (rowData.filter === selection.filter) {
- row.select();
- selected.splice(i, 1);
- this.s.selections.push(rowData.filter);
- break;
- }
- }
- }
- }
- else {
- if (!this.s.dt.page.info().serverSide) {
- // Get the latest count values from the table
- this._activePopulatePane();
- this.s.rowData.binsShown = {};
- for (var _a = 0, _b = this.s.dt.rows({ search: 'applied' }).indexes().toArray(); _a < _b.length; _a++) {
- var index = _b[_a];
- this._updateShown(index, this.s.dt.settings()[0], this.s.rowData.binsShown);
- }
- }
- this.s.dtPane.rows().remove();
- // Go over all of the rows that could be displayed
- for (var _c = 0, _d = this.s.rowData.arrayFilter; _c < _d.length; _c++) {
- var data = _d[_c];
- // Cascade - If there are no rows present in the table don't show the option
- if (data.shown === 0) {
- continue;
- }
- // Add the row to the pane
- var newRow = this.addRow(data.display, data.filter, data.sort, data.type, undefined);
- // Check if this row was selected
- for (var j = 0; j < selected.length; j++) {
- var selectedRow = selected[j];
- if (selectedRow.filter === data.filter) {
- newRow.select();
- // Remove the row from the to find list and then add it to the selections list
- selected.splice(j, 1);
- this.s.selections.push(data.filter);
- break;
- }
- }
- }
- // Add all of the rows that were previously selected but aren't any more
- for (var _e = 0, selected_2 = selected; _e < selected_2.length; _e++) {
- selection = selected_2[_e];
- for (var _f = 0, _g = this.s.rowData.arrayOriginal; _f < _g.length; _f++) {
- var origData = _g[_f];
- if (origData.filter === selection.filter) {
- var addedRow = this.addRow(origData.display, origData.filter, origData.sort, origData.type, undefined);
- addedRow.select();
- this.s.selections.push(origData.filter);
- }
- }
- }
- }
- // Show updates in the pane
- this.s.dtPane.draw();
- this.s.dtPane.table().node().parentNode.scrollTop = this.s.scrollTop;
- // If client side updated the tables results
- if (!this.s.dt.page.info().serverSide) {
- this.s.dt.draw(false);
- }
- };
- /**
- * Fill the array with the values that are currently being displayed in the table
- */
- SearchPaneCascade.prototype._activePopulatePane = function () {
- this.s.rowData.arrayFilter = [];
- this.s.rowData.bins = {};
- var settings = this.s.dt.settings()[0];
- if (!this.s.dt.page.info().serverSide) {
- for (var _i = 0, _a = this.s.dt.rows({ search: 'applied' }).indexes().toArray(); _i < _a.length; _i++) {
- var index = _a[_i];
- this._populatePaneArray(index, this.s.rowData.arrayFilter, settings);
- }
- }
- };
- SearchPaneCascade.prototype._getComparisonRows = function () {
- // Find the appropriate options depending on whether this is a pane for a specific column or a custom pane
- var options = this.s.colOpts.options
- ? this.s.colOpts.options
- : this.s.customPaneSettings && this.s.customPaneSettings.options
- ? this.s.customPaneSettings.options
- : undefined;
- if (options === undefined) {
- return;
- }
- var allRows = this.s.dt.rows();
- var shownRows = this.s.dt.rows({ search: 'applied' });
- var tableValsTotal = allRows.data().toArray();
- var tableValsShown = shownRows.data().toArray();
- var rows = [];
- // Clear all of the other rows from the pane, only custom options are to be displayed when they are defined
- this.s.dtPane.clear();
- this.s.indexes = [];
- for (var _i = 0, options_1 = options; _i < options_1.length; _i++) {
- var comp = options_1[_i];
- // Initialise the object which is to be placed in the row
- var insert = comp.label !== '' ?
- comp.label :
- this.emptyMessage();
- var comparisonObj = {
- className: comp.className,
- display: insert,
- filter: typeof comp.value === 'function' ? comp.value : [],
- shown: 0,
- sort: insert,
- total: 0,
- type: insert
- };
- // If a custom function is in place
- if (typeof comp.value === 'function') {
- // Count the number of times the function evaluates to true for the original data in the Table
- for (var i = 0; i < tableValsTotal.length; i++) {
- if (comp.value.call(this.s.dt, tableValsTotal[i], allRows[0][i])) {
- comparisonObj.total++;
- }
- }
- for (var j = 0; j < tableValsShown.length; j++) {
- if (comp.value.call(this.s.dt, tableValsShown[j], shownRows[0][j])) {
- comparisonObj.shown++;
- }
- }
- // Update the comparisonObj
- if (typeof comparisonObj.filter !== 'function') {
- comparisonObj.filter.push(comp.filter);
- }
- }
- rows.push(this.addRow(comparisonObj.display, comparisonObj.filter, comparisonObj.sort, comparisonObj.type, comparisonObj.className, comparisonObj.total, comparisonObj.shown));
- }
- return rows;
- };
- /**
- * Gets the message that is to be used to indicate the count for each SearchPane row
- *
- * This method overrides _getMessage() in SearchPane and is overridden by SearchPaneCascadeViewTotal
- *
- * @param row The row object that is being processed
- * @returns string - the message that is to be shown for the count of each entry
- */
- SearchPaneCascade.prototype._getMessage = function (row) {
- return this.s.dt.i18n('searchPanes.count', this.c.i18n.count)
- .replace(/{total}/g, row.total)
- .replace(/{shown}/g, row.shown);
- };
- /**
- * Overrides the blank method in SearchPane to return the number of times a given value is currently being displayed
- *
- * @param filter The filter value
- * @returns number - The number of times the value is shown
- */
- SearchPaneCascade.prototype._getShown = function (filter) {
- return this.s.rowData.binsShown && this.s.rowData.binsShown[filter] ?
- this.s.rowData.binsShown[filter] :
- 0;
- };
- /**
- * Decides if a row should be added when being added from the server
- *
- * Overrides method in by SearchPaneST
- *
- * @param data the row data
- * @returns boolean indicating if the row should be added or not
- */
- SearchPaneCascade.prototype._shouldAddRow = function (data) {
- return data.shown > 0;
- };
- return SearchPaneCascade;
- }(SearchPaneST));
- var __extends$1 = (window && window.__extends) || (function () {
- var extendStatics = function (d, b) {
- extendStatics = Object.setPrototypeOf ||
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
- function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
- return extendStatics(d, b);
- };
- return function (d, b) {
- extendStatics(d, b);
- function __() { this.constructor = d; }
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
- };
- })();
- var $$2;
- function setJQuery$1(jq) {
- $$2 = jq;
- }
- var SearchPaneCascadeViewTotal = /** @class */ (function (_super) {
- __extends$1(SearchPaneCascadeViewTotal, _super);
- function SearchPaneCascadeViewTotal(paneSettings, opts, index, panesContainer, panes) {
- var _this = this;
- var override = {
- i18n: {
- count: '{total}',
- countFiltered: '{shown} ({total})'
- }
- };
- _this = _super.call(this, paneSettings, $$2.extend(override, opts), index, panesContainer, panes) || this;
- return _this;
- }
- /**
- * Fill the array with the values that are currently being displayed in the table
- *
- * This method overrides _activePopulatePane() in SearchPaneCascade
- */
- SearchPaneCascadeViewTotal.prototype._activePopulatePane = function () {
- this.s.rowData.arrayFilter = [];
- this.s.rowData.binsShown = {};
- var settings = this.s.dt.settings()[0];
- if (!this.s.dt.page.info().serverSide) {
- for (var _i = 0, _a = this.s.dt.rows({ search: 'applied' }).indexes().toArray(); _i < _a.length; _i++) {
- var index = _a[_i];
- this._populatePaneArray(index, this.s.rowData.arrayFilter, settings, this.s.rowData.binsShown);
- }
- }
- };
- /**
- * Gets the message that is to be used to indicate the count for each SearchPane row
- *
- * This method overrides _getMessage() in SearchPaneCascade
- *
- * @param row The row object that is being processed
- * @returns string - the message that is to be shown for the count of each entry
- */
- SearchPaneCascadeViewTotal.prototype._getMessage = function (row) {
- var countMessage = this.s.dt.i18n('searchPanes.count', this.c.i18n.count);
- var filteredMessage = this.s.dt.i18n('searchPanes.countFiltered', this.c.i18n.countFiltered);
- return (this.s.filteringActive ? filteredMessage : countMessage)
- .replace(/{total}/g, row.total)
- .replace(/{shown}/g, row.shown);
- };
- return SearchPaneCascadeViewTotal;
- }(SearchPaneCascade));
- var $$1;
- var dataTable$1;
- function setJQuery(jq) {
- $$1 = jq;
- dataTable$1 = jq.fn.dataTable;
- }
- var SearchPanes = /** @class */ (function () {
- function SearchPanes(paneSettings, opts, fromPreInit, paneClass) {
- var _this = this;
- if (fromPreInit === void 0) { fromPreInit = false; }
- if (paneClass === void 0) { paneClass = SearchPane; }
- // Check that the required version of DataTables is included
- if (!dataTable$1 || !dataTable$1.versionCheck || !dataTable$1.versionCheck('1.10.0')) {
- throw new Error('SearchPane requires DataTables 1.10 or newer');
- }
- // Check that Select is included
- // eslint-disable-next-line no-extra-parens
- if (!dataTable$1.select) {
- throw new Error('SearchPane requires Select');
- }
- var table = new dataTable$1.Api(paneSettings);
- this.classes = $$1.extend(true, {}, SearchPanes.classes);
- // Get options from user
- this.c = $$1.extend(true, {}, SearchPanes.defaults, opts);
- // Add extra elements to DOM object including clear
- this.dom = {
- clearAll: $$1('<button type="button"/>')
- .addClass(this.classes.clearAll)
- .html(table.i18n('searchPanes.clearMessage', this.c.i18n.clearMessage)),
- collapseAll: $$1('<button type="button"/>')
- .addClass(this.classes.collapseAll)
- .html(table.i18n('searchPanes.collapseMessage', this.c.i18n.collapseMessage)),
- container: $$1('<div/>').addClass(this.classes.panes).html(table.i18n('searchPanes.loadMessage', this.c.i18n.loadMessage)),
- emptyMessage: $$1('<div/>').addClass(this.classes.emptyMessage),
- panes: $$1('<div/>').addClass(this.classes.container),
- showAll: $$1('<button type="button"/>')
- .addClass(this.classes.showAll)
- .addClass(this.classes.disabledButton)
- .attr('disabled', 'true')
- .html(table.i18n('searchPanes.showMessage', this.c.i18n.showMessage)),
- title: $$1('<div/>').addClass(this.classes.title),
- titleRow: $$1('<div/>').addClass(this.classes.titleRow)
- };
- this.s = {
- colOpts: [],
- dt: table,
- filterCount: 0,
- minPaneWidth: 260.0,
- page: 0,
- paging: false,
- pagingST: false,
- paneClass: paneClass,
- panes: [],
- selectionList: [],
- serverData: {},
- stateRead: false,
- updating: false
- };
- // Do not reinitialise if already initialised on table
- if (table.settings()[0]._searchPanes) {
- return;
- }
- // When the panes update, we check it the clear buttons needs to be updated
- $$1(document).on('draw.dt', function (e) {
- if (_this.dom.container.find(e.target).length) {
- _this._updateFilterCount();
- }
- });
- this._getState();
- if (this.s.dt.page.info().serverSide) {
- var hostSettings = this.s.dt.settings()[0];
- // Listener to get the data into the server request before it is made
- this.s.dt.on('preXhr.dtsps', function (e, settings, data) {
- if (hostSettings !== settings) {
- return;
- }
- if (data.searchPanes === undefined) {
- data.searchPanes = {};
- }
- if (data.searchPanes_null === undefined) {
- data.searchPanes_null = {};
- }
- var src;
- for (var _i = 0, _a = _this.s.selectionList; _i < _a.length; _i++) {
- var selection = _a[_i];
- src = _this.s.dt.column(selection.column).dataSrc();
- if (data.searchPanes[src] === undefined) {
- data.searchPanes[src] = {};
- }
- if (data.searchPanes_null[src] === undefined) {
- data.searchPanes_null[src] = {};
- }
- for (var i = 0; i < selection.rows.length; i++) {
- data.searchPanes[src][i] = selection.rows[i];
- if (data.searchPanes[src][i] === null) {
- data.searchPanes_null[src][i] = true;
- }
- else {
- data.searchPanes_null[src][i] = false;
- }
- }
- }
- if (_this.s.selectionList.length > 0) {
- data.searchPanesLast = src;
- }
- // Config options that will change how the querying is done
- data.searchPanes_options = {
- cascade: _this.c.cascadePanes,
- viewCount: _this.c.viewCount,
- viewTotal: _this.c.viewTotal
- };
- });
- }
- this._setXHR();
- table.settings()[0]._searchPanes = this;
- if (this.s.dt.settings()[0]._bInitComplete || fromPreInit) {
- this._paneDeclare(table, paneSettings, opts);
- }
- else {
- table.one('preInit.dtsps', function () {
- _this._paneDeclare(table, paneSettings, opts);
- });
- }
- return this;
- }
- /**
- * Clear the selections of all of the panes
- */
- SearchPanes.prototype.clearSelections = function () {
- var pane;
- for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
- pane = _a[_i];
- if (pane.s.dtPane) {
- pane.s.scrollTop = pane.s.dtPane.table().node().parentNode.scrollTop;
- }
- }
- // Load in all of the searchBoxes in the documents
- var searches = this.dom.container.find('.' + this.classes.search.replace(/\s+/g, '.'));
- // For each searchBox set the input text to be empty and then trigger
- // an input on them so that they no longer filter the panes
- searches.each(function () {
- $$1(this).val('').trigger('input');
- });
- // Clear the selectionList
- this.s.selectionList = [];
- var returnArray = [];
- for (var _b = 0, _c = this.s.panes; _b < _c.length; _b++) {
- pane = _c[_b];
- if (pane.s.dtPane) {
- returnArray.push(pane.clearPane());
- }
- }
- return returnArray;
- };
- /**
- * returns the container node for the searchPanes
- */
- SearchPanes.prototype.getNode = function () {
- return this.dom.container;
- };
- /**
- * rebuilds all of the panes
- */
- SearchPanes.prototype.rebuild = function (targetIdx, maintainSelection) {
- if (targetIdx === void 0) { targetIdx = false; }
- if (maintainSelection === void 0) { maintainSelection = false; }
- this.dom.emptyMessage.detach();
- // As a rebuild from scratch is required, empty the searchpanes container.
- if (targetIdx === false) {
- this.dom.panes.empty();
- }
- // Rebuild each pane individually, if a specific pane has been selected then only rebuild that one
- var returnArray = [];
- for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
- var pane = _a[_i];
- if (targetIdx === false || pane.s.index === targetIdx) {
- pane.clearData();
- pane.rebuildPane(this.s.dt.page.info().serverSide ?
- this.s.serverData :
- undefined, maintainSelection);
- this.dom.panes.append(pane.dom.container);
- returnArray.push(pane);
- }
- }
- this._updateSelection();
- // Attach panes, clear buttons, and title bar to the document
- this._updateFilterCount();
- this._attachPaneContainer();
- this._initSelectionListeners(false);
- // If the selections are to be maintained, then it is safe to assume that paging is also to be maintained
- // Otherwise, the paging should be reset
- this.s.dt.draw(!maintainSelection);
- // Resize the panes incase there has been a change
- this.resizePanes();
- // If a single pane has been rebuilt then return only that pane
- return returnArray.length === 1 ? returnArray[0] : returnArray;
- };
- /**
- * Resizes all of the panes
- */
- SearchPanes.prototype.resizePanes = function () {
- var pane;
- if (this.c.layout === 'auto') {
- var contWidth = $$1(this.s.dt.searchPanes.container()).width();
- var target = Math.floor(contWidth / this.s.minPaneWidth); // The neatest number of panes per row
- var highest_1 = 1;
- var highestmod_1 = 0;
- // Get the indexes of all of the displayed panes
- var dispIndex = [];
- for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
- pane = _a[_i];
- if (pane.s.displayed) {
- dispIndex.push(pane.s.index);
- }
- }
- var displayCount = dispIndex.length;
- // If the neatest number is the number we have then use this.
- if (target === displayCount) {
- highest_1 = target;
- }
- else {
- // Go from the target down and find the value with the most panes left over, this will be the best fit
- for (var ppr = target; ppr > 1; ppr--) {
- var rem = displayCount % ppr;
- if (rem === 0) {
- highest_1 = ppr;
- highestmod_1 = 0;
- break;
- }
- // If there are more left over at this amount of panes per row (ppr)
- // then it fits better so new values
- else if (rem > highestmod_1) {
- highest_1 = ppr;
- highestmod_1 = rem;
- }
- }
- }
- // If there is a perfect fit then none are to be wider
- var widerIndexes_1 = highestmod_1 !== 0 ? dispIndex.slice(dispIndex.length - highestmod_1, dispIndex.length) : [];
- this.s.panes.forEach(function (pane) {
- // Resize the pane with the new layout
- if (pane.s.displayed) {
- pane.resize('columns-' + (!widerIndexes_1.includes(pane.s.index) ? highest_1 : highestmod_1));
- }
- });
- }
- else {
- for (var _b = 0, _c = this.s.panes; _b < _c.length; _b++) {
- pane = _c[_b];
- pane.adjustTopRow();
- }
- }
- return this;
- };
- /**
- * Holder method that is userd in SearchPanesST to set listeners that have an effect on other panes
- *
- * @param isPreselect boolean to indicate if the preselect array is to override the current selection list.
- */
- SearchPanes.prototype._initSelectionListeners = function (isPreselect) {
- return;
- };
- /**
- * Blank method that is overridden in SearchPanesST to retrieve the totals from the server data
- */
- SearchPanes.prototype._serverTotals = function () {
- return;
- };
- /**
- * Set's the xhr listener so that SP can extact appropriate data from the response
- */
- SearchPanes.prototype._setXHR = function () {
- var _this = this;
- var hostSettings = this.s.dt.settings()[0];
- var run = function (json) {
- if (json && json.searchPanes && json.searchPanes.options) {
- _this.s.serverData = json;
- _this.s.serverData.tableLength = json.recordsTotal;
- _this._serverTotals();
- }
- };
- // We are using the xhr event to rebuild the panes if required due to viewTotal being enabled
- // If viewTotal is not enabled then we simply update the data from the server
- this.s.dt.on('xhr.dtsps', function (e, settings, json) {
- if (hostSettings === settings) {
- run(json);
- }
- });
- // Account for the initial JSON fetch having already completed
- run(this.s.dt.ajax.json());
- };
- /**
- * Set's the function that is to be performed when a state is loaded
- *
- * Overridden by the method in SearchPanesST
- */
- SearchPanes.prototype._stateLoadListener = function () {
- var _this = this;
- var hostSettings = this.s.dt.settings()[0];
- this.s.dt.on('stateLoadParams.dtsps', function (e, settings, data) {
- if (data.searchPanes === undefined || settings !== hostSettings) {
- return;
- }
- _this.clearSelections();
- // Set the selection list for the panes so that the correct
- // rows can be reselected and in the right order
- _this.s.selectionList =
- data.searchPanes.selectionList ?
- data.searchPanes.selectionList :
- [];
- // Find the panes that match from the state and the actual instance
- if (data.searchPanes.panes) {
- for (var _i = 0, _a = data.searchPanes.panes; _i < _a.length; _i++) {
- var loadedPane = _a[_i];
- for (var _b = 0, _c = _this.s.panes; _b < _c.length; _b++) {
- var pane = _c[_b];
- if (loadedPane.id === pane.s.index && pane.s.dtPane) {
- // Set the value of the searchbox
- pane.dom.searchBox.val(loadedPane.searchTerm);
- // Set the value of the order
- pane.s.dtPane.order(loadedPane.order);
- }
- }
- }
- }
- _this._makeSelections(_this.s.selectionList);
- });
- };
- /**
- * Updates the selectionList when cascade is not in place
- *
- * Overridden in SearchPanesST
- */
- SearchPanes.prototype._updateSelection = function () {
- this.s.selectionList = [];
- for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
- var pane = _a[_i];
- if (pane.s.dtPane) {
- var rows = pane.s.dtPane.rows({ selected: true }).data().toArray().map(function (el) { return el.filter; });
- if (rows.length) {
- this.s.selectionList.push({
- column: pane.s.index,
- rows: rows
- });
- }
- }
- }
- };
- /**
- * Attach the panes, buttons and title to the document
- */
- SearchPanes.prototype._attach = function () {
- var _this = this;
- this.dom.titleRow
- .removeClass(this.classes.hide)
- .detach()
- .append(this.dom.title);
- // If the clear button is permitted attach it
- if (this.c.clear) {
- this.dom.clearAll
- .appendTo(this.dom.titleRow)
- .off('click.dtsps')
- .on('click.dtsps', function () { return _this.clearSelections(); });
- }
- if (this.c.collapse) {
- this.dom.showAll.appendTo(this.dom.titleRow);
- this.dom.collapseAll.appendTo(this.dom.titleRow);
- this._setCollapseListener();
- }
- // Attach the container for each individual pane to the overall container
- for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
- var pane = _a[_i];
- this.dom.panes.append(pane.dom.container);
- }
- // Attach everything to the document
- this.dom.container
- .text('')
- .removeClass(this.classes.hide)
- .append(this.dom.titleRow)
- .append(this.dom.panes);
- // WORKAROUND
- this.s.panes.forEach(function (pane) { return pane.setListeners(); });
- if ($$1('div.' + this.classes.container).length === 0) {
- this.dom.container.prependTo(this.s.dt);
- }
- };
- /**
- * If there are no panes to display then this method is called to either
- * display a message in their place or hide them completely.
- */
- SearchPanes.prototype._attachMessage = function () {
- // Create a message to display on the screen
- var message;
- try {
- message = this.s.dt.i18n('searchPanes.emptyPanes', this.c.i18n.emptyPanes);
- }
- catch (error) {
- message = null;
- }
- // If the message is an empty string then searchPanes.emptyPanes is undefined,
- // therefore the pane container should be removed from the display
- if (message === null) {
- this.dom.container.addClass(this.classes.hide);
- this.dom.titleRow.removeClass(this.classes.hide);
- return;
- }
- // Otherwise display the message
- this.dom.container.removeClass(this.classes.hide);
- this.dom.titleRow.addClass(this.classes.hide);
- this.dom.emptyMessage.html(message).appendTo(this.dom.container);
- };
- /**
- * Attaches the panes to the document and displays a message or hides if there are none
- */
- SearchPanes.prototype._attachPaneContainer = function () {
- // If a pane is to be displayed then attach the normal pane output
- for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
- var pane = _a[_i];
- if (pane.s.displayed === true) {
- this._attach();
- return;
- }
- }
- // Otherwise attach the custom message or remove the container from the display
- this._attachMessage();
- };
- /**
- * Checks which panes are collapsed and then performs relevant actions to the collapse/show all buttons
- */
- SearchPanes.prototype._checkCollapse = function () {
- var disableClose = true;
- var disableShow = true;
- for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
- var pane = _a[_i];
- if (pane.s.displayed) {
- // If the pane is not collapsed
- if (!pane.dom.collapseButton.hasClass(pane.classes.rotated)) {
- // Enable the collapse all button
- this.dom.collapseAll.removeClass(this.classes.disabledButton).removeAttr('disabled');
- disableClose = false;
- }
- else {
- // Otherwise enable the show all button
- this.dom.showAll.removeClass(this.classes.disabledButton).removeAttr('disabled');
- disableShow = false;
- }
- }
- }
- // If this flag is still true, no panes are open so the close button should be disabled
- if (disableClose) {
- this.dom.collapseAll.addClass(this.classes.disabledButton).attr('disabled', 'true');
- }
- // If this flag is still true, no panes are closed so the show button should be disabled
- if (disableShow) {
- this.dom.showAll.addClass(this.classes.disabledButton).attr('disabled', 'true');
- }
- };
- /**
- * Attaches the message to the document but does not add any panes
- */
- SearchPanes.prototype._checkMessage = function () {
- // If a pane is to be displayed then attach the normal pane output
- for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
- var pane = _a[_i];
- if (pane.s.displayed === true) {
- // Ensure that the empty message is removed if a pane is displayed
- this.dom.emptyMessage.detach();
- this.dom.titleRow.removeClass(this.classes.hide);
- return;
- }
- }
- // Otherwise attach the custom message or remove the container from the display
- this._attachMessage();
- };
- /**
- * Collapses all of the panes
- */
- SearchPanes.prototype._collapseAll = function () {
- for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
- var pane = _a[_i];
- pane.collapse();
- }
- };
- /**
- * Finds a pane based upon the name of that pane
- *
- * @param name string representing the name of the pane
- * @returns SearchPane The pane which has that name
- */
- SearchPanes.prototype._findPane = function (name) {
- for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
- var pane = _a[_i];
- if (name === pane.s.name) {
- return pane;
- }
- }
- };
- /**
- * Gets the selection list from the previous state and stores it in the selectionList Property
- */
- SearchPanes.prototype._getState = function () {
- var loadedFilter = this.s.dt.state.loaded();
- if (loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.selectionList) {
- this.s.selectionList = loadedFilter.searchPanes.selectionList;
- }
- };
- SearchPanes.prototype._makeSelections = function (selectList) {
- for (var _i = 0, selectList_1 = selectList; _i < selectList_1.length; _i++) {
- var selection = selectList_1[_i];
- var pane = void 0;
- for (var _a = 0, _b = this.s.panes; _a < _b.length; _a++) {
- var p = _b[_a];
- if (p.s.index === selection.column) {
- pane = p;
- break;
- }
- }
- if (pane && pane.s.dtPane) {
- for (var j = 0; j < pane.s.dtPane.rows().data().toArray().length; j++) {
- if (selection.rows.includes(typeof pane.s.dtPane.row(j).data().filter === 'function' ?
- pane.s.dtPane.cell(j, 0).data() :
- pane.s.dtPane.row(j).data().filter)) {
- pane.s.dtPane.row(j).select();
- }
- }
- pane.updateTable();
- }
- }
- };
- /**
- * Declares the instances of individual searchpanes dependant on the number of columns.
- * It is necessary to run this once preInit has completed otherwise no panes will be
- * created as the column count will be 0.
- *
- * @param table the DataTable api for the parent table
- * @param paneSettings the settings passed into the constructor
- * @param opts the options passed into the constructor
- */
- SearchPanes.prototype._paneDeclare = function (table, paneSettings, opts) {
- var _this = this;
- // Create Panes
- table
- .columns(this.c.columns.length > 0 ? this.c.columns : undefined)
- .eq(0)
- .each(function (idx) {
- _this.s.panes.push(new _this.s.paneClass(paneSettings, opts, idx, _this.dom.panes));
- });
- // If there is any extra custom panes defined then create panes for them too
- var colCount = table.columns().eq(0).toArray().length;
- for (var i = 0; i < this.c.panes.length; i++) {
- var id = colCount + i;
- this.s.panes.push(new this.s.paneClass(paneSettings, opts, id, this.dom.panes, this.c.panes[i]));
- }
- // If a custom ordering is being used
- if (this.c.order.length > 0) {
- // Make a new Array of panes based upon the order
- this.s.panes = this.c.order.map(function (name) { return _this._findPane(name); });
- }
- // If this internal property is true then the DataTable has been initialised already
- if (this.s.dt.settings()[0]._bInitComplete) {
- this._startup(table);
- }
- else {
- // Otherwise add the paneStartup function to the list of functions
- // that are to be run when the table is initialised. This will garauntee that the
- // panes are initialised before the init event and init Complete callback is fired
- if (dataTable$1.versionCheck('2')) {
- this.s.dt.settings()[0].aoInitComplete.push(function () { return _this._startup(table); });
- }
- else {
- this.s.dt.settings()[0].aoInitComplete.push({
- fn: function () { return _this._startup(table); }
- });
- }
- }
- };
- /**
- * Sets the listeners for the collapse and show all buttons
- * Also sets and performs checks on current panes to see if they are collapsed
- */
- SearchPanes.prototype._setCollapseListener = function () {
- var _this = this;
- this.dom.collapseAll
- .off('click.dtsps')
- .on('click.dtsps', function () {
- _this._collapseAll();
- _this.dom.collapseAll.addClass(_this.classes.disabledButton).attr('disabled', 'true');
- _this.dom.showAll.removeClass(_this.classes.disabledButton).removeAttr('disabled');
- _this.s.dt.state.save();
- });
- this.dom.showAll
- .off('click.dtsps')
- .on('click.dtsps', function () {
- _this._showAll();
- _this.dom.showAll.addClass(_this.classes.disabledButton).attr('disabled', 'true');
- _this.dom.collapseAll.removeClass(_this.classes.disabledButton).removeAttr('disabled');
- _this.s.dt.state.save();
- });
- for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
- var pane = _a[_i];
- // We want to make the same check whenever there is a collapse/expand
- pane.dom.topRow.off('collapse.dtsps').on('collapse.dtsps', function () { return _this._checkCollapse(); });
- }
- this._checkCollapse();
- };
- /**
- * Shows all of the panes
- */
- SearchPanes.prototype._showAll = function () {
- for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
- var pane = _a[_i];
- pane.show();
- }
- };
- /**
- * Initialises the tables previous/preset selections and initialises callbacks for events
- *
- * @param table the parent table for which the searchPanes are being created
- */
- SearchPanes.prototype._startup = function (table) {
- var _this = this;
- // Attach clear button and title bar to the document
- this._attach();
- this.dom.panes.empty();
- var hostSettings = this.s.dt.settings()[0];
- for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
- var pane = _a[_i];
- pane.rebuildPane(Object.keys(this.s.serverData).length > 0 ? this.s.serverData : undefined);
- this.dom.panes.append(pane.dom.container);
- }
- // If the layout is set to auto then the panes need to be resized to their best fit
- if (this.c.layout === 'auto') {
- this.resizePanes();
- }
- var loadedFilter = this.s.dt.state.loaded();
- // Reset the paging if that has been saved in the state
- if (!this.s.stateRead && loadedFilter) {
- this.s.dt
- .page(loadedFilter.start / this.s.dt.page.len())
- .draw('page');
- }
- this.s.stateRead = true;
- this._checkMessage();
- // When a draw is called on the DataTable, update all of the panes incase the data in the DataTable has changed
- table.on('preDraw.dtsps', function () {
- // Check that the panes are not updating to avoid infinite loops
- // Also check that this draw is not due to paging
- if (!_this.s.updating && !_this.s.paging) {
- _this._updateFilterCount();
- _this._updateSelection();
- }
- // Paging flag reset - we only need to dodge the draw once
- _this.s.paging = false;
- });
- $$1(window).on('resize.dtsps', dataTable$1.util.throttle(function () { return _this.resizePanes(); }));
- // Whenever a state save occurs store the selection list in the state object
- this.s.dt.on('stateSaveParams.dtsps', function (e, settings, data) {
- if (settings !== hostSettings) {
- return;
- }
- if (data.searchPanes === undefined) {
- data.searchPanes = {};
- }
- data.searchPanes.selectionList = _this.s.selectionList;
- });
- this._stateLoadListener();
- // Listener for paging on main table
- table.off('page.dtsps page-nc.dtsps').on('page.dtsps page-nc.dtsps', function (e, s) {
- _this.s.paging = true;
- // This is an indicator to any selection tracking classes that paging has occured
- // It has to happen here so that we don't stack event listeners unnecessarily
- // The value is only ever set back to false in the SearchPanesST class
- // Equally it is never read in this class
- _this.s.pagingST = true;
- _this.s.page = _this.s.dt.page();
- });
- if (this.s.dt.page.info().serverSide) {
- table.off('preXhr.dtsps').on('preXhr.dtsps', function (e, settings, data) {
- if (settings !== hostSettings) {
- return;
- }
- if (!data.searchPanes) {
- data.searchPanes = {};
- }
- if (!data.searchPanes_null) {
- data.searchPanes_null = {};
- }
- // Count how many filters are being applied
- var filterCount = 0;
- for (var _i = 0, _a = _this.s.panes; _i < _a.length; _i++) {
- var pane = _a[_i];
- var src = _this.s.dt.column(pane.s.index).dataSrc();
- if (!data.searchPanes[src]) {
- data.searchPanes[src] = {};
- }
- if (!data.searchPanes_null[src]) {
- data.searchPanes_null[src] = {};
- }
- if (pane.s.dtPane) {
- var rowData = pane.s.dtPane.rows({ selected: true }).data().toArray();
- for (var i = 0; i < rowData.length; i++) {
- data.searchPanes[src][i] = rowData[i].filter;
- if (!data.searchPanes[src][i]) {
- data.searchPanes_null[src][i] = true;
- }
- else {
- data.searchPanes_null[src][i] = false;
- }
- filterCount++;
- }
- }
- }
- // If there is a filter to be applied, then we need to read from the start of the result set
- // and set the paging to 0. This matches the behaviour of client side processing
- if (filterCount > 0) {
- // If the number of filters has changed we need to read from the start of the
- // result set and reset the paging
- if (filterCount !== _this.s.filterCount) {
- data.start = 0;
- _this.s.page = 0;
- }
- // Otherwise it is a paging request and we need to read from whatever the paging has been set to
- else {
- data.start = _this.s.page * _this.s.dt.page.len();
- }
- _this.s.dt.page(_this.s.page);
- _this.s.filterCount = filterCount;
- }
- if (_this.s.selectionList.length > 0) {
- data.searchPanesLast = _this.s.dt
- .column(_this.s.selectionList[_this.s.selectionList.length - 1].column)
- .dataSrc();
- }
- // Config options that will change how the querying is done
- data.searchPanes_options = {
- cascade: _this.c.cascadePanes,
- viewCount: _this.c.viewCount,
- viewTotal: _this.c.viewTotal
- };
- });
- }
- else {
- table.on('preXhr.dtsps', function () { return _this.s.panes.forEach(function (pane) { return pane.clearData(); }); });
- }
- // If the data is reloaded from the server then it is possible that it has changed completely,
- // so we need to rebuild the panes
- this.s.dt.on('xhr.dtsps', function (e, settings) {
- if (settings.nTable !== _this.s.dt.table().node()) {
- return;
- }
- if (!_this.s.dt.page.info().serverSide) {
- var processing_1 = false;
- _this.s.dt.one('preDraw.dtsps', function () {
- if (processing_1) {
- return;
- }
- var page = _this.s.dt.page();
- processing_1 = true;
- _this.s.updating = true;
- _this.dom.panes.empty();
- for (var _i = 0, _a = _this.s.panes; _i < _a.length; _i++) {
- var pane = _a[_i];
- pane.clearData(); // Clears all of the bins and will mean that the data has to be re-read
- // Pass a boolean to say whether this is the last choice made for maintaining selections
- // when rebuilding
- pane.rebuildPane(undefined, true);
- _this.dom.panes.append(pane.dom.container);
- }
- if (!_this.s.dt.page.info().serverSide) {
- _this.s.dt.draw();
- }
- _this.s.updating = false;
- _this._updateSelection();
- _this._checkMessage();
- _this.s.dt.one('draw.dtsps', function () {
- _this.s.updating = true;
- _this.s.dt.page(page).draw(false);
- _this.s.updating = false;
- });
- });
- }
- });
- // PreSelect any selections which have been defined using the preSelect option
- var selectList = this.c.preSelect;
- if (loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.selectionList) {
- selectList = loadedFilter.searchPanes.selectionList;
- }
- this._makeSelections(selectList);
- // Update the title bar to show how many filters have been selected
- this._updateFilterCount();
- // If the table is destroyed and restarted then clear the selections so that they do not persist.
- table.on('destroy.dtsps', function (e, settings) {
- if (settings !== hostSettings) {
- return;
- }
- for (var _i = 0, _a = _this.s.panes; _i < _a.length; _i++) {
- var pane = _a[_i];
- pane.destroy();
- }
- table.off('.dtsps');
- _this.dom.showAll.off('.dtsps');
- _this.dom.clearAll.off('.dtsps');
- _this.dom.collapseAll.off('.dtsps');
- $$1(table.table().node()).off('.dtsps');
- _this.dom.container.detach();
- _this.clearSelections();
- });
- if (this.c.collapse) {
- this._setCollapseListener();
- }
- // When the clear All button has been pressed clear all of the selections in the panes
- if (this.c.clear) {
- this.dom.clearAll
- .off('click.dtsps')
- .on('click.dtsps', function () { return _this.clearSelections(); });
- }
- hostSettings._searchPanes = this;
- // This state save is required so that state is maintained over multiple refreshes if no actions are made
- this.s.dt.state.save();
- };
- /**
- * Updates the number of filters that have been applied in the title
- */
- SearchPanes.prototype._updateFilterCount = function () {
- var filterCount = 0;
- var tableSearch = 0;
- // Add the number of all of the filters throughout the panes
- for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
- var pane = _a[_i];
- if (pane.s.dtPane) {
- filterCount += pane.getPaneCount();
- if (pane.s.dtPane.search()) {
- tableSearch++;
- }
- }
- }
- // Run the message through the internationalisation method to improve readability
- this.dom.title.html(this.s.dt.i18n('searchPanes.title', this.c.i18n.title, filterCount));
- if (this.c.filterChanged && typeof this.c.filterChanged === 'function') {
- this.c.filterChanged.call(this.s.dt, filterCount);
- }
- if (filterCount === 0 && tableSearch === 0) {
- this.dom.clearAll.addClass(this.classes.disabledButton).attr('disabled', 'true');
- }
- else {
- this.dom.clearAll.removeClass(this.classes.disabledButton).removeAttr('disabled');
- }
- };
- SearchPanes.version = '2.3.1';
- SearchPanes.classes = {
- clear: 'dtsp-clear',
- clearAll: 'dtsp-clearAll',
- collapseAll: 'dtsp-collapseAll',
- container: 'dtsp-searchPanes',
- disabledButton: 'dtsp-disabledButton',
- emptyMessage: 'dtsp-emptyMessage',
- hide: 'dtsp-hidden',
- panes: 'dtsp-panesContainer',
- search: 'dtsp-search',
- showAll: 'dtsp-showAll',
- title: 'dtsp-title',
- titleRow: 'dtsp-titleRow'
- };
- // Define SearchPanes default options
- SearchPanes.defaults = {
- cascadePanes: false,
- clear: true,
- collapse: true,
- columns: [],
- container: function (dt) {
- return dt.table().container();
- },
- filterChanged: undefined,
- i18n: {
- clearMessage: 'Clear All',
- clearPane: '×',
- collapse: {
- 0: 'SearchPanes',
- _: 'SearchPanes (%d)'
- },
- collapseMessage: 'Collapse All',
- count: '{total}',
- emptyMessage: '<em>No data</em>',
- emptyPanes: 'No SearchPanes',
- loadMessage: 'Loading Search Panes...',
- showMessage: 'Show All',
- title: 'Filters Active - %d'
- },
- layout: 'auto',
- order: [],
- panes: [],
- preSelect: [],
- viewCount: true,
- viewTotal: false
- };
- return SearchPanes;
- }());
- var __extends = (window && window.__extends) || (function () {
- var extendStatics = function (d, b) {
- extendStatics = Object.setPrototypeOf ||
- ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
- function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
- return extendStatics(d, b);
- };
- return function (d, b) {
- extendStatics(d, b);
- function __() { this.constructor = d; }
- d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
- };
- })();
- var SearchPanesST = /** @class */ (function (_super) {
- __extends(SearchPanesST, _super);
- function SearchPanesST(paneSettings, opts, fromPreInit) {
- if (fromPreInit === void 0) { fromPreInit = false; }
- var _this = this;
- var paneClass;
- if (opts.cascadePanes && opts.viewTotal) {
- paneClass = SearchPaneCascadeViewTotal;
- }
- else if (opts.cascadePanes) {
- paneClass = SearchPaneCascade;
- }
- else if (opts.viewTotal) {
- paneClass = SearchPaneViewTotal;
- }
- _this = _super.call(this, paneSettings, opts, fromPreInit, paneClass) || this;
- var dt = _this.s.dt;
- var loadedFilter = dt.state.loaded();
- var loadFn = function () { return _this._initSelectionListeners(true, loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.selectionList ?
- loadedFilter.searchPanes.selectionList :
- _this.c.preSelect); };
- if (dt.settings()[0]._bInitComplete) {
- loadFn();
- }
- else {
- dt.off('init.dtsps').on('init.dtsps', loadFn);
- }
- return _this;
- }
- /**
- * Ensures that the correct selection listeners are set for selection tracking
- *
- * @param preSelect Any values that are to be preselected
- */
- SearchPanesST.prototype._initSelectionListeners = function (isPreselect, preSelect) {
- if (isPreselect === void 0) { isPreselect = true; }
- if (preSelect === void 0) { preSelect = []; }
- if (isPreselect) {
- this.s.selectionList = preSelect;
- }
- // Set selection listeners for each pane
- for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
- var pane = _a[_i];
- if (pane.s.displayed) {
- pane.s.dtPane
- .off('select.dtsp')
- .on('select.dtsp', this._update(pane))
- .off('deselect.dtsp')
- .on('deselect.dtsp', this._updateTimeout(pane));
- }
- }
- // Update on every draw
- this.s.dt.off('draw.dtsps').on('draw.dtsps', this._update());
- // Also update right now as table has just initialised
- this._updateSelectionList();
- };
- /**
- * Retrieve the total values from the server data
- */
- SearchPanesST.prototype._serverTotals = function () {
- for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
- var pane = _a[_i];
- if (pane.s.colOpts.show) {
- var colTitle = this.s.dt.column(pane.s.index).dataSrc();
- var blockVT = true;
- // If any of the counts are not equal to the totals filtering must be active
- if (this.s.serverData.searchPanes.options[colTitle]) {
- for (var _b = 0, _c = this.s.serverData.searchPanes.options[colTitle]; _b < _c.length; _b++) {
- var data = _c[_b];
- if (data.total !== data.count) {
- blockVT = false;
- break;
- }
- }
- }
- // Set if filtering is present on the pane and populate the data arrays
- pane.s.filteringActive = !blockVT;
- pane._serverPopulate(this.s.serverData);
- }
- }
- };
- /**
- * Set's the function that is to be performed when a state is loaded
- *
- * Overrides the method in SearchPanes
- */
- SearchPanesST.prototype._stateLoadListener = function () {
- var _this = this;
- var stateLoadFunction = function (e, settings, data) {
- if (data.searchPanes === undefined) {
- return;
- }
- // Set the selection list for the panes so that the correct
- // rows can be reselected and in the right order
- _this.s.selectionList =
- data.searchPanes.selectionList ?
- data.searchPanes.selectionList :
- [];
- // Find the panes that match from the state and the actual instance
- if (data.searchPanes.panes) {
- for (var _i = 0, _a = data.searchPanes.panes; _i < _a.length; _i++) {
- var loadedPane = _a[_i];
- for (var _b = 0, _c = _this.s.panes; _b < _c.length; _b++) {
- var pane = _c[_b];
- if (loadedPane.id === pane.s.index && pane.s.dtPane) {
- // Set the value of the searchbox
- pane.dom.searchBox.val(loadedPane.searchTerm);
- // Set the value of the order
- pane.s.dtPane.order(loadedPane.order);
- }
- }
- }
- }
- _this._updateSelectionList();
- };
- this.s.dt.off('stateLoadParams.dtsps', stateLoadFunction).on('stateLoadParams.dtsps', stateLoadFunction);
- };
- /**
- * Remove the function's actions when using cascade
- *
- * Overrides the method in SearchPanes
- */
- SearchPanesST.prototype._updateSelection = function () {
- return;
- };
- /**
- * Returns a function that updates the selection list based on a specific pane
- * Also clears the timeout to stop the deselect from running
- *
- * @param pane the pane that is to have it's selections loaded
- */
- SearchPanesST.prototype._update = function (pane) {
- var _this = this;
- if (pane === void 0) { pane = undefined; }
- return function () {
- if (pane) {
- clearTimeout(pane.s.deselectTimeout);
- }
- _this._updateSelectionList(pane);
- };
- };
- /**
- * Returns a function that updates the selection list based on a specific pane
- * Also sets a timeout incase a select is about to be made
- *
- * @param pane the pane that is to have it's selections loaded
- */
- SearchPanesST.prototype._updateTimeout = function (pane) {
- var _this = this;
- if (pane === void 0) { pane = undefined; }
- return function () { return pane ?
- pane.s.deselectTimeout = setTimeout(function () { return _this._updateSelectionList(pane); }, 50) :
- _this._updateSelectionList(); };
- };
- /**
- * Updates the selection list to include the latest selections for a given pane
- *
- * @param index The index of the pane that is to be updated
- * @param selected Which rows are selected within the pane
- */
- SearchPanesST.prototype._updateSelectionList = function (paneIn) {
- if (paneIn === void 0) { paneIn = undefined; }
- // Bail if any of these flags are set
- if (this.s.pagingST) {
- // Reset pagingST flag
- this.s.pagingST = false;
- return;
- }
- else if (this.s.updating || paneIn && paneIn.s.serverSelecting) {
- return;
- }
- if (paneIn !== undefined) {
- if (this.s.dt.page.info().serverSide) {
- paneIn._updateSelection();
- }
- // Get filter values for all of the rows and the selections
- var rows = paneIn.s.dtPane.rows({ selected: true }).data().toArray().map(function (el) { return el.filter; });
- this.s.selectionList = this.s.selectionList.filter(function (selection) { return selection.column !== paneIn.s.index; });
- if (rows.length > 0) {
- this.s.selectionList.push({
- column: paneIn.s.index,
- rows: rows
- });
- paneIn.dom.clear.removeClass(this.classes.disabledButton).removeAttr('disabled');
- }
- else {
- paneIn.dom.clear.addClass(this.classes.disabledButton).attr('disabled', 'true');
- }
- if (this.s.dt.page.info().serverSide) {
- this.s.dt.draw(false);
- }
- }
- this._remakeSelections();
- this._updateFilterCount();
- };
- /**
- * Remake the selections that were present before new data or calculations have occured
- */
- SearchPanesST.prototype._remakeSelections = function () {
- var currPane;
- var pane;
- this.s.updating = true;
- if (!this.s.dt.page.info().serverSide) {
- var tmpSL = this.s.selectionList;
- var anotherFilter = false;
- this.clearSelections();
- this.s.dt.draw(false);
- // When there are no selections present if the length of the data does not match the searched data
- // then another filter is present
- if (this.s.dt.rows().toArray()[0].length > this.s.dt.rows({ search: 'applied' }).toArray()[0].length) {
- anotherFilter = true;
- }
- this.s.selectionList = tmpSL;
- // Update the rows in each pane
- for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
- pane = _a[_i];
- if (pane.s.displayed) {
- pane.s.filteringActive = anotherFilter;
- pane.updateRows();
- }
- }
- for (var _b = 0, _c = this.s.selectionList; _b < _c.length; _b++) {
- var selection = _c[_b];
- pane = null;
- for (var _d = 0, _e = this.s.panes; _d < _e.length; _d++) {
- var paneCheck = _e[_d];
- if (paneCheck.s.index === selection.column) {
- pane = paneCheck;
- break;
- }
- }
- if (!pane.s.dtPane) {
- continue;
- }
- var ids = pane.s.dtPane.rows().indexes().toArray();
- // Select the rows that are present in the selection list
- for (var i = 0; i < selection.rows.length; i++) {
- var rowFound = false;
- for (var _f = 0, ids_1 = ids; _f < ids_1.length; _f++) {
- var id = ids_1[_f];
- var currRow = pane.s.dtPane.row(id);
- var data = currRow.data();
- if (selection.rows[i] === data.filter) {
- currRow.select();
- rowFound = true;
- }
- }
- if (!rowFound) {
- selection.rows.splice(i, 1);
- i--;
- }
- }
- pane.s.selections = selection.rows;
- // If there are no rows selected then don't bother continuing past here
- // Will just increase processing time and skew the rows that are shown in the table
- if (selection.rows.length === 0) {
- continue;
- }
- // Update the table to display the current results
- this.s.dt.draw();
- var filteringActive = false;
- var filterCount = 0;
- var prevSelectedPanes = 0;
- var selectedPanes = 0;
- // Add the number of all of the filters throughout the panes
- for (var _g = 0, _h = this.s.panes; _g < _h.length; _g++) {
- currPane = _h[_g];
- if (currPane.s.dtPane) {
- filterCount += currPane.getPaneCount();
- if (filterCount > prevSelectedPanes) {
- selectedPanes++;
- prevSelectedPanes = filterCount;
- }
- }
- }
- filteringActive = filterCount > 0;
- for (var _j = 0, _k = this.s.panes; _j < _k.length; _j++) {
- currPane = _k[_j];
- if (currPane.s.displayed) {
- // Set the filtering active flag
- if (anotherFilter || pane.s.index !== currPane.s.index || !filteringActive) {
- currPane.s.filteringActive = filteringActive || anotherFilter;
- }
- else if (selectedPanes === 1) {
- currPane.s.filteringActive = false;
- }
- // Update the rows to show correct counts
- if (currPane.s.index !== pane.s.index) {
- currPane.updateRows();
- }
- }
- }
- }
- // Update table to show final search results
- this.s.dt.draw(false);
- }
- else {
- // Identify the last pane to have a change in selection
- if (this.s.selectionList.length > 0) {
- pane = this.s.panes[this.s.selectionList[this.s.selectionList.length - 1].column];
- }
- // Update the rows of all of the other panes
- for (var _l = 0, _m = this.s.panes; _l < _m.length; _l++) {
- currPane = _m[_l];
- if (currPane.s.displayed && (!pane || currPane.s.index !== pane.s.index)) {
- currPane.updateRows();
- }
- }
- }
- this.s.updating = false;
- };
- return SearchPanesST;
- }(SearchPanes));
- /*! SearchPanes 2.3.1
- * © SpryMedia Ltd - datatables.net/license
- */
- setJQuery$4($);
- setJQuery($);
- setJQuery$3($);
- setJQuery$2($);
- setJQuery$1($);
- var dataTable = $.fn.dataTable;
- // eslint-disable-next-line no-extra-parens
- dataTable.SearchPanes = SearchPanes;
- // eslint-disable-next-line no-extra-parens
- DataTable.SearchPanes = SearchPanes;
- // eslint-disable-next-line no-extra-parens
- dataTable.SearchPanesST = SearchPanesST;
- // eslint-disable-next-line no-extra-parens
- DataTable.SearchPanesST = SearchPanesST;
- // eslint-disable-next-line no-extra-parens
- dataTable.SearchPane = SearchPane;
- // eslint-disable-next-line no-extra-parens
- DataTable.SearchPane = SearchPane;
- // eslint-disable-next-line no-extra-parens
- dataTable.SearchPaneViewTotal = SearchPaneViewTotal;
- // eslint-disable-next-line no-extra-parens
- DataTable.SearchPaneViewTotal = SearchPaneViewTotal;
- // eslint-disable-next-line no-extra-parens
- dataTable.SearchPaneCascade = SearchPaneCascade;
- // eslint-disable-next-line no-extra-parens
- DataTable.SearchPaneCascade = SearchPaneCascade;
- // eslint-disable-next-line no-extra-parens
- dataTable.SearchPaneCascadeViewTotal = SearchPaneCascadeViewTotal;
- // eslint-disable-next-line no-extra-parens
- DataTable.SearchPaneCascadeViewTotal = SearchPaneCascadeViewTotal;
- // eslint-disable-next-line no-extra-parens
- var apiRegister = $.fn.dataTable.Api.register;
- apiRegister('searchPanes()', function () {
- return this;
- });
- apiRegister('searchPanes.clearSelections()', function () {
- return this.iterator('table', function (ctx) {
- if (ctx._searchPanes) {
- ctx._searchPanes.clearSelections();
- }
- });
- });
- apiRegister('searchPanes.rebuildPane()', function (targetIdx, maintainSelections) {
- return this.iterator('table', function (ctx) {
- if (ctx._searchPanes) {
- ctx._searchPanes.rebuild(targetIdx, maintainSelections);
- }
- });
- });
- apiRegister('searchPanes.resizePanes()', function () {
- var ctx = this.context[0];
- return ctx._searchPanes ?
- ctx._searchPanes.resizePanes() :
- null;
- });
- apiRegister('searchPanes.container()', function () {
- var ctx = this.context[0];
- return ctx._searchPanes
- ? ctx._searchPanes.getNode()
- : null;
- });
- DataTable.ext.buttons.searchPanesClear = {
- action: function (e, dt) {
- dt.searchPanes.clearSelections();
- },
- text: 'Clear Panes'
- };
- DataTable.ext.buttons.searchPanes = {
- action: function (e, dt, node, config) {
- var _this = this;
- var that = this;
- if (!config._panes) {
- // No SearchPanes on this button yet - initialise and show
- this.processing(true);
- setTimeout(function () {
- _buttonSourced(dt, node, config);
- _this.popover(config._panes.getNode(), {
- align: 'container',
- span: 'container'
- });
- config._panes.rebuild(undefined, true);
- // Tables were hidden in the popover, need to be resized
- $('table.dataTable', config._panes.getNode()).DataTable().columns.adjust();
- that.processing(false);
- }, 10);
- }
- else {
- // Already got SP - show it
- this.popover(config._panes.getNode(), {
- align: 'container',
- span: 'container'
- });
- config._panes.rebuild(undefined, true);
- }
- },
- init: function (dt, node, config) {
- dt.button(node).text(config.text || dt.i18n('searchPanes.collapse', 'SearchPanes', 0));
- // For cases when we need to initialise the SearchPane immediately
- if (dt.init().stateSave || config.delayInit === false) {
- _buttonSourced(dt, node, config);
- }
- },
- config: {},
- text: '',
- delayInit: true
- };
- function _buttonSourced(dt, node, config) {
- var buttonOpts = $.extend({
- filterChanged: function (count) {
- dt.button(node).text(dt.i18n('searchPanes.collapse', dt.context[0].oLanguage.searchPanes !== undefined ?
- dt.context[0].oLanguage.searchPanes.collapse :
- dt.context[0]._searchPanes.c.i18n.collapse, count));
- }
- }, config.config);
- var panes = buttonOpts && (buttonOpts.cascadePanes || buttonOpts.viewTotal) ?
- new DataTable.SearchPanesST(dt, buttonOpts) :
- new DataTable.SearchPanes(dt, buttonOpts);
- dt.button(node).text(config.text || dt.i18n('searchPanes.collapse', panes.c.i18n.collapse, 0));
- config._panes = panes;
- }
- function _init(settings, options, fromPre) {
- if (options === void 0) { options = null; }
- if (fromPre === void 0) { fromPre = false; }
- var api = new dataTable.Api(settings);
- var opts = options
- ? options
- : api.init().searchPanes || dataTable.defaults.searchPanes;
- var searchPanes = opts && (opts.cascadePanes || opts.viewTotal) ?
- new SearchPanesST(api, opts, fromPre) :
- new SearchPanes(api, opts, fromPre);
- var node = searchPanes.getNode();
- return node;
- }
- // Attach a listener to the document which listens for DataTables initialisation
- // events so we can automatically initialise
- $(document).on('preInit.dt.dtsp', function (e, settings) {
- if (e.namespace !== 'dt') {
- return;
- }
- if (settings.oInit.searchPanes ||
- DataTable.defaults.searchPanes) {
- if (!settings._searchPanes) {
- _init(settings, null, true);
- }
- }
- });
- // DataTables `dom` feature option
- DataTable.ext.feature.push({
- cFeature: 'P',
- fnInit: _init
- });
- // DataTables 2 layout feature
- if (DataTable.feature) {
- DataTable.feature.register('searchPanes', _init);
- }
- })();
- return DataTable;
- }));
- /*! Bootstrap 5 integration for DataTables' SearchPanes
- * © SpryMedia Ltd - datatables.net/license
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net-bs5', 'datatables.net-searchpanes'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- var jq = require('jquery');
- var cjsRequires = function (root, $) {
- if ( ! $.fn.dataTable ) {
- require('datatables.net-bs5')(root, $);
- }
- if ( ! $.fn.dataTable.SearchPanes ) {
- require('datatables.net-searchpanes')(root, $);
- }
- };
- if (typeof window === 'undefined') {
- 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 ( ! $ ) {
- $ = jq( root );
- }
- cjsRequires( root, $ );
- return factory( $, root, root.document );
- };
- }
- else {
- cjsRequires( window, jq );
- module.exports = factory( jq, window, window.document );
- }
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- $.extend(true, DataTable.SearchPane.classes, {
- buttonGroup: 'btn-group',
- disabledButton: 'disabled',
- narrow: 'col',
- pane: {
- container: 'table'
- },
- paneButton: 'btn btn-subtle',
- pill: 'badge rounded-pill bg-secondary',
- search: 'form-control search',
- table: 'table table-sm table-borderless',
- topRow: 'dtsp-topRow'
- });
- $.extend(true, DataTable.SearchPanes.classes, {
- clearAll: 'dtsp-clearAll btn btn-subtle',
- collapseAll: 'dtsp-collapseAll btn btn-subtle',
- container: 'dtsp-searchPanes',
- disabledButton: 'disabled',
- panes: 'dtsp-panes dtsp-panesContainer',
- search: DataTable.SearchPane.classes.search,
- showAll: 'dtsp-showAll btn btn-subtle',
- title: 'dtsp-title',
- titleRow: 'dtsp-titleRow'
- });
- return DataTable;
- }));
- /*! Select for DataTables 2.0.1
- * © SpryMedia Ltd - datatables.net/license/mit
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- var jq = require('jquery');
- var cjsRequires = function (root, $) {
- if ( ! $.fn.dataTable ) {
- require('datatables.net')(root, $);
- }
- };
- if (typeof window === 'undefined') {
- 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 ( ! $ ) {
- $ = jq( root );
- }
- cjsRequires( root, $ );
- return factory( $, root, root.document );
- };
- }
- else {
- cjsRequires( window, jq );
- module.exports = factory( jq, window, window.document );
- }
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- // Version information for debugger
- DataTable.select = {};
- DataTable.select.version = '2.0.1';
- DataTable.select.init = function (dt) {
- var ctx = dt.settings()[0];
- if (!DataTable.versionCheck('2')) {
- throw 'Warning: Select requires DataTables 2 or newer';
- }
- if (ctx._select) {
- return;
- }
- var savedSelected = dt.state.loaded();
- var selectAndSave = function (e, settings, data) {
- if (data === null || data.select === undefined) {
- return;
- }
- // Clear any currently selected rows, before restoring state
- // None will be selected on first initialisation
- if (dt.rows({ selected: true }).any()) {
- dt.rows().deselect();
- }
- if (data.select.rows !== undefined) {
- dt.rows(data.select.rows).select();
- }
- if (dt.columns({ selected: true }).any()) {
- dt.columns().deselect();
- }
- if (data.select.columns !== undefined) {
- dt.columns(data.select.columns).select();
- }
- if (dt.cells({ selected: true }).any()) {
- dt.cells().deselect();
- }
- if (data.select.cells !== undefined) {
- for (var i = 0; i < data.select.cells.length; i++) {
- dt.cell(data.select.cells[i].row, data.select.cells[i].column).select();
- }
- }
- dt.state.save();
- };
- dt.on('stateSaveParams', function (e, settings, data) {
- data.select = {};
- data.select.rows = dt.rows({ selected: true }).ids(true).toArray();
- data.select.columns = dt.columns({ selected: true })[0];
- data.select.cells = dt.cells({ selected: true })[0].map(function (coords) {
- return { row: dt.row(coords.row).id(true), column: coords.column };
- });
- })
- .on('stateLoadParams', selectAndSave)
- .one('init', function () {
- selectAndSave(undefined, undefined, savedSelected);
- });
- 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 toggleable = true;
- var info = true;
- var selector = 'td, th';
- var className = 'selected';
- var headerCheckbox = true;
- var setStyle = false;
- ctx._select = {
- infoEls: []
- };
- // 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.toggleable !== undefined) {
- toggleable = opts.toggleable;
- }
- if (opts.info !== undefined) {
- info = opts.info;
- }
- if (opts.items !== undefined) {
- items = opts.items;
- }
- if (opts.style !== undefined) {
- style = opts.style;
- setStyle = true;
- }
- else {
- style = 'os';
- setStyle = true;
- }
- if (opts.selector !== undefined) {
- selector = opts.selector;
- }
- if (opts.className !== undefined) {
- className = opts.className;
- }
- if (opts.headerCheckbox !== undefined) {
- headerCheckbox = opts.headerCheckbox;
- }
- }
- dt.select.selector(selector);
- dt.select.items(items);
- dt.select.style(style);
- dt.select.blurable(blurable);
- dt.select.toggleable(toggleable);
- dt.select.info(info);
- ctx._select.className = className;
- // 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');
- }
- // Insert a checkbox into the header if needed - might need to wait
- // for init complete, or it might already be done
- if (headerCheckbox) {
- initCheckboxHeader(dt);
- dt.on('init', function () {
- initCheckboxHeader(dt);
- });
- }
- };
- /*
- 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
- toggleable:boolean - If row selection can be cancelled by repeated clicking
- on the row
- info:boolean - If the selection summary should be shown in the table
- information elements
- infoEls:element[] - List of HTML elements with info elements for a table
- }
- ```
- 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' + _safeId(dt.table().node()));
- }
- /**
- * 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;
- var matchSelection;
- 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;
- });
- }
- if (window.getSelection) {
- matchSelection = window.getSelection();
- }
- })
- .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 (matchSelection) {
- 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 (selection !== matchSelection) {
- return;
- }
- }
- }
- var ctx = dt.settings()[0];
- var container = dt.table().container();
- // Ignore clicks inside a sub-table
- if ($(e.target).closest('div.dt-container')[0] != 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' + _safeId(dt.table().node()), 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;
- }
- var event = $.Event('select-blur.dt');
- eventTrigger(dt, event, [e.target, e]);
- if (event.isDefaultPrevented()) {
- 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, node) {
- if (api.select.style() === 'api' || api.select.info() === false) {
- 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
- )
- )
- );
- };
- var el = $(node);
- var output = $('<span class="select-info"/>');
- add(output, 'row', rows);
- add(output, 'column', columns);
- add(output, 'cell', cells);
- var existing = el.children('span.select-info');
- if (existing.length) {
- existing.remove();
- }
- if (output.text() !== '') {
- el.append(output);
- }
- }
- /**
- * Add a checkbox to the header for checkbox columns, allowing all rows to
- * be selected, deselected or just to show the state.
- *
- * @param {*} dt API
- */
- function initCheckboxHeader( dt ) {
- // Find any checkbox column(s)
- dt.columns('.dt-select').every(function () {
- var header = this.header();
- if (! $('input', header).length) {
- // If no checkbox yet, insert one
- var input = $('<input>')
- .attr({
- class: 'dt-select-checkbox',
- type: 'checkbox',
- 'aria-label': dt.i18n('select.aria.headerCheckbox') || 'Select all rows'
- })
- .appendTo(header)
- .on('change', function () {
- if (this.checked) {
- dt.rows({search: 'applied'}).select();
- }
- else {
- dt.rows({selected: true}).deselect();
- }
- })
- .on('click', function (e) {
- e.stopPropagation();
- });
-
- // Update the header checkbox's state when the selection in the
- // table changes
- dt.on('draw select deselect', function (e, pass, type) {
- if (type === 'row' || ! type) {
- var count = dt.rows({selected: true}).count();
- var search = dt.rows({search: 'applied', selected: true}).count();
- var available = dt.rows({search: 'applied'}).count();
- if (search && search <= count && search === available) {
- input
- .prop('checked', true)
- .prop('indeterminate', false);
- }
- else if (search === 0 && count === 0) {
- input
- .prop('checked', false)
- .prop('indeterminate', false);
- }
- else {
- input
- .prop('checked', false)
- .prop('indeterminate', true);
- }
- }
- });
- }
- });
- }
- /**
- * 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);
- ctx._select_init = true;
- // 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(function (row, data, index) {
- var i, ien;
- var d = ctx.aoData[index];
- // Row
- if (d._select_selected) {
- $(row)
- .addClass(ctx._select.className)
- .find('input.dt-select-checkbox').prop('checked', true);
- }
- // 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)
- }
- }
- }
- );
- // 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 (e, settings) {
- if (settings !== api.settings()[0]) {
- // Not triggered by our DataTable!
- return;
- }
- // 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('info.dt', function (e, ctx, node) {
- // Store the info node for updating on select / deselect
- if (!ctx._select.infoEls.includes(node)) {
- ctx._select.infoEls.push(node);
- }
- info(api, node);
- });
- api.on('select.dtSelect.dt deselect.dtSelect.dt', function () {
- ctx._select.infoEls.forEach(function (el) {
- info(api, el);
- });
- api.state.save();
- });
- // Clean up and release
- api.on('destroy.dtSelect', function () {
- // Remove class directly rather than calling deselect - which would trigger events
- $(api.rows({ selected: true }).nodes()).removeClass(api.settings()[0]._select.className);
- disableMouseSelection(api);
- api.off('.dtSelect');
- $('body').off('.dtSelect' + _safeId(api.table().node()));
- });
- }
- /**
- * 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 = indexes.indexOf(last);
- var idx2 = indexes.indexOf(idx);
- 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(indexes.indexOf(idx) + 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(indexes.indexOf(idx), 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 toggleable = dt.select.toggleable();
- var isSelected = dt[type](idx, { selected: true }).any();
- if (isSelected && !toggleable) {
- return;
- }
- 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);
- }
- }
- function _safeId(node) {
- return node.id.replace(/[^a-zA-Z0-9\-\_]/g, '-');
- }
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * 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 (
- data && (
- (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 (
- rowData && (
- (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.toggleable()', function (flag) {
- if (flag === undefined) {
- return this.context[0]._select.toggleable;
- }
- return this.iterator('table', function (ctx) {
- ctx._select.toggleable = flag;
- });
- });
- apiRegister('select.info()', function (flag) {
- if (flag === 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) {
- if (!ctx._select) {
- DataTable.select.init(new DataTable.Api(ctx));
- }
- if (!ctx._select_init) {
- init(ctx);
- }
- ctx._select.style = style;
- // 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));
- }
- });
- });
- apiRegister('select.last()', function (set) {
- let ctx = this.context[0];
- if (set) {
- ctx._select_lastCell = set;
- return this;
- }
- return ctx._select_lastCell;
- });
- apiRegisterPlural('rows().select()', 'row().select()', function (select) {
- var api = this;
- if (select === false) {
- return this.deselect();
- }
- this.iterator('row', function (ctx, idx) {
- clear(ctx);
- // There is a good amount of knowledge of DataTables internals in
- // this function. It _could_ be done without that, but it would hurt
- // performance (or DT would need new APIs for this work)
- var dtData = ctx.aoData[idx];
- var dtColumns = ctx.aoColumns;
- $(dtData.nTr).addClass(ctx._select.className);
- dtData._select_selected = true;
- for (var i=0 ; i<dtColumns.length ; i++) {
- var col = dtColumns[i];
- // Regenerate the column type if not present
- if (col.sType === null) {
- api.columns().types()
- }
-
- if (col.sType === 'select-checkbox') {
- var cells = dtData.anCells;
- // Make sure the checkbox shows the right state
- if (cells && cells[i]) {
- $('input.dt-select-checkbox', cells[i]).prop('checked', true);
- }
- // Invalidate the sort data for this column, if not already done
- if (dtData._aSortData !== null) {
- dtData._aSortData[i] = null;
- }
- }
- }
- });
- this.iterator('table', function (ctx, i) {
- eventTrigger(api, 'select', ['row', api[i]], true);
- });
- return this;
- });
- apiRegister('row().selected()', function () {
- var ctx = this.context[0];
- if (ctx && this.length && ctx.aoData[this[0]] && ctx.aoData[this[0]]._select_selected) {
- return true;
- }
- return false;
- });
- 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;
- });
- apiRegister('column().selected()', function () {
- var ctx = this.context[0];
- if (ctx && this.length && ctx.aoColumns[this[0]] && ctx.aoColumns[this[0]]._select_selected) {
- return true;
- }
- return false;
- });
- 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.cells(api[i]).indexes().toArray()], true);
- });
- return this;
- });
- apiRegister('cell().selected()', function () {
- var ctx = this.context[0];
- if (ctx && this.length) {
- var row = ctx.aoData[this[0][0].row];
- if (row && row._selected_cells && row._selected_cells[this[0][0].column]) {
- return true;
- }
- }
- return false;
- });
- apiRegisterPlural('rows().deselect()', 'row().deselect()', function () {
- var api = this;
- this.iterator('row', function (ctx, idx) {
- // Like the select action, this has a lot of knowledge about DT internally
- var dtData = ctx.aoData[idx];
- var dtColumns = ctx.aoColumns;
- $(dtData.nTr).removeClass(ctx._select.className);
- dtData._select_selected = false;
- ctx._select_lastCell = null;
- for (var i=0 ; i<dtColumns.length ; i++) {
- var col = dtColumns[i];
- // Regenerate the column type if not present
- if (col.sType === null) {
- api.columns().types()
- }
-
- if (col.sType === 'select-checkbox') {
- var cells = dtData.anCells;
- // Make sure the checkbox shows the right state
- if (cells && cells[i]) {
- $('input.dt-select-checkbox', dtData.anCells[i]).prop('checked', false);
- }
- // Invalidate the sort data for this column, if not already done
- if (dtData._aSortData !== null) {
- dtData._aSortData[i] = null;
- }
- }
- }
- });
- 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];
- if (data._selected_cells !== undefined) {
- 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 (config.limitTo.indexOf('rows') !== -1 && dt.rows({ selected: true }).any()) {
- return true;
- }
- if (config.limitTo.indexOf('columns') !== -1 && dt.columns({ selected: true }).any()) {
- return true;
- }
- if (config.limitTo.indexOf('cells') !== -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 (e, dt, node, config) {
- var items = this.select.items();
- var mod = config.selectorModifier;
-
- if (mod) {
- if (typeof mod === 'function') {
- mod = mod.call(dt, e, dt, node, config);
- }
- this[items + 's'](mod).select();
- }
- else {
- this[items + 's']().select();
- }
- }
- // selectorModifier can be specified
- },
- 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);
- }
- },
- showSelected: {
- text: i18n('showSelected', 'Show only selected'),
- className: 'buttons-show-selected',
- action: function (e, dt) {
- if (dt.search.fixed('dt-select')) {
- // Remove existing function
- dt.search.fixed('dt-select', null);
- this.active(false);
- }
- else {
- // Use a fixed filtering function to match on selected rows
- // This needs to reference the internal aoData since that is
- // where Select stores its reference for the selected state
- var dataSrc = dt.settings()[0].aoData;
- dt.search.fixed('dt-select', function (text, data, idx) {
- // _select_selected is set by Select on the data object for the row
- return dataSrc[idx]._select_selected;
- });
- this.active(true);
- }
- dt.draw();
- }
- }
- });
- $.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);
- });
- }
- };
- });
- DataTable.type('select-checkbox', {
- className: 'dt-select',
- detect: function (data) {
- // Rendering function will tell us if it is a checkbox type
- return data === 'select-checkbox' ? data : false;
- },
- order: {
- pre: function (d) {
- return d === 'X' ? -1 : 0;
- }
- }
- });
- $.extend(true, DataTable.defaults.oLanguage, {
- select: {
- aria: {
- rowCheckbox: 'Select row'
- }
- }
- });
- DataTable.render.select = function (valueProp, nameProp) {
- var valueFn = valueProp ? DataTable.util.get(valueProp) : null;
- var nameFn = nameProp ? DataTable.util.get(nameProp) : null;
- return function (data, type, row, meta) {
- var dtRow = meta.settings.aoData[meta.row];
- var selected = dtRow._select_selected;
- var ariaLabel = meta.settings.oLanguage.select.aria.rowCheckbox;
- if (type === 'display') {
- return $('<input>')
- .attr({
- 'aria-label': ariaLabel,
- class: 'dt-select-checkbox',
- name: nameFn ? nameFn(row) : null,
- type: 'checkbox',
- value: valueFn ? valueFn(row) : null,
- checked: selected
- })[0];
- }
- else if (type === 'type') {
- return 'select-checkbox';
- }
- else if (type === 'filter') {
- return '';
- }
- return selected ? 'X' : '';
- }
- }
- // Legacy checkbox ordering
- 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;
- });
- };
- $.fn.DataTable.select = DataTable.select;
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * 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;
- }));
|