datatables.js 885 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348183491835018351183521835318354183551835618357183581835918360183611836218363183641836518366183671836818369183701837118372183731837418375183761837718378183791838018381183821838318384183851838618387183881838918390183911839218393183941839518396183971839818399184001840118402184031840418405184061840718408184091841018411184121841318414184151841618417184181841918420184211842218423184241842518426184271842818429184301843118432184331843418435184361843718438184391844018441184421844318444184451844618447184481844918450184511845218453184541845518456184571845818459184601846118462184631846418465184661846718468184691847018471184721847318474184751847618477184781847918480184811848218483184841848518486184871848818489184901849118492184931849418495184961849718498184991850018501185021850318504185051850618507185081850918510185111851218513185141851518516185171851818519185201852118522185231852418525185261852718528185291853018531185321853318534185351853618537185381853918540185411854218543185441854518546185471854818549185501855118552185531855418555185561855718558185591856018561185621856318564185651856618567185681856918570185711857218573185741857518576185771857818579185801858118582185831858418585185861858718588185891859018591185921859318594185951859618597185981859918600186011860218603186041860518606186071860818609186101861118612186131861418615186161861718618186191862018621186221862318624186251862618627186281862918630186311863218633186341863518636186371863818639186401864118642186431864418645186461864718648186491865018651186521865318654186551865618657186581865918660186611866218663186641866518666186671866818669186701867118672186731867418675186761867718678186791868018681186821868318684186851868618687186881868918690186911869218693186941869518696186971869818699187001870118702187031870418705187061870718708187091871018711187121871318714187151871618717187181871918720187211872218723187241872518726187271872818729187301873118732187331873418735187361873718738187391874018741187421874318744187451874618747187481874918750187511875218753187541875518756187571875818759187601876118762187631876418765187661876718768187691877018771187721877318774187751877618777187781877918780187811878218783187841878518786187871878818789187901879118792187931879418795187961879718798187991880018801188021880318804188051880618807188081880918810188111881218813188141881518816188171881818819188201882118822188231882418825188261882718828188291883018831188321883318834188351883618837188381883918840188411884218843188441884518846188471884818849188501885118852188531885418855188561885718858188591886018861188621886318864188651886618867188681886918870188711887218873188741887518876188771887818879188801888118882188831888418885188861888718888188891889018891188921889318894188951889618897188981889918900189011890218903189041890518906189071890818909189101891118912189131891418915189161891718918189191892018921189221892318924189251892618927189281892918930189311893218933189341893518936189371893818939189401894118942189431894418945189461894718948189491895018951189521895318954189551895618957189581895918960189611896218963189641896518966189671896818969189701897118972189731897418975189761897718978189791898018981189821898318984189851898618987189881898918990189911899218993189941899518996189971899818999190001900119002190031900419005190061900719008190091901019011190121901319014190151901619017190181901919020190211902219023190241902519026190271902819029190301903119032190331903419035190361903719038190391904019041190421904319044190451904619047190481904919050190511905219053190541905519056190571905819059190601906119062190631906419065190661906719068190691907019071190721907319074190751907619077190781907919080190811908219083190841908519086190871908819089190901909119092190931909419095190961909719098190991910019101191021910319104191051910619107191081910919110191111911219113191141911519116191171911819119191201912119122191231912419125191261912719128191291913019131191321913319134191351913619137191381913919140191411914219143191441914519146191471914819149191501915119152191531915419155191561915719158191591916019161191621916319164191651916619167191681916919170191711917219173191741917519176191771917819179191801918119182191831918419185191861918719188191891919019191191921919319194191951919619197191981919919200192011920219203192041920519206192071920819209192101921119212192131921419215192161921719218192191922019221192221922319224192251922619227192281922919230192311923219233192341923519236192371923819239192401924119242192431924419245192461924719248192491925019251192521925319254192551925619257192581925919260192611926219263192641926519266192671926819269192701927119272192731927419275192761927719278192791928019281192821928319284192851928619287192881928919290192911929219293192941929519296192971929819299193001930119302193031930419305193061930719308193091931019311193121931319314193151931619317193181931919320193211932219323193241932519326193271932819329193301933119332193331933419335193361933719338193391934019341193421934319344193451934619347193481934919350193511935219353193541935519356193571935819359193601936119362193631936419365193661936719368193691937019371193721937319374193751937619377193781937919380193811938219383193841938519386193871938819389193901939119392193931939419395193961939719398193991940019401194021940319404194051940619407194081940919410194111941219413194141941519416194171941819419194201942119422194231942419425194261942719428194291943019431194321943319434194351943619437194381943919440194411944219443194441944519446194471944819449194501945119452194531945419455194561945719458194591946019461194621946319464194651946619467194681946919470194711947219473194741947519476194771947819479194801948119482194831948419485194861948719488194891949019491194921949319494194951949619497194981949919500195011950219503195041950519506195071950819509195101951119512195131951419515195161951719518195191952019521195221952319524195251952619527195281952919530195311953219533195341953519536195371953819539195401954119542195431954419545195461954719548195491955019551195521955319554195551955619557195581955919560195611956219563195641956519566195671956819569195701957119572195731957419575195761957719578195791958019581195821958319584195851958619587195881958919590195911959219593195941959519596195971959819599196001960119602196031960419605196061960719608196091961019611196121961319614196151961619617196181961919620196211962219623196241962519626196271962819629196301963119632196331963419635196361963719638196391964019641196421964319644196451964619647196481964919650196511965219653196541965519656196571965819659196601966119662196631966419665196661966719668196691967019671196721967319674196751967619677196781967919680196811968219683196841968519686196871968819689196901969119692196931969419695196961969719698196991970019701197021970319704197051970619707197081970919710197111971219713197141971519716197171971819719197201972119722197231972419725197261972719728197291973019731197321973319734197351973619737197381973919740197411974219743197441974519746197471974819749197501975119752197531975419755197561975719758197591976019761197621976319764197651976619767197681976919770197711977219773197741977519776197771977819779197801978119782197831978419785197861978719788197891979019791197921979319794197951979619797197981979919800198011980219803198041980519806198071980819809198101981119812198131981419815198161981719818198191982019821198221982319824198251982619827198281982919830198311983219833198341983519836198371983819839198401984119842198431984419845198461984719848198491985019851198521985319854198551985619857198581985919860198611986219863198641986519866198671986819869198701987119872198731987419875198761987719878198791988019881198821988319884198851988619887198881988919890198911989219893198941989519896198971989819899199001990119902199031990419905199061990719908199091991019911199121991319914199151991619917199181991919920199211992219923199241992519926199271992819929199301993119932199331993419935199361993719938199391994019941199421994319944199451994619947199481994919950199511995219953199541995519956199571995819959199601996119962199631996419965199661996719968199691997019971199721997319974199751997619977199781997919980199811998219983199841998519986199871998819989199901999119992199931999419995199961999719998199992000020001200022000320004200052000620007200082000920010200112001220013200142001520016200172001820019200202002120022200232002420025200262002720028200292003020031200322003320034200352003620037200382003920040200412004220043200442004520046200472004820049200502005120052200532005420055200562005720058200592006020061200622006320064200652006620067200682006920070200712007220073200742007520076200772007820079200802008120082200832008420085200862008720088200892009020091200922009320094200952009620097200982009920100201012010220103201042010520106201072010820109201102011120112201132011420115201162011720118201192012020121201222012320124201252012620127201282012920130201312013220133201342013520136201372013820139201402014120142201432014420145201462014720148201492015020151201522015320154201552015620157201582015920160201612016220163201642016520166201672016820169201702017120172201732017420175201762017720178201792018020181201822018320184201852018620187201882018920190201912019220193201942019520196201972019820199202002020120202202032020420205202062020720208202092021020211202122021320214202152021620217202182021920220202212022220223202242022520226202272022820229202302023120232202332023420235202362023720238202392024020241202422024320244202452024620247202482024920250202512025220253202542025520256202572025820259202602026120262202632026420265202662026720268202692027020271202722027320274202752027620277202782027920280202812028220283202842028520286202872028820289202902029120292202932029420295202962029720298202992030020301203022030320304203052030620307203082030920310203112031220313203142031520316203172031820319203202032120322203232032420325203262032720328203292033020331203322033320334203352033620337203382033920340203412034220343203442034520346203472034820349203502035120352203532035420355203562035720358203592036020361203622036320364203652036620367203682036920370203712037220373203742037520376203772037820379203802038120382203832038420385203862038720388203892039020391203922039320394203952039620397203982039920400204012040220403204042040520406204072040820409204102041120412204132041420415204162041720418204192042020421204222042320424204252042620427204282042920430204312043220433204342043520436204372043820439204402044120442204432044420445204462044720448204492045020451204522045320454204552045620457204582045920460204612046220463204642046520466204672046820469204702047120472204732047420475204762047720478204792048020481204822048320484204852048620487204882048920490204912049220493204942049520496204972049820499205002050120502205032050420505205062050720508205092051020511205122051320514205152051620517205182051920520205212052220523205242052520526205272052820529205302053120532205332053420535205362053720538205392054020541205422054320544205452054620547205482054920550205512055220553205542055520556205572055820559205602056120562205632056420565205662056720568205692057020571205722057320574205752057620577205782057920580205812058220583205842058520586205872058820589205902059120592205932059420595205962059720598205992060020601206022060320604206052060620607206082060920610206112061220613206142061520616206172061820619206202062120622206232062420625206262062720628206292063020631206322063320634206352063620637206382063920640206412064220643206442064520646206472064820649206502065120652206532065420655206562065720658206592066020661206622066320664206652066620667206682066920670206712067220673206742067520676206772067820679206802068120682206832068420685206862068720688206892069020691206922069320694206952069620697206982069920700207012070220703207042070520706207072070820709207102071120712207132071420715207162071720718207192072020721207222072320724207252072620727207282072920730207312073220733207342073520736207372073820739207402074120742207432074420745207462074720748207492075020751207522075320754207552075620757207582075920760207612076220763207642076520766207672076820769207702077120772207732077420775207762077720778207792078020781207822078320784207852078620787207882078920790207912079220793207942079520796207972079820799208002080120802208032080420805208062080720808208092081020811208122081320814208152081620817208182081920820208212082220823208242082520826208272082820829208302083120832208332083420835208362083720838208392084020841208422084320844208452084620847208482084920850208512085220853208542085520856208572085820859208602086120862208632086420865208662086720868208692087020871208722087320874208752087620877208782087920880208812088220883208842088520886208872088820889208902089120892208932089420895208962089720898208992090020901209022090320904209052090620907209082090920910209112091220913209142091520916209172091820919209202092120922209232092420925209262092720928209292093020931209322093320934209352093620937209382093920940209412094220943209442094520946209472094820949209502095120952209532095420955209562095720958209592096020961209622096320964209652096620967209682096920970209712097220973209742097520976209772097820979209802098120982209832098420985209862098720988209892099020991209922099320994209952099620997209982099921000210012100221003210042100521006210072100821009210102101121012210132101421015210162101721018210192102021021210222102321024210252102621027210282102921030210312103221033210342103521036210372103821039210402104121042210432104421045210462104721048210492105021051210522105321054210552105621057210582105921060210612106221063210642106521066210672106821069210702107121072210732107421075210762107721078210792108021081210822108321084210852108621087210882108921090210912109221093210942109521096210972109821099211002110121102211032110421105211062110721108211092111021111211122111321114211152111621117211182111921120211212112221123211242112521126211272112821129211302113121132211332113421135211362113721138211392114021141211422114321144211452114621147211482114921150211512115221153211542115521156211572115821159211602116121162211632116421165211662116721168211692117021171211722117321174211752117621177211782117921180211812118221183211842118521186211872118821189211902119121192211932119421195211962119721198211992120021201212022120321204212052120621207212082120921210212112121221213212142121521216212172121821219212202122121222212232122421225212262122721228212292123021231212322123321234212352123621237212382123921240212412124221243212442124521246212472124821249212502125121252212532125421255212562125721258212592126021261212622126321264212652126621267212682126921270212712127221273212742127521276212772127821279212802128121282212832128421285212862128721288212892129021291212922129321294212952129621297212982129921300213012130221303213042130521306213072130821309213102131121312213132131421315213162131721318213192132021321213222132321324213252132621327213282132921330213312133221333213342133521336213372133821339213402134121342213432134421345213462134721348213492135021351213522135321354213552135621357213582135921360213612136221363213642136521366213672136821369213702137121372213732137421375213762137721378213792138021381213822138321384213852138621387213882138921390213912139221393213942139521396213972139821399214002140121402214032140421405214062140721408214092141021411214122141321414214152141621417214182141921420214212142221423214242142521426214272142821429214302143121432214332143421435214362143721438214392144021441214422144321444214452144621447214482144921450214512145221453214542145521456214572145821459214602146121462214632146421465214662146721468214692147021471214722147321474214752147621477214782147921480214812148221483214842148521486214872148821489214902149121492214932149421495214962149721498214992150021501215022150321504215052150621507215082150921510215112151221513215142151521516215172151821519215202152121522215232152421525215262152721528215292153021531215322153321534215352153621537215382153921540215412154221543215442154521546215472154821549215502155121552215532155421555215562155721558215592156021561215622156321564215652156621567215682156921570215712157221573215742157521576215772157821579215802158121582215832158421585215862158721588215892159021591215922159321594215952159621597215982159921600216012160221603216042160521606216072160821609216102161121612216132161421615216162161721618216192162021621216222162321624216252162621627216282162921630216312163221633216342163521636216372163821639216402164121642216432164421645216462164721648216492165021651216522165321654216552165621657216582165921660216612166221663216642166521666216672166821669216702167121672216732167421675216762167721678216792168021681216822168321684216852168621687216882168921690216912169221693216942169521696216972169821699217002170121702217032170421705217062170721708217092171021711217122171321714217152171621717217182171921720217212172221723217242172521726217272172821729217302173121732217332173421735217362173721738217392174021741217422174321744217452174621747217482174921750217512175221753217542175521756217572175821759217602176121762217632176421765217662176721768217692177021771217722177321774217752177621777217782177921780217812178221783217842178521786217872178821789217902179121792217932179421795217962179721798217992180021801218022180321804218052180621807218082180921810218112181221813218142181521816218172181821819218202182121822218232182421825218262182721828218292183021831218322183321834218352183621837218382183921840218412184221843218442184521846218472184821849218502185121852218532185421855218562185721858218592186021861218622186321864218652186621867218682186921870218712187221873218742187521876218772187821879218802188121882218832188421885218862188721888218892189021891218922189321894218952189621897218982189921900219012190221903219042190521906219072190821909219102191121912219132191421915219162191721918219192192021921219222192321924219252192621927219282192921930219312193221933219342193521936219372193821939219402194121942219432194421945219462194721948219492195021951219522195321954219552195621957219582195921960219612196221963219642196521966219672196821969219702197121972219732197421975219762197721978219792198021981219822198321984219852198621987219882198921990219912199221993219942199521996219972199821999220002200122002220032200422005220062200722008220092201022011220122201322014220152201622017220182201922020220212202222023220242202522026220272202822029220302203122032220332203422035220362203722038220392204022041220422204322044220452204622047220482204922050220512205222053220542205522056220572205822059220602206122062220632206422065220662206722068220692207022071220722207322074220752207622077220782207922080220812208222083220842208522086220872208822089220902209122092220932209422095220962209722098220992210022101221022210322104221052210622107221082210922110221112211222113221142211522116221172211822119221202212122122221232212422125221262212722128221292213022131221322213322134221352213622137221382213922140221412214222143221442214522146221472214822149221502215122152221532215422155221562215722158221592216022161221622216322164221652216622167221682216922170221712217222173221742217522176221772217822179221802218122182221832218422185221862218722188221892219022191221922219322194221952219622197221982219922200222012220222203222042220522206222072220822209222102221122212222132221422215222162221722218222192222022221222222222322224222252222622227222282222922230222312223222233222342223522236222372223822239222402224122242222432224422245222462224722248222492225022251222522225322254222552225622257222582225922260222612226222263222642226522266222672226822269222702227122272222732227422275222762227722278222792228022281222822228322284222852228622287222882228922290222912229222293222942229522296222972229822299223002230122302223032230422305223062230722308223092231022311223122231322314223152231622317223182231922320223212232222323223242232522326223272232822329223302233122332223332233422335223362233722338223392234022341223422234322344223452234622347223482234922350223512235222353223542235522356223572235822359223602236122362223632236422365223662236722368223692237022371223722237322374223752237622377223782237922380223812238222383223842238522386223872238822389223902239122392223932239422395223962239722398223992240022401224022240322404224052240622407224082240922410224112241222413224142241522416224172241822419224202242122422224232242422425224262242722428224292243022431224322243322434224352243622437224382243922440224412244222443224442244522446224472244822449224502245122452224532245422455224562245722458224592246022461224622246322464224652246622467224682246922470224712247222473224742247522476224772247822479224802248122482224832248422485224862248722488224892249022491224922249322494224952249622497224982249922500225012250222503225042250522506225072250822509225102251122512225132251422515225162251722518225192252022521225222252322524225252252622527225282252922530225312253222533225342253522536225372253822539225402254122542225432254422545225462254722548225492255022551225522255322554225552255622557225582255922560225612256222563225642256522566225672256822569225702257122572225732257422575225762257722578225792258022581225822258322584225852258622587225882258922590225912259222593225942259522596225972259822599226002260122602226032260422605226062260722608226092261022611226122261322614226152261622617226182261922620226212262222623226242262522626226272262822629226302263122632226332263422635226362263722638226392264022641226422264322644226452264622647226482264922650226512265222653226542265522656226572265822659226602266122662226632266422665226662266722668226692267022671226722267322674226752267622677226782267922680226812268222683226842268522686226872268822689226902269122692226932269422695226962269722698226992270022701227022270322704227052270622707227082270922710227112271222713227142271522716227172271822719227202272122722227232272422725227262272722728227292273022731227322273322734227352273622737227382273922740227412274222743227442274522746227472274822749227502275122752227532275422755227562275722758227592276022761227622276322764227652276622767227682276922770227712277222773227742277522776227772277822779227802278122782227832278422785227862278722788227892279022791227922279322794227952279622797227982279922800228012280222803228042280522806228072280822809228102281122812228132281422815228162281722818228192282022821228222282322824228252282622827228282282922830228312283222833228342283522836228372283822839228402284122842228432284422845228462284722848228492285022851228522285322854228552285622857228582285922860228612286222863228642286522866228672286822869228702287122872228732287422875228762287722878228792288022881228822288322884228852288622887228882288922890228912289222893228942289522896228972289822899229002290122902229032290422905229062290722908229092291022911229122291322914229152291622917229182291922920229212292222923229242292522926229272292822929229302293122932229332293422935229362293722938229392294022941229422294322944229452294622947229482294922950229512295222953229542295522956229572295822959229602296122962229632296422965229662296722968229692297022971229722297322974229752297622977229782297922980229812298222983229842298522986229872298822989229902299122992229932299422995229962299722998229992300023001230022300323004230052300623007230082300923010230112301223013230142301523016230172301823019230202302123022230232302423025230262302723028230292303023031230322303323034230352303623037230382303923040230412304223043230442304523046230472304823049230502305123052230532305423055230562305723058230592306023061230622306323064230652306623067230682306923070230712307223073230742307523076230772307823079230802308123082230832308423085230862308723088230892309023091230922309323094230952309623097230982309923100231012310223103231042310523106231072310823109231102311123112231132311423115231162311723118231192312023121231222312323124231252312623127231282312923130231312313223133231342313523136231372313823139231402314123142231432314423145231462314723148231492315023151231522315323154231552315623157231582315923160231612316223163231642316523166231672316823169231702317123172231732317423175231762317723178231792318023181231822318323184231852318623187231882318923190231912319223193231942319523196231972319823199232002320123202232032320423205232062320723208232092321023211232122321323214232152321623217232182321923220232212322223223232242322523226232272322823229232302323123232232332323423235232362323723238232392324023241232422324323244232452324623247232482324923250232512325223253232542325523256232572325823259232602326123262232632326423265232662326723268232692327023271232722327323274232752327623277232782327923280232812328223283232842328523286232872328823289232902329123292232932329423295232962329723298232992330023301233022330323304233052330623307233082330923310233112331223313233142331523316233172331823319233202332123322233232332423325233262332723328233292333023331233322333323334233352333623337233382333923340233412334223343233442334523346233472334823349233502335123352233532335423355233562335723358233592336023361233622336323364233652336623367233682336923370233712337223373233742337523376233772337823379233802338123382233832338423385233862338723388233892339023391233922339323394233952339623397233982339923400234012340223403234042340523406234072340823409234102341123412234132341423415234162341723418234192342023421234222342323424234252342623427234282342923430234312343223433234342343523436234372343823439234402344123442234432344423445234462344723448234492345023451234522345323454234552345623457234582345923460234612346223463234642346523466234672346823469234702347123472234732347423475234762347723478234792348023481234822348323484234852348623487234882348923490234912349223493234942349523496234972349823499235002350123502235032350423505235062350723508235092351023511235122351323514235152351623517235182351923520235212352223523235242352523526235272352823529235302353123532235332353423535235362353723538235392354023541235422354323544235452354623547235482354923550235512355223553235542355523556235572355823559235602356123562235632356423565235662356723568235692357023571235722357323574235752357623577235782357923580235812358223583235842358523586235872358823589235902359123592235932359423595235962359723598235992360023601236022360323604236052360623607236082360923610236112361223613236142361523616236172361823619236202362123622236232362423625236262362723628236292363023631236322363323634236352363623637236382363923640236412364223643236442364523646236472364823649236502365123652236532365423655236562365723658236592366023661236622366323664236652366623667236682366923670236712367223673236742367523676236772367823679236802368123682236832368423685236862368723688236892369023691236922369323694236952369623697236982369923700237012370223703237042370523706237072370823709237102371123712237132371423715237162371723718237192372023721237222372323724237252372623727237282372923730237312373223733237342373523736237372373823739237402374123742237432374423745237462374723748237492375023751237522375323754237552375623757237582375923760237612376223763237642376523766237672376823769237702377123772237732377423775237762377723778237792378023781237822378323784237852378623787237882378923790237912379223793237942379523796237972379823799238002380123802238032380423805238062380723808238092381023811238122381323814238152381623817238182381923820238212382223823238242382523826238272382823829238302383123832238332383423835238362383723838238392384023841238422384323844238452384623847238482384923850238512385223853238542385523856238572385823859238602386123862238632386423865238662386723868238692387023871238722387323874238752387623877238782387923880238812388223883238842388523886238872388823889238902389123892238932389423895238962389723898238992390023901239022390323904239052390623907239082390923910239112391223913239142391523916239172391823919239202392123922239232392423925239262392723928239292393023931239322393323934239352393623937239382393923940239412394223943239442394523946239472394823949239502395123952239532395423955239562395723958239592396023961239622396323964239652396623967239682396923970239712397223973239742397523976239772397823979239802398123982239832398423985239862398723988239892399023991239922399323994239952399623997239982399924000240012400224003240042400524006240072400824009240102401124012240132401424015240162401724018240192402024021240222402324024240252402624027240282402924030240312403224033240342403524036240372403824039240402404124042240432404424045240462404724048240492405024051240522405324054240552405624057240582405924060240612406224063240642406524066240672406824069240702407124072240732407424075240762407724078240792408024081240822408324084240852408624087240882408924090240912409224093240942409524096240972409824099241002410124102241032410424105241062410724108241092411024111241122411324114241152411624117241182411924120241212412224123241242412524126241272412824129241302413124132241332413424135241362413724138241392414024141241422414324144241452414624147241482414924150241512415224153241542415524156241572415824159241602416124162241632416424165241662416724168241692417024171241722417324174241752417624177241782417924180241812418224183241842418524186241872418824189241902419124192241932419424195241962419724198241992420024201242022420324204242052420624207242082420924210242112421224213242142421524216242172421824219242202422124222242232422424225242262422724228242292423024231242322423324234242352423624237242382423924240242412424224243242442424524246242472424824249242502425124252242532425424255242562425724258242592426024261242622426324264242652426624267242682426924270242712427224273242742427524276242772427824279242802428124282242832428424285242862428724288242892429024291242922429324294242952429624297242982429924300243012430224303243042430524306243072430824309243102431124312243132431424315243162431724318243192432024321243222432324324243252432624327243282432924330243312433224333243342433524336243372433824339243402434124342243432434424345243462434724348243492435024351243522435324354243552435624357243582435924360243612436224363243642436524366243672436824369243702437124372243732437424375243762437724378243792438024381243822438324384243852438624387243882438924390243912439224393243942439524396243972439824399244002440124402244032440424405244062440724408244092441024411244122441324414244152441624417244182441924420244212442224423244242442524426244272442824429244302443124432244332443424435244362443724438244392444024441244422444324444244452444624447244482444924450244512445224453244542445524456244572445824459244602446124462244632446424465244662446724468244692447024471244722447324474244752447624477244782447924480244812448224483244842448524486244872448824489244902449124492244932449424495244962449724498244992450024501245022450324504245052450624507245082450924510245112451224513245142451524516245172451824519245202452124522245232452424525245262452724528245292453024531245322453324534245352453624537245382453924540245412454224543245442454524546245472454824549245502455124552245532455424555245562455724558245592456024561245622456324564245652456624567245682456924570245712457224573245742457524576245772457824579245802458124582245832458424585245862458724588245892459024591245922459324594245952459624597245982459924600246012460224603246042460524606246072460824609246102461124612246132461424615246162461724618246192462024621246222462324624246252462624627246282462924630246312463224633246342463524636246372463824639246402464124642246432464424645246462464724648246492465024651246522465324654246552465624657246582465924660246612466224663246642466524666246672466824669246702467124672246732467424675246762467724678246792468024681246822468324684246852468624687246882468924690246912469224693246942469524696246972469824699247002470124702247032470424705247062470724708247092471024711247122471324714247152471624717247182471924720247212472224723247242472524726247272472824729247302473124732247332473424735247362473724738247392474024741247422474324744247452474624747247482474924750247512475224753247542475524756247572475824759247602476124762247632476424765247662476724768247692477024771247722477324774247752477624777247782477924780247812478224783247842478524786247872478824789247902479124792247932479424795247962479724798247992480024801248022480324804248052480624807248082480924810248112481224813248142481524816248172481824819248202482124822248232482424825248262482724828248292483024831248322483324834248352483624837248382483924840248412484224843248442484524846248472484824849248502485124852248532485424855248562485724858248592486024861248622486324864248652486624867248682486924870248712487224873248742487524876248772487824879248802488124882248832488424885248862488724888248892489024891248922489324894248952489624897248982489924900249012490224903249042490524906249072490824909249102491124912249132491424915249162491724918249192492024921249222492324924249252492624927249282492924930249312493224933249342493524936249372493824939249402494124942249432494424945249462494724948249492495024951249522495324954249552495624957249582495924960249612496224963249642496524966249672496824969249702497124972249732497424975249762497724978249792498024981249822498324984249852498624987249882498924990249912499224993249942499524996249972499824999250002500125002250032500425005250062500725008250092501025011250122501325014250152501625017250182501925020250212502225023250242502525026250272502825029250302503125032250332503425035250362503725038250392504025041250422504325044250452504625047250482504925050250512505225053250542505525056250572505825059250602506125062250632506425065250662506725068250692507025071250722507325074250752507625077250782507925080250812508225083250842508525086250872508825089250902509125092250932509425095250962509725098250992510025101251022510325104251052510625107251082510925110251112511225113251142511525116251172511825119251202512125122251232512425125251262512725128251292513025131251322513325134251352513625137251382513925140251412514225143251442514525146251472514825149251502515125152251532515425155251562515725158251592516025161251622516325164251652516625167251682516925170251712517225173251742517525176251772517825179251802518125182251832518425185251862518725188251892519025191251922519325194251952519625197251982519925200252012520225203252042520525206252072520825209252102521125212252132521425215252162521725218252192522025221252222522325224252252522625227252282522925230252312523225233252342523525236252372523825239252402524125242252432524425245252462524725248252492525025251252522525325254252552525625257252582525925260252612526225263252642526525266252672526825269252702527125272252732527425275252762527725278252792528025281252822528325284252852528625287252882528925290252912529225293252942529525296252972529825299253002530125302253032530425305253062530725308253092531025311253122531325314253152531625317253182531925320253212532225323253242532525326253272532825329253302533125332253332533425335253362533725338253392534025341253422534325344253452534625347253482534925350253512535225353253542535525356253572535825359253602536125362253632536425365253662536725368253692537025371253722537325374253752537625377253782537925380253812538225383253842538525386253872538825389253902539125392253932539425395253962539725398253992540025401254022540325404254052540625407254082540925410254112541225413254142541525416254172541825419254202542125422254232542425425254262542725428254292543025431254322543325434254352543625437254382543925440254412544225443254442544525446254472544825449254502545125452254532545425455254562545725458254592546025461254622546325464254652546625467254682546925470254712547225473254742547525476254772547825479254802548125482254832548425485254862548725488254892549025491254922549325494254952549625497254982549925500255012550225503255042550525506255072550825509255102551125512255132551425515255162551725518255192552025521255222552325524255252552625527255282552925530255312553225533255342553525536255372553825539255402554125542255432554425545255462554725548255492555025551255522555325554255552555625557255582555925560255612556225563255642556525566255672556825569255702557125572255732557425575255762557725578255792558025581255822558325584255852558625587255882558925590255912559225593255942559525596255972559825599256002560125602256032560425605256062560725608256092561025611256122561325614256152561625617256182561925620256212562225623256242562525626256272562825629256302563125632256332563425635256362563725638256392564025641256422564325644256452564625647256482564925650256512565225653256542565525656256572565825659256602566125662256632566425665256662566725668256692567025671256722567325674256752567625677256782567925680256812568225683256842568525686256872568825689256902569125692256932569425695256962569725698256992570025701257022570325704257052570625707257082570925710257112571225713257142571525716257172571825719257202572125722257232572425725257262572725728257292573025731257322573325734257352573625737257382573925740257412574225743257442574525746257472574825749257502575125752257532575425755257562575725758257592576025761257622576325764257652576625767257682576925770257712577225773257742577525776257772577825779257802578125782257832578425785257862578725788257892579025791257922579325794257952579625797257982579925800258012580225803258042580525806258072580825809258102581125812258132581425815258162581725818258192582025821258222582325824258252582625827258282582925830258312583225833258342583525836258372583825839258402584125842258432584425845258462584725848258492585025851258522585325854258552585625857258582585925860258612586225863258642586525866258672586825869258702587125872258732587425875258762587725878258792588025881258822588325884258852588625887258882588925890258912589225893258942589525896258972589825899259002590125902259032590425905259062590725908259092591025911259122591325914259152591625917259182591925920259212592225923259242592525926259272592825929259302593125932259332593425935259362593725938259392594025941259422594325944259452594625947259482594925950259512595225953259542595525956259572595825959259602596125962259632596425965259662596725968259692597025971259722597325974259752597625977259782597925980259812598225983259842598525986259872598825989259902599125992259932599425995259962599725998259992600026001260022600326004260052600626007260082600926010260112601226013260142601526016260172601826019260202602126022260232602426025260262602726028260292603026031260322603326034260352603626037260382603926040260412604226043260442604526046260472604826049260502605126052260532605426055260562605726058260592606026061260622606326064260652606626067260682606926070260712607226073260742607526076260772607826079260802608126082260832608426085260862608726088260892609026091260922609326094260952609626097260982609926100261012610226103261042610526106261072610826109261102611126112261132611426115261162611726118261192612026121261222612326124261252612626127261282612926130261312613226133261342613526136261372613826139261402614126142261432614426145261462614726148261492615026151261522615326154261552615626157261582615926160261612616226163261642616526166261672616826169261702617126172261732617426175261762617726178261792618026181261822618326184261852618626187261882618926190261912619226193261942619526196261972619826199262002620126202262032620426205262062620726208262092621026211262122621326214262152621626217262182621926220262212622226223262242622526226262272622826229262302623126232262332623426235262362623726238262392624026241262422624326244262452624626247262482624926250262512625226253262542625526256262572625826259262602626126262262632626426265262662626726268262692627026271262722627326274262752627626277262782627926280262812628226283262842628526286262872628826289262902629126292262932629426295262962629726298262992630026301263022630326304263052630626307263082630926310263112631226313263142631526316263172631826319263202632126322263232632426325263262632726328263292633026331263322633326334263352633626337263382633926340263412634226343263442634526346263472634826349263502635126352263532635426355263562635726358263592636026361263622636326364263652636626367263682636926370263712637226373263742637526376263772637826379263802638126382263832638426385263862638726388263892639026391263922639326394263952639626397263982639926400264012640226403264042640526406264072640826409264102641126412264132641426415264162641726418264192642026421264222642326424264252642626427264282642926430264312643226433264342643526436264372643826439264402644126442264432644426445264462644726448264492645026451264522645326454264552645626457264582645926460264612646226463264642646526466264672646826469264702647126472264732647426475264762647726478264792648026481264822648326484264852648626487264882648926490264912649226493264942649526496264972649826499265002650126502265032650426505265062650726508265092651026511265122651326514265152651626517265182651926520265212652226523265242652526526265272652826529265302653126532265332653426535265362653726538265392654026541265422654326544265452654626547265482654926550265512655226553265542655526556265572655826559265602656126562265632656426565265662656726568265692657026571265722657326574265752657626577265782657926580265812658226583265842658526586265872658826589265902659126592265932659426595265962659726598265992660026601266022660326604266052660626607266082660926610266112661226613266142661526616266172661826619266202662126622266232662426625266262662726628266292663026631266322663326634266352663626637266382663926640266412664226643266442664526646266472664826649266502665126652266532665426655266562665726658266592666026661266622666326664266652666626667266682666926670266712667226673266742667526676266772667826679266802668126682266832668426685266862668726688266892669026691266922669326694266952669626697266982669926700267012670226703267042670526706267072670826709267102671126712267132671426715267162671726718267192672026721267222672326724267252672626727267282672926730267312673226733267342673526736267372673826739267402674126742267432674426745267462674726748267492675026751267522675326754267552675626757267582675926760267612676226763267642676526766267672676826769267702677126772267732677426775267762677726778267792678026781267822678326784267852678626787267882678926790267912679226793267942679526796267972679826799268002680126802268032680426805268062680726808268092681026811268122681326814268152681626817268182681926820268212682226823268242682526826268272682826829268302683126832268332683426835268362683726838268392684026841268422684326844268452684626847268482684926850268512685226853268542685526856268572685826859268602686126862268632686426865268662686726868268692687026871268722687326874268752687626877268782687926880268812688226883268842688526886268872688826889268902689126892268932689426895268962689726898268992690026901269022690326904269052690626907269082690926910269112691226913269142691526916269172691826919269202692126922269232692426925269262692726928269292693026931269322693326934269352693626937269382693926940269412694226943269442694526946269472694826949269502695126952269532695426955269562695726958269592696026961269622696326964269652696626967269682696926970269712697226973269742697526976269772697826979269802698126982269832698426985269862698726988269892699026991269922699326994269952699626997269982699927000270012700227003270042700527006270072700827009270102701127012270132701427015270162701727018270192702027021270222702327024270252702627027270282702927030270312703227033270342703527036270372703827039270402704127042270432704427045270462704727048270492705027051270522705327054270552705627057270582705927060270612706227063270642706527066270672706827069270702707127072270732707427075270762707727078270792708027081270822708327084270852708627087270882708927090270912709227093270942709527096270972709827099271002710127102271032710427105271062710727108271092711027111271122711327114271152711627117271182711927120271212712227123271242712527126271272712827129271302713127132271332713427135271362713727138271392714027141271422714327144271452714627147271482714927150271512715227153271542715527156271572715827159271602716127162271632716427165271662716727168271692717027171271722717327174271752717627177271782717927180271812718227183271842718527186271872718827189271902719127192271932719427195271962719727198271992720027201272022720327204272052720627207272082720927210272112721227213272142721527216272172721827219272202722127222272232722427225272262722727228272292723027231272322723327234272352723627237272382723927240272412724227243272442724527246272472724827249272502725127252272532725427255272562725727258272592726027261272622726327264272652726627267272682726927270272712727227273272742727527276272772727827279272802728127282272832728427285272862728727288272892729027291272922729327294272952729627297272982729927300273012730227303273042730527306273072730827309273102731127312273132731427315273162731727318273192732027321273222732327324273252732627327273282732927330273312733227333273342733527336273372733827339273402734127342273432734427345273462734727348273492735027351273522735327354273552735627357273582735927360273612736227363273642736527366273672736827369273702737127372273732737427375273762737727378273792738027381273822738327384273852738627387273882738927390273912739227393273942739527396273972739827399274002740127402274032740427405274062740727408274092741027411274122741327414274152741627417274182741927420274212742227423274242742527426274272742827429274302743127432274332743427435274362743727438274392744027441274422744327444274452744627447274482744927450274512745227453274542745527456274572745827459274602746127462274632746427465274662746727468274692747027471274722747327474274752747627477274782747927480274812748227483274842748527486274872748827489274902749127492274932749427495274962749727498274992750027501275022750327504275052750627507275082750927510275112751227513275142751527516275172751827519275202752127522275232752427525275262752727528275292753027531275322753327534275352753627537275382753927540275412754227543275442754527546275472754827549275502755127552275532755427555275562755727558275592756027561275622756327564275652756627567275682756927570275712757227573275742757527576275772757827579275802758127582275832758427585275862758727588275892759027591275922759327594275952759627597275982759927600276012760227603276042760527606276072760827609276102761127612276132761427615276162761727618276192762027621276222762327624276252762627627276282762927630276312763227633276342763527636276372763827639276402764127642276432764427645276462764727648276492765027651276522765327654276552765627657276582765927660276612766227663276642766527666276672766827669276702767127672276732767427675276762767727678276792768027681276822768327684276852768627687276882768927690276912769227693276942769527696276972769827699277002770127702277032770427705277062770727708277092771027711277122771327714277152771627717277182771927720277212772227723277242772527726277272772827729277302773127732277332773427735277362773727738277392774027741277422774327744277452774627747277482774927750277512775227753277542775527756277572775827759277602776127762277632776427765277662776727768277692777027771277722777327774277752777627777277782777927780277812778227783277842778527786277872778827789277902779127792277932779427795277962779727798277992780027801278022780327804278052780627807278082780927810278112781227813278142781527816278172781827819278202782127822278232782427825278262782727828278292783027831278322783327834278352783627837278382783927840278412784227843278442784527846278472784827849278502785127852278532785427855278562785727858278592786027861278622786327864278652786627867278682786927870278712787227873278742787527876278772787827879278802788127882278832788427885278862788727888278892789027891278922789327894278952789627897278982789927900279012790227903279042790527906279072790827909279102791127912279132791427915279162791727918279192792027921279222792327924279252792627927279282792927930279312793227933279342793527936279372793827939279402794127942279432794427945279462794727948279492795027951279522795327954279552795627957279582795927960279612796227963279642796527966279672796827969279702797127972279732797427975279762797727978279792798027981279822798327984279852798627987279882798927990279912799227993279942799527996279972799827999280002800128002280032800428005280062800728008280092801028011280122801328014280152801628017280182801928020280212802228023280242802528026280272802828029280302803128032280332803428035280362803728038280392804028041280422804328044280452804628047280482804928050280512805228053280542805528056280572805828059280602806128062280632806428065280662806728068280692807028071280722807328074280752807628077280782807928080280812808228083280842808528086280872808828089280902809128092280932809428095280962809728098280992810028101281022810328104281052810628107281082810928110281112811228113281142811528116281172811828119281202812128122281232812428125281262812728128281292813028131281322813328134281352813628137281382813928140281412814228143281442814528146281472814828149281502815128152281532815428155281562815728158281592816028161281622816328164281652816628167281682816928170281712817228173281742817528176281772817828179281802818128182281832818428185281862818728188281892819028191281922819328194281952819628197281982819928200282012820228203282042820528206282072820828209282102821128212282132821428215282162821728218282192822028221282222822328224282252822628227282282822928230282312823228233282342823528236282372823828239282402824128242282432824428245282462824728248282492825028251282522825328254282552825628257282582825928260282612826228263282642826528266282672826828269282702827128272282732827428275282762827728278282792828028281282822828328284282852828628287282882828928290282912829228293282942829528296282972829828299283002830128302283032830428305283062830728308283092831028311283122831328314283152831628317283182831928320283212832228323283242832528326283272832828329283302833128332283332833428335283362833728338283392834028341283422834328344283452834628347283482834928350283512835228353283542835528356283572835828359283602836128362283632836428365283662836728368283692837028371283722837328374283752837628377283782837928380283812838228383283842838528386283872838828389283902839128392283932839428395283962839728398283992840028401284022840328404284052840628407284082840928410284112841228413284142841528416284172841828419284202842128422284232842428425284262842728428284292843028431284322843328434284352843628437284382843928440284412844228443284442844528446284472844828449284502845128452284532845428455284562845728458284592846028461284622846328464284652846628467284682846928470284712847228473284742847528476284772847828479284802848128482284832848428485284862848728488284892849028491284922849328494284952849628497284982849928500285012850228503285042850528506285072850828509285102851128512285132851428515285162851728518285192852028521285222852328524285252852628527285282852928530285312853228533285342853528536285372853828539285402854128542285432854428545285462854728548285492855028551285522855328554285552855628557
  1. /*
  2. * This combined file was created by the DataTables downloader builder:
  3. * https://datatables.net/download
  4. *
  5. * To rebuild or modify this file with the latest versions of the included
  6. * software please visit:
  7. * 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
  8. *
  9. * Included libraries:
  10. * 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
  11. */
  12. /*! DataTables 2.0.7
  13. * © SpryMedia Ltd - datatables.net/license
  14. */
  15. /**
  16. * @summary DataTables
  17. * @description Paginate, search and order HTML tables
  18. * @version 2.0.7
  19. * @author SpryMedia Ltd
  20. * @contact www.datatables.net
  21. * @copyright SpryMedia Ltd.
  22. *
  23. * This source file is free software, available under the following license:
  24. * MIT license - https://datatables.net/license
  25. *
  26. * This source file is distributed in the hope that it will be useful, but
  27. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  28. * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
  29. *
  30. * For details please refer to: https://www.datatables.net
  31. */
  32. (function( factory ) {
  33. "use strict";
  34. if ( typeof define === 'function' && define.amd ) {
  35. // AMD
  36. define( ['jquery'], function ( $ ) {
  37. return factory( $, window, document );
  38. } );
  39. }
  40. else if ( typeof exports === 'object' ) {
  41. // CommonJS
  42. // jQuery's factory checks for a global window - if it isn't present then it
  43. // returns a factory function that expects the window object
  44. var jq = require('jquery');
  45. if (typeof window === 'undefined') {
  46. module.exports = function (root, $) {
  47. if ( ! root ) {
  48. // CommonJS environments without a window global must pass a
  49. // root. This will give an error otherwise
  50. root = window;
  51. }
  52. if ( ! $ ) {
  53. $ = jq( root );
  54. }
  55. return factory( $, root, root.document );
  56. };
  57. }
  58. else {
  59. module.exports = factory( jq, window, window.document );
  60. }
  61. }
  62. else {
  63. // Browser
  64. window.DataTable = factory( jQuery, window, document );
  65. }
  66. }(function( $, window, document ) {
  67. "use strict";
  68. var DataTable = function ( selector, options )
  69. {
  70. // Check if called with a window or jQuery object for DOM less applications
  71. // This is for backwards compatibility
  72. if (DataTable.factory(selector, options)) {
  73. return DataTable;
  74. }
  75. // When creating with `new`, create a new DataTable, returning the API instance
  76. if (this instanceof DataTable) {
  77. return $(selector).DataTable(options);
  78. }
  79. else {
  80. // Argument switching
  81. options = selector;
  82. }
  83. var _that = this;
  84. var emptyInit = options === undefined;
  85. var len = this.length;
  86. if ( emptyInit ) {
  87. options = {};
  88. }
  89. // Method to get DT API instance from jQuery object
  90. this.api = function ()
  91. {
  92. return new _Api( this );
  93. };
  94. this.each(function() {
  95. // For each initialisation we want to give it a clean initialisation
  96. // object that can be bashed around
  97. var o = {};
  98. var oInit = len > 1 ? // optimisation for single table case
  99. _fnExtend( o, options, true ) :
  100. options;
  101. var i=0, iLen;
  102. var sId = this.getAttribute( 'id' );
  103. var bInitHandedOff = false;
  104. var defaults = DataTable.defaults;
  105. var $this = $(this);
  106. /* Sanity check */
  107. if ( this.nodeName.toLowerCase() != 'table' )
  108. {
  109. _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
  110. return;
  111. }
  112. $(this).trigger( 'options.dt', oInit );
  113. /* Backwards compatibility for the defaults */
  114. _fnCompatOpts( defaults );
  115. _fnCompatCols( defaults.column );
  116. /* Convert the camel-case defaults to Hungarian */
  117. _fnCamelToHungarian( defaults, defaults, true );
  118. _fnCamelToHungarian( defaults.column, defaults.column, true );
  119. /* Setting up the initialisation object */
  120. _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ), true );
  121. /* Check to see if we are re-initialising a table */
  122. var allSettings = DataTable.settings;
  123. for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
  124. {
  125. var s = allSettings[i];
  126. /* Base check on table node */
  127. if (
  128. s.nTable == this ||
  129. (s.nTHead && s.nTHead.parentNode == this) ||
  130. (s.nTFoot && s.nTFoot.parentNode == this)
  131. ) {
  132. var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
  133. var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
  134. if ( emptyInit || bRetrieve )
  135. {
  136. return s.oInstance;
  137. }
  138. else if ( bDestroy )
  139. {
  140. new DataTable.Api(s).destroy();
  141. break;
  142. }
  143. else
  144. {
  145. _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 );
  146. return;
  147. }
  148. }
  149. /* If the element we are initialising has the same ID as a table which was previously
  150. * initialised, but the table nodes don't match (from before) then we destroy the old
  151. * instance by simply deleting it. This is under the assumption that the table has been
  152. * destroyed by other methods. Anyone using non-id selectors will need to do this manually
  153. */
  154. if ( s.sTableId == this.id )
  155. {
  156. allSettings.splice( i, 1 );
  157. break;
  158. }
  159. }
  160. /* Ensure the table has an ID - required for accessibility */
  161. if ( sId === null || sId === "" )
  162. {
  163. sId = "DataTables_Table_"+(DataTable.ext._unique++);
  164. this.id = sId;
  165. }
  166. /* Create the settings object for this table and set some of the default parameters */
  167. var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
  168. "sDestroyWidth": $this[0].style.width,
  169. "sInstance": sId,
  170. "sTableId": sId,
  171. colgroup: $('<colgroup>').prependTo(this),
  172. fastData: function (row, column, type) {
  173. return _fnGetCellData(oSettings, row, column, type);
  174. }
  175. } );
  176. oSettings.nTable = this;
  177. oSettings.oInit = oInit;
  178. allSettings.push( oSettings );
  179. // Make a single API instance available for internal handling
  180. oSettings.api = new _Api( oSettings );
  181. // Need to add the instance after the instance after the settings object has been added
  182. // to the settings array, so we can self reference the table instance if more than one
  183. oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();
  184. // Backwards compatibility, before we apply all the defaults
  185. _fnCompatOpts( oInit );
  186. // If the length menu is given, but the init display length is not, use the length menu
  187. if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
  188. {
  189. oInit.iDisplayLength = Array.isArray(oInit.aLengthMenu[0])
  190. ? oInit.aLengthMenu[0][0]
  191. : $.isPlainObject( oInit.aLengthMenu[0] )
  192. ? oInit.aLengthMenu[0].value
  193. : oInit.aLengthMenu[0];
  194. }
  195. // Apply the defaults and init options to make a single init object will all
  196. // options defined from defaults and instance options.
  197. oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
  198. // Map the initialisation options onto the settings object
  199. _fnMap( oSettings.oFeatures, oInit, [
  200. "bPaginate",
  201. "bLengthChange",
  202. "bFilter",
  203. "bSort",
  204. "bSortMulti",
  205. "bInfo",
  206. "bProcessing",
  207. "bAutoWidth",
  208. "bSortClasses",
  209. "bServerSide",
  210. "bDeferRender"
  211. ] );
  212. _fnMap( oSettings, oInit, [
  213. "ajax",
  214. "fnFormatNumber",
  215. "sServerMethod",
  216. "aaSorting",
  217. "aaSortingFixed",
  218. "aLengthMenu",
  219. "sPaginationType",
  220. "iStateDuration",
  221. "bSortCellsTop",
  222. "iTabIndex",
  223. "sDom",
  224. "fnStateLoadCallback",
  225. "fnStateSaveCallback",
  226. "renderer",
  227. "searchDelay",
  228. "rowId",
  229. "caption",
  230. "layout",
  231. [ "iCookieDuration", "iStateDuration" ], // backwards compat
  232. [ "oSearch", "oPreviousSearch" ],
  233. [ "aoSearchCols", "aoPreSearchCols" ],
  234. [ "iDisplayLength", "_iDisplayLength" ]
  235. ] );
  236. _fnMap( oSettings.oScroll, oInit, [
  237. [ "sScrollX", "sX" ],
  238. [ "sScrollXInner", "sXInner" ],
  239. [ "sScrollY", "sY" ],
  240. [ "bScrollCollapse", "bCollapse" ]
  241. ] );
  242. _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
  243. /* Callback functions which are array driven */
  244. _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback );
  245. _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams );
  246. _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams );
  247. _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded );
  248. _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback );
  249. _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow );
  250. _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback );
  251. _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback );
  252. _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete );
  253. _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback );
  254. oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
  255. /* Browser support detection */
  256. _fnBrowserDetect( oSettings );
  257. var oClasses = oSettings.oClasses;
  258. $.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
  259. $this.addClass( oClasses.table );
  260. if (! oSettings.oFeatures.bPaginate) {
  261. oInit.iDisplayStart = 0;
  262. }
  263. if ( oSettings.iInitDisplayStart === undefined )
  264. {
  265. /* Display start point, taking into account the save saving */
  266. oSettings.iInitDisplayStart = oInit.iDisplayStart;
  267. oSettings._iDisplayStart = oInit.iDisplayStart;
  268. }
  269. /* Language definitions */
  270. var oLanguage = oSettings.oLanguage;
  271. $.extend( true, oLanguage, oInit.oLanguage );
  272. if ( oLanguage.sUrl )
  273. {
  274. /* Get the language definitions from a file - because this Ajax call makes the language
  275. * get async to the remainder of this function we use bInitHandedOff to indicate that
  276. * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
  277. */
  278. $.ajax( {
  279. dataType: 'json',
  280. url: oLanguage.sUrl,
  281. success: function ( json ) {
  282. _fnCamelToHungarian( defaults.oLanguage, json );
  283. $.extend( true, oLanguage, json, oSettings.oInit.oLanguage );
  284. _fnCallbackFire( oSettings, null, 'i18n', [oSettings], true);
  285. _fnInitialise( oSettings );
  286. },
  287. error: function () {
  288. // Error occurred loading language file
  289. _fnLog( oSettings, 0, 'i18n file loading error', 21 );
  290. // continue on as best we can
  291. _fnInitialise( oSettings );
  292. }
  293. } );
  294. bInitHandedOff = true;
  295. }
  296. else {
  297. _fnCallbackFire( oSettings, null, 'i18n', [oSettings]);
  298. }
  299. /*
  300. * Columns
  301. * See if we should load columns automatically or use defined ones
  302. */
  303. var columnsInit = [];
  304. var thead = this.getElementsByTagName('thead');
  305. var initHeaderLayout = _fnDetectHeader( oSettings, thead[0] );
  306. // If we don't have a columns array, then generate one with nulls
  307. if ( oInit.aoColumns ) {
  308. columnsInit = oInit.aoColumns;
  309. }
  310. else if ( initHeaderLayout.length ) {
  311. for ( i=0, iLen=initHeaderLayout[0].length ; i<iLen ; i++ ) {
  312. columnsInit.push( null );
  313. }
  314. }
  315. // Add the columns
  316. for ( i=0, iLen=columnsInit.length ; i<iLen ; i++ ) {
  317. _fnAddColumn( oSettings );
  318. }
  319. // Apply the column definitions
  320. _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, columnsInit, initHeaderLayout, function (iCol, oDef) {
  321. _fnColumnOptions( oSettings, iCol, oDef );
  322. } );
  323. /* HTML5 attribute detection - build an mData object automatically if the
  324. * attributes are found
  325. */
  326. var rowOne = $this.children('tbody').find('tr').eq(0);
  327. if ( rowOne.length ) {
  328. var a = function ( cell, name ) {
  329. return cell.getAttribute( 'data-'+name ) !== null ? name : null;
  330. };
  331. $( rowOne[0] ).children('th, td').each( function (i, cell) {
  332. var col = oSettings.aoColumns[i];
  333. if (! col) {
  334. _fnLog( oSettings, 0, 'Incorrect column count', 18 );
  335. }
  336. if ( col.mData === i ) {
  337. var sort = a( cell, 'sort' ) || a( cell, 'order' );
  338. var filter = a( cell, 'filter' ) || a( cell, 'search' );
  339. if ( sort !== null || filter !== null ) {
  340. col.mData = {
  341. _: i+'.display',
  342. sort: sort !== null ? i+'.@data-'+sort : undefined,
  343. type: sort !== null ? i+'.@data-'+sort : undefined,
  344. filter: filter !== null ? i+'.@data-'+filter : undefined
  345. };
  346. col._isArrayHost = true;
  347. _fnColumnOptions( oSettings, i );
  348. }
  349. }
  350. } );
  351. }
  352. var features = oSettings.oFeatures;
  353. var loadedInit = function () {
  354. /*
  355. * Sorting
  356. * @todo For modularisation (1.11) this needs to do into a sort start up handler
  357. */
  358. // If aaSorting is not defined, then we use the first indicator in asSorting
  359. // in case that has been altered, so the default sort reflects that option
  360. if ( oInit.aaSorting === undefined ) {
  361. var sorting = oSettings.aaSorting;
  362. for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) {
  363. sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
  364. }
  365. }
  366. /* Do a first pass on the sorting classes (allows any size changes to be taken into
  367. * account, and also will apply sorting disabled classes if disabled
  368. */
  369. _fnSortingClasses( oSettings );
  370. _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
  371. if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
  372. _fnSortingClasses( oSettings );
  373. }
  374. } );
  375. /*
  376. * Final init
  377. * Cache the header, body and footer as required, creating them if needed
  378. */
  379. var caption = $this.children('caption');
  380. if ( oSettings.caption ) {
  381. if ( caption.length === 0 ) {
  382. caption = $('<caption/>').appendTo( $this );
  383. }
  384. caption.html( oSettings.caption );
  385. }
  386. // Store the caption side, so we can remove the element from the document
  387. // when creating the element
  388. if (caption.length) {
  389. caption[0]._captionSide = caption.css('caption-side');
  390. oSettings.captionNode = caption[0];
  391. }
  392. if ( thead.length === 0 ) {
  393. thead = $('<thead/>').appendTo($this);
  394. }
  395. oSettings.nTHead = thead[0];
  396. $('tr', thead).addClass(oClasses.thead.row);
  397. var tbody = $this.children('tbody');
  398. if ( tbody.length === 0 ) {
  399. tbody = $('<tbody/>').insertAfter(thead);
  400. }
  401. oSettings.nTBody = tbody[0];
  402. var tfoot = $this.children('tfoot');
  403. if ( tfoot.length === 0 ) {
  404. // If we are a scrolling table, and no footer has been given, then we need to create
  405. // a tfoot element for the caption element to be appended to
  406. tfoot = $('<tfoot/>').appendTo($this);
  407. }
  408. oSettings.nTFoot = tfoot[0];
  409. $('tr', tfoot).addClass(oClasses.tfoot.row);
  410. // Check if there is data passing into the constructor
  411. if ( oInit.aaData ) {
  412. for ( i=0 ; i<oInit.aaData.length ; i++ ) {
  413. _fnAddData( oSettings, oInit.aaData[ i ] );
  414. }
  415. }
  416. else if ( _fnDataSource( oSettings ) == 'dom' ) {
  417. // Grab the data from the page
  418. _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
  419. }
  420. /* Copy the data index array */
  421. oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
  422. /* Initialisation complete - table can be drawn */
  423. oSettings.bInitialised = true;
  424. /* Check if we need to initialise the table (it might not have been handed off to the
  425. * language processor)
  426. */
  427. if ( bInitHandedOff === false ) {
  428. _fnInitialise( oSettings );
  429. }
  430. };
  431. /* Must be done after everything which can be overridden by the state saving! */
  432. _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState );
  433. if ( oInit.bStateSave )
  434. {
  435. features.bStateSave = true;
  436. _fnLoadState( oSettings, oInit, loadedInit );
  437. }
  438. else {
  439. loadedInit();
  440. }
  441. } );
  442. _that = null;
  443. return this;
  444. };
  445. /**
  446. * DataTables extensions
  447. *
  448. * This namespace acts as a collection area for plug-ins that can be used to
  449. * extend DataTables capabilities. Indeed many of the build in methods
  450. * use this method to provide their own capabilities (sorting methods for
  451. * example).
  452. *
  453. * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy
  454. * reasons
  455. *
  456. * @namespace
  457. */
  458. DataTable.ext = _ext = {
  459. /**
  460. * Buttons. For use with the Buttons extension for DataTables. This is
  461. * defined here so other extensions can define buttons regardless of load
  462. * order. It is _not_ used by DataTables core.
  463. *
  464. * @type object
  465. * @default {}
  466. */
  467. buttons: {},
  468. /**
  469. * Element class names
  470. *
  471. * @type object
  472. * @default {}
  473. */
  474. classes: {},
  475. /**
  476. * DataTables build type (expanded by the download builder)
  477. *
  478. * @type string
  479. */
  480. 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",
  481. /**
  482. * Error reporting.
  483. *
  484. * How should DataTables report an error. Can take the value 'alert',
  485. * 'throw', 'none' or a function.
  486. *
  487. * @type string|function
  488. * @default alert
  489. */
  490. errMode: "alert",
  491. /**
  492. * Legacy so v1 plug-ins don't throw js errors on load
  493. */
  494. feature: [],
  495. /**
  496. * Feature plug-ins.
  497. *
  498. * This is an object of callbacks which provide the features for DataTables
  499. * to be initialised via the `layout` option.
  500. */
  501. features: {},
  502. /**
  503. * Row searching.
  504. *
  505. * This method of searching is complimentary to the default type based
  506. * searching, and a lot more comprehensive as it allows you complete control
  507. * over the searching logic. Each element in this array is a function
  508. * (parameters described below) that is called for every row in the table,
  509. * and your logic decides if it should be included in the searching data set
  510. * or not.
  511. *
  512. * Searching functions have the following input parameters:
  513. *
  514. * 1. `{object}` DataTables settings object: see
  515. * {@link DataTable.models.oSettings}
  516. * 2. `{array|object}` Data for the row to be processed (same as the
  517. * original format that was passed in as the data source, or an array
  518. * from a DOM data source
  519. * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which
  520. * can be useful to retrieve the `TR` element if you need DOM interaction.
  521. *
  522. * And the following return is expected:
  523. *
  524. * * {boolean} Include the row in the searched result set (true) or not
  525. * (false)
  526. *
  527. * Note that as with the main search ability in DataTables, technically this
  528. * is "filtering", since it is subtractive. However, for consistency in
  529. * naming we call it searching here.
  530. *
  531. * @type array
  532. * @default []
  533. *
  534. * @example
  535. * // The following example shows custom search being applied to the
  536. * // fourth column (i.e. the data[3] index) based on two input values
  537. * // from the end-user, matching the data in a certain range.
  538. * $.fn.dataTable.ext.search.push(
  539. * function( settings, data, dataIndex ) {
  540. * var min = document.getElementById('min').value * 1;
  541. * var max = document.getElementById('max').value * 1;
  542. * var version = data[3] == "-" ? 0 : data[3]*1;
  543. *
  544. * if ( min == "" && max == "" ) {
  545. * return true;
  546. * }
  547. * else if ( min == "" && version < max ) {
  548. * return true;
  549. * }
  550. * else if ( min < version && "" == max ) {
  551. * return true;
  552. * }
  553. * else if ( min < version && version < max ) {
  554. * return true;
  555. * }
  556. * return false;
  557. * }
  558. * );
  559. */
  560. search: [],
  561. /**
  562. * Selector extensions
  563. *
  564. * The `selector` option can be used to extend the options available for the
  565. * selector modifier options (`selector-modifier` object data type) that
  566. * each of the three built in selector types offer (row, column and cell +
  567. * their plural counterparts). For example the Select extension uses this
  568. * mechanism to provide an option to select only rows, columns and cells
  569. * that have been marked as selected by the end user (`{selected: true}`),
  570. * which can be used in conjunction with the existing built in selector
  571. * options.
  572. *
  573. * Each property is an array to which functions can be pushed. The functions
  574. * take three attributes:
  575. *
  576. * * Settings object for the host table
  577. * * Options object (`selector-modifier` object type)
  578. * * Array of selected item indexes
  579. *
  580. * The return is an array of the resulting item indexes after the custom
  581. * selector has been applied.
  582. *
  583. * @type object
  584. */
  585. selector: {
  586. cell: [],
  587. column: [],
  588. row: []
  589. },
  590. /**
  591. * Legacy configuration options. Enable and disable legacy options that
  592. * are available in DataTables.
  593. *
  594. * @type object
  595. */
  596. legacy: {
  597. /**
  598. * Enable / disable DataTables 1.9 compatible server-side processing
  599. * requests
  600. *
  601. * @type boolean
  602. * @default null
  603. */
  604. ajax: null
  605. },
  606. /**
  607. * Pagination plug-in methods.
  608. *
  609. * Each entry in this object is a function and defines which buttons should
  610. * be shown by the pagination rendering method that is used for the table:
  611. * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the
  612. * buttons are displayed in the document, while the functions here tell it
  613. * what buttons to display. This is done by returning an array of button
  614. * descriptions (what each button will do).
  615. *
  616. * Pagination types (the four built in options and any additional plug-in
  617. * options defined here) can be used through the `paginationType`
  618. * initialisation parameter.
  619. *
  620. * The functions defined take two parameters:
  621. *
  622. * 1. `{int} page` The current page index
  623. * 2. `{int} pages` The number of pages in the table
  624. *
  625. * Each function is expected to return an array where each element of the
  626. * array can be one of:
  627. *
  628. * * `first` - Jump to first page when activated
  629. * * `last` - Jump to last page when activated
  630. * * `previous` - Show previous page when activated
  631. * * `next` - Show next page when activated
  632. * * `{int}` - Show page of the index given
  633. * * `{array}` - A nested array containing the above elements to add a
  634. * containing 'DIV' element (might be useful for styling).
  635. *
  636. * Note that DataTables v1.9- used this object slightly differently whereby
  637. * an object with two functions would be defined for each plug-in. That
  638. * ability is still supported by DataTables 1.10+ to provide backwards
  639. * compatibility, but this option of use is now decremented and no longer
  640. * documented in DataTables 1.10+.
  641. *
  642. * @type object
  643. * @default {}
  644. *
  645. * @example
  646. * // Show previous, next and current page buttons only
  647. * $.fn.dataTableExt.oPagination.current = function ( page, pages ) {
  648. * return [ 'previous', page, 'next' ];
  649. * };
  650. */
  651. pager: {},
  652. renderer: {
  653. pageButton: {},
  654. header: {}
  655. },
  656. /**
  657. * Ordering plug-ins - custom data source
  658. *
  659. * The extension options for ordering of data available here is complimentary
  660. * to the default type based ordering that DataTables typically uses. It
  661. * allows much greater control over the the data that is being used to
  662. * order a column, but is necessarily therefore more complex.
  663. *
  664. * This type of ordering is useful if you want to do ordering based on data
  665. * live from the DOM (for example the contents of an 'input' element) rather
  666. * than just the static string that DataTables knows of.
  667. *
  668. * The way these plug-ins work is that you create an array of the values you
  669. * wish to be ordering for the column in question and then return that
  670. * array. The data in the array much be in the index order of the rows in
  671. * the table (not the currently ordering order!). Which order data gathering
  672. * function is run here depends on the `dt-init columns.orderDataType`
  673. * parameter that is used for the column (if any).
  674. *
  675. * The functions defined take two parameters:
  676. *
  677. * 1. `{object}` DataTables settings object: see
  678. * {@link DataTable.models.oSettings}
  679. * 2. `{int}` Target column index
  680. *
  681. * Each function is expected to return an array:
  682. *
  683. * * `{array}` Data for the column to be ordering upon
  684. *
  685. * @type array
  686. *
  687. * @example
  688. * // Ordering using `input` node values
  689. * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col )
  690. * {
  691. * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) {
  692. * return $('input', td).val();
  693. * } );
  694. * }
  695. */
  696. order: {},
  697. /**
  698. * Type based plug-ins.
  699. *
  700. * Each column in DataTables has a type assigned to it, either by automatic
  701. * detection or by direct assignment using the `type` option for the column.
  702. * The type of a column will effect how it is ordering and search (plug-ins
  703. * can also make use of the column type if required).
  704. *
  705. * @namespace
  706. */
  707. type: {
  708. /**
  709. * Automatic column class assignment
  710. */
  711. className: {},
  712. /**
  713. * Type detection functions.
  714. *
  715. * The functions defined in this object are used to automatically detect
  716. * a column's type, making initialisation of DataTables super easy, even
  717. * when complex data is in the table.
  718. *
  719. * The functions defined take two parameters:
  720. *
  721. * 1. `{*}` Data from the column cell to be analysed
  722. * 2. `{settings}` DataTables settings object. This can be used to
  723. * perform context specific type detection - for example detection
  724. * based on language settings such as using a comma for a decimal
  725. * place. Generally speaking the options from the settings will not
  726. * be required
  727. *
  728. * Each function is expected to return:
  729. *
  730. * * `{string|null}` Data type detected, or null if unknown (and thus
  731. * pass it on to the other type detection functions.
  732. *
  733. * @type array
  734. *
  735. * @example
  736. * // Currency type detection plug-in:
  737. * $.fn.dataTable.ext.type.detect.push(
  738. * function ( data, settings ) {
  739. * // Check the numeric part
  740. * if ( ! data.substring(1).match(/[0-9]/) ) {
  741. * return null;
  742. * }
  743. *
  744. * // Check prefixed by currency
  745. * if ( data.charAt(0) == '$' || data.charAt(0) == '&pound;' ) {
  746. * return 'currency';
  747. * }
  748. * return null;
  749. * }
  750. * );
  751. */
  752. detect: [],
  753. /**
  754. * Automatic renderer assignment
  755. */
  756. render: {},
  757. /**
  758. * Type based search formatting.
  759. *
  760. * The type based searching functions can be used to pre-format the
  761. * data to be search on. For example, it can be used to strip HTML
  762. * tags or to de-format telephone numbers for numeric only searching.
  763. *
  764. * Note that is a search is not defined for a column of a given type,
  765. * no search formatting will be performed.
  766. *
  767. * Pre-processing of searching data plug-ins - When you assign the sType
  768. * for a column (or have it automatically detected for you by DataTables
  769. * or a type detection plug-in), you will typically be using this for
  770. * custom sorting, but it can also be used to provide custom searching
  771. * by allowing you to pre-processing the data and returning the data in
  772. * the format that should be searched upon. This is done by adding
  773. * functions this object with a parameter name which matches the sType
  774. * for that target column. This is the corollary of <i>afnSortData</i>
  775. * for searching data.
  776. *
  777. * The functions defined take a single parameter:
  778. *
  779. * 1. `{*}` Data from the column cell to be prepared for searching
  780. *
  781. * Each function is expected to return:
  782. *
  783. * * `{string|null}` Formatted string that will be used for the searching.
  784. *
  785. * @type object
  786. * @default {}
  787. *
  788. * @example
  789. * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) {
  790. * return d.replace(/\n/g," ").replace( /<.*?>/g, "" );
  791. * }
  792. */
  793. search: {},
  794. /**
  795. * Type based ordering.
  796. *
  797. * The column type tells DataTables what ordering to apply to the table
  798. * when a column is sorted upon. The order for each type that is defined,
  799. * is defined by the functions available in this object.
  800. *
  801. * Each ordering option can be described by three properties added to
  802. * this object:
  803. *
  804. * * `{type}-pre` - Pre-formatting function
  805. * * `{type}-asc` - Ascending order function
  806. * * `{type}-desc` - Descending order function
  807. *
  808. * All three can be used together, only `{type}-pre` or only
  809. * `{type}-asc` and `{type}-desc` together. It is generally recommended
  810. * that only `{type}-pre` is used, as this provides the optimal
  811. * implementation in terms of speed, although the others are provided
  812. * for compatibility with existing Javascript sort functions.
  813. *
  814. * `{type}-pre`: Functions defined take a single parameter:
  815. *
  816. * 1. `{*}` Data from the column cell to be prepared for ordering
  817. *
  818. * And return:
  819. *
  820. * * `{*}` Data to be sorted upon
  821. *
  822. * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort
  823. * functions, taking two parameters:
  824. *
  825. * 1. `{*}` Data to compare to the second parameter
  826. * 2. `{*}` Data to compare to the first parameter
  827. *
  828. * And returning:
  829. *
  830. * * `{*}` Ordering match: <0 if first parameter should be sorted lower
  831. * than the second parameter, ===0 if the two parameters are equal and
  832. * >0 if the first parameter should be sorted height than the second
  833. * parameter.
  834. *
  835. * @type object
  836. * @default {}
  837. *
  838. * @example
  839. * // Numeric ordering of formatted numbers with a pre-formatter
  840. * $.extend( $.fn.dataTable.ext.type.order, {
  841. * "string-pre": function(x) {
  842. * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" );
  843. * return parseFloat( a );
  844. * }
  845. * } );
  846. *
  847. * @example
  848. * // Case-sensitive string ordering, with no pre-formatting method
  849. * $.extend( $.fn.dataTable.ext.order, {
  850. * "string-case-asc": function(x,y) {
  851. * return ((x < y) ? -1 : ((x > y) ? 1 : 0));
  852. * },
  853. * "string-case-desc": function(x,y) {
  854. * return ((x < y) ? 1 : ((x > y) ? -1 : 0));
  855. * }
  856. * } );
  857. */
  858. order: {}
  859. },
  860. /**
  861. * Unique DataTables instance counter
  862. *
  863. * @type int
  864. * @private
  865. */
  866. _unique: 0,
  867. //
  868. // Depreciated
  869. // The following properties are retained for backwards compatibility only.
  870. // The should not be used in new projects and will be removed in a future
  871. // version
  872. //
  873. /**
  874. * Version check function.
  875. * @type function
  876. * @depreciated Since 1.10
  877. */
  878. fnVersionCheck: DataTable.fnVersionCheck,
  879. /**
  880. * Index for what 'this' index API functions should use
  881. * @type int
  882. * @deprecated Since v1.10
  883. */
  884. iApiIndex: 0,
  885. /**
  886. * Software version
  887. * @type string
  888. * @deprecated Since v1.10
  889. */
  890. sVersion: DataTable.version
  891. };
  892. //
  893. // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
  894. //
  895. $.extend( _ext, {
  896. afnFiltering: _ext.search,
  897. aTypes: _ext.type.detect,
  898. ofnSearch: _ext.type.search,
  899. oSort: _ext.type.order,
  900. afnSortData: _ext.order,
  901. aoFeatures: _ext.feature,
  902. oStdClasses: _ext.classes,
  903. oPagination: _ext.pager
  904. } );
  905. $.extend( DataTable.ext.classes, {
  906. container: 'dt-container',
  907. empty: {
  908. row: 'dt-empty'
  909. },
  910. info: {
  911. container: 'dt-info'
  912. },
  913. length: {
  914. container: 'dt-length',
  915. select: 'dt-input'
  916. },
  917. order: {
  918. canAsc: 'dt-orderable-asc',
  919. canDesc: 'dt-orderable-desc',
  920. isAsc: 'dt-ordering-asc',
  921. isDesc: 'dt-ordering-desc',
  922. none: 'dt-orderable-none',
  923. position: 'sorting_'
  924. },
  925. processing: {
  926. container: 'dt-processing'
  927. },
  928. scrolling: {
  929. body: 'dt-scroll-body',
  930. container: 'dt-scroll',
  931. footer: {
  932. self: 'dt-scroll-foot',
  933. inner: 'dt-scroll-footInner'
  934. },
  935. header: {
  936. self: 'dt-scroll-head',
  937. inner: 'dt-scroll-headInner'
  938. }
  939. },
  940. search: {
  941. container: 'dt-search',
  942. input: 'dt-input'
  943. },
  944. table: 'dataTable',
  945. tbody: {
  946. cell: '',
  947. row: ''
  948. },
  949. thead: {
  950. cell: '',
  951. row: ''
  952. },
  953. tfoot: {
  954. cell: '',
  955. row: ''
  956. },
  957. paging: {
  958. active: 'current',
  959. button: 'dt-paging-button',
  960. container: 'dt-paging',
  961. disabled: 'disabled'
  962. }
  963. } );
  964. /*
  965. * It is useful to have variables which are scoped locally so only the
  966. * DataTables functions can access them and they don't leak into global space.
  967. * At the same time these functions are often useful over multiple files in the
  968. * core and API, so we list, or at least document, all variables which are used
  969. * by DataTables as private variables here. This also ensures that there is no
  970. * clashing of variable names and that they can easily referenced for reuse.
  971. */
  972. // Defined else where
  973. // _selector_run
  974. // _selector_opts
  975. // _selector_row_indexes
  976. var _ext; // DataTable.ext
  977. var _Api; // DataTable.Api
  978. var _api_register; // DataTable.Api.register
  979. var _api_registerPlural; // DataTable.Api.registerPlural
  980. var _re_dic = {};
  981. var _re_new_lines = /[\r\n\u2028]/g;
  982. var _re_html = /<([^>]*>)/g;
  983. var _max_str_len = Math.pow(2, 28);
  984. // This is not strict ISO8601 - Date.parse() is quite lax, although
  985. // implementations differ between browsers.
  986. var _re_date = /^\d{2,4}[./-]\d{1,2}[./-]\d{1,2}([T ]{1}\d{1,2}[:.]\d{2}([.:]\d{2})?)?$/;
  987. // Escape regular expression special characters
  988. var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
  989. // https://en.wikipedia.org/wiki/Foreign_exchange_market
  990. // - \u20BD - Russian ruble.
  991. // - \u20a9 - South Korean Won
  992. // - \u20BA - Turkish Lira
  993. // - \u20B9 - Indian Rupee
  994. // - R - Brazil (R$) and South Africa
  995. // - fr - Swiss Franc
  996. // - kr - Swedish krona, Norwegian krone and Danish krone
  997. // - \u2009 is thin space and \u202F is narrow no-break space, both used in many
  998. // - Ƀ - Bitcoin
  999. // - Ξ - Ethereum
  1000. // standards as thousands separators.
  1001. var _re_formatted_numeric = /['\u00A0,$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi;
  1002. var _empty = function ( d ) {
  1003. return !d || d === true || d === '-' ? true : false;
  1004. };
  1005. var _intVal = function ( s ) {
  1006. var integer = parseInt( s, 10 );
  1007. return !isNaN(integer) && isFinite(s) ? integer : null;
  1008. };
  1009. // Convert from a formatted number with characters other than `.` as the
  1010. // decimal place, to a Javascript number
  1011. var _numToDecimal = function ( num, decimalPoint ) {
  1012. // Cache created regular expressions for speed as this function is called often
  1013. if ( ! _re_dic[ decimalPoint ] ) {
  1014. _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' );
  1015. }
  1016. return typeof num === 'string' && decimalPoint !== '.' ?
  1017. num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :
  1018. num;
  1019. };
  1020. var _isNumber = function ( d, decimalPoint, formatted ) {
  1021. var type = typeof d;
  1022. var strType = type === 'string';
  1023. if ( type === 'number' || type === 'bigint') {
  1024. return true;
  1025. }
  1026. // If empty return immediately so there must be a number if it is a
  1027. // formatted string (this stops the string "k", or "kr", etc being detected
  1028. // as a formatted number for currency
  1029. if ( _empty( d ) ) {
  1030. return true;
  1031. }
  1032. if ( decimalPoint && strType ) {
  1033. d = _numToDecimal( d, decimalPoint );
  1034. }
  1035. if ( formatted && strType ) {
  1036. d = d.replace( _re_formatted_numeric, '' );
  1037. }
  1038. return !isNaN( parseFloat(d) ) && isFinite( d );
  1039. };
  1040. // A string without HTML in it can be considered to be HTML still
  1041. var _isHtml = function ( d ) {
  1042. return _empty( d ) || typeof d === 'string';
  1043. };
  1044. // Is a string a number surrounded by HTML?
  1045. var _htmlNumeric = function ( d, decimalPoint, formatted ) {
  1046. if ( _empty( d ) ) {
  1047. return true;
  1048. }
  1049. // input and select strings mean that this isn't just a number
  1050. if (typeof d === 'string' && d.match(/<(input|select)/i)) {
  1051. return null;
  1052. }
  1053. var html = _isHtml( d );
  1054. return ! html ?
  1055. null :
  1056. _isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
  1057. true :
  1058. null;
  1059. };
  1060. var _pluck = function ( a, prop, prop2 ) {
  1061. var out = [];
  1062. var i=0, ien=a.length;
  1063. // Could have the test in the loop for slightly smaller code, but speed
  1064. // is essential here
  1065. if ( prop2 !== undefined ) {
  1066. for ( ; i<ien ; i++ ) {
  1067. if ( a[i] && a[i][ prop ] ) {
  1068. out.push( a[i][ prop ][ prop2 ] );
  1069. }
  1070. }
  1071. }
  1072. else {
  1073. for ( ; i<ien ; i++ ) {
  1074. if ( a[i] ) {
  1075. out.push( a[i][ prop ] );
  1076. }
  1077. }
  1078. }
  1079. return out;
  1080. };
  1081. // Basically the same as _pluck, but rather than looping over `a` we use `order`
  1082. // as the indexes to pick from `a`
  1083. var _pluck_order = function ( a, order, prop, prop2 )
  1084. {
  1085. var out = [];
  1086. var i=0, ien=order.length;
  1087. // Could have the test in the loop for slightly smaller code, but speed
  1088. // is essential here
  1089. if ( prop2 !== undefined ) {
  1090. for ( ; i<ien ; i++ ) {
  1091. if ( a[ order[i] ][ prop ] ) {
  1092. out.push( a[ order[i] ][ prop ][ prop2 ] );
  1093. }
  1094. }
  1095. }
  1096. else {
  1097. for ( ; i<ien ; i++ ) {
  1098. if ( a[ order[i] ] ) {
  1099. out.push( a[ order[i] ][ prop ] );
  1100. }
  1101. }
  1102. }
  1103. return out;
  1104. };
  1105. var _range = function ( len, start )
  1106. {
  1107. var out = [];
  1108. var end;
  1109. if ( start === undefined ) {
  1110. start = 0;
  1111. end = len;
  1112. }
  1113. else {
  1114. end = start;
  1115. start = len;
  1116. }
  1117. for ( var i=start ; i<end ; i++ ) {
  1118. out.push( i );
  1119. }
  1120. return out;
  1121. };
  1122. var _removeEmpty = function ( a )
  1123. {
  1124. var out = [];
  1125. for ( var i=0, ien=a.length ; i<ien ; i++ ) {
  1126. if ( a[i] ) { // careful - will remove all falsy values!
  1127. out.push( a[i] );
  1128. }
  1129. }
  1130. return out;
  1131. };
  1132. // Replaceable function in api.util
  1133. var _stripHtml = function (input) {
  1134. // Irrelevant check to workaround CodeQL's false positive on the regex
  1135. if (input.length > _max_str_len) {
  1136. throw new Error('Exceeded max str len');
  1137. }
  1138. var previous;
  1139. input = input.replace(_re_html, ''); // Complete tags
  1140. // Safety for incomplete script tag - use do / while to ensure that
  1141. // we get all instances
  1142. do {
  1143. previous = input;
  1144. input = input.replace(/<script/i, '');
  1145. } while (input !== previous);
  1146. return previous;
  1147. };
  1148. // Replaceable function in api.util
  1149. var _escapeHtml = function ( d ) {
  1150. if (Array.isArray(d)) {
  1151. d = d.join(',');
  1152. }
  1153. return typeof d === 'string' ?
  1154. d
  1155. .replace(/&/g, '&amp;')
  1156. .replace(/</g, '&lt;')
  1157. .replace(/>/g, '&gt;')
  1158. .replace(/"/g, '&quot;') :
  1159. d;
  1160. };
  1161. // Remove diacritics from a string by decomposing it and then removing
  1162. // non-ascii characters
  1163. var _normalize = function (str, both) {
  1164. if (typeof str !== 'string') {
  1165. return str;
  1166. }
  1167. // It is faster to just run `normalize` than it is to check if
  1168. // we need to with a regex!
  1169. var res = str.normalize("NFD");
  1170. // Equally, here we check if a regex is needed or not
  1171. return res.length !== str.length
  1172. ? (both === true ? str + ' ' : '' ) + res.replace(/[\u0300-\u036f]/g, "")
  1173. : res;
  1174. }
  1175. /**
  1176. * Determine if all values in the array are unique. This means we can short
  1177. * cut the _unique method at the cost of a single loop. A sorted array is used
  1178. * to easily check the values.
  1179. *
  1180. * @param {array} src Source array
  1181. * @return {boolean} true if all unique, false otherwise
  1182. * @ignore
  1183. */
  1184. var _areAllUnique = function ( src ) {
  1185. if ( src.length < 2 ) {
  1186. return true;
  1187. }
  1188. var sorted = src.slice().sort();
  1189. var last = sorted[0];
  1190. for ( var i=1, ien=sorted.length ; i<ien ; i++ ) {
  1191. if ( sorted[i] === last ) {
  1192. return false;
  1193. }
  1194. last = sorted[i];
  1195. }
  1196. return true;
  1197. };
  1198. /**
  1199. * Find the unique elements in a source array.
  1200. *
  1201. * @param {array} src Source array
  1202. * @return {array} Array of unique items
  1203. * @ignore
  1204. */
  1205. var _unique = function ( src )
  1206. {
  1207. if (Array.from && Set) {
  1208. return Array.from(new Set(src));
  1209. }
  1210. if ( _areAllUnique( src ) ) {
  1211. return src.slice();
  1212. }
  1213. // A faster unique method is to use object keys to identify used values,
  1214. // but this doesn't work with arrays or objects, which we must also
  1215. // consider. See jsperf.app/compare-array-unique-versions/4 for more
  1216. // information.
  1217. var
  1218. out = [],
  1219. val,
  1220. i, ien=src.length,
  1221. j, k=0;
  1222. again: for ( i=0 ; i<ien ; i++ ) {
  1223. val = src[i];
  1224. for ( j=0 ; j<k ; j++ ) {
  1225. if ( out[j] === val ) {
  1226. continue again;
  1227. }
  1228. }
  1229. out.push( val );
  1230. k++;
  1231. }
  1232. return out;
  1233. };
  1234. // Surprisingly this is faster than [].concat.apply
  1235. // https://jsperf.com/flatten-an-array-loop-vs-reduce/2
  1236. var _flatten = function (out, val) {
  1237. if (Array.isArray(val)) {
  1238. for (var i=0 ; i<val.length ; i++) {
  1239. _flatten(out, val[i]);
  1240. }
  1241. }
  1242. else {
  1243. out.push(val);
  1244. }
  1245. return out;
  1246. }
  1247. // Similar to jQuery's addClass, but use classList.add
  1248. function _addClass(el, name) {
  1249. if (name) {
  1250. name.split(' ').forEach(function (n) {
  1251. if (n) {
  1252. // `add` does deduplication, so no need to check `contains`
  1253. el.classList.add(n);
  1254. }
  1255. });
  1256. }
  1257. }
  1258. /**
  1259. * DataTables utility methods
  1260. *
  1261. * This namespace provides helper methods that DataTables uses internally to
  1262. * create a DataTable, but which are not exclusively used only for DataTables.
  1263. * These methods can be used by extension authors to save the duplication of
  1264. * code.
  1265. *
  1266. * @namespace
  1267. */
  1268. DataTable.util = {
  1269. /**
  1270. * Return a string with diacritic characters decomposed
  1271. * @param {*} mixed Function or string to normalize
  1272. * @param {*} both Return original string and the normalized string
  1273. * @returns String or undefined
  1274. */
  1275. diacritics: function (mixed, both) {
  1276. var type = typeof mixed;
  1277. if (type !== 'function') {
  1278. return _normalize(mixed, both);
  1279. }
  1280. _normalize = mixed;
  1281. },
  1282. /**
  1283. * Debounce a function
  1284. *
  1285. * @param {function} fn Function to be called
  1286. * @param {integer} freq Call frequency in mS
  1287. * @return {function} Wrapped function
  1288. */
  1289. debounce: function ( fn, timeout ) {
  1290. var timer;
  1291. return function () {
  1292. var that = this;
  1293. var args = arguments;
  1294. clearTimeout(timer);
  1295. timer = setTimeout( function () {
  1296. fn.apply(that, args);
  1297. }, timeout || 250 );
  1298. };
  1299. },
  1300. /**
  1301. * Throttle the calls to a function. Arguments and context are maintained
  1302. * for the throttled function.
  1303. *
  1304. * @param {function} fn Function to be called
  1305. * @param {integer} freq Call frequency in mS
  1306. * @return {function} Wrapped function
  1307. */
  1308. throttle: function ( fn, freq ) {
  1309. var
  1310. frequency = freq !== undefined ? freq : 200,
  1311. last,
  1312. timer;
  1313. return function () {
  1314. var
  1315. that = this,
  1316. now = +new Date(),
  1317. args = arguments;
  1318. if ( last && now < last + frequency ) {
  1319. clearTimeout( timer );
  1320. timer = setTimeout( function () {
  1321. last = undefined;
  1322. fn.apply( that, args );
  1323. }, frequency );
  1324. }
  1325. else {
  1326. last = now;
  1327. fn.apply( that, args );
  1328. }
  1329. };
  1330. },
  1331. /**
  1332. * Escape a string such that it can be used in a regular expression
  1333. *
  1334. * @param {string} val string to escape
  1335. * @returns {string} escaped string
  1336. */
  1337. escapeRegex: function ( val ) {
  1338. return val.replace( _re_escape_regex, '\\$1' );
  1339. },
  1340. /**
  1341. * Create a function that will write to a nested object or array
  1342. * @param {*} source JSON notation string
  1343. * @returns Write function
  1344. */
  1345. set: function ( source ) {
  1346. if ( $.isPlainObject( source ) ) {
  1347. /* Unlike get, only the underscore (global) option is used for for
  1348. * setting data since we don't know the type here. This is why an object
  1349. * option is not documented for `mData` (which is read/write), but it is
  1350. * for `mRender` which is read only.
  1351. */
  1352. return DataTable.util.set( source._ );
  1353. }
  1354. else if ( source === null ) {
  1355. // Nothing to do when the data source is null
  1356. return function () {};
  1357. }
  1358. else if ( typeof source === 'function' ) {
  1359. return function (data, val, meta) {
  1360. source( data, 'set', val, meta );
  1361. };
  1362. }
  1363. else if (
  1364. typeof source === 'string' && (source.indexOf('.') !== -1 ||
  1365. source.indexOf('[') !== -1 || source.indexOf('(') !== -1)
  1366. ) {
  1367. // Like the get, we need to get data from a nested object
  1368. var setData = function (data, val, src) {
  1369. var a = _fnSplitObjNotation( src ), b;
  1370. var aLast = a[a.length-1];
  1371. var arrayNotation, funcNotation, o, innerSrc;
  1372. for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ ) {
  1373. // Protect against prototype pollution
  1374. if (a[i] === '__proto__' || a[i] === 'constructor') {
  1375. throw new Error('Cannot set prototype values');
  1376. }
  1377. // Check if we are dealing with an array notation request
  1378. arrayNotation = a[i].match(__reArray);
  1379. funcNotation = a[i].match(__reFn);
  1380. if ( arrayNotation ) {
  1381. a[i] = a[i].replace(__reArray, '');
  1382. data[ a[i] ] = [];
  1383. // Get the remainder of the nested object to set so we can recurse
  1384. b = a.slice();
  1385. b.splice( 0, i+1 );
  1386. innerSrc = b.join('.');
  1387. // Traverse each entry in the array setting the properties requested
  1388. if ( Array.isArray( val ) ) {
  1389. for ( var j=0, jLen=val.length ; j<jLen ; j++ ) {
  1390. o = {};
  1391. setData( o, val[j], innerSrc );
  1392. data[ a[i] ].push( o );
  1393. }
  1394. }
  1395. else {
  1396. // We've been asked to save data to an array, but it
  1397. // isn't array data to be saved. Best that can be done
  1398. // is to just save the value.
  1399. data[ a[i] ] = val;
  1400. }
  1401. // The inner call to setData has already traversed through the remainder
  1402. // of the source and has set the data, thus we can exit here
  1403. return;
  1404. }
  1405. else if ( funcNotation ) {
  1406. // Function call
  1407. a[i] = a[i].replace(__reFn, '');
  1408. data = data[ a[i] ]( val );
  1409. }
  1410. // If the nested object doesn't currently exist - since we are
  1411. // trying to set the value - create it
  1412. if ( data[ a[i] ] === null || data[ a[i] ] === undefined ) {
  1413. data[ a[i] ] = {};
  1414. }
  1415. data = data[ a[i] ];
  1416. }
  1417. // Last item in the input - i.e, the actual set
  1418. if ( aLast.match(__reFn ) ) {
  1419. // Function call
  1420. data = data[ aLast.replace(__reFn, '') ]( val );
  1421. }
  1422. else {
  1423. // If array notation is used, we just want to strip it and use the property name
  1424. // and assign the value. If it isn't used, then we get the result we want anyway
  1425. data[ aLast.replace(__reArray, '') ] = val;
  1426. }
  1427. };
  1428. return function (data, val) { // meta is also passed in, but not used
  1429. return setData( data, val, source );
  1430. };
  1431. }
  1432. else {
  1433. // Array or flat object mapping
  1434. return function (data, val) { // meta is also passed in, but not used
  1435. data[source] = val;
  1436. };
  1437. }
  1438. },
  1439. /**
  1440. * Create a function that will read nested objects from arrays, based on JSON notation
  1441. * @param {*} source JSON notation string
  1442. * @returns Value read
  1443. */
  1444. get: function ( source ) {
  1445. if ( $.isPlainObject( source ) ) {
  1446. // Build an object of get functions, and wrap them in a single call
  1447. var o = {};
  1448. $.each( source, function (key, val) {
  1449. if ( val ) {
  1450. o[key] = DataTable.util.get( val );
  1451. }
  1452. } );
  1453. return function (data, type, row, meta) {
  1454. var t = o[type] || o._;
  1455. return t !== undefined ?
  1456. t(data, type, row, meta) :
  1457. data;
  1458. };
  1459. }
  1460. else if ( source === null ) {
  1461. // Give an empty string for rendering / sorting etc
  1462. return function (data) { // type, row and meta also passed, but not used
  1463. return data;
  1464. };
  1465. }
  1466. else if ( typeof source === 'function' ) {
  1467. return function (data, type, row, meta) {
  1468. return source( data, type, row, meta );
  1469. };
  1470. }
  1471. else if (
  1472. typeof source === 'string' && (source.indexOf('.') !== -1 ||
  1473. source.indexOf('[') !== -1 || source.indexOf('(') !== -1)
  1474. ) {
  1475. /* If there is a . in the source string then the data source is in a
  1476. * nested object so we loop over the data for each level to get the next
  1477. * level down. On each loop we test for undefined, and if found immediately
  1478. * return. This allows entire objects to be missing and sDefaultContent to
  1479. * be used if defined, rather than throwing an error
  1480. */
  1481. var fetchData = function (data, type, src) {
  1482. var arrayNotation, funcNotation, out, innerSrc;
  1483. if ( src !== "" ) {
  1484. var a = _fnSplitObjNotation( src );
  1485. for ( var i=0, iLen=a.length ; i<iLen ; i++ ) {
  1486. // Check if we are dealing with special notation
  1487. arrayNotation = a[i].match(__reArray);
  1488. funcNotation = a[i].match(__reFn);
  1489. if ( arrayNotation ) {
  1490. // Array notation
  1491. a[i] = a[i].replace(__reArray, '');
  1492. // Condition allows simply [] to be passed in
  1493. if ( a[i] !== "" ) {
  1494. data = data[ a[i] ];
  1495. }
  1496. out = [];
  1497. // Get the remainder of the nested object to get
  1498. a.splice( 0, i+1 );
  1499. innerSrc = a.join('.');
  1500. // Traverse each entry in the array getting the properties requested
  1501. if ( Array.isArray( data ) ) {
  1502. for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
  1503. out.push( fetchData( data[j], type, innerSrc ) );
  1504. }
  1505. }
  1506. // If a string is given in between the array notation indicators, that
  1507. // is used to join the strings together, otherwise an array is returned
  1508. var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
  1509. data = (join==="") ? out : out.join(join);
  1510. // The inner call to fetchData has already traversed through the remainder
  1511. // of the source requested, so we exit from the loop
  1512. break;
  1513. }
  1514. else if ( funcNotation ) {
  1515. // Function call
  1516. a[i] = a[i].replace(__reFn, '');
  1517. data = data[ a[i] ]();
  1518. continue;
  1519. }
  1520. if (data === null || data[ a[i] ] === null) {
  1521. return null;
  1522. }
  1523. else if ( data === undefined || data[ a[i] ] === undefined ) {
  1524. return undefined;
  1525. }
  1526. data = data[ a[i] ];
  1527. }
  1528. }
  1529. return data;
  1530. };
  1531. return function (data, type) { // row and meta also passed, but not used
  1532. return fetchData( data, type, source );
  1533. };
  1534. }
  1535. else {
  1536. // Array or flat object mapping
  1537. return function (data) { // row and meta also passed, but not used
  1538. return data[source];
  1539. };
  1540. }
  1541. },
  1542. stripHtml: function (mixed) {
  1543. var type = typeof mixed;
  1544. if (type === 'function') {
  1545. _stripHtml = mixed;
  1546. return;
  1547. }
  1548. else if (type === 'string') {
  1549. return _stripHtml(mixed);
  1550. }
  1551. return mixed;
  1552. },
  1553. escapeHtml: function (mixed) {
  1554. var type = typeof mixed;
  1555. if (type === 'function') {
  1556. _escapeHtml = mixed;
  1557. return;
  1558. }
  1559. else if (type === 'string' || Array.isArray(mixed)) {
  1560. return _escapeHtml(mixed);
  1561. }
  1562. return mixed;
  1563. },
  1564. unique: _unique
  1565. };
  1566. /**
  1567. * Create a mapping object that allows camel case parameters to be looked up
  1568. * for their Hungarian counterparts. The mapping is stored in a private
  1569. * parameter called `_hungarianMap` which can be accessed on the source object.
  1570. * @param {object} o
  1571. * @memberof DataTable#oApi
  1572. */
  1573. function _fnHungarianMap ( o )
  1574. {
  1575. var
  1576. hungarian = 'a aa ai ao as b fn i m o s ',
  1577. match,
  1578. newKey,
  1579. map = {};
  1580. $.each( o, function (key) {
  1581. match = key.match(/^([^A-Z]+?)([A-Z])/);
  1582. if ( match && hungarian.indexOf(match[1]+' ') !== -1 )
  1583. {
  1584. newKey = key.replace( match[0], match[2].toLowerCase() );
  1585. map[ newKey ] = key;
  1586. if ( match[1] === 'o' )
  1587. {
  1588. _fnHungarianMap( o[key] );
  1589. }
  1590. }
  1591. } );
  1592. o._hungarianMap = map;
  1593. }
  1594. /**
  1595. * Convert from camel case parameters to Hungarian, based on a Hungarian map
  1596. * created by _fnHungarianMap.
  1597. * @param {object} src The model object which holds all parameters that can be
  1598. * mapped.
  1599. * @param {object} user The object to convert from camel case to Hungarian.
  1600. * @param {boolean} force When set to `true`, properties which already have a
  1601. * Hungarian value in the `user` object will be overwritten. Otherwise they
  1602. * won't be.
  1603. * @memberof DataTable#oApi
  1604. */
  1605. function _fnCamelToHungarian ( src, user, force )
  1606. {
  1607. if ( ! src._hungarianMap ) {
  1608. _fnHungarianMap( src );
  1609. }
  1610. var hungarianKey;
  1611. $.each( user, function (key) {
  1612. hungarianKey = src._hungarianMap[ key ];
  1613. if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )
  1614. {
  1615. // For objects, we need to buzz down into the object to copy parameters
  1616. if ( hungarianKey.charAt(0) === 'o' )
  1617. {
  1618. // Copy the camelCase options over to the hungarian
  1619. if ( ! user[ hungarianKey ] ) {
  1620. user[ hungarianKey ] = {};
  1621. }
  1622. $.extend( true, user[hungarianKey], user[key] );
  1623. _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );
  1624. }
  1625. else {
  1626. user[hungarianKey] = user[ key ];
  1627. }
  1628. }
  1629. } );
  1630. }
  1631. /**
  1632. * Map one parameter onto another
  1633. * @param {object} o Object to map
  1634. * @param {*} knew The new parameter name
  1635. * @param {*} old The old parameter name
  1636. */
  1637. var _fnCompatMap = function ( o, knew, old ) {
  1638. if ( o[ knew ] !== undefined ) {
  1639. o[ old ] = o[ knew ];
  1640. }
  1641. };
  1642. /**
  1643. * Provide backwards compatibility for the main DT options. Note that the new
  1644. * options are mapped onto the old parameters, so this is an external interface
  1645. * change only.
  1646. * @param {object} init Object to map
  1647. */
  1648. function _fnCompatOpts ( init )
  1649. {
  1650. _fnCompatMap( init, 'ordering', 'bSort' );
  1651. _fnCompatMap( init, 'orderMulti', 'bSortMulti' );
  1652. _fnCompatMap( init, 'orderClasses', 'bSortClasses' );
  1653. _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' );
  1654. _fnCompatMap( init, 'order', 'aaSorting' );
  1655. _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' );
  1656. _fnCompatMap( init, 'paging', 'bPaginate' );
  1657. _fnCompatMap( init, 'pagingType', 'sPaginationType' );
  1658. _fnCompatMap( init, 'pageLength', 'iDisplayLength' );
  1659. _fnCompatMap( init, 'searching', 'bFilter' );
  1660. // Boolean initialisation of x-scrolling
  1661. if ( typeof init.sScrollX === 'boolean' ) {
  1662. init.sScrollX = init.sScrollX ? '100%' : '';
  1663. }
  1664. if ( typeof init.scrollX === 'boolean' ) {
  1665. init.scrollX = init.scrollX ? '100%' : '';
  1666. }
  1667. // Column search objects are in an array, so it needs to be converted
  1668. // element by element
  1669. var searchCols = init.aoSearchCols;
  1670. if ( searchCols ) {
  1671. for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {
  1672. if ( searchCols[i] ) {
  1673. _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );
  1674. }
  1675. }
  1676. }
  1677. // Enable search delay if server-side processing is enabled
  1678. if (init.serverSide && ! init.searchDelay) {
  1679. init.searchDelay = 400;
  1680. }
  1681. }
  1682. /**
  1683. * Provide backwards compatibility for column options. Note that the new options
  1684. * are mapped onto the old parameters, so this is an external interface change
  1685. * only.
  1686. * @param {object} init Object to map
  1687. */
  1688. function _fnCompatCols ( init )
  1689. {
  1690. _fnCompatMap( init, 'orderable', 'bSortable' );
  1691. _fnCompatMap( init, 'orderData', 'aDataSort' );
  1692. _fnCompatMap( init, 'orderSequence', 'asSorting' );
  1693. _fnCompatMap( init, 'orderDataType', 'sortDataType' );
  1694. // orderData can be given as an integer
  1695. var dataSort = init.aDataSort;
  1696. if ( typeof dataSort === 'number' && ! Array.isArray( dataSort ) ) {
  1697. init.aDataSort = [ dataSort ];
  1698. }
  1699. }
  1700. /**
  1701. * Browser feature detection for capabilities, quirks
  1702. * @param {object} settings dataTables settings object
  1703. * @memberof DataTable#oApi
  1704. */
  1705. function _fnBrowserDetect( settings )
  1706. {
  1707. // We don't need to do this every time DataTables is constructed, the values
  1708. // calculated are specific to the browser and OS configuration which we
  1709. // don't expect to change between initialisations
  1710. if ( ! DataTable.__browser ) {
  1711. var browser = {};
  1712. DataTable.__browser = browser;
  1713. // Scrolling feature / quirks detection
  1714. var n = $('<div/>')
  1715. .css( {
  1716. position: 'fixed',
  1717. top: 0,
  1718. left: -1 * window.pageXOffset, // allow for scrolling
  1719. height: 1,
  1720. width: 1,
  1721. overflow: 'hidden'
  1722. } )
  1723. .append(
  1724. $('<div/>')
  1725. .css( {
  1726. position: 'absolute',
  1727. top: 1,
  1728. left: 1,
  1729. width: 100,
  1730. overflow: 'scroll'
  1731. } )
  1732. .append(
  1733. $('<div/>')
  1734. .css( {
  1735. width: '100%',
  1736. height: 10
  1737. } )
  1738. )
  1739. )
  1740. .appendTo( 'body' );
  1741. var outer = n.children();
  1742. var inner = outer.children();
  1743. // Get scrollbar width
  1744. browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;
  1745. // In rtl text layout, some browsers (most, but not all) will place the
  1746. // scrollbar on the left, rather than the right.
  1747. browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1;
  1748. n.remove();
  1749. }
  1750. $.extend( settings.oBrowser, DataTable.__browser );
  1751. settings.oScroll.iBarWidth = DataTable.__browser.barWidth;
  1752. }
  1753. /**
  1754. * Add a column to the list used for the table with default values
  1755. * @param {object} oSettings dataTables settings object
  1756. * @memberof DataTable#oApi
  1757. */
  1758. function _fnAddColumn( oSettings )
  1759. {
  1760. // Add column to aoColumns array
  1761. var oDefaults = DataTable.defaults.column;
  1762. var iCol = oSettings.aoColumns.length;
  1763. var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
  1764. "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
  1765. "mData": oDefaults.mData ? oDefaults.mData : iCol,
  1766. idx: iCol,
  1767. searchFixed: {},
  1768. colEl: $('<col>').attr('data-dt-column', iCol)
  1769. } );
  1770. oSettings.aoColumns.push( oCol );
  1771. // Add search object for column specific search. Note that the `searchCols[ iCol ]`
  1772. // passed into extend can be undefined. This allows the user to give a default
  1773. // with only some of the parameters defined, and also not give a default
  1774. var searchCols = oSettings.aoPreSearchCols;
  1775. searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );
  1776. }
  1777. /**
  1778. * Apply options for a column
  1779. * @param {object} oSettings dataTables settings object
  1780. * @param {int} iCol column index to consider
  1781. * @param {object} oOptions object with sType, bVisible and bSearchable etc
  1782. * @memberof DataTable#oApi
  1783. */
  1784. function _fnColumnOptions( oSettings, iCol, oOptions )
  1785. {
  1786. var oCol = oSettings.aoColumns[ iCol ];
  1787. /* User specified column options */
  1788. if ( oOptions !== undefined && oOptions !== null )
  1789. {
  1790. // Backwards compatibility
  1791. _fnCompatCols( oOptions );
  1792. // Map camel case parameters to their Hungarian counterparts
  1793. _fnCamelToHungarian( DataTable.defaults.column, oOptions, true );
  1794. /* Backwards compatibility for mDataProp */
  1795. if ( oOptions.mDataProp !== undefined && !oOptions.mData )
  1796. {
  1797. oOptions.mData = oOptions.mDataProp;
  1798. }
  1799. if ( oOptions.sType )
  1800. {
  1801. oCol._sManualType = oOptions.sType;
  1802. }
  1803. // `class` is a reserved word in Javascript, so we need to provide
  1804. // the ability to use a valid name for the camel case input
  1805. if ( oOptions.className && ! oOptions.sClass )
  1806. {
  1807. oOptions.sClass = oOptions.className;
  1808. }
  1809. var origClass = oCol.sClass;
  1810. $.extend( oCol, oOptions );
  1811. _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
  1812. // Merge class from previously defined classes with this one, rather than just
  1813. // overwriting it in the extend above
  1814. if (origClass !== oCol.sClass) {
  1815. oCol.sClass = origClass + ' ' + oCol.sClass;
  1816. }
  1817. /* iDataSort to be applied (backwards compatibility), but aDataSort will take
  1818. * priority if defined
  1819. */
  1820. if ( oOptions.iDataSort !== undefined )
  1821. {
  1822. oCol.aDataSort = [ oOptions.iDataSort ];
  1823. }
  1824. _fnMap( oCol, oOptions, "aDataSort" );
  1825. }
  1826. /* Cache the data get and set functions for speed */
  1827. var mDataSrc = oCol.mData;
  1828. var mData = _fnGetObjectDataFn( mDataSrc );
  1829. // The `render` option can be given as an array to access the helper rendering methods.
  1830. // The first element is the rendering method to use, the rest are the parameters to pass
  1831. if ( oCol.mRender && Array.isArray( oCol.mRender ) ) {
  1832. var copy = oCol.mRender.slice();
  1833. var name = copy.shift();
  1834. oCol.mRender = DataTable.render[name].apply(window, copy);
  1835. }
  1836. oCol._render = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
  1837. var attrTest = function( src ) {
  1838. return typeof src === 'string' && src.indexOf('@') !== -1;
  1839. };
  1840. oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
  1841. attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
  1842. );
  1843. oCol._setter = null;
  1844. oCol.fnGetData = function (rowData, type, meta) {
  1845. var innerData = mData( rowData, type, undefined, meta );
  1846. return oCol._render && type ?
  1847. oCol._render( innerData, type, rowData, meta ) :
  1848. innerData;
  1849. };
  1850. oCol.fnSetData = function ( rowData, val, meta ) {
  1851. return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );
  1852. };
  1853. // Indicate if DataTables should read DOM data as an object or array
  1854. // Used in _fnGetRowElements
  1855. if ( typeof mDataSrc !== 'number' && ! oCol._isArrayHost ) {
  1856. oSettings._rowReadObject = true;
  1857. }
  1858. /* Feature sorting overrides column specific when off */
  1859. if ( !oSettings.oFeatures.bSort )
  1860. {
  1861. oCol.bSortable = false;
  1862. }
  1863. }
  1864. /**
  1865. * Adjust the table column widths for new data. Note: you would probably want to
  1866. * do a redraw after calling this function!
  1867. * @param {object} settings dataTables settings object
  1868. * @memberof DataTable#oApi
  1869. */
  1870. function _fnAdjustColumnSizing ( settings )
  1871. {
  1872. _fnCalculateColumnWidths( settings );
  1873. _fnColumnSizes( settings );
  1874. var scroll = settings.oScroll;
  1875. if ( scroll.sY !== '' || scroll.sX !== '') {
  1876. _fnScrollDraw( settings );
  1877. }
  1878. _fnCallbackFire( settings, null, 'column-sizing', [settings] );
  1879. }
  1880. /**
  1881. * Apply column sizes
  1882. *
  1883. * @param {*} settings DataTables settings object
  1884. */
  1885. function _fnColumnSizes ( settings )
  1886. {
  1887. var cols = settings.aoColumns;
  1888. for (var i=0 ; i<cols.length ; i++) {
  1889. var width = _fnColumnsSumWidth(settings, [i], false, false);
  1890. cols[i].colEl.css('width', width);
  1891. }
  1892. }
  1893. /**
  1894. * Convert the index of a visible column to the index in the data array (take account
  1895. * of hidden columns)
  1896. * @param {object} oSettings dataTables settings object
  1897. * @param {int} iMatch Visible column index to lookup
  1898. * @returns {int} i the data index
  1899. * @memberof DataTable#oApi
  1900. */
  1901. function _fnVisibleToColumnIndex( oSettings, iMatch )
  1902. {
  1903. var aiVis = _fnGetColumns( oSettings, 'bVisible' );
  1904. return typeof aiVis[iMatch] === 'number' ?
  1905. aiVis[iMatch] :
  1906. null;
  1907. }
  1908. /**
  1909. * Convert the index of an index in the data array and convert it to the visible
  1910. * column index (take account of hidden columns)
  1911. * @param {int} iMatch Column index to lookup
  1912. * @param {object} oSettings dataTables settings object
  1913. * @returns {int} i the data index
  1914. * @memberof DataTable#oApi
  1915. */
  1916. function _fnColumnIndexToVisible( oSettings, iMatch )
  1917. {
  1918. var aiVis = _fnGetColumns( oSettings, 'bVisible' );
  1919. var iPos = aiVis.indexOf(iMatch);
  1920. return iPos !== -1 ? iPos : null;
  1921. }
  1922. /**
  1923. * Get the number of visible columns
  1924. * @param {object} oSettings dataTables settings object
  1925. * @returns {int} i the number of visible columns
  1926. * @memberof DataTable#oApi
  1927. */
  1928. function _fnVisbleColumns( settings )
  1929. {
  1930. var layout = settings.aoHeader;
  1931. var columns = settings.aoColumns;
  1932. var vis = 0;
  1933. if ( layout.length ) {
  1934. for ( var i=0, ien=layout[0].length ; i<ien ; i++ ) {
  1935. if ( columns[i].bVisible && $(layout[0][i].cell).css('display') !== 'none' ) {
  1936. vis++;
  1937. }
  1938. }
  1939. }
  1940. return vis;
  1941. }
  1942. /**
  1943. * Get an array of column indexes that match a given property
  1944. * @param {object} oSettings dataTables settings object
  1945. * @param {string} sParam Parameter in aoColumns to look for - typically
  1946. * bVisible or bSearchable
  1947. * @returns {array} Array of indexes with matched properties
  1948. * @memberof DataTable#oApi
  1949. */
  1950. function _fnGetColumns( oSettings, sParam )
  1951. {
  1952. var a = [];
  1953. oSettings.aoColumns.map( function(val, i) {
  1954. if ( val[sParam] ) {
  1955. a.push( i );
  1956. }
  1957. } );
  1958. return a;
  1959. }
  1960. /**
  1961. * Calculate the 'type' of a column
  1962. * @param {object} settings dataTables settings object
  1963. * @memberof DataTable#oApi
  1964. */
  1965. function _fnColumnTypes ( settings )
  1966. {
  1967. var columns = settings.aoColumns;
  1968. var data = settings.aoData;
  1969. var types = DataTable.ext.type.detect;
  1970. var i, ien, j, jen, k, ken;
  1971. var col, detectedType, cache;
  1972. // For each column, spin over the
  1973. for ( i=0, ien=columns.length ; i<ien ; i++ ) {
  1974. col = columns[i];
  1975. cache = [];
  1976. if ( ! col.sType && col._sManualType ) {
  1977. col.sType = col._sManualType;
  1978. }
  1979. else if ( ! col.sType ) {
  1980. for ( j=0, jen=types.length ; j<jen ; j++ ) {
  1981. for ( k=0, ken=data.length ; k<ken ; k++ ) {
  1982. if (! data[k]) {
  1983. continue;
  1984. }
  1985. // Use a cache array so we only need to get the type data
  1986. // from the formatter once (when using multiple detectors)
  1987. if ( cache[k] === undefined ) {
  1988. cache[k] = _fnGetCellData( settings, k, i, 'type' );
  1989. }
  1990. detectedType = types[j]( cache[k], settings );
  1991. // If null, then this type can't apply to this column, so
  1992. // rather than testing all cells, break out. There is an
  1993. // exception for the last type which is `html`. We need to
  1994. // scan all rows since it is possible to mix string and HTML
  1995. // types
  1996. if ( ! detectedType && j !== types.length-2 ) {
  1997. break;
  1998. }
  1999. // Only a single match is needed for html type since it is
  2000. // bottom of the pile and very similar to string - but it
  2001. // must not be empty
  2002. if ( detectedType === 'html' && ! _empty(cache[k]) ) {
  2003. break;
  2004. }
  2005. }
  2006. // Type is valid for all data points in the column - use this
  2007. // type
  2008. if ( detectedType ) {
  2009. col.sType = detectedType;
  2010. break;
  2011. }
  2012. }
  2013. // Fall back - if no type was detected, always use string
  2014. if ( ! col.sType ) {
  2015. col.sType = 'string';
  2016. }
  2017. }
  2018. // Set class names for header / footer for auto type classes
  2019. var autoClass = _ext.type.className[col.sType];
  2020. if (autoClass) {
  2021. _columnAutoClass(settings.aoHeader, i, autoClass);
  2022. _columnAutoClass(settings.aoFooter, i, autoClass);
  2023. }
  2024. var renderer = _ext.type.render[col.sType];
  2025. // This can only happen once! There is no way to remover
  2026. // a renderer. After the first time the renderer has
  2027. // already been set so createTr will run the renderer itself.
  2028. if (renderer && ! col._render) {
  2029. col._render = DataTable.util.get(renderer);
  2030. _columnAutoRender(settings, i);
  2031. }
  2032. }
  2033. }
  2034. /**
  2035. * Apply an auto detected renderer to data which doesn't yet have
  2036. * a renderer
  2037. */
  2038. function _columnAutoRender(settings, colIdx) {
  2039. var data = settings.aoData;
  2040. for (var i=0 ; i<data.length ; i++) {
  2041. if (data[i].nTr) {
  2042. // We have to update the display here since there is no
  2043. // invalidation check for the data
  2044. var display = _fnGetCellData( settings, i, colIdx, 'display' );
  2045. data[i].displayData[colIdx] = display;
  2046. _fnWriteCell(data[i].anCells[colIdx], display);
  2047. // No need to update sort / filter data since it has
  2048. // been invalidated and will be re-read with the
  2049. // renderer now applied
  2050. }
  2051. }
  2052. }
  2053. /**
  2054. * Apply a class name to a column's header cells
  2055. */
  2056. function _columnAutoClass(container, colIdx, className) {
  2057. container.forEach(function (row) {
  2058. if (row[colIdx] && row[colIdx].unique) {
  2059. _addClass(row[colIdx].cell, className);
  2060. }
  2061. });
  2062. }
  2063. /**
  2064. * Take the column definitions and static columns arrays and calculate how
  2065. * they relate to column indexes. The callback function will then apply the
  2066. * definition found for a column to a suitable configuration object.
  2067. * @param {object} oSettings dataTables settings object
  2068. * @param {array} aoColDefs The aoColumnDefs array that is to be applied
  2069. * @param {array} aoCols The aoColumns array that defines columns individually
  2070. * @param {array} headerLayout Layout for header as it was loaded
  2071. * @param {function} fn Callback function - takes two parameters, the calculated
  2072. * column index and the definition for that column.
  2073. * @memberof DataTable#oApi
  2074. */
  2075. function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, headerLayout, fn )
  2076. {
  2077. var i, iLen, j, jLen, k, kLen, def;
  2078. var columns = oSettings.aoColumns;
  2079. if ( aoCols ) {
  2080. for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) {
  2081. if (aoCols[i] && aoCols[i].name) {
  2082. columns[i].sName = aoCols[i].name;
  2083. }
  2084. }
  2085. }
  2086. // Column definitions with aTargets
  2087. if ( aoColDefs )
  2088. {
  2089. /* Loop over the definitions array - loop in reverse so first instance has priority */
  2090. for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
  2091. {
  2092. def = aoColDefs[i];
  2093. /* Each definition can target multiple columns, as it is an array */
  2094. var aTargets = def.target !== undefined
  2095. ? def.target
  2096. : def.targets !== undefined
  2097. ? def.targets
  2098. : def.aTargets;
  2099. if ( ! Array.isArray( aTargets ) )
  2100. {
  2101. aTargets = [ aTargets ];
  2102. }
  2103. for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
  2104. {
  2105. var target = aTargets[j];
  2106. if ( typeof target === 'number' && target >= 0 )
  2107. {
  2108. /* Add columns that we don't yet know about */
  2109. while( columns.length <= target )
  2110. {
  2111. _fnAddColumn( oSettings );
  2112. }
  2113. /* Integer, basic index */
  2114. fn( target, def );
  2115. }
  2116. else if ( typeof target === 'number' && target < 0 )
  2117. {
  2118. /* Negative integer, right to left column counting */
  2119. fn( columns.length+target, def );
  2120. }
  2121. else if ( typeof target === 'string' )
  2122. {
  2123. for ( k=0, kLen=columns.length ; k<kLen ; k++ ) {
  2124. if (target === '_all') {
  2125. // Apply to all columns
  2126. fn( k, def );
  2127. }
  2128. else if (target.indexOf(':name') !== -1) {
  2129. // Column selector
  2130. if (columns[k].sName === target.replace(':name', '')) {
  2131. fn( k, def );
  2132. }
  2133. }
  2134. else {
  2135. // Cell selector
  2136. headerLayout.forEach(function (row) {
  2137. if (row[k]) {
  2138. var cell = $(row[k].cell);
  2139. // Legacy support. Note that it means that we don't support
  2140. // an element name selector only, since they are treated as
  2141. // class names for 1.x compat.
  2142. if (target.match(/^[a-z][\w-]*$/i)) {
  2143. target = '.' + target;
  2144. }
  2145. if (cell.is( target )) {
  2146. fn( k, def );
  2147. }
  2148. }
  2149. });
  2150. }
  2151. }
  2152. }
  2153. }
  2154. }
  2155. }
  2156. // Statically defined columns array
  2157. if ( aoCols ) {
  2158. for ( i=0, iLen=aoCols.length ; i<iLen ; i++ ) {
  2159. fn( i, aoCols[i] );
  2160. }
  2161. }
  2162. }
  2163. /**
  2164. * Get the width for a given set of columns
  2165. *
  2166. * @param {*} settings DataTables settings object
  2167. * @param {*} targets Columns - comma separated string or array of numbers
  2168. * @param {*} original Use the original width (true) or calculated (false)
  2169. * @param {*} incVisible Include visible columns (true) or not (false)
  2170. * @returns Combined CSS value
  2171. */
  2172. function _fnColumnsSumWidth( settings, targets, original, incVisible ) {
  2173. if ( ! Array.isArray( targets ) ) {
  2174. targets = _fnColumnsFromHeader( targets );
  2175. }
  2176. var sum = 0;
  2177. var unit;
  2178. var columns = settings.aoColumns;
  2179. for ( var i=0, ien=targets.length ; i<ien ; i++ ) {
  2180. var column = columns[ targets[i] ];
  2181. var definedWidth = original ?
  2182. column.sWidthOrig :
  2183. column.sWidth;
  2184. if ( ! incVisible && column.bVisible === false ) {
  2185. continue;
  2186. }
  2187. if ( definedWidth === null || definedWidth === undefined ) {
  2188. return null; // can't determine a defined width - browser defined
  2189. }
  2190. else if ( typeof definedWidth === 'number' ) {
  2191. unit = 'px';
  2192. sum += definedWidth;
  2193. }
  2194. else {
  2195. var matched = definedWidth.match(/([\d\.]+)([^\d]*)/);
  2196. if ( matched ) {
  2197. sum += matched[1] * 1;
  2198. unit = matched.length === 3 ?
  2199. matched[2] :
  2200. 'px';
  2201. }
  2202. }
  2203. }
  2204. return sum + unit;
  2205. }
  2206. function _fnColumnsFromHeader( cell )
  2207. {
  2208. var attr = $(cell).closest('[data-dt-column]').attr('data-dt-column');
  2209. if ( ! attr ) {
  2210. return [];
  2211. }
  2212. return attr.split(',').map( function (val) {
  2213. return val * 1;
  2214. } );
  2215. }
  2216. /**
  2217. * Add a data array to the table, creating DOM node etc. This is the parallel to
  2218. * _fnGatherData, but for adding rows from a Javascript source, rather than a
  2219. * DOM source.
  2220. * @param {object} settings dataTables settings object
  2221. * @param {array} data data array to be added
  2222. * @param {node} [tr] TR element to add to the table - optional. If not given,
  2223. * DataTables will create a row automatically
  2224. * @param {array} [tds] Array of TD|TH elements for the row - must be given
  2225. * if nTr is.
  2226. * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
  2227. * @memberof DataTable#oApi
  2228. */
  2229. function _fnAddData ( settings, dataIn, tr, tds )
  2230. {
  2231. /* Create the object for storing information about this new row */
  2232. var rowIdx = settings.aoData.length;
  2233. var rowModel = $.extend( true, {}, DataTable.models.oRow, {
  2234. src: tr ? 'dom' : 'data',
  2235. idx: rowIdx
  2236. } );
  2237. rowModel._aData = dataIn;
  2238. settings.aoData.push( rowModel );
  2239. var columns = settings.aoColumns;
  2240. for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
  2241. {
  2242. // Invalidate the column types as the new data needs to be revalidated
  2243. columns[i].sType = null;
  2244. }
  2245. /* Add to the display array */
  2246. settings.aiDisplayMaster.push( rowIdx );
  2247. var id = settings.rowIdFn( dataIn );
  2248. if ( id !== undefined ) {
  2249. settings.aIds[ id ] = rowModel;
  2250. }
  2251. /* Create the DOM information, or register it if already present */
  2252. if ( tr || ! settings.oFeatures.bDeferRender )
  2253. {
  2254. _fnCreateTr( settings, rowIdx, tr, tds );
  2255. }
  2256. return rowIdx;
  2257. }
  2258. /**
  2259. * Add one or more TR elements to the table. Generally we'd expect to
  2260. * use this for reading data from a DOM sourced table, but it could be
  2261. * used for an TR element. Note that if a TR is given, it is used (i.e.
  2262. * it is not cloned).
  2263. * @param {object} settings dataTables settings object
  2264. * @param {array|node|jQuery} trs The TR element(s) to add to the table
  2265. * @returns {array} Array of indexes for the added rows
  2266. * @memberof DataTable#oApi
  2267. */
  2268. function _fnAddTr( settings, trs )
  2269. {
  2270. var row;
  2271. // Allow an individual node to be passed in
  2272. if ( ! (trs instanceof $) ) {
  2273. trs = $(trs);
  2274. }
  2275. return trs.map( function (i, el) {
  2276. row = _fnGetRowElements( settings, el );
  2277. return _fnAddData( settings, row.data, el, row.cells );
  2278. } );
  2279. }
  2280. /**
  2281. * Get the data for a given cell from the internal cache, taking into account data mapping
  2282. * @param {object} settings dataTables settings object
  2283. * @param {int} rowIdx aoData row id
  2284. * @param {int} colIdx Column index
  2285. * @param {string} type data get type ('display', 'type' 'filter|search' 'sort|order')
  2286. * @returns {*} Cell data
  2287. * @memberof DataTable#oApi
  2288. */
  2289. function _fnGetCellData( settings, rowIdx, colIdx, type )
  2290. {
  2291. if (type === 'search') {
  2292. type = 'filter';
  2293. }
  2294. else if (type === 'order') {
  2295. type = 'sort';
  2296. }
  2297. var row = settings.aoData[rowIdx];
  2298. if (! row) {
  2299. return undefined;
  2300. }
  2301. var draw = settings.iDraw;
  2302. var col = settings.aoColumns[colIdx];
  2303. var rowData = row._aData;
  2304. var defaultContent = col.sDefaultContent;
  2305. var cellData = col.fnGetData( rowData, type, {
  2306. settings: settings,
  2307. row: rowIdx,
  2308. col: colIdx
  2309. } );
  2310. // Allow for a node being returned for non-display types
  2311. if (type !== 'display' && cellData && typeof cellData === 'object' && cellData.nodeName) {
  2312. cellData = cellData.innerHTML;
  2313. }
  2314. if ( cellData === undefined ) {
  2315. if ( settings.iDrawError != draw && defaultContent === null ) {
  2316. _fnLog( settings, 0, "Requested unknown parameter "+
  2317. (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+
  2318. " for row "+rowIdx+", column "+colIdx, 4 );
  2319. settings.iDrawError = draw;
  2320. }
  2321. return defaultContent;
  2322. }
  2323. // When the data source is null and a specific data type is requested (i.e.
  2324. // not the original data), we can use default column data
  2325. if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) {
  2326. cellData = defaultContent;
  2327. }
  2328. else if ( typeof cellData === 'function' ) {
  2329. // If the data source is a function, then we run it and use the return,
  2330. // executing in the scope of the data object (for instances)
  2331. return cellData.call( rowData );
  2332. }
  2333. if ( cellData === null && type === 'display' ) {
  2334. return '';
  2335. }
  2336. if ( type === 'filter' ) {
  2337. var fomatters = DataTable.ext.type.search;
  2338. if ( fomatters[ col.sType ] ) {
  2339. cellData = fomatters[ col.sType ]( cellData );
  2340. }
  2341. }
  2342. return cellData;
  2343. }
  2344. /**
  2345. * Set the value for a specific cell, into the internal data cache
  2346. * @param {object} settings dataTables settings object
  2347. * @param {int} rowIdx aoData row id
  2348. * @param {int} colIdx Column index
  2349. * @param {*} val Value to set
  2350. * @memberof DataTable#oApi
  2351. */
  2352. function _fnSetCellData( settings, rowIdx, colIdx, val )
  2353. {
  2354. var col = settings.aoColumns[colIdx];
  2355. var rowData = settings.aoData[rowIdx]._aData;
  2356. col.fnSetData( rowData, val, {
  2357. settings: settings,
  2358. row: rowIdx,
  2359. col: colIdx
  2360. } );
  2361. }
  2362. /**
  2363. * Write a value to a cell
  2364. * @param {*} td Cell
  2365. * @param {*} val Value
  2366. */
  2367. function _fnWriteCell(td, val)
  2368. {
  2369. if (val && typeof val === 'object' && val.nodeName) {
  2370. $(td)
  2371. .empty()
  2372. .append(val);
  2373. }
  2374. else {
  2375. td.innerHTML = val;
  2376. }
  2377. }
  2378. // Private variable that is used to match action syntax in the data property object
  2379. var __reArray = /\[.*?\]$/;
  2380. var __reFn = /\(\)$/;
  2381. /**
  2382. * Split string on periods, taking into account escaped periods
  2383. * @param {string} str String to split
  2384. * @return {array} Split string
  2385. */
  2386. function _fnSplitObjNotation( str )
  2387. {
  2388. var parts = str.match(/(\\.|[^.])+/g) || [''];
  2389. return parts.map( function ( s ) {
  2390. return s.replace(/\\\./g, '.');
  2391. } );
  2392. }
  2393. /**
  2394. * Return a function that can be used to get data from a source object, taking
  2395. * into account the ability to use nested objects as a source
  2396. * @param {string|int|function} mSource The data source for the object
  2397. * @returns {function} Data get function
  2398. * @memberof DataTable#oApi
  2399. */
  2400. var _fnGetObjectDataFn = DataTable.util.get;
  2401. /**
  2402. * Return a function that can be used to set data from a source object, taking
  2403. * into account the ability to use nested objects as a source
  2404. * @param {string|int|function} mSource The data source for the object
  2405. * @returns {function} Data set function
  2406. * @memberof DataTable#oApi
  2407. */
  2408. var _fnSetObjectDataFn = DataTable.util.set;
  2409. /**
  2410. * Return an array with the full table data
  2411. * @param {object} oSettings dataTables settings object
  2412. * @returns array {array} aData Master data array
  2413. * @memberof DataTable#oApi
  2414. */
  2415. function _fnGetDataMaster ( settings )
  2416. {
  2417. return _pluck( settings.aoData, '_aData' );
  2418. }
  2419. /**
  2420. * Nuke the table
  2421. * @param {object} oSettings dataTables settings object
  2422. * @memberof DataTable#oApi
  2423. */
  2424. function _fnClearTable( settings )
  2425. {
  2426. settings.aoData.length = 0;
  2427. settings.aiDisplayMaster.length = 0;
  2428. settings.aiDisplay.length = 0;
  2429. settings.aIds = {};
  2430. }
  2431. /**
  2432. * Mark cached data as invalid such that a re-read of the data will occur when
  2433. * the cached data is next requested. Also update from the data source object.
  2434. *
  2435. * @param {object} settings DataTables settings object
  2436. * @param {int} rowIdx Row index to invalidate
  2437. * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom'
  2438. * or 'data'
  2439. * @param {int} [colIdx] Column index to invalidate. If undefined the whole
  2440. * row will be invalidated
  2441. * @memberof DataTable#oApi
  2442. *
  2443. * @todo For the modularisation of v1.11 this will need to become a callback, so
  2444. * the sort and filter methods can subscribe to it. That will required
  2445. * initialisation options for sorting, which is why it is not already baked in
  2446. */
  2447. function _fnInvalidate( settings, rowIdx, src, colIdx )
  2448. {
  2449. var row = settings.aoData[ rowIdx ];
  2450. var i, ien;
  2451. // Remove the cached data for the row
  2452. row._aSortData = null;
  2453. row._aFilterData = null;
  2454. row.displayData = null;
  2455. // Are we reading last data from DOM or the data object?
  2456. if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {
  2457. // Read the data from the DOM
  2458. row._aData = _fnGetRowElements(
  2459. settings, row, colIdx, colIdx === undefined ? undefined : row._aData
  2460. )
  2461. .data;
  2462. }
  2463. else {
  2464. // Reading from data object, update the DOM
  2465. var cells = row.anCells;
  2466. var display = _fnGetRowDisplay(settings, rowIdx);
  2467. if ( cells ) {
  2468. if ( colIdx !== undefined ) {
  2469. _fnWriteCell(cells[colIdx], display[colIdx]);
  2470. }
  2471. else {
  2472. for ( i=0, ien=cells.length ; i<ien ; i++ ) {
  2473. _fnWriteCell(cells[i], display[i]);
  2474. }
  2475. }
  2476. }
  2477. }
  2478. // Column specific invalidation
  2479. var cols = settings.aoColumns;
  2480. if ( colIdx !== undefined ) {
  2481. // Type - the data might have changed
  2482. cols[ colIdx ].sType = null;
  2483. // Max length string. Its a fairly cheep recalculation, so not worth
  2484. // something more complicated
  2485. cols[ colIdx ].maxLenString = null;
  2486. }
  2487. else {
  2488. for ( i=0, ien=cols.length ; i<ien ; i++ ) {
  2489. cols[i].sType = null;
  2490. cols[i].maxLenString = null;
  2491. }
  2492. // Update DataTables special `DT_*` attributes for the row
  2493. _fnRowAttributes( settings, row );
  2494. }
  2495. }
  2496. /**
  2497. * Build a data source object from an HTML row, reading the contents of the
  2498. * cells that are in the row.
  2499. *
  2500. * @param {object} settings DataTables settings object
  2501. * @param {node|object} TR element from which to read data or existing row
  2502. * object from which to re-read the data from the cells
  2503. * @param {int} [colIdx] Optional column index
  2504. * @param {array|object} [d] Data source object. If `colIdx` is given then this
  2505. * parameter should also be given and will be used to write the data into.
  2506. * Only the column in question will be written
  2507. * @returns {object} Object with two parameters: `data` the data read, in
  2508. * document order, and `cells` and array of nodes (they can be useful to the
  2509. * caller, so rather than needing a second traversal to get them, just return
  2510. * them from here).
  2511. * @memberof DataTable#oApi
  2512. */
  2513. function _fnGetRowElements( settings, row, colIdx, d )
  2514. {
  2515. var
  2516. tds = [],
  2517. td = row.firstChild,
  2518. name, col, i=0, contents,
  2519. columns = settings.aoColumns,
  2520. objectRead = settings._rowReadObject;
  2521. // Allow the data object to be passed in, or construct
  2522. d = d !== undefined ?
  2523. d :
  2524. objectRead ?
  2525. {} :
  2526. [];
  2527. var attr = function ( str, td ) {
  2528. if ( typeof str === 'string' ) {
  2529. var idx = str.indexOf('@');
  2530. if ( idx !== -1 ) {
  2531. var attr = str.substring( idx+1 );
  2532. var setter = _fnSetObjectDataFn( str );
  2533. setter( d, td.getAttribute( attr ) );
  2534. }
  2535. }
  2536. };
  2537. // Read data from a cell and store into the data object
  2538. var cellProcess = function ( cell ) {
  2539. if ( colIdx === undefined || colIdx === i ) {
  2540. col = columns[i];
  2541. contents = (cell.innerHTML).trim();
  2542. if ( col && col._bAttrSrc ) {
  2543. var setter = _fnSetObjectDataFn( col.mData._ );
  2544. setter( d, contents );
  2545. attr( col.mData.sort, cell );
  2546. attr( col.mData.type, cell );
  2547. attr( col.mData.filter, cell );
  2548. }
  2549. else {
  2550. // Depending on the `data` option for the columns the data can
  2551. // be read to either an object or an array.
  2552. if ( objectRead ) {
  2553. if ( ! col._setter ) {
  2554. // Cache the setter function
  2555. col._setter = _fnSetObjectDataFn( col.mData );
  2556. }
  2557. col._setter( d, contents );
  2558. }
  2559. else {
  2560. d[i] = contents;
  2561. }
  2562. }
  2563. }
  2564. i++;
  2565. };
  2566. if ( td ) {
  2567. // `tr` element was passed in
  2568. while ( td ) {
  2569. name = td.nodeName.toUpperCase();
  2570. if ( name == "TD" || name == "TH" ) {
  2571. cellProcess( td );
  2572. tds.push( td );
  2573. }
  2574. td = td.nextSibling;
  2575. }
  2576. }
  2577. else {
  2578. // Existing row object passed in
  2579. tds = row.anCells;
  2580. for ( var j=0, jen=tds.length ; j<jen ; j++ ) {
  2581. cellProcess( tds[j] );
  2582. }
  2583. }
  2584. // Read the ID from the DOM if present
  2585. var rowNode = row.firstChild ? row : row.nTr;
  2586. if ( rowNode ) {
  2587. var id = rowNode.getAttribute( 'id' );
  2588. if ( id ) {
  2589. _fnSetObjectDataFn( settings.rowId )( d, id );
  2590. }
  2591. }
  2592. return {
  2593. data: d,
  2594. cells: tds
  2595. };
  2596. }
  2597. /**
  2598. * Render and cache a row's display data for the columns, if required
  2599. * @returns
  2600. */
  2601. function _fnGetRowDisplay (settings, rowIdx) {
  2602. let rowModal = settings.aoData[rowIdx];
  2603. let columns = settings.aoColumns;
  2604. if (! rowModal.displayData) {
  2605. // Need to render and cache
  2606. rowModal.displayData = [];
  2607. for ( var colIdx=0, len=columns.length ; colIdx<len ; colIdx++ ) {
  2608. rowModal.displayData.push(
  2609. _fnGetCellData( settings, rowIdx, colIdx, 'display' )
  2610. );
  2611. }
  2612. }
  2613. return rowModal.displayData;
  2614. }
  2615. /**
  2616. * Create a new TR element (and it's TD children) for a row
  2617. * @param {object} oSettings dataTables settings object
  2618. * @param {int} iRow Row to consider
  2619. * @param {node} [nTrIn] TR element to add to the table - optional. If not given,
  2620. * DataTables will create a row automatically
  2621. * @param {array} [anTds] Array of TD|TH elements for the row - must be given
  2622. * if nTr is.
  2623. * @memberof DataTable#oApi
  2624. */
  2625. function _fnCreateTr ( oSettings, iRow, nTrIn, anTds )
  2626. {
  2627. var
  2628. row = oSettings.aoData[iRow],
  2629. rowData = row._aData,
  2630. cells = [],
  2631. nTr, nTd, oCol,
  2632. i, iLen, create,
  2633. trClass = oSettings.oClasses.tbody.row;
  2634. if ( row.nTr === null )
  2635. {
  2636. nTr = nTrIn || document.createElement('tr');
  2637. row.nTr = nTr;
  2638. row.anCells = cells;
  2639. _addClass(nTr, trClass);
  2640. /* Use a private property on the node to allow reserve mapping from the node
  2641. * to the aoData array for fast look up
  2642. */
  2643. nTr._DT_RowIndex = iRow;
  2644. /* Special parameters can be given by the data source to be used on the row */
  2645. _fnRowAttributes( oSettings, row );
  2646. /* Process each column */
  2647. for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
  2648. {
  2649. oCol = oSettings.aoColumns[i];
  2650. create = nTrIn && anTds[i] ? false : true;
  2651. nTd = create ? document.createElement( oCol.sCellType ) : anTds[i];
  2652. if (! nTd) {
  2653. _fnLog( oSettings, 0, 'Incorrect column count', 18 );
  2654. }
  2655. nTd._DT_CellIndex = {
  2656. row: iRow,
  2657. column: i
  2658. };
  2659. cells.push( nTd );
  2660. var display = _fnGetRowDisplay(oSettings, iRow);
  2661. // Need to create the HTML if new, or if a rendering function is defined
  2662. if (
  2663. create ||
  2664. (
  2665. (oCol.mRender || oCol.mData !== i) &&
  2666. (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')
  2667. )
  2668. ) {
  2669. _fnWriteCell(nTd, display[i]);
  2670. }
  2671. // Visibility - add or remove as required
  2672. if ( oCol.bVisible && create )
  2673. {
  2674. nTr.appendChild( nTd );
  2675. }
  2676. else if ( ! oCol.bVisible && ! create )
  2677. {
  2678. nTd.parentNode.removeChild( nTd );
  2679. }
  2680. if ( oCol.fnCreatedCell )
  2681. {
  2682. oCol.fnCreatedCell.call( oSettings.oInstance,
  2683. nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
  2684. );
  2685. }
  2686. }
  2687. _fnCallbackFire( oSettings, 'aoRowCreatedCallback', 'row-created', [nTr, rowData, iRow, cells] );
  2688. }
  2689. else {
  2690. _addClass(row.nTr, trClass);
  2691. }
  2692. }
  2693. /**
  2694. * Add attributes to a row based on the special `DT_*` parameters in a data
  2695. * source object.
  2696. * @param {object} settings DataTables settings object
  2697. * @param {object} DataTables row object for the row to be modified
  2698. * @memberof DataTable#oApi
  2699. */
  2700. function _fnRowAttributes( settings, row )
  2701. {
  2702. var tr = row.nTr;
  2703. var data = row._aData;
  2704. if ( tr ) {
  2705. var id = settings.rowIdFn( data );
  2706. if ( id ) {
  2707. tr.id = id;
  2708. }
  2709. if ( data.DT_RowClass ) {
  2710. // Remove any classes added by DT_RowClass before
  2711. var a = data.DT_RowClass.split(' ');
  2712. row.__rowc = row.__rowc ?
  2713. _unique( row.__rowc.concat( a ) ) :
  2714. a;
  2715. $(tr)
  2716. .removeClass( row.__rowc.join(' ') )
  2717. .addClass( data.DT_RowClass );
  2718. }
  2719. if ( data.DT_RowAttr ) {
  2720. $(tr).attr( data.DT_RowAttr );
  2721. }
  2722. if ( data.DT_RowData ) {
  2723. $(tr).data( data.DT_RowData );
  2724. }
  2725. }
  2726. }
  2727. /**
  2728. * Create the HTML header for the table
  2729. * @param {object} oSettings dataTables settings object
  2730. * @memberof DataTable#oApi
  2731. */
  2732. function _fnBuildHead( settings, side )
  2733. {
  2734. var classes = settings.oClasses;
  2735. var columns = settings.aoColumns;
  2736. var i, ien, row;
  2737. var target = side === 'header'
  2738. ? settings.nTHead
  2739. : settings.nTFoot;
  2740. var titleProp = side === 'header' ? 'sTitle' : side;
  2741. // Footer might be defined
  2742. if (! target) {
  2743. return;
  2744. }
  2745. // If no cells yet and we have content for them, then create
  2746. if (side === 'header' || _pluck(settings.aoColumns, titleProp).join('')) {
  2747. row = $('tr', target);
  2748. // Add a row if needed
  2749. if (! row.length) {
  2750. row = $('<tr/>').appendTo(target)
  2751. }
  2752. // Add the number of cells needed to make up to the number of columns
  2753. if (row.length === 1) {
  2754. var cells = $('td, th', row);
  2755. for ( i=cells.length, ien=columns.length ; i<ien ; i++ ) {
  2756. $('<th/>')
  2757. .html( columns[i][titleProp] || '' )
  2758. .appendTo( row );
  2759. }
  2760. }
  2761. }
  2762. var detected = _fnDetectHeader( settings, target, true );
  2763. if (side === 'header') {
  2764. settings.aoHeader = detected;
  2765. }
  2766. else {
  2767. settings.aoFooter = detected;
  2768. }
  2769. // ARIA role for the rows
  2770. $(target).children('tr').attr('role', 'row');
  2771. // Every cell needs to be passed through the renderer
  2772. $(target).children('tr').children('th, td')
  2773. .each( function () {
  2774. _fnRenderer( settings, side )(
  2775. settings, $(this), classes
  2776. );
  2777. } );
  2778. }
  2779. /**
  2780. * Build a layout structure for a header or footer
  2781. *
  2782. * @param {*} settings DataTables settings
  2783. * @param {*} source Source layout array
  2784. * @param {*} incColumns What columns should be included
  2785. * @returns Layout array
  2786. */
  2787. function _fnHeaderLayout( settings, source, incColumns )
  2788. {
  2789. var row, column, cell;
  2790. var local = [];
  2791. var structure = [];
  2792. var columns = settings.aoColumns;
  2793. var columnCount = columns.length;
  2794. var rowspan, colspan;
  2795. if ( ! source ) {
  2796. return;
  2797. }
  2798. // Default is to work on only visible columns
  2799. if ( ! incColumns ) {
  2800. incColumns = _range(columnCount)
  2801. .filter(function (idx) {
  2802. return columns[idx].bVisible;
  2803. });
  2804. }
  2805. // Make a copy of the master layout array, but with only the columns we want
  2806. for ( row=0 ; row<source.length ; row++ ) {
  2807. // Remove any columns we haven't selected
  2808. local[row] = source[row].slice().filter(function (cell, i) {
  2809. return incColumns.includes(i);
  2810. });
  2811. // Prep the structure array - it needs an element for each row
  2812. structure.push( [] );
  2813. }
  2814. for ( row=0 ; row<local.length ; row++ ) {
  2815. for ( column=0 ; column<local[row].length ; column++ ) {
  2816. rowspan = 1;
  2817. colspan = 1;
  2818. // Check to see if there is already a cell (row/colspan) covering our target
  2819. // insert point. If there is, then there is nothing to do.
  2820. if ( structure[row][column] === undefined ) {
  2821. cell = local[row][column].cell;
  2822. // Expand for rowspan
  2823. while (
  2824. local[row+rowspan] !== undefined &&
  2825. local[row][column].cell == local[row+rowspan][column].cell
  2826. ) {
  2827. structure[row+rowspan][column] = null;
  2828. rowspan++;
  2829. }
  2830. // And for colspan
  2831. while (
  2832. local[row][column+colspan] !== undefined &&
  2833. local[row][column].cell == local[row][column+colspan].cell
  2834. ) {
  2835. // Which also needs to go over rows
  2836. for ( var k=0 ; k<rowspan ; k++ ) {
  2837. structure[row+k][column+colspan] = null;
  2838. }
  2839. colspan++;
  2840. }
  2841. var titleSpan = $('span.dt-column-title', cell);
  2842. structure[row][column] = {
  2843. cell: cell,
  2844. colspan: colspan,
  2845. rowspan: rowspan,
  2846. title: titleSpan.length
  2847. ? titleSpan.html()
  2848. : $(cell).html()
  2849. };
  2850. }
  2851. }
  2852. }
  2853. return structure;
  2854. }
  2855. /**
  2856. * Draw the header (or footer) element based on the column visibility states.
  2857. *
  2858. * @param object oSettings dataTables settings object
  2859. * @param array aoSource Layout array from _fnDetectHeader
  2860. * @memberof DataTable#oApi
  2861. */
  2862. function _fnDrawHead( settings, source )
  2863. {
  2864. var layout = _fnHeaderLayout(settings, source);
  2865. var tr, n;
  2866. for ( var row=0 ; row<source.length ; row++ ) {
  2867. tr = source[row].row;
  2868. // All cells are going to be replaced, so empty out the row
  2869. // Can't use $().empty() as that kills event handlers
  2870. if (tr) {
  2871. while( (n = tr.firstChild) ) {
  2872. tr.removeChild( n );
  2873. }
  2874. }
  2875. for ( var column=0 ; column<layout[row].length ; column++ ) {
  2876. var point = layout[row][column];
  2877. if (point) {
  2878. $(point.cell)
  2879. .appendTo(tr)
  2880. .attr('rowspan', point.rowspan)
  2881. .attr('colspan', point.colspan);
  2882. }
  2883. }
  2884. }
  2885. }
  2886. /**
  2887. * Insert the required TR nodes into the table for display
  2888. * @param {object} oSettings dataTables settings object
  2889. * @param ajaxComplete true after ajax call to complete rendering
  2890. * @memberof DataTable#oApi
  2891. */
  2892. function _fnDraw( oSettings, ajaxComplete )
  2893. {
  2894. // Allow for state saving and a custom start position
  2895. _fnStart( oSettings );
  2896. /* Provide a pre-callback function which can be used to cancel the draw is false is returned */
  2897. var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
  2898. if ( aPreDraw.indexOf(false) !== -1 )
  2899. {
  2900. _fnProcessingDisplay( oSettings, false );
  2901. return;
  2902. }
  2903. var anRows = [];
  2904. var iRowCount = 0;
  2905. var bServerSide = _fnDataSource( oSettings ) == 'ssp';
  2906. var aiDisplay = oSettings.aiDisplay;
  2907. var iDisplayStart = oSettings._iDisplayStart;
  2908. var iDisplayEnd = oSettings.fnDisplayEnd();
  2909. var columns = oSettings.aoColumns;
  2910. var body = $(oSettings.nTBody);
  2911. oSettings.bDrawing = true;
  2912. /* Server-side processing draw intercept */
  2913. if ( !bServerSide )
  2914. {
  2915. oSettings.iDraw++;
  2916. }
  2917. else if ( !oSettings.bDestroying && !ajaxComplete)
  2918. {
  2919. // Show loading message for server-side processing
  2920. if (oSettings.iDraw === 0) {
  2921. body.empty().append(_emptyRow(oSettings));
  2922. }
  2923. _fnAjaxUpdate( oSettings );
  2924. return;
  2925. }
  2926. if ( aiDisplay.length !== 0 )
  2927. {
  2928. var iStart = bServerSide ? 0 : iDisplayStart;
  2929. var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;
  2930. for ( var j=iStart ; j<iEnd ; j++ )
  2931. {
  2932. var iDataIndex = aiDisplay[j];
  2933. var aoData = oSettings.aoData[ iDataIndex ];
  2934. if ( aoData.nTr === null )
  2935. {
  2936. _fnCreateTr( oSettings, iDataIndex );
  2937. }
  2938. var nRow = aoData.nTr;
  2939. // Add various classes as needed
  2940. for (var i=0 ; i<columns.length ; i++) {
  2941. var col = columns[i];
  2942. var td = aoData.anCells[i];
  2943. _addClass(td, _ext.type.className[col.sType]); // auto class
  2944. _addClass(td, col.sClass); // column class
  2945. _addClass(td, oSettings.oClasses.tbody.cell); // all cells
  2946. }
  2947. // Row callback functions - might want to manipulate the row
  2948. // iRowCount and j are not currently documented. Are they at all
  2949. // useful?
  2950. _fnCallbackFire( oSettings, 'aoRowCallback', null,
  2951. [nRow, aoData._aData, iRowCount, j, iDataIndex] );
  2952. anRows.push( nRow );
  2953. iRowCount++;
  2954. }
  2955. }
  2956. else
  2957. {
  2958. anRows[ 0 ] = _emptyRow(oSettings);
  2959. }
  2960. /* Header and footer callbacks */
  2961. _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],
  2962. _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
  2963. _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],
  2964. _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
  2965. // replaceChildren is faster, but only became widespread in 2020,
  2966. // so a fall back in jQuery is provided for older browsers.
  2967. if (body[0].replaceChildren) {
  2968. body[0].replaceChildren.apply(body[0], anRows);
  2969. }
  2970. else {
  2971. body.children().detach();
  2972. body.append( $(anRows) );
  2973. }
  2974. // Empty table needs a specific class
  2975. $(oSettings.nTableWrapper).toggleClass('dt-empty-footer', $('tr', oSettings.nTFoot).length === 0);
  2976. /* Call all required callback functions for the end of a draw */
  2977. _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings], true );
  2978. /* Draw is complete, sorting and filtering must be as well */
  2979. oSettings.bSorted = false;
  2980. oSettings.bFiltered = false;
  2981. oSettings.bDrawing = false;
  2982. }
  2983. /**
  2984. * Redraw the table - taking account of the various features which are enabled
  2985. * @param {object} oSettings dataTables settings object
  2986. * @param {boolean} [holdPosition] Keep the current paging position. By default
  2987. * the paging is reset to the first page
  2988. * @memberof DataTable#oApi
  2989. */
  2990. function _fnReDraw( settings, holdPosition, recompute )
  2991. {
  2992. var
  2993. features = settings.oFeatures,
  2994. sort = features.bSort,
  2995. filter = features.bFilter;
  2996. if (recompute === undefined || recompute === true) {
  2997. if ( sort ) {
  2998. _fnSort( settings );
  2999. }
  3000. if ( filter ) {
  3001. _fnFilterComplete( settings, settings.oPreviousSearch );
  3002. }
  3003. else {
  3004. // No filtering, so we want to just use the display master
  3005. settings.aiDisplay = settings.aiDisplayMaster.slice();
  3006. }
  3007. }
  3008. if ( holdPosition !== true ) {
  3009. settings._iDisplayStart = 0;
  3010. }
  3011. // Let any modules know about the draw hold position state (used by
  3012. // scrolling internally)
  3013. settings._drawHold = holdPosition;
  3014. _fnDraw( settings );
  3015. settings._drawHold = false;
  3016. }
  3017. /*
  3018. * Table is empty - create a row with an empty message in it
  3019. */
  3020. function _emptyRow ( settings ) {
  3021. var oLang = settings.oLanguage;
  3022. var zero = oLang.sZeroRecords;
  3023. var dataSrc = _fnDataSource( settings );
  3024. if (
  3025. (settings.iDraw < 1 && dataSrc === 'ssp') ||
  3026. (settings.iDraw <= 1 && dataSrc === 'ajax')
  3027. ) {
  3028. zero = oLang.sLoadingRecords;
  3029. }
  3030. else if ( oLang.sEmptyTable && settings.fnRecordsTotal() === 0 )
  3031. {
  3032. zero = oLang.sEmptyTable;
  3033. }
  3034. return $( '<tr/>' )
  3035. .append( $('<td />', {
  3036. 'colSpan': _fnVisbleColumns( settings ),
  3037. 'class': settings.oClasses.empty.row
  3038. } ).html( zero ) )[0];
  3039. }
  3040. /**
  3041. * Convert a `layout` object given by a user to the object structure needed
  3042. * for the renderer. This is done twice, once for above and once for below
  3043. * the table. Ordering must also be considered.
  3044. *
  3045. * @param {*} settings DataTables settings object
  3046. * @param {*} layout Layout object to convert
  3047. * @param {string} side `top` or `bottom`
  3048. * @returns Converted array structure - one item for each row.
  3049. */
  3050. function _layoutArray ( settings, layout, side )
  3051. {
  3052. var groups = {};
  3053. // Combine into like groups (e.g. `top`, `top2`, etc)
  3054. $.each( layout, function ( pos, val ) {
  3055. if (val === null) {
  3056. return;
  3057. }
  3058. var splitPos = pos.replace(/([A-Z])/g, ' $1').split(' ');
  3059. if ( ! groups[ splitPos[0] ] ) {
  3060. groups[ splitPos[0] ] = {};
  3061. }
  3062. var align = splitPos.length === 1 ?
  3063. 'full' :
  3064. splitPos[1].toLowerCase();
  3065. var group = groups[ splitPos[0] ];
  3066. var groupRun = function (contents, innerVal) {
  3067. // If it is an object, then there can be multiple features contained in it
  3068. if ( $.isPlainObject( innerVal ) ) {
  3069. Object.keys(innerVal).map(function (key) {
  3070. contents.push( {
  3071. feature: key,
  3072. opts: innerVal[key]
  3073. });
  3074. });
  3075. }
  3076. else {
  3077. contents.push(innerVal);
  3078. }
  3079. }
  3080. // Transform to an object with a contents property
  3081. if (! group[align] || ! group[align].contents) {
  3082. group[align] = { contents: [] };
  3083. }
  3084. // Allow for an array or just a single object
  3085. if ( Array.isArray(val)) {
  3086. for (var i=0 ; i<val.length ; i++) {
  3087. groupRun(group[align].contents, val[i]);
  3088. }
  3089. }
  3090. else {
  3091. groupRun(group[ align ].contents, val);
  3092. }
  3093. // And make contents an array
  3094. if ( ! Array.isArray( group[ align ].contents ) ) {
  3095. group[ align ].contents = [ group[ align ].contents ];
  3096. }
  3097. } );
  3098. var filtered = Object.keys(groups)
  3099. .map( function ( pos ) {
  3100. // Filter to only the side we need
  3101. if ( pos.indexOf(side) !== 0 ) {
  3102. return null;
  3103. }
  3104. return {
  3105. name: pos,
  3106. val: groups[pos]
  3107. };
  3108. } )
  3109. .filter( function (item) {
  3110. return item !== null;
  3111. });
  3112. // Order by item identifier
  3113. filtered.sort( function ( a, b ) {
  3114. var order1 = a.name.replace(/[^0-9]/g, '') * 1;
  3115. var order2 = b.name.replace(/[^0-9]/g, '') * 1;
  3116. return order2 - order1;
  3117. } );
  3118. if ( side === 'bottom' ) {
  3119. filtered.reverse();
  3120. }
  3121. // Split into rows
  3122. var rows = [];
  3123. for ( var i=0, ien=filtered.length ; i<ien ; i++ ) {
  3124. if ( filtered[i].val.full ) {
  3125. rows.push( { full: filtered[i].val.full } );
  3126. _layoutResolve( settings, rows[ rows.length - 1 ] );
  3127. delete filtered[i].val.full;
  3128. }
  3129. if ( Object.keys(filtered[i].val).length ) {
  3130. rows.push( filtered[i].val );
  3131. _layoutResolve( settings, rows[ rows.length - 1 ] );
  3132. }
  3133. }
  3134. return rows;
  3135. }
  3136. /**
  3137. * Convert the contents of a row's layout object to nodes that can be inserted
  3138. * into the document by a renderer. Execute functions, look up plug-ins, etc.
  3139. *
  3140. * @param {*} settings DataTables settings object
  3141. * @param {*} row Layout object for this row
  3142. */
  3143. function _layoutResolve( settings, row ) {
  3144. var getFeature = function (feature, opts) {
  3145. if ( ! _ext.features[ feature ] ) {
  3146. _fnLog( settings, 0, 'Unknown feature: '+ feature );
  3147. }
  3148. return _ext.features[ feature ].apply( this, [settings, opts] );
  3149. };
  3150. var resolve = function ( item ) {
  3151. var line = row[ item ].contents;
  3152. for ( var i=0, ien=line.length ; i<ien ; i++ ) {
  3153. if ( ! line[i] ) {
  3154. continue;
  3155. }
  3156. else if ( typeof line[i] === 'string' ) {
  3157. line[i] = getFeature( line[i], null );
  3158. }
  3159. else if ( $.isPlainObject(line[i]) ) {
  3160. // If it's an object, it just has feature and opts properties from
  3161. // the transform in _layoutArray
  3162. line[i] = getFeature(line[i].feature, line[i].opts);
  3163. }
  3164. else if ( typeof line[i].node === 'function' ) {
  3165. line[i] = line[i].node( settings );
  3166. }
  3167. else if ( typeof line[i] === 'function' ) {
  3168. var inst = line[i]( settings );
  3169. line[i] = typeof inst.node === 'function' ?
  3170. inst.node() :
  3171. inst;
  3172. }
  3173. }
  3174. };
  3175. $.each( row, function ( key ) {
  3176. resolve( key );
  3177. } );
  3178. }
  3179. /**
  3180. * Add the options to the page HTML for the table
  3181. * @param {object} settings DataTables settings object
  3182. * @memberof DataTable#oApi
  3183. */
  3184. function _fnAddOptionsHtml ( settings )
  3185. {
  3186. var classes = settings.oClasses;
  3187. var table = $(settings.nTable);
  3188. // Wrapper div around everything DataTables controls
  3189. var insert = $('<div/>')
  3190. .attr({
  3191. id: settings.sTableId+'_wrapper',
  3192. 'class': classes.container
  3193. })
  3194. .insertBefore(table);
  3195. settings.nTableWrapper = insert[0];
  3196. if (settings.sDom) {
  3197. // Legacy
  3198. _fnLayoutDom(settings, settings.sDom, insert);
  3199. }
  3200. else {
  3201. var top = _layoutArray( settings, settings.layout, 'top' );
  3202. var bottom = _layoutArray( settings, settings.layout, 'bottom' );
  3203. var renderer = _fnRenderer( settings, 'layout' );
  3204. // Everything above - the renderer will actually insert the contents into the document
  3205. top.forEach(function (item) {
  3206. renderer( settings, insert, item );
  3207. });
  3208. // The table - always the center of attention
  3209. renderer( settings, insert, {
  3210. full: {
  3211. table: true,
  3212. contents: [ _fnFeatureHtmlTable(settings) ]
  3213. }
  3214. } );
  3215. // Everything below
  3216. bottom.forEach(function (item) {
  3217. renderer( settings, insert, item );
  3218. });
  3219. }
  3220. // Processing floats on top, so it isn't an inserted feature
  3221. _processingHtml( settings );
  3222. }
  3223. /**
  3224. * Draw the table with the legacy DOM property
  3225. * @param {*} settings DT settings object
  3226. * @param {*} dom DOM string
  3227. * @param {*} insert Insert point
  3228. */
  3229. function _fnLayoutDom( settings, dom, insert )
  3230. {
  3231. var parts = dom.match(/(".*?")|('.*?')|./g);
  3232. var featureNode, option, newNode, next, attr;
  3233. for ( var i=0 ; i<parts.length ; i++ ) {
  3234. featureNode = null;
  3235. option = parts[i];
  3236. if ( option == '<' ) {
  3237. // New container div
  3238. newNode = $('<div/>');
  3239. // Check to see if we should append an id and/or a class name to the container
  3240. next = parts[i+1];
  3241. if ( next[0] == "'" || next[0] == '"' ) {
  3242. attr = next.replace(/['"]/g, '');
  3243. var id = '', className;
  3244. /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
  3245. * breaks the string into parts and applies them as needed
  3246. */
  3247. if ( attr.indexOf('.') != -1 ) {
  3248. var split = attr.split('.');
  3249. id = split[0];
  3250. className = split[1];
  3251. }
  3252. else if ( attr[0] == "#" ) {
  3253. id = attr;
  3254. }
  3255. else {
  3256. className = attr;
  3257. }
  3258. newNode
  3259. .attr('id', id.substring(1))
  3260. .addClass(className);
  3261. i++; // Move along the position array
  3262. }
  3263. insert.append( newNode );
  3264. insert = newNode;
  3265. }
  3266. else if ( option == '>' ) {
  3267. // End container div
  3268. insert = insert.parent();
  3269. }
  3270. else if ( option == 't' ) {
  3271. // Table
  3272. featureNode = _fnFeatureHtmlTable( settings );
  3273. }
  3274. else
  3275. {
  3276. DataTable.ext.feature.forEach(function(feature) {
  3277. if ( option == feature.cFeature ) {
  3278. featureNode = feature.fnInit( settings );
  3279. }
  3280. });
  3281. }
  3282. // Add to the display
  3283. if ( featureNode ) {
  3284. insert.append( featureNode );
  3285. }
  3286. }
  3287. }
  3288. /**
  3289. * Use the DOM source to create up an array of header cells. The idea here is to
  3290. * create a layout grid (array) of rows x columns, which contains a reference
  3291. * to the cell that that point in the grid (regardless of col/rowspan), such that
  3292. * any column / row could be removed and the new grid constructed
  3293. * @param {node} thead The header/footer element for the table
  3294. * @returns {array} Calculated layout array
  3295. * @memberof DataTable#oApi
  3296. */
  3297. function _fnDetectHeader ( settings, thead, write )
  3298. {
  3299. var columns = settings.aoColumns;
  3300. var rows = $(thead).children('tr');
  3301. var row, cell;
  3302. var i, k, l, iLen, shifted, column, colspan, rowspan;
  3303. var isHeader = thead && thead.nodeName.toLowerCase() === 'thead';
  3304. var layout = [];
  3305. var unique;
  3306. var shift = function ( a, i, j ) {
  3307. var k = a[i];
  3308. while ( k[j] ) {
  3309. j++;
  3310. }
  3311. return j;
  3312. };
  3313. // We know how many rows there are in the layout - so prep it
  3314. for ( i=0, iLen=rows.length ; i<iLen ; i++ ) {
  3315. layout.push( [] );
  3316. }
  3317. for ( i=0, iLen=rows.length ; i<iLen ; i++ ) {
  3318. row = rows[i];
  3319. column = 0;
  3320. // For every cell in the row..
  3321. cell = row.firstChild;
  3322. while ( cell ) {
  3323. if (
  3324. cell.nodeName.toUpperCase() == 'TD' ||
  3325. cell.nodeName.toUpperCase() == 'TH'
  3326. ) {
  3327. var cols = [];
  3328. // Get the col and rowspan attributes from the DOM and sanitise them
  3329. colspan = cell.getAttribute('colspan') * 1;
  3330. rowspan = cell.getAttribute('rowspan') * 1;
  3331. colspan = (!colspan || colspan===0 || colspan===1) ? 1 : colspan;
  3332. rowspan = (!rowspan || rowspan===0 || rowspan===1) ? 1 : rowspan;
  3333. // There might be colspan cells already in this row, so shift our target
  3334. // accordingly
  3335. shifted = shift( layout, i, column );
  3336. // Cache calculation for unique columns
  3337. unique = colspan === 1 ?
  3338. true :
  3339. false;
  3340. // Perform header setup
  3341. if ( write ) {
  3342. if (unique) {
  3343. // Allow column options to be set from HTML attributes
  3344. _fnColumnOptions( settings, shifted, $(cell).data() );
  3345. // Get the width for the column. This can be defined from the
  3346. // width attribute, style attribute or `columns.width` option
  3347. var columnDef = columns[shifted];
  3348. var width = cell.getAttribute('width') || null;
  3349. var t = cell.style.width.match(/width:\s*(\d+[pxem%]+)/);
  3350. if ( t ) {
  3351. width = t[1];
  3352. }
  3353. columnDef.sWidthOrig = columnDef.sWidth || width;
  3354. if (isHeader) {
  3355. // Column title handling - can be user set, or read from the DOM
  3356. // This happens before the render, so the original is still in place
  3357. if ( columnDef.sTitle !== null && ! columnDef.autoTitle ) {
  3358. cell.innerHTML = columnDef.sTitle;
  3359. }
  3360. if (! columnDef.sTitle && unique) {
  3361. columnDef.sTitle = _stripHtml(cell.innerHTML);
  3362. columnDef.autoTitle = true;
  3363. }
  3364. }
  3365. else {
  3366. // Footer specific operations
  3367. if (columnDef.footer) {
  3368. cell.innerHTML = columnDef.footer;
  3369. }
  3370. }
  3371. // Fall back to the aria-label attribute on the table header if no ariaTitle is
  3372. // provided.
  3373. if (! columnDef.ariaTitle) {
  3374. columnDef.ariaTitle = $(cell).attr("aria-label") || columnDef.sTitle;
  3375. }
  3376. // Column specific class names
  3377. if ( columnDef.className ) {
  3378. $(cell).addClass( columnDef.className );
  3379. }
  3380. }
  3381. // Wrap the column title so we can write to it in future
  3382. if ( $('span.dt-column-title', cell).length === 0) {
  3383. $('<span>')
  3384. .addClass('dt-column-title')
  3385. .append(cell.childNodes)
  3386. .appendTo(cell);
  3387. }
  3388. if ( isHeader && $('span.dt-column-order', cell).length === 0) {
  3389. $('<span>')
  3390. .addClass('dt-column-order')
  3391. .appendTo(cell);
  3392. }
  3393. }
  3394. // If there is col / rowspan, copy the information into the layout grid
  3395. for ( l=0 ; l<colspan ; l++ ) {
  3396. for ( k=0 ; k<rowspan ; k++ ) {
  3397. layout[i+k][shifted+l] = {
  3398. cell: cell,
  3399. unique: unique
  3400. };
  3401. layout[i+k].row = row;
  3402. }
  3403. cols.push( shifted+l );
  3404. }
  3405. // Assign an attribute so spanning cells can still be identified
  3406. // as belonging to a column
  3407. cell.setAttribute('data-dt-column', _unique(cols).join(','));
  3408. }
  3409. cell = cell.nextSibling;
  3410. }
  3411. }
  3412. return layout;
  3413. }
  3414. /**
  3415. * Set the start position for draw
  3416. * @param {object} oSettings dataTables settings object
  3417. */
  3418. function _fnStart( oSettings )
  3419. {
  3420. var bServerSide = _fnDataSource( oSettings ) == 'ssp';
  3421. var iInitDisplayStart = oSettings.iInitDisplayStart;
  3422. // Check and see if we have an initial draw position from state saving
  3423. if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 )
  3424. {
  3425. oSettings._iDisplayStart = bServerSide ?
  3426. iInitDisplayStart :
  3427. iInitDisplayStart >= oSettings.fnRecordsDisplay() ?
  3428. 0 :
  3429. iInitDisplayStart;
  3430. oSettings.iInitDisplayStart = -1;
  3431. }
  3432. }
  3433. /**
  3434. * Create an Ajax call based on the table's settings, taking into account that
  3435. * parameters can have multiple forms, and backwards compatibility.
  3436. *
  3437. * @param {object} oSettings dataTables settings object
  3438. * @param {array} data Data to send to the server, required by
  3439. * DataTables - may be augmented by developer callbacks
  3440. * @param {function} fn Callback function to run when data is obtained
  3441. */
  3442. function _fnBuildAjax( oSettings, data, fn )
  3443. {
  3444. var ajaxData;
  3445. var ajax = oSettings.ajax;
  3446. var instance = oSettings.oInstance;
  3447. var callback = function ( json ) {
  3448. var status = oSettings.jqXHR
  3449. ? oSettings.jqXHR.status
  3450. : null;
  3451. if ( json === null || (typeof status === 'number' && status == 204 ) ) {
  3452. json = {};
  3453. _fnAjaxDataSrc( oSettings, json, [] );
  3454. }
  3455. var error = json.error || json.sError;
  3456. if ( error ) {
  3457. _fnLog( oSettings, 0, error );
  3458. }
  3459. oSettings.json = json;
  3460. _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR], true );
  3461. fn( json );
  3462. };
  3463. if ( $.isPlainObject( ajax ) && ajax.data )
  3464. {
  3465. ajaxData = ajax.data;
  3466. var newData = typeof ajaxData === 'function' ?
  3467. ajaxData( data, oSettings ) : // fn can manipulate data or return
  3468. ajaxData; // an object object or array to merge
  3469. // If the function returned something, use that alone
  3470. data = typeof ajaxData === 'function' && newData ?
  3471. newData :
  3472. $.extend( true, data, newData );
  3473. // Remove the data property as we've resolved it already and don't want
  3474. // jQuery to do it again (it is restored at the end of the function)
  3475. delete ajax.data;
  3476. }
  3477. var baseAjax = {
  3478. "url": typeof ajax === 'string' ?
  3479. ajax :
  3480. '',
  3481. "data": data,
  3482. "success": callback,
  3483. "dataType": "json",
  3484. "cache": false,
  3485. "type": oSettings.sServerMethod,
  3486. "error": function (xhr, error) {
  3487. var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR], true );
  3488. if ( ret.indexOf(true) === -1 ) {
  3489. if ( error == "parsererror" ) {
  3490. _fnLog( oSettings, 0, 'Invalid JSON response', 1 );
  3491. }
  3492. else if ( xhr.readyState === 4 ) {
  3493. _fnLog( oSettings, 0, 'Ajax error', 7 );
  3494. }
  3495. }
  3496. _fnProcessingDisplay( oSettings, false );
  3497. }
  3498. };
  3499. // If `ajax` option is an object, extend and override our default base
  3500. if ( $.isPlainObject( ajax ) ) {
  3501. $.extend( baseAjax, ajax )
  3502. }
  3503. // Store the data submitted for the API
  3504. oSettings.oAjaxData = data;
  3505. // Allow plug-ins and external processes to modify the data
  3506. _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data, baseAjax], true );
  3507. if ( typeof ajax === 'function' )
  3508. {
  3509. // Is a function - let the caller define what needs to be done
  3510. oSettings.jqXHR = ajax.call( instance, data, callback, oSettings );
  3511. }
  3512. else if (ajax.url === '') {
  3513. // No url, so don't load any data. Just apply an empty data array
  3514. // to the object for the callback.
  3515. var empty = {};
  3516. DataTable.util.set(ajax.dataSrc)(empty, []);
  3517. callback(empty);
  3518. }
  3519. else {
  3520. // Object to extend the base settings
  3521. oSettings.jqXHR = $.ajax( baseAjax );
  3522. // Restore for next time around
  3523. if ( ajaxData ) {
  3524. ajax.data = ajaxData;
  3525. }
  3526. }
  3527. }
  3528. /**
  3529. * Update the table using an Ajax call
  3530. * @param {object} settings dataTables settings object
  3531. * @returns {boolean} Block the table drawing or not
  3532. * @memberof DataTable#oApi
  3533. */
  3534. function _fnAjaxUpdate( settings )
  3535. {
  3536. settings.iDraw++;
  3537. _fnProcessingDisplay( settings, true );
  3538. _fnBuildAjax(
  3539. settings,
  3540. _fnAjaxParameters( settings ),
  3541. function(json) {
  3542. _fnAjaxUpdateDraw( settings, json );
  3543. }
  3544. );
  3545. }
  3546. /**
  3547. * Build up the parameters in an object needed for a server-side processing
  3548. * request.
  3549. * @param {object} oSettings dataTables settings object
  3550. * @returns {bool} block the table drawing or not
  3551. * @memberof DataTable#oApi
  3552. */
  3553. function _fnAjaxParameters( settings )
  3554. {
  3555. var
  3556. columns = settings.aoColumns,
  3557. features = settings.oFeatures,
  3558. preSearch = settings.oPreviousSearch,
  3559. preColSearch = settings.aoPreSearchCols,
  3560. colData = function ( idx, prop ) {
  3561. return typeof columns[idx][prop] === 'function' ?
  3562. 'function' :
  3563. columns[idx][prop];
  3564. };
  3565. return {
  3566. draw: settings.iDraw,
  3567. columns: columns.map( function ( column, i ) {
  3568. return {
  3569. data: colData(i, 'mData'),
  3570. name: column.sName,
  3571. searchable: column.bSearchable,
  3572. orderable: column.bSortable,
  3573. search: {
  3574. value: preColSearch[i].search,
  3575. regex: preColSearch[i].regex,
  3576. fixed: Object.keys(column.searchFixed).map( function(name) {
  3577. return {
  3578. name: name,
  3579. term: column.searchFixed[name].toString()
  3580. }
  3581. })
  3582. }
  3583. };
  3584. } ),
  3585. order: _fnSortFlatten( settings ).map( function ( val ) {
  3586. return {
  3587. column: val.col,
  3588. dir: val.dir,
  3589. name: colData(val.col, 'sName')
  3590. };
  3591. } ),
  3592. start: settings._iDisplayStart,
  3593. length: features.bPaginate ?
  3594. settings._iDisplayLength :
  3595. -1,
  3596. search: {
  3597. value: preSearch.search,
  3598. regex: preSearch.regex,
  3599. fixed: Object.keys(settings.searchFixed).map( function(name) {
  3600. return {
  3601. name: name,
  3602. term: settings.searchFixed[name].toString()
  3603. }
  3604. })
  3605. }
  3606. };
  3607. }
  3608. /**
  3609. * Data the data from the server (nuking the old) and redraw the table
  3610. * @param {object} oSettings dataTables settings object
  3611. * @param {object} json json data return from the server.
  3612. * @param {string} json.sEcho Tracking flag for DataTables to match requests
  3613. * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
  3614. * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
  3615. * @param {array} json.aaData The data to display on this page
  3616. * @param {string} [json.sColumns] Column ordering (sName, comma separated)
  3617. * @memberof DataTable#oApi
  3618. */
  3619. function _fnAjaxUpdateDraw ( settings, json )
  3620. {
  3621. var data = _fnAjaxDataSrc(settings, json);
  3622. var draw = _fnAjaxDataSrcParam(settings, 'draw', json);
  3623. var recordsTotal = _fnAjaxDataSrcParam(settings, 'recordsTotal', json);
  3624. var recordsFiltered = _fnAjaxDataSrcParam(settings, 'recordsFiltered', json);
  3625. if ( draw !== undefined ) {
  3626. // Protect against out of sequence returns
  3627. if ( draw*1 < settings.iDraw ) {
  3628. return;
  3629. }
  3630. settings.iDraw = draw * 1;
  3631. }
  3632. // No data in returned object, so rather than an array, we show an empty table
  3633. if ( ! data ) {
  3634. data = [];
  3635. }
  3636. _fnClearTable( settings );
  3637. settings._iRecordsTotal = parseInt(recordsTotal, 10);
  3638. settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
  3639. for ( var i=0, ien=data.length ; i<ien ; i++ ) {
  3640. _fnAddData( settings, data[i] );
  3641. }
  3642. settings.aiDisplay = settings.aiDisplayMaster.slice();
  3643. _fnDraw( settings, true );
  3644. _fnInitComplete( settings );
  3645. _fnProcessingDisplay( settings, false );
  3646. }
  3647. /**
  3648. * Get the data from the JSON data source to use for drawing a table. Using
  3649. * `_fnGetObjectDataFn` allows the data to be sourced from a property of the
  3650. * source object, or from a processing function.
  3651. * @param {object} settings dataTables settings object
  3652. * @param {object} json Data source object / array from the server
  3653. * @return {array} Array of data to use
  3654. */
  3655. function _fnAjaxDataSrc ( settings, json, write )
  3656. {
  3657. var dataProp = 'data';
  3658. if ($.isPlainObject( settings.ajax ) && settings.ajax.dataSrc !== undefined) {
  3659. // Could in inside a `dataSrc` object, or not!
  3660. var dataSrc = settings.ajax.dataSrc;
  3661. // string, function and object are valid types
  3662. if (typeof dataSrc === 'string' || typeof dataSrc === 'function') {
  3663. dataProp = dataSrc;
  3664. }
  3665. else if (dataSrc.data !== undefined) {
  3666. dataProp = dataSrc.data;
  3667. }
  3668. }
  3669. if ( ! write ) {
  3670. if ( dataProp === 'data' ) {
  3671. // If the default, then we still want to support the old style, and safely ignore
  3672. // it if possible
  3673. return json.aaData || json[dataProp];
  3674. }
  3675. return dataProp !== "" ?
  3676. _fnGetObjectDataFn( dataProp )( json ) :
  3677. json;
  3678. }
  3679. // set
  3680. _fnSetObjectDataFn( dataProp )( json, write );
  3681. }
  3682. /**
  3683. * Very similar to _fnAjaxDataSrc, but for the other SSP properties
  3684. * @param {*} settings DataTables settings object
  3685. * @param {*} param Target parameter
  3686. * @param {*} json JSON data
  3687. * @returns Resolved value
  3688. */
  3689. function _fnAjaxDataSrcParam (settings, param, json) {
  3690. var dataSrc = $.isPlainObject( settings.ajax )
  3691. ? settings.ajax.dataSrc
  3692. : null;
  3693. if (dataSrc && dataSrc[param]) {
  3694. // Get from custom location
  3695. return _fnGetObjectDataFn( dataSrc[param] )( json );
  3696. }
  3697. // else - Default behaviour
  3698. var old = '';
  3699. // Legacy support
  3700. if (param === 'draw') {
  3701. old = 'sEcho';
  3702. }
  3703. else if (param === 'recordsTotal') {
  3704. old = 'iTotalRecords';
  3705. }
  3706. else if (param === 'recordsFiltered') {
  3707. old = 'iTotalDisplayRecords';
  3708. }
  3709. return json[old] !== undefined
  3710. ? json[old]
  3711. : json[param];
  3712. }
  3713. /**
  3714. * Filter the table using both the global filter and column based filtering
  3715. * @param {object} settings dataTables settings object
  3716. * @param {object} input search information
  3717. * @memberof DataTable#oApi
  3718. */
  3719. function _fnFilterComplete ( settings, input )
  3720. {
  3721. var columnsSearch = settings.aoPreSearchCols;
  3722. // Resolve any column types that are unknown due to addition or invalidation
  3723. // @todo As per sort - can this be moved into an event handler?
  3724. _fnColumnTypes( settings );
  3725. // In server-side processing all filtering is done by the server, so no point hanging around here
  3726. if ( _fnDataSource( settings ) != 'ssp' )
  3727. {
  3728. // Check if any of the rows were invalidated
  3729. _fnFilterData( settings );
  3730. // Start from the full data set
  3731. settings.aiDisplay = settings.aiDisplayMaster.slice();
  3732. // Global filter first
  3733. _fnFilter( settings.aiDisplay, settings, input.search, input );
  3734. $.each(settings.searchFixed, function (name, term) {
  3735. _fnFilter(settings.aiDisplay, settings, term, {});
  3736. });
  3737. // Then individual column filters
  3738. for ( var i=0 ; i<columnsSearch.length ; i++ )
  3739. {
  3740. var col = columnsSearch[i];
  3741. _fnFilter(
  3742. settings.aiDisplay,
  3743. settings,
  3744. col.search,
  3745. col,
  3746. i
  3747. );
  3748. $.each(settings.aoColumns[i].searchFixed, function (name, term) {
  3749. _fnFilter(settings.aiDisplay, settings, term, {}, i);
  3750. });
  3751. }
  3752. // And finally global filtering
  3753. _fnFilterCustom( settings );
  3754. }
  3755. // Tell the draw function we have been filtering
  3756. settings.bFiltered = true;
  3757. _fnCallbackFire( settings, null, 'search', [settings] );
  3758. }
  3759. /**
  3760. * Apply custom filtering functions
  3761. *
  3762. * This is legacy now that we have named functions, but it is widely used
  3763. * from 1.x, so it is not yet deprecated.
  3764. * @param {object} oSettings dataTables settings object
  3765. * @memberof DataTable#oApi
  3766. */
  3767. function _fnFilterCustom( settings )
  3768. {
  3769. var filters = DataTable.ext.search;
  3770. var displayRows = settings.aiDisplay;
  3771. var row, rowIdx;
  3772. for ( var i=0, ien=filters.length ; i<ien ; i++ ) {
  3773. var rows = [];
  3774. // Loop over each row and see if it should be included
  3775. for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {
  3776. rowIdx = displayRows[ j ];
  3777. row = settings.aoData[ rowIdx ];
  3778. if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {
  3779. rows.push( rowIdx );
  3780. }
  3781. }
  3782. // So the array reference doesn't break set the results into the
  3783. // existing array
  3784. displayRows.length = 0;
  3785. displayRows.push.apply(displayRows, rows);
  3786. }
  3787. }
  3788. /**
  3789. * Filter the data table based on user input and draw the table
  3790. */
  3791. function _fnFilter( searchRows, settings, input, options, column )
  3792. {
  3793. if ( input === '' ) {
  3794. return;
  3795. }
  3796. var i = 0;
  3797. var matched = [];
  3798. // Search term can be a function, regex or string - if a string we apply our
  3799. // smart filtering regex (assuming the options require that)
  3800. var searchFunc = typeof input === 'function' ? input : null;
  3801. var rpSearch = input instanceof RegExp
  3802. ? input
  3803. : searchFunc
  3804. ? null
  3805. : _fnFilterCreateSearch( input, options );
  3806. // Then for each row, does the test pass. If not, lop the row from the array
  3807. for (i=0 ; i<searchRows.length ; i++) {
  3808. var row = settings.aoData[ searchRows[i] ];
  3809. var data = column === undefined
  3810. ? row._sFilterRow
  3811. : row._aFilterData[ column ];
  3812. if ( (searchFunc && searchFunc(data, row._aData, searchRows[i], column)) || (rpSearch && rpSearch.test(data)) ) {
  3813. matched.push(searchRows[i]);
  3814. }
  3815. }
  3816. // Mutate the searchRows array
  3817. searchRows.length = matched.length;
  3818. for (i=0 ; i<matched.length ; i++) {
  3819. searchRows[i] = matched[i];
  3820. }
  3821. }
  3822. /**
  3823. * Build a regular expression object suitable for searching a table
  3824. * @param {string} sSearch string to search for
  3825. * @param {bool} bRegex treat as a regular expression or not
  3826. * @param {bool} bSmart perform smart filtering or not
  3827. * @param {bool} bCaseInsensitive Do case insensitive matching or not
  3828. * @returns {RegExp} constructed object
  3829. * @memberof DataTable#oApi
  3830. */
  3831. function _fnFilterCreateSearch( search, inOpts )
  3832. {
  3833. var not = [];
  3834. var options = $.extend({}, {
  3835. boundary: false,
  3836. caseInsensitive: true,
  3837. exact: false,
  3838. regex: false,
  3839. smart: true
  3840. }, inOpts);
  3841. if (typeof search !== 'string') {
  3842. search = search.toString();
  3843. }
  3844. // Remove diacritics if normalize is set up to do so
  3845. search = _normalize(search);
  3846. if (options.exact) {
  3847. return new RegExp(
  3848. '^'+_fnEscapeRegex(search)+'$',
  3849. options.caseInsensitive ? 'i' : ''
  3850. );
  3851. }
  3852. search = options.regex ?
  3853. search :
  3854. _fnEscapeRegex( search );
  3855. if ( options.smart ) {
  3856. /* For smart filtering we want to allow the search to work regardless of
  3857. * word order. We also want double quoted text to be preserved, so word
  3858. * order is important - a la google. And a negative look around for
  3859. * finding rows which don't contain a given string.
  3860. *
  3861. * So this is the sort of thing we want to generate:
  3862. *
  3863. * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
  3864. */
  3865. var parts = search.match( /!?["\u201C][^"\u201D]+["\u201D]|[^ ]+/g ) || [''];
  3866. var a = parts.map( function ( word ) {
  3867. var negative = false;
  3868. var m;
  3869. // Determine if it is a "does not include"
  3870. if ( word.charAt(0) === '!' ) {
  3871. negative = true;
  3872. word = word.substring(1);
  3873. }
  3874. // Strip the quotes from around matched phrases
  3875. if ( word.charAt(0) === '"' ) {
  3876. m = word.match( /^"(.*)"$/ );
  3877. word = m ? m[1] : word;
  3878. }
  3879. else if ( word.charAt(0) === '\u201C' ) {
  3880. // Smart quote match (iPhone users)
  3881. m = word.match( /^\u201C(.*)\u201D$/ );
  3882. word = m ? m[1] : word;
  3883. }
  3884. // For our "not" case, we need to modify the string that is
  3885. // allowed to match at the end of the expression.
  3886. if (negative) {
  3887. if (word.length > 1) {
  3888. not.push('(?!'+word+')');
  3889. }
  3890. word = '';
  3891. }
  3892. return word.replace(/"/g, '');
  3893. } );
  3894. var match = not.length
  3895. ? not.join('')
  3896. : '';
  3897. var boundary = options.boundary
  3898. ? '\\b'
  3899. : '';
  3900. search = '^(?=.*?'+boundary+a.join( ')(?=.*?'+boundary )+')('+match+'.)*$';
  3901. }
  3902. return new RegExp( search, options.caseInsensitive ? 'i' : '' );
  3903. }
  3904. /**
  3905. * Escape a string such that it can be used in a regular expression
  3906. * @param {string} sVal string to escape
  3907. * @returns {string} escaped string
  3908. * @memberof DataTable#oApi
  3909. */
  3910. var _fnEscapeRegex = DataTable.util.escapeRegex;
  3911. var __filter_div = $('<div>')[0];
  3912. var __filter_div_textContent = __filter_div.textContent !== undefined;
  3913. // Update the filtering data for each row if needed (by invalidation or first run)
  3914. function _fnFilterData ( settings )
  3915. {
  3916. var columns = settings.aoColumns;
  3917. var data = settings.aoData;
  3918. var column;
  3919. var j, jen, filterData, cellData, row;
  3920. var wasInvalidated = false;
  3921. for ( var rowIdx=0 ; rowIdx<data.length ; rowIdx++ ) {
  3922. if (! data[rowIdx]) {
  3923. continue;
  3924. }
  3925. row = data[rowIdx];
  3926. if ( ! row._aFilterData ) {
  3927. filterData = [];
  3928. for ( j=0, jen=columns.length ; j<jen ; j++ ) {
  3929. column = columns[j];
  3930. if ( column.bSearchable ) {
  3931. cellData = _fnGetCellData( settings, rowIdx, j, 'filter' );
  3932. // Search in DataTables is string based
  3933. if ( cellData === null ) {
  3934. cellData = '';
  3935. }
  3936. if ( typeof cellData !== 'string' && cellData.toString ) {
  3937. cellData = cellData.toString();
  3938. }
  3939. }
  3940. else {
  3941. cellData = '';
  3942. }
  3943. // If it looks like there is an HTML entity in the string,
  3944. // attempt to decode it so sorting works as expected. Note that
  3945. // we could use a single line of jQuery to do this, but the DOM
  3946. // method used here is much faster https://jsperf.com/html-decode
  3947. if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {
  3948. __filter_div.innerHTML = cellData;
  3949. cellData = __filter_div_textContent ?
  3950. __filter_div.textContent :
  3951. __filter_div.innerText;
  3952. }
  3953. if ( cellData.replace ) {
  3954. cellData = cellData.replace(/[\r\n\u2028]/g, '');
  3955. }
  3956. filterData.push( cellData );
  3957. }
  3958. row._aFilterData = filterData;
  3959. row._sFilterRow = filterData.join(' ');
  3960. wasInvalidated = true;
  3961. }
  3962. }
  3963. return wasInvalidated;
  3964. }
  3965. /**
  3966. * Draw the table for the first time, adding all required features
  3967. * @param {object} settings dataTables settings object
  3968. * @memberof DataTable#oApi
  3969. */
  3970. function _fnInitialise ( settings )
  3971. {
  3972. var i, iAjaxStart=settings.iInitDisplayStart;
  3973. /* Ensure that the table data is fully initialised */
  3974. if ( ! settings.bInitialised ) {
  3975. setTimeout( function(){ _fnInitialise( settings ); }, 200 );
  3976. return;
  3977. }
  3978. /* Build and draw the header / footer for the table */
  3979. _fnBuildHead( settings, 'header' );
  3980. _fnBuildHead( settings, 'footer' );
  3981. _fnDrawHead( settings, settings.aoHeader );
  3982. _fnDrawHead( settings, settings.aoFooter );
  3983. // Enable features
  3984. _fnAddOptionsHtml( settings );
  3985. _fnSortInit( settings );
  3986. _colGroup( settings );
  3987. /* Okay to show that something is going on now */
  3988. _fnProcessingDisplay( settings, true );
  3989. _fnCallbackFire( settings, null, 'preInit', [settings], true );
  3990. // If there is default sorting required - let's do it. The sort function
  3991. // will do the drawing for us. Otherwise we draw the table regardless of the
  3992. // Ajax source - this allows the table to look initialised for Ajax sourcing
  3993. // data (show 'loading' message possibly)
  3994. _fnReDraw( settings );
  3995. var dataSrc = _fnDataSource( settings );
  3996. // Server-side processing init complete is done by _fnAjaxUpdateDraw
  3997. if ( dataSrc != 'ssp' ) {
  3998. // if there is an ajax source load the data
  3999. if ( dataSrc == 'ajax' ) {
  4000. _fnBuildAjax( settings, {}, function(json) {
  4001. var aData = _fnAjaxDataSrc( settings, json );
  4002. // Got the data - add it to the table
  4003. for ( i=0 ; i<aData.length ; i++ ) {
  4004. _fnAddData( settings, aData[i] );
  4005. }
  4006. // Reset the init display for cookie saving. We've already done
  4007. // a filter, and therefore cleared it before. So we need to make
  4008. // it appear 'fresh'
  4009. settings.iInitDisplayStart = iAjaxStart;
  4010. _fnReDraw( settings );
  4011. _fnProcessingDisplay( settings, false );
  4012. _fnInitComplete( settings );
  4013. }, settings );
  4014. }
  4015. else {
  4016. _fnInitComplete( settings );
  4017. _fnProcessingDisplay( settings, false );
  4018. }
  4019. }
  4020. }
  4021. /**
  4022. * Draw the table for the first time, adding all required features
  4023. * @param {object} settings dataTables settings object
  4024. * @memberof DataTable#oApi
  4025. */
  4026. function _fnInitComplete ( settings )
  4027. {
  4028. if (settings._bInitComplete) {
  4029. return;
  4030. }
  4031. var args = [settings, settings.json];
  4032. settings._bInitComplete = true;
  4033. // Table is fully set up and we have data, so calculate the
  4034. // column widths
  4035. _fnAdjustColumnSizing( settings );
  4036. _fnCallbackFire( settings, null, 'plugin-init', args, true );
  4037. _fnCallbackFire( settings, 'aoInitComplete', 'init', args, true );
  4038. }
  4039. function _fnLengthChange ( settings, val )
  4040. {
  4041. var len = parseInt( val, 10 );
  4042. settings._iDisplayLength = len;
  4043. _fnLengthOverflow( settings );
  4044. // Fire length change event
  4045. _fnCallbackFire( settings, null, 'length', [settings, len] );
  4046. }
  4047. /**
  4048. * Alter the display settings to change the page
  4049. * @param {object} settings DataTables settings object
  4050. * @param {string|int} action Paging action to take: "first", "previous",
  4051. * "next" or "last" or page number to jump to (integer)
  4052. * @param [bool] redraw Automatically draw the update or not
  4053. * @returns {bool} true page has changed, false - no change
  4054. * @memberof DataTable#oApi
  4055. */
  4056. function _fnPageChange ( settings, action, redraw )
  4057. {
  4058. var
  4059. start = settings._iDisplayStart,
  4060. len = settings._iDisplayLength,
  4061. records = settings.fnRecordsDisplay();
  4062. if ( records === 0 || len === -1 )
  4063. {
  4064. start = 0;
  4065. }
  4066. else if ( typeof action === "number" )
  4067. {
  4068. start = action * len;
  4069. if ( start > records )
  4070. {
  4071. start = 0;
  4072. }
  4073. }
  4074. else if ( action == "first" )
  4075. {
  4076. start = 0;
  4077. }
  4078. else if ( action == "previous" )
  4079. {
  4080. start = len >= 0 ?
  4081. start - len :
  4082. 0;
  4083. if ( start < 0 )
  4084. {
  4085. start = 0;
  4086. }
  4087. }
  4088. else if ( action == "next" )
  4089. {
  4090. if ( start + len < records )
  4091. {
  4092. start += len;
  4093. }
  4094. }
  4095. else if ( action == "last" )
  4096. {
  4097. start = Math.floor( (records-1) / len) * len;
  4098. }
  4099. else if ( action === 'ellipsis' )
  4100. {
  4101. return;
  4102. }
  4103. else
  4104. {
  4105. _fnLog( settings, 0, "Unknown paging action: "+action, 5 );
  4106. }
  4107. var changed = settings._iDisplayStart !== start;
  4108. settings._iDisplayStart = start;
  4109. _fnCallbackFire( settings, null, changed ? 'page' : 'page-nc', [settings] );
  4110. if ( changed && redraw ) {
  4111. _fnDraw( settings );
  4112. }
  4113. return changed;
  4114. }
  4115. /**
  4116. * Generate the node required for the processing node
  4117. * @param {object} settings DataTables settings object
  4118. */
  4119. function _processingHtml ( settings )
  4120. {
  4121. var table = settings.nTable;
  4122. var scrolling = settings.oScroll.sX !== '' || settings.oScroll.sY !== '';
  4123. if ( settings.oFeatures.bProcessing ) {
  4124. var n = $('<div/>', {
  4125. 'id': settings.sTableId + '_processing',
  4126. 'class': settings.oClasses.processing.container,
  4127. 'role': 'status'
  4128. } )
  4129. .html( settings.oLanguage.sProcessing )
  4130. .append('<div><div></div><div></div><div></div><div></div></div>');
  4131. // Different positioning depending on if scrolling is enabled or not
  4132. if (scrolling) {
  4133. n.prependTo( $('div.dt-scroll', settings.nTableWrapper) );
  4134. }
  4135. else {
  4136. n.insertBefore( table );
  4137. }
  4138. $(table).on( 'processing.dt.DT', function (e, s, show) {
  4139. n.css( 'display', show ? 'block' : 'none' );
  4140. } );
  4141. }
  4142. }
  4143. /**
  4144. * Display or hide the processing indicator
  4145. * @param {object} settings DataTables settings object
  4146. * @param {bool} show Show the processing indicator (true) or not (false)
  4147. */
  4148. function _fnProcessingDisplay ( settings, show )
  4149. {
  4150. _fnCallbackFire( settings, null, 'processing', [settings, show] );
  4151. }
  4152. /**
  4153. * Add any control elements for the table - specifically scrolling
  4154. * @param {object} settings dataTables settings object
  4155. * @returns {node} Node to add to the DOM
  4156. * @memberof DataTable#oApi
  4157. */
  4158. function _fnFeatureHtmlTable ( settings )
  4159. {
  4160. var table = $(settings.nTable);
  4161. // Scrolling from here on in
  4162. var scroll = settings.oScroll;
  4163. if ( scroll.sX === '' && scroll.sY === '' ) {
  4164. return settings.nTable;
  4165. }
  4166. var scrollX = scroll.sX;
  4167. var scrollY = scroll.sY;
  4168. var classes = settings.oClasses.scrolling;
  4169. var caption = settings.captionNode;
  4170. var captionSide = caption ? caption._captionSide : null;
  4171. var headerClone = $( table[0].cloneNode(false) );
  4172. var footerClone = $( table[0].cloneNode(false) );
  4173. var footer = table.children('tfoot');
  4174. var _div = '<div/>';
  4175. var size = function ( s ) {
  4176. return !s ? null : _fnStringToCss( s );
  4177. };
  4178. if ( ! footer.length ) {
  4179. footer = null;
  4180. }
  4181. /*
  4182. * The HTML structure that we want to generate in this function is:
  4183. * div - scroller
  4184. * div - scroll head
  4185. * div - scroll head inner
  4186. * table - scroll head table
  4187. * thead - thead
  4188. * div - scroll body
  4189. * table - table (master table)
  4190. * thead - thead clone for sizing
  4191. * tbody - tbody
  4192. * div - scroll foot
  4193. * div - scroll foot inner
  4194. * table - scroll foot table
  4195. * tfoot - tfoot
  4196. */
  4197. var scroller = $( _div, { 'class': classes.container } )
  4198. .append(
  4199. $(_div, { 'class': classes.header.self } )
  4200. .css( {
  4201. overflow: 'hidden',
  4202. position: 'relative',
  4203. border: 0,
  4204. width: scrollX ? size(scrollX) : '100%'
  4205. } )
  4206. .append(
  4207. $(_div, { 'class': classes.header.inner } )
  4208. .css( {
  4209. 'box-sizing': 'content-box',
  4210. width: scroll.sXInner || '100%'
  4211. } )
  4212. .append(
  4213. headerClone
  4214. .removeAttr('id')
  4215. .css( 'margin-left', 0 )
  4216. .append( captionSide === 'top' ? caption : null )
  4217. .append(
  4218. table.children('thead')
  4219. )
  4220. )
  4221. )
  4222. )
  4223. .append(
  4224. $(_div, { 'class': classes.body } )
  4225. .css( {
  4226. position: 'relative',
  4227. overflow: 'auto',
  4228. width: size( scrollX )
  4229. } )
  4230. .append( table )
  4231. );
  4232. if ( footer ) {
  4233. scroller.append(
  4234. $(_div, { 'class': classes.footer.self } )
  4235. .css( {
  4236. overflow: 'hidden',
  4237. border: 0,
  4238. width: scrollX ? size(scrollX) : '100%'
  4239. } )
  4240. .append(
  4241. $(_div, { 'class': classes.footer.inner } )
  4242. .append(
  4243. footerClone
  4244. .removeAttr('id')
  4245. .css( 'margin-left', 0 )
  4246. .append( captionSide === 'bottom' ? caption : null )
  4247. .append(
  4248. table.children('tfoot')
  4249. )
  4250. )
  4251. )
  4252. );
  4253. }
  4254. var children = scroller.children();
  4255. var scrollHead = children[0];
  4256. var scrollBody = children[1];
  4257. var scrollFoot = footer ? children[2] : null;
  4258. // When the body is scrolled, then we also want to scroll the headers
  4259. $(scrollBody).on( 'scroll.DT', function () {
  4260. var scrollLeft = this.scrollLeft;
  4261. scrollHead.scrollLeft = scrollLeft;
  4262. if ( footer ) {
  4263. scrollFoot.scrollLeft = scrollLeft;
  4264. }
  4265. } );
  4266. // When focus is put on the header cells, we might need to scroll the body
  4267. $('th, td', scrollHead).on('focus', function () {
  4268. var scrollLeft = scrollHead.scrollLeft;
  4269. scrollBody.scrollLeft = scrollLeft;
  4270. if ( footer ) {
  4271. scrollBody.scrollLeft = scrollLeft;
  4272. }
  4273. });
  4274. $(scrollBody).css('max-height', scrollY);
  4275. if (! scroll.bCollapse) {
  4276. $(scrollBody).css('height', scrollY);
  4277. }
  4278. settings.nScrollHead = scrollHead;
  4279. settings.nScrollBody = scrollBody;
  4280. settings.nScrollFoot = scrollFoot;
  4281. // On redraw - align columns
  4282. settings.aoDrawCallback.push(_fnScrollDraw);
  4283. return scroller[0];
  4284. }
  4285. /**
  4286. * Update the header, footer and body tables for resizing - i.e. column
  4287. * alignment.
  4288. *
  4289. * Welcome to the most horrible function DataTables. The process that this
  4290. * function follows is basically:
  4291. * 1. Re-create the table inside the scrolling div
  4292. * 2. Correct colgroup > col values if needed
  4293. * 3. Copy colgroup > col over to header and footer
  4294. * 4. Clean up
  4295. *
  4296. * @param {object} settings dataTables settings object
  4297. * @memberof DataTable#oApi
  4298. */
  4299. function _fnScrollDraw ( settings )
  4300. {
  4301. // Given that this is such a monster function, a lot of variables are use
  4302. // to try and keep the minimised size as small as possible
  4303. var
  4304. scroll = settings.oScroll,
  4305. barWidth = scroll.iBarWidth,
  4306. divHeader = $(settings.nScrollHead),
  4307. divHeaderInner = divHeader.children('div'),
  4308. divHeaderTable = divHeaderInner.children('table'),
  4309. divBodyEl = settings.nScrollBody,
  4310. divBody = $(divBodyEl),
  4311. divFooter = $(settings.nScrollFoot),
  4312. divFooterInner = divFooter.children('div'),
  4313. divFooterTable = divFooterInner.children('table'),
  4314. header = $(settings.nTHead),
  4315. table = $(settings.nTable),
  4316. footer = settings.nTFoot && $('th, td', settings.nTFoot).length ? $(settings.nTFoot) : null,
  4317. browser = settings.oBrowser,
  4318. headerCopy, footerCopy;
  4319. // If the scrollbar visibility has changed from the last draw, we need to
  4320. // adjust the column sizes as the table width will have changed to account
  4321. // for the scrollbar
  4322. var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight;
  4323. if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) {
  4324. settings.scrollBarVis = scrollBarVis;
  4325. _fnAdjustColumnSizing( settings );
  4326. return; // adjust column sizing will call this function again
  4327. }
  4328. else {
  4329. settings.scrollBarVis = scrollBarVis;
  4330. }
  4331. // 1. Re-create the table inside the scrolling div
  4332. // Remove the old minimised thead and tfoot elements in the inner table
  4333. table.children('thead, tfoot').remove();
  4334. // Clone the current header and footer elements and then place it into the inner table
  4335. headerCopy = header.clone().prependTo( table );
  4336. headerCopy.find('th, td').removeAttr('tabindex');
  4337. headerCopy.find('[id]').removeAttr('id');
  4338. if ( footer ) {
  4339. footerCopy = footer.clone().prependTo( table );
  4340. footerCopy.find('[id]').removeAttr('id');
  4341. }
  4342. // 2. Correct colgroup > col values if needed
  4343. // It is possible that the cell sizes are smaller than the content, so we need to
  4344. // correct colgroup>col for such cases. This can happen if the auto width detection
  4345. // uses a cell which has a longer string, but isn't the widest! For example
  4346. // "Chief Executive Officer (CEO)" is the longest string in the demo, but
  4347. // "Systems Administrator" is actually the widest string since it doesn't collapse.
  4348. // Note the use of translating into a column index to get the `col` element. This
  4349. // is because of Responsive which might remove `col` elements, knocking the alignment
  4350. // of the indexes out.
  4351. if (settings.aiDisplay.length) {
  4352. // Get the column sizes from the first row in the table
  4353. var colSizes = table.children('tbody').eq(0).children('tr').eq(0).children('th, td').map(function (vis) {
  4354. return {
  4355. idx: _fnVisibleToColumnIndex(settings, vis),
  4356. width: $(this).outerWidth()
  4357. }
  4358. });
  4359. // Check against what the colgroup > col is set to and correct if needed
  4360. for (var i=0 ; i<colSizes.length ; i++) {
  4361. var colEl = settings.aoColumns[ colSizes[i].idx ].colEl[0];
  4362. var colWidth = colEl.style.width.replace('px', '');
  4363. if (colWidth !== colSizes[i].width) {
  4364. colEl.style.width = colSizes[i].width + 'px';
  4365. }
  4366. }
  4367. }
  4368. // 3. Copy the colgroup over to the header and footer
  4369. divHeaderTable
  4370. .find('colgroup')
  4371. .remove();
  4372. divHeaderTable.append(settings.colgroup.clone());
  4373. if ( footer ) {
  4374. divFooterTable
  4375. .find('colgroup')
  4376. .remove();
  4377. divFooterTable.append(settings.colgroup.clone());
  4378. }
  4379. // "Hide" the header and footer that we used for the sizing. We need to keep
  4380. // the content of the cell so that the width applied to the header and body
  4381. // both match, but we want to hide it completely.
  4382. $('th, td', headerCopy).each(function () {
  4383. $(this.childNodes).wrapAll('<div class="dt-scroll-sizing">');
  4384. });
  4385. if ( footer ) {
  4386. $('th, td', footerCopy).each(function () {
  4387. $(this.childNodes).wrapAll('<div class="dt-scroll-sizing">');
  4388. });
  4389. }
  4390. // 4. Clean up
  4391. // Figure out if there are scrollbar present - if so then we need a the header and footer to
  4392. // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
  4393. var isScrolling = Math.floor(table.height()) > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
  4394. var paddingSide = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
  4395. // Set the width's of the header and footer tables
  4396. var outerWidth = table.outerWidth();
  4397. divHeaderTable.css('width', _fnStringToCss( outerWidth ));
  4398. divHeaderInner
  4399. .css('width', _fnStringToCss( outerWidth ))
  4400. .css(paddingSide, isScrolling ? barWidth+"px" : "0px");
  4401. if ( footer ) {
  4402. divFooterTable.css('width', _fnStringToCss( outerWidth ));
  4403. divFooterInner
  4404. .css('width', _fnStringToCss( outerWidth ))
  4405. .css(paddingSide, isScrolling ? barWidth+"px" : "0px");
  4406. }
  4407. // Correct DOM ordering for colgroup - comes before the thead
  4408. table.children('colgroup').prependTo(table);
  4409. // Adjust the position of the header in case we loose the y-scrollbar
  4410. divBody.trigger('scroll');
  4411. // If sorting or filtering has occurred, jump the scrolling back to the top
  4412. // only if we aren't holding the position
  4413. if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {
  4414. divBodyEl.scrollTop = 0;
  4415. }
  4416. }
  4417. /**
  4418. * Calculate the width of columns for the table
  4419. * @param {object} settings dataTables settings object
  4420. * @memberof DataTable#oApi
  4421. */
  4422. function _fnCalculateColumnWidths ( settings )
  4423. {
  4424. // Not interested in doing column width calculation if auto-width is disabled
  4425. if (! settings.oFeatures.bAutoWidth) {
  4426. return;
  4427. }
  4428. var
  4429. table = settings.nTable,
  4430. columns = settings.aoColumns,
  4431. scroll = settings.oScroll,
  4432. scrollY = scroll.sY,
  4433. scrollX = scroll.sX,
  4434. scrollXInner = scroll.sXInner,
  4435. visibleColumns = _fnGetColumns( settings, 'bVisible' ),
  4436. tableWidthAttr = table.getAttribute('width'), // from DOM element
  4437. tableContainer = table.parentNode,
  4438. i, column, columnIdx;
  4439. var styleWidth = table.style.width;
  4440. if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
  4441. tableWidthAttr = styleWidth;
  4442. }
  4443. // Let plug-ins know that we are doing a recalc, in case they have changed any of the
  4444. // visible columns their own way (e.g. Responsive uses display:none).
  4445. _fnCallbackFire(
  4446. settings,
  4447. null,
  4448. 'column-calc',
  4449. {visible: visibleColumns},
  4450. false
  4451. );
  4452. // Construct a single row, worst case, table with the widest
  4453. // node in the data, assign any user defined widths, then insert it into
  4454. // the DOM and allow the browser to do all the hard work of calculating
  4455. // table widths
  4456. var tmpTable = $(table.cloneNode())
  4457. .css( 'visibility', 'hidden' )
  4458. .removeAttr( 'id' );
  4459. // Clean up the table body
  4460. tmpTable.append('<tbody>')
  4461. var tr = $('<tr/>').appendTo( tmpTable.find('tbody') );
  4462. // Clone the table header and footer - we can't use the header / footer
  4463. // from the cloned table, since if scrolling is active, the table's
  4464. // real header and footer are contained in different table tags
  4465. tmpTable
  4466. .append( $(settings.nTHead).clone() )
  4467. .append( $(settings.nTFoot).clone() );
  4468. // Remove any assigned widths from the footer (from scrolling)
  4469. tmpTable.find('tfoot th, tfoot td').css('width', '');
  4470. // Apply custom sizing to the cloned header
  4471. tmpTable.find('thead th, thead td').each( function () {
  4472. // Get the `width` from the header layout
  4473. var width = _fnColumnsSumWidth( settings, this, true, false );
  4474. if ( width ) {
  4475. this.style.width = width;
  4476. // For scrollX we need to force the column width otherwise the
  4477. // browser will collapse it. If this width is smaller than the
  4478. // width the column requires, then it will have no effect
  4479. if ( scrollX ) {
  4480. $( this ).append( $('<div/>').css( {
  4481. width: width,
  4482. margin: 0,
  4483. padding: 0,
  4484. border: 0,
  4485. height: 1
  4486. } ) );
  4487. }
  4488. }
  4489. else {
  4490. this.style.width = '';
  4491. }
  4492. } );
  4493. // Find the widest piece of data for each column and put it into the table
  4494. for ( i=0 ; i<visibleColumns.length ; i++ ) {
  4495. columnIdx = visibleColumns[i];
  4496. column = columns[ columnIdx ];
  4497. var longest = _fnGetMaxLenString(settings, columnIdx);
  4498. var autoClass = _ext.type.className[column.sType];
  4499. var text = longest + column.sContentPadding;
  4500. var insert = longest.indexOf('<') === -1
  4501. ? document.createTextNode(text)
  4502. : text
  4503. $('<td/>')
  4504. .addClass(autoClass)
  4505. .addClass(column.sClass)
  4506. .append(insert)
  4507. .appendTo(tr);
  4508. }
  4509. // Tidy the temporary table - remove name attributes so there aren't
  4510. // duplicated in the dom (radio elements for example)
  4511. $('[name]', tmpTable).removeAttr('name');
  4512. // Table has been built, attach to the document so we can work with it.
  4513. // A holding element is used, positioned at the top of the container
  4514. // with minimal height, so it has no effect on if the container scrolls
  4515. // or not. Otherwise it might trigger scrolling when it actually isn't
  4516. // needed
  4517. var holder = $('<div/>').css( scrollX || scrollY ?
  4518. {
  4519. position: 'absolute',
  4520. top: 0,
  4521. left: 0,
  4522. height: 1,
  4523. right: 0,
  4524. overflow: 'hidden'
  4525. } :
  4526. {}
  4527. )
  4528. .append( tmpTable )
  4529. .appendTo( tableContainer );
  4530. // When scrolling (X or Y) we want to set the width of the table as
  4531. // appropriate. However, when not scrolling leave the table width as it
  4532. // is. This results in slightly different, but I think correct behaviour
  4533. if ( scrollX && scrollXInner ) {
  4534. tmpTable.width( scrollXInner );
  4535. }
  4536. else if ( scrollX ) {
  4537. tmpTable.css( 'width', 'auto' );
  4538. tmpTable.removeAttr('width');
  4539. // If there is no width attribute or style, then allow the table to
  4540. // collapse
  4541. if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) {
  4542. tmpTable.width( tableContainer.clientWidth );
  4543. }
  4544. }
  4545. else if ( scrollY ) {
  4546. tmpTable.width( tableContainer.clientWidth );
  4547. }
  4548. else if ( tableWidthAttr ) {
  4549. tmpTable.width( tableWidthAttr );
  4550. }
  4551. // Get the width of each column in the constructed table
  4552. var total = 0;
  4553. var bodyCells = tmpTable.find('tbody tr').eq(0).children();
  4554. for ( i=0 ; i<visibleColumns.length ; i++ ) {
  4555. // Use getBounding for sub-pixel accuracy, which we then want to round up!
  4556. var bounding = bodyCells[i].getBoundingClientRect().width;
  4557. // Total is tracked to remove any sub-pixel errors as the outerWidth
  4558. // of the table might not equal the total given here
  4559. total += bounding;
  4560. // Width for each column to use
  4561. columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding );
  4562. }
  4563. table.style.width = _fnStringToCss( total );
  4564. // Finished with the table - ditch it
  4565. holder.remove();
  4566. // If there is a width attr, we want to attach an event listener which
  4567. // allows the table sizing to automatically adjust when the window is
  4568. // resized. Use the width attr rather than CSS, since we can't know if the
  4569. // CSS is a relative value or absolute - DOM read is always px.
  4570. if ( tableWidthAttr ) {
  4571. table.style.width = _fnStringToCss( tableWidthAttr );
  4572. }
  4573. if ( (tableWidthAttr || scrollX) && ! settings._reszEvt ) {
  4574. var bindResize = function () {
  4575. $(window).on('resize.DT-'+settings.sInstance, DataTable.util.throttle( function () {
  4576. if (! settings.bDestroying) {
  4577. _fnAdjustColumnSizing( settings );
  4578. }
  4579. } ) );
  4580. };
  4581. bindResize();
  4582. settings._reszEvt = true;
  4583. }
  4584. }
  4585. /**
  4586. * Get the maximum strlen for each data column
  4587. * @param {object} settings dataTables settings object
  4588. * @param {int} colIdx column of interest
  4589. * @returns {string} string of the max length
  4590. * @memberof DataTable#oApi
  4591. */
  4592. function _fnGetMaxLenString( settings, colIdx )
  4593. {
  4594. var column = settings.aoColumns[colIdx];
  4595. if (! column.maxLenString) {
  4596. var s, max='', maxLen = -1;
  4597. for ( var i=0, ien=settings.aiDisplayMaster.length ; i<ien ; i++ ) {
  4598. var rowIdx = settings.aiDisplayMaster[i];
  4599. var data = _fnGetRowDisplay(settings, rowIdx)[colIdx];
  4600. var cellString = data && typeof data === 'object' && data.nodeType
  4601. ? data.innerHTML
  4602. : data+'';
  4603. // Remove id / name attributes from elements so they
  4604. // don't interfere with existing elements
  4605. cellString = cellString
  4606. .replace(/id=".*?"/g, '')
  4607. .replace(/name=".*?"/g, '');
  4608. s = _stripHtml(cellString)
  4609. .replace( /&nbsp;/g, ' ' );
  4610. if ( s.length > maxLen ) {
  4611. // We want the HTML in the string, but the length that
  4612. // is important is the stripped string
  4613. max = cellString;
  4614. maxLen = s.length;
  4615. }
  4616. }
  4617. column.maxLenString = max;
  4618. }
  4619. return column.maxLenString;
  4620. }
  4621. /**
  4622. * Append a CSS unit (only if required) to a string
  4623. * @param {string} value to css-ify
  4624. * @returns {string} value with css unit
  4625. * @memberof DataTable#oApi
  4626. */
  4627. function _fnStringToCss( s )
  4628. {
  4629. if ( s === null ) {
  4630. return '0px';
  4631. }
  4632. if ( typeof s == 'number' ) {
  4633. return s < 0 ?
  4634. '0px' :
  4635. s+'px';
  4636. }
  4637. // Check it has a unit character already
  4638. return s.match(/\d$/) ?
  4639. s+'px' :
  4640. s;
  4641. }
  4642. /**
  4643. * Re-insert the `col` elements for current visibility
  4644. *
  4645. * @param {*} settings DT settings
  4646. */
  4647. function _colGroup( settings ) {
  4648. var cols = settings.aoColumns;
  4649. settings.colgroup.empty();
  4650. for (i=0 ; i<cols.length ; i++) {
  4651. if (cols[i].bVisible) {
  4652. settings.colgroup.append(cols[i].colEl);
  4653. }
  4654. }
  4655. }
  4656. function _fnSortInit( settings ) {
  4657. var target = settings.nTHead;
  4658. var headerRows = target.querySelectorAll('tr');
  4659. var legacyTop = settings.bSortCellsTop;
  4660. var notSelector = ':not([data-dt-order="disable"]):not([data-dt-order="icon-only"])';
  4661. // Legacy support for `orderCellsTop`
  4662. if (legacyTop === true) {
  4663. target = headerRows[0];
  4664. }
  4665. else if (legacyTop === false) {
  4666. target = headerRows[ headerRows.length - 1 ];
  4667. }
  4668. _fnSortAttachListener(
  4669. settings,
  4670. target,
  4671. target === settings.nTHead
  4672. ? 'tr'+notSelector+' th'+notSelector+', tr'+notSelector+' td'+notSelector
  4673. : 'th'+notSelector+', td'+notSelector
  4674. );
  4675. // Need to resolve the user input array into our internal structure
  4676. var order = [];
  4677. _fnSortResolve( settings, order, settings.aaSorting );
  4678. settings.aaSorting = order;
  4679. }
  4680. function _fnSortAttachListener(settings, node, selector, column, callback) {
  4681. _fnBindAction( node, selector, function (e) {
  4682. var run = false;
  4683. var columns = column === undefined
  4684. ? _fnColumnsFromHeader( e.target )
  4685. : [column];
  4686. if ( columns.length ) {
  4687. for ( var i=0, ien=columns.length ; i<ien ; i++ ) {
  4688. var ret = _fnSortAdd( settings, columns[i], i, e.shiftKey );
  4689. if (ret !== false) {
  4690. run = true;
  4691. }
  4692. // If the first entry is no sort, then subsequent
  4693. // sort columns are ignored
  4694. if (settings.aaSorting.length === 1 && settings.aaSorting[0][1] === '') {
  4695. break;
  4696. }
  4697. }
  4698. if (run) {
  4699. _fnProcessingDisplay( settings, true );
  4700. // Allow the processing display to show
  4701. setTimeout( function () {
  4702. _fnSort( settings );
  4703. _fnSortDisplay( settings, settings.aiDisplay );
  4704. // Sort processing done - redraw has its own processing display
  4705. _fnProcessingDisplay( settings, false );
  4706. _fnReDraw( settings, false, false );
  4707. if (callback) {
  4708. callback();
  4709. }
  4710. }, 0);
  4711. }
  4712. }
  4713. } );
  4714. }
  4715. /**
  4716. * Sort the display array to match the master's order
  4717. * @param {*} settings
  4718. */
  4719. function _fnSortDisplay(settings, display) {
  4720. if (display.length < 2) {
  4721. return;
  4722. }
  4723. var master = settings.aiDisplayMaster;
  4724. var masterMap = {};
  4725. var map = {};
  4726. var i;
  4727. // Rather than needing an `indexOf` on master array, we can create a map
  4728. for (i=0 ; i<master.length ; i++) {
  4729. masterMap[master[i]] = i;
  4730. }
  4731. // And then cache what would be the indexOf fom the display
  4732. for (i=0 ; i<display.length ; i++) {
  4733. map[display[i]] = masterMap[display[i]];
  4734. }
  4735. display.sort(function(a, b){
  4736. // Short version of this function is simply `master.indexOf(a) - master.indexOf(b);`
  4737. return map[a] - map[b];
  4738. });
  4739. }
  4740. function _fnSortResolve (settings, nestedSort, sort) {
  4741. var push = function ( a ) {
  4742. if ($.isPlainObject(a)) {
  4743. if (a.idx !== undefined) {
  4744. // Index based ordering
  4745. nestedSort.push([a.idx, a.dir]);
  4746. }
  4747. else if (a.name) {
  4748. // Name based ordering
  4749. var cols = _pluck( settings.aoColumns, 'sName');
  4750. var idx = cols.indexOf(a.name);
  4751. if (idx !== -1) {
  4752. nestedSort.push([idx, a.dir]);
  4753. }
  4754. }
  4755. }
  4756. else {
  4757. // Plain column index and direction pair
  4758. nestedSort.push(a);
  4759. }
  4760. };
  4761. if ( $.isPlainObject(sort) ) {
  4762. // Object
  4763. push(sort);
  4764. }
  4765. else if ( sort.length && typeof sort[0] === 'number' ) {
  4766. // 1D array
  4767. push(sort);
  4768. }
  4769. else if ( sort.length ) {
  4770. // 2D array
  4771. for (var z=0; z<sort.length; z++) {
  4772. push(sort[z]); // Object or array
  4773. }
  4774. }
  4775. }
  4776. function _fnSortFlatten ( settings )
  4777. {
  4778. var
  4779. i, k, kLen,
  4780. aSort = [],
  4781. extSort = DataTable.ext.type.order,
  4782. aoColumns = settings.aoColumns,
  4783. aDataSort, iCol, sType, srcCol,
  4784. fixed = settings.aaSortingFixed,
  4785. fixedObj = $.isPlainObject( fixed ),
  4786. nestedSort = [];
  4787. if ( ! settings.oFeatures.bSort ) {
  4788. return aSort;
  4789. }
  4790. // Build the sort array, with pre-fix and post-fix options if they have been
  4791. // specified
  4792. if ( Array.isArray( fixed ) ) {
  4793. _fnSortResolve( settings, nestedSort, fixed );
  4794. }
  4795. if ( fixedObj && fixed.pre ) {
  4796. _fnSortResolve( settings, nestedSort, fixed.pre );
  4797. }
  4798. _fnSortResolve( settings, nestedSort, settings.aaSorting );
  4799. if (fixedObj && fixed.post ) {
  4800. _fnSortResolve( settings, nestedSort, fixed.post );
  4801. }
  4802. for ( i=0 ; i<nestedSort.length ; i++ )
  4803. {
  4804. srcCol = nestedSort[i][0];
  4805. if ( aoColumns[ srcCol ] ) {
  4806. aDataSort = aoColumns[ srcCol ].aDataSort;
  4807. for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
  4808. {
  4809. iCol = aDataSort[k];
  4810. sType = aoColumns[ iCol ].sType || 'string';
  4811. if ( nestedSort[i]._idx === undefined ) {
  4812. nestedSort[i]._idx = aoColumns[iCol].asSorting.indexOf(nestedSort[i][1]);
  4813. }
  4814. if ( nestedSort[i][1] ) {
  4815. aSort.push( {
  4816. src: srcCol,
  4817. col: iCol,
  4818. dir: nestedSort[i][1],
  4819. index: nestedSort[i]._idx,
  4820. type: sType,
  4821. formatter: extSort[ sType+"-pre" ],
  4822. sorter: extSort[ sType+"-"+nestedSort[i][1] ]
  4823. } );
  4824. }
  4825. }
  4826. }
  4827. }
  4828. return aSort;
  4829. }
  4830. /**
  4831. * Change the order of the table
  4832. * @param {object} oSettings dataTables settings object
  4833. * @memberof DataTable#oApi
  4834. */
  4835. function _fnSort ( oSettings, col, dir )
  4836. {
  4837. var
  4838. i, ien, iLen,
  4839. aiOrig = [],
  4840. extSort = DataTable.ext.type.order,
  4841. aoData = oSettings.aoData,
  4842. sortCol,
  4843. displayMaster = oSettings.aiDisplayMaster,
  4844. aSort;
  4845. // Resolve any column types that are unknown due to addition or invalidation
  4846. // @todo Can this be moved into a 'data-ready' handler which is called when
  4847. // data is going to be used in the table?
  4848. _fnColumnTypes( oSettings );
  4849. // Allow a specific column to be sorted, which will _not_ alter the display
  4850. // master
  4851. if (col !== undefined) {
  4852. var srcCol = oSettings.aoColumns[col];
  4853. aSort = [{
  4854. src: col,
  4855. col: col,
  4856. dir: dir,
  4857. index: 0,
  4858. type: srcCol.sType,
  4859. formatter: extSort[ srcCol.sType+"-pre" ],
  4860. sorter: extSort[ srcCol.sType+"-"+dir ]
  4861. }];
  4862. displayMaster = displayMaster.slice();
  4863. }
  4864. else {
  4865. aSort = _fnSortFlatten( oSettings );
  4866. }
  4867. for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
  4868. sortCol = aSort[i];
  4869. // Load the data needed for the sort, for each cell
  4870. _fnSortData( oSettings, sortCol.col );
  4871. }
  4872. /* No sorting required if server-side or no sorting array */
  4873. if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )
  4874. {
  4875. // Reset the initial positions on each pass so we get a stable sort
  4876. for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
  4877. aiOrig[ i ] = i;
  4878. }
  4879. // If the first sort is desc, then reverse the array to preserve original
  4880. // order, just in reverse
  4881. if (aSort.length && aSort[0].dir === 'desc') {
  4882. aiOrig.reverse();
  4883. }
  4884. /* Do the sort - here we want multi-column sorting based on a given data source (column)
  4885. * and sorting function (from oSort) in a certain direction. It's reasonably complex to
  4886. * follow on it's own, but this is what we want (example two column sorting):
  4887. * fnLocalSorting = function(a,b){
  4888. * var test;
  4889. * test = oSort['string-asc']('data11', 'data12');
  4890. * if (test !== 0)
  4891. * return test;
  4892. * test = oSort['numeric-desc']('data21', 'data22');
  4893. * if (test !== 0)
  4894. * return test;
  4895. * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
  4896. * }
  4897. * Basically we have a test for each sorting column, if the data in that column is equal,
  4898. * test the next column. If all columns match, then we use a numeric sort on the row
  4899. * positions in the original data array to provide a stable sort.
  4900. */
  4901. displayMaster.sort( function ( a, b ) {
  4902. var
  4903. x, y, k, test, sort,
  4904. len=aSort.length,
  4905. dataA = aoData[a]._aSortData,
  4906. dataB = aoData[b]._aSortData;
  4907. for ( k=0 ; k<len ; k++ ) {
  4908. sort = aSort[k];
  4909. // Data, which may have already been through a `-pre` function
  4910. x = dataA[ sort.col ];
  4911. y = dataB[ sort.col ];
  4912. if (sort.sorter) {
  4913. // If there is a custom sorter (`-asc` or `-desc`) for this
  4914. // data type, use it
  4915. test = sort.sorter(x, y);
  4916. if ( test !== 0 ) {
  4917. return test;
  4918. }
  4919. }
  4920. else {
  4921. // Otherwise, use generic sorting
  4922. test = x<y ? -1 : x>y ? 1 : 0;
  4923. if ( test !== 0 ) {
  4924. return sort.dir === 'asc' ? test : -test;
  4925. }
  4926. }
  4927. }
  4928. x = aiOrig[a];
  4929. y = aiOrig[b];
  4930. return x<y ? -1 : x>y ? 1 : 0;
  4931. } );
  4932. }
  4933. else if ( aSort.length === 0 ) {
  4934. // Apply index order
  4935. displayMaster.sort(function (x, y) {
  4936. return x<y ? -1 : x>y ? 1 : 0;
  4937. });
  4938. }
  4939. if (col === undefined) {
  4940. // Tell the draw function that we have sorted the data
  4941. oSettings.bSorted = true;
  4942. _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort] );
  4943. }
  4944. return displayMaster;
  4945. }
  4946. /**
  4947. * Function to run on user sort request
  4948. * @param {object} settings dataTables settings object
  4949. * @param {node} attachTo node to attach the handler to
  4950. * @param {int} colIdx column sorting index
  4951. * @param {int} addIndex Counter
  4952. * @param {boolean} [shift=false] Shift click add
  4953. * @param {function} [callback] callback function
  4954. * @memberof DataTable#oApi
  4955. */
  4956. function _fnSortAdd ( settings, colIdx, addIndex, shift )
  4957. {
  4958. var col = settings.aoColumns[ colIdx ];
  4959. var sorting = settings.aaSorting;
  4960. var asSorting = col.asSorting;
  4961. var nextSortIdx;
  4962. var next = function ( a, overflow ) {
  4963. var idx = a._idx;
  4964. if ( idx === undefined ) {
  4965. idx = asSorting.indexOf(a[1]);
  4966. }
  4967. return idx+1 < asSorting.length ?
  4968. idx+1 :
  4969. overflow ?
  4970. null :
  4971. 0;
  4972. };
  4973. if ( ! col.bSortable ) {
  4974. return false;
  4975. }
  4976. // Convert to 2D array if needed
  4977. if ( typeof sorting[0] === 'number' ) {
  4978. sorting = settings.aaSorting = [ sorting ];
  4979. }
  4980. // If appending the sort then we are multi-column sorting
  4981. if ( (shift || addIndex) && settings.oFeatures.bSortMulti ) {
  4982. // Are we already doing some kind of sort on this column?
  4983. var sortIdx = _pluck(sorting, '0').indexOf(colIdx);
  4984. if ( sortIdx !== -1 ) {
  4985. // Yes, modify the sort
  4986. nextSortIdx = next( sorting[sortIdx], true );
  4987. if ( nextSortIdx === null && sorting.length === 1 ) {
  4988. nextSortIdx = 0; // can't remove sorting completely
  4989. }
  4990. if ( nextSortIdx === null ) {
  4991. sorting.splice( sortIdx, 1 );
  4992. }
  4993. else {
  4994. sorting[sortIdx][1] = asSorting[ nextSortIdx ];
  4995. sorting[sortIdx]._idx = nextSortIdx;
  4996. }
  4997. }
  4998. else if (shift) {
  4999. // No sort on this column yet, being added by shift click
  5000. // add it as itself
  5001. sorting.push( [ colIdx, asSorting[0], 0 ] );
  5002. sorting[sorting.length-1]._idx = 0;
  5003. }
  5004. else {
  5005. // No sort on this column yet, being added from a colspan
  5006. // so add with same direction as first column
  5007. sorting.push( [ colIdx, sorting[0][1], 0 ] );
  5008. sorting[sorting.length-1]._idx = 0;
  5009. }
  5010. }
  5011. else if ( sorting.length && sorting[0][0] == colIdx ) {
  5012. // Single column - already sorting on this column, modify the sort
  5013. nextSortIdx = next( sorting[0] );
  5014. sorting.length = 1;
  5015. sorting[0][1] = asSorting[ nextSortIdx ];
  5016. sorting[0]._idx = nextSortIdx;
  5017. }
  5018. else {
  5019. // Single column - sort only on this column
  5020. sorting.length = 0;
  5021. sorting.push( [ colIdx, asSorting[0] ] );
  5022. sorting[0]._idx = 0;
  5023. }
  5024. }
  5025. /**
  5026. * Set the sorting classes on table's body, Note: it is safe to call this function
  5027. * when bSort and bSortClasses are false
  5028. * @param {object} oSettings dataTables settings object
  5029. * @memberof DataTable#oApi
  5030. */
  5031. function _fnSortingClasses( settings )
  5032. {
  5033. var oldSort = settings.aLastSort;
  5034. var sortClass = settings.oClasses.order.position;
  5035. var sort = _fnSortFlatten( settings );
  5036. var features = settings.oFeatures;
  5037. var i, ien, colIdx;
  5038. if ( features.bSort && features.bSortClasses ) {
  5039. // Remove old sorting classes
  5040. for ( i=0, ien=oldSort.length ; i<ien ; i++ ) {
  5041. colIdx = oldSort[i].src;
  5042. // Remove column sorting
  5043. $( _pluck( settings.aoData, 'anCells', colIdx ) )
  5044. .removeClass( sortClass + (i<2 ? i+1 : 3) );
  5045. }
  5046. // Add new column sorting
  5047. for ( i=0, ien=sort.length ; i<ien ; i++ ) {
  5048. colIdx = sort[i].src;
  5049. $( _pluck( settings.aoData, 'anCells', colIdx ) )
  5050. .addClass( sortClass + (i<2 ? i+1 : 3) );
  5051. }
  5052. }
  5053. settings.aLastSort = sort;
  5054. }
  5055. // Get the data to sort a column, be it from cache, fresh (populating the
  5056. // cache), or from a sort formatter
  5057. function _fnSortData( settings, colIdx )
  5058. {
  5059. // Custom sorting function - provided by the sort data type
  5060. var column = settings.aoColumns[ colIdx ];
  5061. var customSort = DataTable.ext.order[ column.sSortDataType ];
  5062. var customData;
  5063. if ( customSort ) {
  5064. customData = customSort.call( settings.oInstance, settings, colIdx,
  5065. _fnColumnIndexToVisible( settings, colIdx )
  5066. );
  5067. }
  5068. // Use / populate cache
  5069. var row, cellData;
  5070. var formatter = DataTable.ext.type.order[ column.sType+"-pre" ];
  5071. var data = settings.aoData;
  5072. for ( var rowIdx=0 ; rowIdx<data.length ; rowIdx++ ) {
  5073. // Sparse array
  5074. if (! data[rowIdx]) {
  5075. continue;
  5076. }
  5077. row = data[rowIdx];
  5078. if ( ! row._aSortData ) {
  5079. row._aSortData = [];
  5080. }
  5081. if ( ! row._aSortData[colIdx] || customSort ) {
  5082. cellData = customSort ?
  5083. customData[rowIdx] : // If there was a custom sort function, use data from there
  5084. _fnGetCellData( settings, rowIdx, colIdx, 'sort' );
  5085. row._aSortData[ colIdx ] = formatter ?
  5086. formatter( cellData, settings ) :
  5087. cellData;
  5088. }
  5089. }
  5090. }
  5091. /**
  5092. * State information for a table
  5093. *
  5094. * @param {*} settings
  5095. * @returns State object
  5096. */
  5097. function _fnSaveState ( settings )
  5098. {
  5099. if (settings._bLoadingState) {
  5100. return;
  5101. }
  5102. /* Store the interesting variables */
  5103. var state = {
  5104. time: +new Date(),
  5105. start: settings._iDisplayStart,
  5106. length: settings._iDisplayLength,
  5107. order: $.extend( true, [], settings.aaSorting ),
  5108. search: $.extend({}, settings.oPreviousSearch),
  5109. columns: settings.aoColumns.map( function ( col, i ) {
  5110. return {
  5111. visible: col.bVisible,
  5112. search: $.extend({}, settings.aoPreSearchCols[i])
  5113. };
  5114. } )
  5115. };
  5116. settings.oSavedState = state;
  5117. _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] );
  5118. if ( settings.oFeatures.bStateSave && !settings.bDestroying )
  5119. {
  5120. settings.fnStateSaveCallback.call( settings.oInstance, settings, state );
  5121. }
  5122. }
  5123. /**
  5124. * Attempt to load a saved table state
  5125. * @param {object} oSettings dataTables settings object
  5126. * @param {object} oInit DataTables init object so we can override settings
  5127. * @param {function} callback Callback to execute when the state has been loaded
  5128. * @memberof DataTable#oApi
  5129. */
  5130. function _fnLoadState ( settings, init, callback )
  5131. {
  5132. if ( ! settings.oFeatures.bStateSave ) {
  5133. callback();
  5134. return;
  5135. }
  5136. var loaded = function(state) {
  5137. _fnImplementState(settings, state, callback);
  5138. }
  5139. var state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded );
  5140. if ( state !== undefined ) {
  5141. _fnImplementState( settings, state, callback );
  5142. }
  5143. // otherwise, wait for the loaded callback to be executed
  5144. return true;
  5145. }
  5146. function _fnImplementState ( settings, s, callback) {
  5147. var i, ien;
  5148. var columns = settings.aoColumns;
  5149. settings._bLoadingState = true;
  5150. // When StateRestore was introduced the state could now be implemented at any time
  5151. // Not just initialisation. To do this an api instance is required in some places
  5152. var api = settings._bInitComplete ? new DataTable.Api(settings) : null;
  5153. if ( ! s || ! s.time ) {
  5154. settings._bLoadingState = false;
  5155. callback();
  5156. return;
  5157. }
  5158. // Reject old data
  5159. var duration = settings.iStateDuration;
  5160. if ( duration > 0 && s.time < +new Date() - (duration*1000) ) {
  5161. settings._bLoadingState = false;
  5162. callback();
  5163. return;
  5164. }
  5165. // Allow custom and plug-in manipulation functions to alter the saved data set and
  5166. // cancelling of loading by returning false
  5167. var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, s] );
  5168. if ( abStateLoad.indexOf(false) !== -1 ) {
  5169. settings._bLoadingState = false;
  5170. callback();
  5171. return;
  5172. }
  5173. // Number of columns have changed - all bets are off, no restore of settings
  5174. if ( s.columns && columns.length !== s.columns.length ) {
  5175. settings._bLoadingState = false;
  5176. callback();
  5177. return;
  5178. }
  5179. // Store the saved state so it might be accessed at any time
  5180. settings.oLoadedState = $.extend( true, {}, s );
  5181. // This is needed for ColReorder, which has to happen first to allow all
  5182. // the stored indexes to be usable. It is not publicly documented.
  5183. _fnCallbackFire( settings, null, 'stateLoadInit', [settings, s], true );
  5184. // Page Length
  5185. if ( s.length !== undefined ) {
  5186. // If already initialised just set the value directly so that the select element is also updated
  5187. if (api) {
  5188. api.page.len(s.length)
  5189. }
  5190. else {
  5191. settings._iDisplayLength = s.length;
  5192. }
  5193. }
  5194. // Restore key features - todo - for 1.11 this needs to be done by
  5195. // subscribed events
  5196. if ( s.start !== undefined ) {
  5197. if(api === null) {
  5198. settings._iDisplayStart = s.start;
  5199. settings.iInitDisplayStart = s.start;
  5200. }
  5201. else {
  5202. _fnPageChange(settings, s.start/settings._iDisplayLength);
  5203. }
  5204. }
  5205. // Order
  5206. if ( s.order !== undefined ) {
  5207. settings.aaSorting = [];
  5208. $.each( s.order, function ( i, col ) {
  5209. settings.aaSorting.push( col[0] >= columns.length ?
  5210. [ 0, col[1] ] :
  5211. col
  5212. );
  5213. } );
  5214. }
  5215. // Search
  5216. if ( s.search !== undefined ) {
  5217. $.extend( settings.oPreviousSearch, s.search );
  5218. }
  5219. // Columns
  5220. if ( s.columns ) {
  5221. for ( i=0, ien=s.columns.length ; i<ien ; i++ ) {
  5222. var col = s.columns[i];
  5223. // Visibility
  5224. if ( col.visible !== undefined ) {
  5225. // If the api is defined, the table has been initialised so we need to use it rather than internal settings
  5226. if (api) {
  5227. // Don't redraw the columns on every iteration of this loop, we will do this at the end instead
  5228. api.column(i).visible(col.visible, false);
  5229. }
  5230. else {
  5231. columns[i].bVisible = col.visible;
  5232. }
  5233. }
  5234. // Search
  5235. if ( col.search !== undefined ) {
  5236. $.extend( settings.aoPreSearchCols[i], col.search );
  5237. }
  5238. }
  5239. // If the api is defined then we need to adjust the columns once the visibility has been changed
  5240. if (api) {
  5241. api.columns.adjust();
  5242. }
  5243. }
  5244. settings._bLoadingState = false;
  5245. _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, s] );
  5246. callback();
  5247. }
  5248. /**
  5249. * Log an error message
  5250. * @param {object} settings dataTables settings object
  5251. * @param {int} level log error messages, or display them to the user
  5252. * @param {string} msg error message
  5253. * @param {int} tn Technical note id to get more information about the error.
  5254. * @memberof DataTable#oApi
  5255. */
  5256. function _fnLog( settings, level, msg, tn )
  5257. {
  5258. msg = 'DataTables warning: '+
  5259. (settings ? 'table id='+settings.sTableId+' - ' : '')+msg;
  5260. if ( tn ) {
  5261. msg += '. For more information about this error, please see '+
  5262. 'https://datatables.net/tn/'+tn;
  5263. }
  5264. if ( ! level ) {
  5265. // Backwards compatibility pre 1.10
  5266. var ext = DataTable.ext;
  5267. var type = ext.sErrMode || ext.errMode;
  5268. if ( settings ) {
  5269. _fnCallbackFire( settings, null, 'dt-error', [ settings, tn, msg ], true );
  5270. }
  5271. if ( type == 'alert' ) {
  5272. alert( msg );
  5273. }
  5274. else if ( type == 'throw' ) {
  5275. throw new Error(msg);
  5276. }
  5277. else if ( typeof type == 'function' ) {
  5278. type( settings, tn, msg );
  5279. }
  5280. }
  5281. else if ( window.console && console.log ) {
  5282. console.log( msg );
  5283. }
  5284. }
  5285. /**
  5286. * See if a property is defined on one object, if so assign it to the other object
  5287. * @param {object} ret target object
  5288. * @param {object} src source object
  5289. * @param {string} name property
  5290. * @param {string} [mappedName] name to map too - optional, name used if not given
  5291. * @memberof DataTable#oApi
  5292. */
  5293. function _fnMap( ret, src, name, mappedName )
  5294. {
  5295. if ( Array.isArray( name ) ) {
  5296. $.each( name, function (i, val) {
  5297. if ( Array.isArray( val ) ) {
  5298. _fnMap( ret, src, val[0], val[1] );
  5299. }
  5300. else {
  5301. _fnMap( ret, src, val );
  5302. }
  5303. } );
  5304. return;
  5305. }
  5306. if ( mappedName === undefined ) {
  5307. mappedName = name;
  5308. }
  5309. if ( src[name] !== undefined ) {
  5310. ret[mappedName] = src[name];
  5311. }
  5312. }
  5313. /**
  5314. * Extend objects - very similar to jQuery.extend, but deep copy objects, and
  5315. * shallow copy arrays. The reason we need to do this, is that we don't want to
  5316. * deep copy array init values (such as aaSorting) since the dev wouldn't be
  5317. * able to override them, but we do want to deep copy arrays.
  5318. * @param {object} out Object to extend
  5319. * @param {object} extender Object from which the properties will be applied to
  5320. * out
  5321. * @param {boolean} breakRefs If true, then arrays will be sliced to take an
  5322. * independent copy with the exception of the `data` or `aaData` parameters
  5323. * if they are present. This is so you can pass in a collection to
  5324. * DataTables and have that used as your data source without breaking the
  5325. * references
  5326. * @returns {object} out Reference, just for convenience - out === the return.
  5327. * @memberof DataTable#oApi
  5328. * @todo This doesn't take account of arrays inside the deep copied objects.
  5329. */
  5330. function _fnExtend( out, extender, breakRefs )
  5331. {
  5332. var val;
  5333. for ( var prop in extender ) {
  5334. if ( Object.prototype.hasOwnProperty.call(extender, prop) ) {
  5335. val = extender[prop];
  5336. if ( $.isPlainObject( val ) ) {
  5337. if ( ! $.isPlainObject( out[prop] ) ) {
  5338. out[prop] = {};
  5339. }
  5340. $.extend( true, out[prop], val );
  5341. }
  5342. else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && Array.isArray(val) ) {
  5343. out[prop] = val.slice();
  5344. }
  5345. else {
  5346. out[prop] = val;
  5347. }
  5348. }
  5349. }
  5350. return out;
  5351. }
  5352. /**
  5353. * Bind an event handers to allow a click or return key to activate the callback.
  5354. * This is good for accessibility since a return on the keyboard will have the
  5355. * same effect as a click, if the element has focus.
  5356. * @param {element} n Element to bind the action to
  5357. * @param {object|string} selector Selector (for delegated events) or data object
  5358. * to pass to the triggered function
  5359. * @param {function} fn Callback function for when the event is triggered
  5360. * @memberof DataTable#oApi
  5361. */
  5362. function _fnBindAction( n, selector, fn )
  5363. {
  5364. $(n)
  5365. .on( 'click.DT', selector, function (e) {
  5366. fn(e);
  5367. } )
  5368. .on( 'keypress.DT', selector, function (e){
  5369. if ( e.which === 13 ) {
  5370. e.preventDefault();
  5371. fn(e);
  5372. }
  5373. } )
  5374. .on( 'selectstart.DT', selector, function () {
  5375. // Don't want a double click resulting in text selection
  5376. return false;
  5377. } );
  5378. }
  5379. /**
  5380. * Register a callback function. Easily allows a callback function to be added to
  5381. * an array store of callback functions that can then all be called together.
  5382. * @param {object} settings dataTables settings object
  5383. * @param {string} store Name of the array storage for the callbacks in oSettings
  5384. * @param {function} fn Function to be called back
  5385. * @memberof DataTable#oApi
  5386. */
  5387. function _fnCallbackReg( settings, store, fn )
  5388. {
  5389. if ( fn ) {
  5390. settings[store].push(fn);
  5391. }
  5392. }
  5393. /**
  5394. * Fire callback functions and trigger events. Note that the loop over the
  5395. * callback array store is done backwards! Further note that you do not want to
  5396. * fire off triggers in time sensitive applications (for example cell creation)
  5397. * as its slow.
  5398. * @param {object} settings dataTables settings object
  5399. * @param {string} callbackArr Name of the array storage for the callbacks in
  5400. * oSettings
  5401. * @param {string} eventName Name of the jQuery custom event to trigger. If
  5402. * null no trigger is fired
  5403. * @param {array} args Array of arguments to pass to the callback function /
  5404. * trigger
  5405. * @param {boolean} [bubbles] True if the event should bubble
  5406. * @memberof DataTable#oApi
  5407. */
  5408. function _fnCallbackFire( settings, callbackArr, eventName, args, bubbles )
  5409. {
  5410. var ret = [];
  5411. if ( callbackArr ) {
  5412. ret = settings[callbackArr].slice().reverse().map( function (val) {
  5413. return val.apply( settings.oInstance, args );
  5414. } );
  5415. }
  5416. if ( eventName !== null) {
  5417. var e = $.Event( eventName+'.dt' );
  5418. var table = $(settings.nTable);
  5419. // Expose the DataTables API on the event object for easy access
  5420. e.dt = settings.api;
  5421. table[bubbles ? 'trigger' : 'triggerHandler']( e, args );
  5422. // If not yet attached to the document, trigger the event
  5423. // on the body directly to sort of simulate the bubble
  5424. if (bubbles && table.parents('body').length === 0) {
  5425. $('body').trigger( e, args );
  5426. }
  5427. ret.push( e.result );
  5428. }
  5429. return ret;
  5430. }
  5431. function _fnLengthOverflow ( settings )
  5432. {
  5433. var
  5434. start = settings._iDisplayStart,
  5435. end = settings.fnDisplayEnd(),
  5436. len = settings._iDisplayLength;
  5437. /* If we have space to show extra rows (backing up from the end point - then do so */
  5438. if ( start >= end )
  5439. {
  5440. start = end - len;
  5441. }
  5442. // Keep the start record on the current page
  5443. start -= (start % len);
  5444. if ( len === -1 || start < 0 )
  5445. {
  5446. start = 0;
  5447. }
  5448. settings._iDisplayStart = start;
  5449. }
  5450. function _fnRenderer( settings, type )
  5451. {
  5452. var renderer = settings.renderer;
  5453. var host = DataTable.ext.renderer[type];
  5454. if ( $.isPlainObject( renderer ) && renderer[type] ) {
  5455. // Specific renderer for this type. If available use it, otherwise use
  5456. // the default.
  5457. return host[renderer[type]] || host._;
  5458. }
  5459. else if ( typeof renderer === 'string' ) {
  5460. // Common renderer - if there is one available for this type use it,
  5461. // otherwise use the default
  5462. return host[renderer] || host._;
  5463. }
  5464. // Use the default
  5465. return host._;
  5466. }
  5467. /**
  5468. * Detect the data source being used for the table. Used to simplify the code
  5469. * a little (ajax) and to make it compress a little smaller.
  5470. *
  5471. * @param {object} settings dataTables settings object
  5472. * @returns {string} Data source
  5473. * @memberof DataTable#oApi
  5474. */
  5475. function _fnDataSource ( settings )
  5476. {
  5477. if ( settings.oFeatures.bServerSide ) {
  5478. return 'ssp';
  5479. }
  5480. else if ( settings.ajax ) {
  5481. return 'ajax';
  5482. }
  5483. return 'dom';
  5484. }
  5485. /**
  5486. * Common replacement for language strings
  5487. *
  5488. * @param {*} settings DT settings object
  5489. * @param {*} str String with values to replace
  5490. * @param {*} entries Plural number for _ENTRIES_ - can be undefined
  5491. * @returns String
  5492. */
  5493. function _fnMacros ( settings, str, entries )
  5494. {
  5495. // When infinite scrolling, we are always starting at 1. _iDisplayStart is
  5496. // used only internally
  5497. var
  5498. formatter = settings.fnFormatNumber,
  5499. start = settings._iDisplayStart+1,
  5500. len = settings._iDisplayLength,
  5501. vis = settings.fnRecordsDisplay(),
  5502. max = settings.fnRecordsTotal(),
  5503. all = len === -1;
  5504. return str.
  5505. replace(/_START_/g, formatter.call( settings, start ) ).
  5506. replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ).
  5507. replace(/_MAX_/g, formatter.call( settings, max ) ).
  5508. replace(/_TOTAL_/g, formatter.call( settings, vis ) ).
  5509. replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).
  5510. replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) ).
  5511. replace(/_ENTRIES_/g, settings.api.i18n('entries', '', entries) ).
  5512. replace(/_ENTRIES-MAX_/g, settings.api.i18n('entries', '', max) ).
  5513. replace(/_ENTRIES-TOTAL_/g, settings.api.i18n('entries', '', vis) );
  5514. }
  5515. /**
  5516. * Computed structure of the DataTables API, defined by the options passed to
  5517. * `DataTable.Api.register()` when building the API.
  5518. *
  5519. * The structure is built in order to speed creation and extension of the Api
  5520. * objects since the extensions are effectively pre-parsed.
  5521. *
  5522. * The array is an array of objects with the following structure, where this
  5523. * base array represents the Api prototype base:
  5524. *
  5525. * [
  5526. * {
  5527. * name: 'data' -- string - Property name
  5528. * val: function () {}, -- function - Api method (or undefined if just an object
  5529. * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
  5530. * propExt: [ ... ] -- array - Array of Api object definitions to extend the property
  5531. * },
  5532. * {
  5533. * name: 'row'
  5534. * val: {},
  5535. * methodExt: [ ... ],
  5536. * propExt: [
  5537. * {
  5538. * name: 'data'
  5539. * val: function () {},
  5540. * methodExt: [ ... ],
  5541. * propExt: [ ... ]
  5542. * },
  5543. * ...
  5544. * ]
  5545. * }
  5546. * ]
  5547. *
  5548. * @type {Array}
  5549. * @ignore
  5550. */
  5551. var __apiStruct = [];
  5552. /**
  5553. * `Array.prototype` reference.
  5554. *
  5555. * @type object
  5556. * @ignore
  5557. */
  5558. var __arrayProto = Array.prototype;
  5559. /**
  5560. * Abstraction for `context` parameter of the `Api` constructor to allow it to
  5561. * take several different forms for ease of use.
  5562. *
  5563. * Each of the input parameter types will be converted to a DataTables settings
  5564. * object where possible.
  5565. *
  5566. * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one
  5567. * of:
  5568. *
  5569. * * `string` - jQuery selector. Any DataTables' matching the given selector
  5570. * with be found and used.
  5571. * * `node` - `TABLE` node which has already been formed into a DataTable.
  5572. * * `jQuery` - A jQuery object of `TABLE` nodes.
  5573. * * `object` - DataTables settings object
  5574. * * `DataTables.Api` - API instance
  5575. * @return {array|null} Matching DataTables settings objects. `null` or
  5576. * `undefined` is returned if no matching DataTable is found.
  5577. * @ignore
  5578. */
  5579. var _toSettings = function ( mixed )
  5580. {
  5581. var idx, jq;
  5582. var settings = DataTable.settings;
  5583. var tables = _pluck(settings, 'nTable');
  5584. if ( ! mixed ) {
  5585. return [];
  5586. }
  5587. else if ( mixed.nTable && mixed.oFeatures ) {
  5588. // DataTables settings object
  5589. return [ mixed ];
  5590. }
  5591. else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) {
  5592. // Table node
  5593. idx = tables.indexOf(mixed);
  5594. return idx !== -1 ? [ settings[idx] ] : null;
  5595. }
  5596. else if ( mixed && typeof mixed.settings === 'function' ) {
  5597. return mixed.settings().toArray();
  5598. }
  5599. else if ( typeof mixed === 'string' ) {
  5600. // jQuery selector
  5601. jq = $(mixed).get();
  5602. }
  5603. else if ( mixed instanceof $ ) {
  5604. // jQuery object (also DataTables instance)
  5605. jq = mixed.get();
  5606. }
  5607. if ( jq ) {
  5608. return settings.filter(function (v, idx) {
  5609. return jq.includes(tables[idx]);
  5610. });
  5611. }
  5612. };
  5613. /**
  5614. * DataTables API class - used to control and interface with one or more
  5615. * DataTables enhanced tables.
  5616. *
  5617. * The API class is heavily based on jQuery, presenting a chainable interface
  5618. * that you can use to interact with tables. Each instance of the API class has
  5619. * a "context" - i.e. the tables that it will operate on. This could be a single
  5620. * table, all tables on a page or a sub-set thereof.
  5621. *
  5622. * Additionally the API is designed to allow you to easily work with the data in
  5623. * the tables, retrieving and manipulating it as required. This is done by
  5624. * presenting the API class as an array like interface. The contents of the
  5625. * array depend upon the actions requested by each method (for example
  5626. * `rows().nodes()` will return an array of nodes, while `rows().data()` will
  5627. * return an array of objects or arrays depending upon your table's
  5628. * configuration). The API object has a number of array like methods (`push`,
  5629. * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`,
  5630. * `unique` etc) to assist your working with the data held in a table.
  5631. *
  5632. * Most methods (those which return an Api instance) are chainable, which means
  5633. * the return from a method call also has all of the methods available that the
  5634. * top level object had. For example, these two calls are equivalent:
  5635. *
  5636. * // Not chained
  5637. * api.row.add( {...} );
  5638. * api.draw();
  5639. *
  5640. * // Chained
  5641. * api.row.add( {...} ).draw();
  5642. *
  5643. * @class DataTable.Api
  5644. * @param {array|object|string|jQuery} context DataTable identifier. This is
  5645. * used to define which DataTables enhanced tables this API will operate on.
  5646. * Can be one of:
  5647. *
  5648. * * `string` - jQuery selector. Any DataTables' matching the given selector
  5649. * with be found and used.
  5650. * * `node` - `TABLE` node which has already been formed into a DataTable.
  5651. * * `jQuery` - A jQuery object of `TABLE` nodes.
  5652. * * `object` - DataTables settings object
  5653. * @param {array} [data] Data to initialise the Api instance with.
  5654. *
  5655. * @example
  5656. * // Direct initialisation during DataTables construction
  5657. * var api = $('#example').DataTable();
  5658. *
  5659. * @example
  5660. * // Initialisation using a DataTables jQuery object
  5661. * var api = $('#example').dataTable().api();
  5662. *
  5663. * @example
  5664. * // Initialisation as a constructor
  5665. * var api = new DataTable.Api( 'table.dataTable' );
  5666. */
  5667. _Api = function ( context, data )
  5668. {
  5669. if ( ! (this instanceof _Api) ) {
  5670. return new _Api( context, data );
  5671. }
  5672. var settings = [];
  5673. var ctxSettings = function ( o ) {
  5674. var a = _toSettings( o );
  5675. if ( a ) {
  5676. settings.push.apply( settings, a );
  5677. }
  5678. };
  5679. if ( Array.isArray( context ) ) {
  5680. for ( var i=0, ien=context.length ; i<ien ; i++ ) {
  5681. ctxSettings( context[i] );
  5682. }
  5683. }
  5684. else {
  5685. ctxSettings( context );
  5686. }
  5687. // Remove duplicates
  5688. this.context = settings.length > 1
  5689. ? _unique( settings )
  5690. : settings;
  5691. // Initial data
  5692. if ( data ) {
  5693. this.push.apply(this, data);
  5694. }
  5695. // selector
  5696. this.selector = {
  5697. rows: null,
  5698. cols: null,
  5699. opts: null
  5700. };
  5701. _Api.extend( this, this, __apiStruct );
  5702. };
  5703. DataTable.Api = _Api;
  5704. // Don't destroy the existing prototype, just extend it. Required for jQuery 2's
  5705. // isPlainObject.
  5706. $.extend( _Api.prototype, {
  5707. any: function ()
  5708. {
  5709. return this.count() !== 0;
  5710. },
  5711. context: [], // array of table settings objects
  5712. count: function ()
  5713. {
  5714. return this.flatten().length;
  5715. },
  5716. each: function ( fn )
  5717. {
  5718. for ( var i=0, ien=this.length ; i<ien; i++ ) {
  5719. fn.call( this, this[i], i, this );
  5720. }
  5721. return this;
  5722. },
  5723. eq: function ( idx )
  5724. {
  5725. var ctx = this.context;
  5726. return ctx.length > idx ?
  5727. new _Api( ctx[idx], this[idx] ) :
  5728. null;
  5729. },
  5730. filter: function ( fn )
  5731. {
  5732. var a = __arrayProto.filter.call( this, fn, this );
  5733. return new _Api( this.context, a );
  5734. },
  5735. flatten: function ()
  5736. {
  5737. var a = [];
  5738. return new _Api( this.context, a.concat.apply( a, this.toArray() ) );
  5739. },
  5740. get: function ( idx )
  5741. {
  5742. return this[ idx ];
  5743. },
  5744. join: __arrayProto.join,
  5745. includes: function ( find ) {
  5746. return this.indexOf( find ) === -1 ? false : true;
  5747. },
  5748. indexOf: __arrayProto.indexOf,
  5749. iterator: function ( flatten, type, fn, alwaysNew ) {
  5750. var
  5751. a = [], ret,
  5752. i, ien, j, jen,
  5753. context = this.context,
  5754. rows, items, item,
  5755. selector = this.selector;
  5756. // Argument shifting
  5757. if ( typeof flatten === 'string' ) {
  5758. alwaysNew = fn;
  5759. fn = type;
  5760. type = flatten;
  5761. flatten = false;
  5762. }
  5763. for ( i=0, ien=context.length ; i<ien ; i++ ) {
  5764. var apiInst = new _Api( context[i] );
  5765. if ( type === 'table' ) {
  5766. ret = fn.call( apiInst, context[i], i );
  5767. if ( ret !== undefined ) {
  5768. a.push( ret );
  5769. }
  5770. }
  5771. else if ( type === 'columns' || type === 'rows' ) {
  5772. // this has same length as context - one entry for each table
  5773. ret = fn.call( apiInst, context[i], this[i], i );
  5774. if ( ret !== undefined ) {
  5775. a.push( ret );
  5776. }
  5777. }
  5778. else if ( type === 'every' || type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) {
  5779. // columns and rows share the same structure.
  5780. // 'this' is an array of column indexes for each context
  5781. items = this[i];
  5782. if ( type === 'column-rows' ) {
  5783. rows = _selector_row_indexes( context[i], selector.opts );
  5784. }
  5785. for ( j=0, jen=items.length ; j<jen ; j++ ) {
  5786. item = items[j];
  5787. if ( type === 'cell' ) {
  5788. ret = fn.call( apiInst, context[i], item.row, item.column, i, j );
  5789. }
  5790. else {
  5791. ret = fn.call( apiInst, context[i], item, i, j, rows );
  5792. }
  5793. if ( ret !== undefined ) {
  5794. a.push( ret );
  5795. }
  5796. }
  5797. }
  5798. }
  5799. if ( a.length || alwaysNew ) {
  5800. var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );
  5801. var apiSelector = api.selector;
  5802. apiSelector.rows = selector.rows;
  5803. apiSelector.cols = selector.cols;
  5804. apiSelector.opts = selector.opts;
  5805. return api;
  5806. }
  5807. return this;
  5808. },
  5809. lastIndexOf: __arrayProto.lastIndexOf,
  5810. length: 0,
  5811. map: function ( fn )
  5812. {
  5813. var a = __arrayProto.map.call( this, fn, this );
  5814. return new _Api( this.context, a );
  5815. },
  5816. pluck: function ( prop )
  5817. {
  5818. var fn = DataTable.util.get(prop);
  5819. return this.map( function ( el ) {
  5820. return fn(el);
  5821. } );
  5822. },
  5823. pop: __arrayProto.pop,
  5824. push: __arrayProto.push,
  5825. reduce: __arrayProto.reduce,
  5826. reduceRight: __arrayProto.reduceRight,
  5827. reverse: __arrayProto.reverse,
  5828. // Object with rows, columns and opts
  5829. selector: null,
  5830. shift: __arrayProto.shift,
  5831. slice: function () {
  5832. return new _Api( this.context, this );
  5833. },
  5834. sort: __arrayProto.sort,
  5835. splice: __arrayProto.splice,
  5836. toArray: function ()
  5837. {
  5838. return __arrayProto.slice.call( this );
  5839. },
  5840. to$: function ()
  5841. {
  5842. return $( this );
  5843. },
  5844. toJQuery: function ()
  5845. {
  5846. return $( this );
  5847. },
  5848. unique: function ()
  5849. {
  5850. return new _Api( this.context, _unique(this.toArray()) );
  5851. },
  5852. unshift: __arrayProto.unshift
  5853. } );
  5854. function _api_scope( scope, fn, struc ) {
  5855. return function () {
  5856. var ret = fn.apply( scope || this, arguments );
  5857. // Method extension
  5858. _Api.extend( ret, ret, struc.methodExt );
  5859. return ret;
  5860. };
  5861. }
  5862. function _api_find( src, name ) {
  5863. for ( var i=0, ien=src.length ; i<ien ; i++ ) {
  5864. if ( src[i].name === name ) {
  5865. return src[i];
  5866. }
  5867. }
  5868. return null;
  5869. }
  5870. window.__apiStruct = __apiStruct;
  5871. _Api.extend = function ( scope, obj, ext )
  5872. {
  5873. // Only extend API instances and static properties of the API
  5874. if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) {
  5875. return;
  5876. }
  5877. var
  5878. i, ien,
  5879. struct;
  5880. for ( i=0, ien=ext.length ; i<ien ; i++ ) {
  5881. struct = ext[i];
  5882. if (struct.name === '__proto__') {
  5883. continue;
  5884. }
  5885. // Value
  5886. obj[ struct.name ] = struct.type === 'function' ?
  5887. _api_scope( scope, struct.val, struct ) :
  5888. struct.type === 'object' ?
  5889. {} :
  5890. struct.val;
  5891. obj[ struct.name ].__dt_wrapper = true;
  5892. // Property extension
  5893. _Api.extend( scope, obj[ struct.name ], struct.propExt );
  5894. }
  5895. };
  5896. // [
  5897. // {
  5898. // name: 'data' -- string - Property name
  5899. // val: function () {}, -- function - Api method (or undefined if just an object
  5900. // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
  5901. // propExt: [ ... ] -- array - Array of Api object definitions to extend the property
  5902. // },
  5903. // {
  5904. // name: 'row'
  5905. // val: {},
  5906. // methodExt: [ ... ],
  5907. // propExt: [
  5908. // {
  5909. // name: 'data'
  5910. // val: function () {},
  5911. // methodExt: [ ... ],
  5912. // propExt: [ ... ]
  5913. // },
  5914. // ...
  5915. // ]
  5916. // }
  5917. // ]
  5918. _Api.register = _api_register = function ( name, val )
  5919. {
  5920. if ( Array.isArray( name ) ) {
  5921. for ( var j=0, jen=name.length ; j<jen ; j++ ) {
  5922. _Api.register( name[j], val );
  5923. }
  5924. return;
  5925. }
  5926. var
  5927. i, ien,
  5928. heir = name.split('.'),
  5929. struct = __apiStruct,
  5930. key, method;
  5931. for ( i=0, ien=heir.length ; i<ien ; i++ ) {
  5932. method = heir[i].indexOf('()') !== -1;
  5933. key = method ?
  5934. heir[i].replace('()', '') :
  5935. heir[i];
  5936. var src = _api_find( struct, key );
  5937. if ( ! src ) {
  5938. src = {
  5939. name: key,
  5940. val: {},
  5941. methodExt: [],
  5942. propExt: [],
  5943. type: 'object'
  5944. };
  5945. struct.push( src );
  5946. }
  5947. if ( i === ien-1 ) {
  5948. src.val = val;
  5949. src.type = typeof val === 'function' ?
  5950. 'function' :
  5951. $.isPlainObject( val ) ?
  5952. 'object' :
  5953. 'other';
  5954. }
  5955. else {
  5956. struct = method ?
  5957. src.methodExt :
  5958. src.propExt;
  5959. }
  5960. }
  5961. };
  5962. _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {
  5963. _Api.register( pluralName, val );
  5964. _Api.register( singularName, function () {
  5965. var ret = val.apply( this, arguments );
  5966. if ( ret === this ) {
  5967. // Returned item is the API instance that was passed in, return it
  5968. return this;
  5969. }
  5970. else if ( ret instanceof _Api ) {
  5971. // New API instance returned, want the value from the first item
  5972. // in the returned array for the singular result.
  5973. return ret.length ?
  5974. Array.isArray( ret[0] ) ?
  5975. new _Api( ret.context, ret[0] ) : // Array results are 'enhanced'
  5976. ret[0] :
  5977. undefined;
  5978. }
  5979. // Non-API return - just fire it back
  5980. return ret;
  5981. } );
  5982. };
  5983. /**
  5984. * Selector for HTML tables. Apply the given selector to the give array of
  5985. * DataTables settings objects.
  5986. *
  5987. * @param {string|integer} [selector] jQuery selector string or integer
  5988. * @param {array} Array of DataTables settings objects to be filtered
  5989. * @return {array}
  5990. * @ignore
  5991. */
  5992. var __table_selector = function ( selector, a )
  5993. {
  5994. if ( Array.isArray(selector) ) {
  5995. var result = [];
  5996. selector.forEach(function (sel) {
  5997. var inner = __table_selector(sel, a);
  5998. result.push.apply(result, inner);
  5999. });
  6000. return result.filter( function (item) {
  6001. return item;
  6002. });
  6003. }
  6004. // Integer is used to pick out a table by index
  6005. if ( typeof selector === 'number' ) {
  6006. return [ a[ selector ] ];
  6007. }
  6008. // Perform a jQuery selector on the table nodes
  6009. var nodes = a.map( function (el) {
  6010. return el.nTable;
  6011. } );
  6012. return $(nodes)
  6013. .filter( selector )
  6014. .map( function () {
  6015. // Need to translate back from the table node to the settings
  6016. var idx = nodes.indexOf(this);
  6017. return a[ idx ];
  6018. } )
  6019. .toArray();
  6020. };
  6021. /**
  6022. * Context selector for the API's context (i.e. the tables the API instance
  6023. * refers to.
  6024. *
  6025. * @name DataTable.Api#tables
  6026. * @param {string|integer} [selector] Selector to pick which tables the iterator
  6027. * should operate on. If not given, all tables in the current context are
  6028. * used. This can be given as a jQuery selector (for example `':gt(0)'`) to
  6029. * select multiple tables or as an integer to select a single table.
  6030. * @returns {DataTable.Api} Returns a new API instance if a selector is given.
  6031. */
  6032. _api_register( 'tables()', function ( selector ) {
  6033. // A new instance is created if there was a selector specified
  6034. return selector !== undefined && selector !== null ?
  6035. new _Api( __table_selector( selector, this.context ) ) :
  6036. this;
  6037. } );
  6038. _api_register( 'table()', function ( selector ) {
  6039. var tables = this.tables( selector );
  6040. var ctx = tables.context;
  6041. // Truncate to the first matched table
  6042. return ctx.length ?
  6043. new _Api( ctx[0] ) :
  6044. tables;
  6045. } );
  6046. // Common methods, combined to reduce size
  6047. [
  6048. ['nodes', 'node', 'nTable'],
  6049. ['body', 'body', 'nTBody'],
  6050. ['header', 'header', 'nTHead'],
  6051. ['footer', 'footer', 'nTFoot'],
  6052. ].forEach(function (item) {
  6053. _api_registerPlural(
  6054. 'tables().' + item[0] + '()',
  6055. 'table().' + item[1] + '()' ,
  6056. function () {
  6057. return this.iterator( 'table', function ( ctx ) {
  6058. return ctx[item[2]];
  6059. }, 1 );
  6060. }
  6061. );
  6062. });
  6063. // Structure methods
  6064. [
  6065. ['header', 'aoHeader'],
  6066. ['footer', 'aoFooter'],
  6067. ].forEach(function (item) {
  6068. _api_register( 'table().' + item[0] + '.structure()' , function (selector) {
  6069. var indexes = this.columns(selector).indexes().flatten();
  6070. var ctx = this.context[0];
  6071. return _fnHeaderLayout(ctx, ctx[item[1]], indexes);
  6072. } );
  6073. })
  6074. _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
  6075. return this.iterator( 'table', function ( ctx ) {
  6076. return ctx.nTableWrapper;
  6077. }, 1 );
  6078. } );
  6079. _api_register( 'tables().every()', function ( fn ) {
  6080. var that = this;
  6081. return this.iterator('table', function (s, i) {
  6082. fn.call(that.table(i), i);
  6083. });
  6084. });
  6085. _api_register( 'caption()', function ( value, side ) {
  6086. var context = this.context;
  6087. // Getter - return existing node's content
  6088. if ( value === undefined ) {
  6089. var caption = context[0].captionNode;
  6090. return caption && context.length ?
  6091. caption.innerHTML :
  6092. null;
  6093. }
  6094. return this.iterator( 'table', function ( ctx ) {
  6095. var table = $(ctx.nTable);
  6096. var caption = $(ctx.captionNode);
  6097. var container = $(ctx.nTableWrapper);
  6098. // Create the node if it doesn't exist yet
  6099. if ( ! caption.length ) {
  6100. caption = $('<caption/>').html( value );
  6101. ctx.captionNode = caption[0];
  6102. // If side isn't set, we need to insert into the document to let the
  6103. // CSS decide so we can read it back, otherwise there is no way to
  6104. // know if the CSS would put it top or bottom for scrolling
  6105. if (! side) {
  6106. table.prepend(caption);
  6107. side = caption.css('caption-side');
  6108. }
  6109. }
  6110. caption.html( value );
  6111. if ( side ) {
  6112. caption.css( 'caption-side', side );
  6113. caption[0]._captionSide = side;
  6114. }
  6115. if (container.find('div.dataTables_scroll').length) {
  6116. var selector = (side === 'top' ? 'Head' : 'Foot');
  6117. container.find('div.dataTables_scroll'+ selector +' table').prepend(caption);
  6118. }
  6119. else {
  6120. table.prepend(caption);
  6121. }
  6122. }, 1 );
  6123. } );
  6124. _api_register( 'caption.node()', function () {
  6125. var ctx = this.context;
  6126. return ctx.length ? ctx[0].captionNode : null;
  6127. } );
  6128. /**
  6129. * Redraw the tables in the current context.
  6130. */
  6131. _api_register( 'draw()', function ( paging ) {
  6132. return this.iterator( 'table', function ( settings ) {
  6133. if ( paging === 'page' ) {
  6134. _fnDraw( settings );
  6135. }
  6136. else {
  6137. if ( typeof paging === 'string' ) {
  6138. paging = paging === 'full-hold' ?
  6139. false :
  6140. true;
  6141. }
  6142. _fnReDraw( settings, paging===false );
  6143. }
  6144. } );
  6145. } );
  6146. /**
  6147. * Get the current page index.
  6148. *
  6149. * @return {integer} Current page index (zero based)
  6150. *//**
  6151. * Set the current page.
  6152. *
  6153. * Note that if you attempt to show a page which does not exist, DataTables will
  6154. * not throw an error, but rather reset the paging.
  6155. *
  6156. * @param {integer|string} action The paging action to take. This can be one of:
  6157. * * `integer` - The page index to jump to
  6158. * * `string` - An action to take:
  6159. * * `first` - Jump to first page.
  6160. * * `next` - Jump to the next page
  6161. * * `previous` - Jump to previous page
  6162. * * `last` - Jump to the last page.
  6163. * @returns {DataTables.Api} this
  6164. */
  6165. _api_register( 'page()', function ( action ) {
  6166. if ( action === undefined ) {
  6167. return this.page.info().page; // not an expensive call
  6168. }
  6169. // else, have an action to take on all tables
  6170. return this.iterator( 'table', function ( settings ) {
  6171. _fnPageChange( settings, action );
  6172. } );
  6173. } );
  6174. /**
  6175. * Paging information for the first table in the current context.
  6176. *
  6177. * If you require paging information for another table, use the `table()` method
  6178. * with a suitable selector.
  6179. *
  6180. * @return {object} Object with the following properties set:
  6181. * * `page` - Current page index (zero based - i.e. the first page is `0`)
  6182. * * `pages` - Total number of pages
  6183. * * `start` - Display index for the first record shown on the current page
  6184. * * `end` - Display index for the last record shown on the current page
  6185. * * `length` - Display length (number of records). Note that generally `start
  6186. * + length = end`, but this is not always true, for example if there are
  6187. * only 2 records to show on the final page, with a length of 10.
  6188. * * `recordsTotal` - Full data set length
  6189. * * `recordsDisplay` - Data set length once the current filtering criterion
  6190. * are applied.
  6191. */
  6192. _api_register( 'page.info()', function () {
  6193. if ( this.context.length === 0 ) {
  6194. return undefined;
  6195. }
  6196. var
  6197. settings = this.context[0],
  6198. start = settings._iDisplayStart,
  6199. len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1,
  6200. visRecords = settings.fnRecordsDisplay(),
  6201. all = len === -1;
  6202. return {
  6203. "page": all ? 0 : Math.floor( start / len ),
  6204. "pages": all ? 1 : Math.ceil( visRecords / len ),
  6205. "start": start,
  6206. "end": settings.fnDisplayEnd(),
  6207. "length": len,
  6208. "recordsTotal": settings.fnRecordsTotal(),
  6209. "recordsDisplay": visRecords,
  6210. "serverSide": _fnDataSource( settings ) === 'ssp'
  6211. };
  6212. } );
  6213. /**
  6214. * Get the current page length.
  6215. *
  6216. * @return {integer} Current page length. Note `-1` indicates that all records
  6217. * are to be shown.
  6218. *//**
  6219. * Set the current page length.
  6220. *
  6221. * @param {integer} Page length to set. Use `-1` to show all records.
  6222. * @returns {DataTables.Api} this
  6223. */
  6224. _api_register( 'page.len()', function ( len ) {
  6225. // Note that we can't call this function 'length()' because `length`
  6226. // is a Javascript property of functions which defines how many arguments
  6227. // the function expects.
  6228. if ( len === undefined ) {
  6229. return this.context.length !== 0 ?
  6230. this.context[0]._iDisplayLength :
  6231. undefined;
  6232. }
  6233. // else, set the page length
  6234. return this.iterator( 'table', function ( settings ) {
  6235. _fnLengthChange( settings, len );
  6236. } );
  6237. } );
  6238. var __reload = function ( settings, holdPosition, callback ) {
  6239. // Use the draw event to trigger a callback
  6240. if ( callback ) {
  6241. var api = new _Api( settings );
  6242. api.one( 'draw', function () {
  6243. callback( api.ajax.json() );
  6244. } );
  6245. }
  6246. if ( _fnDataSource( settings ) == 'ssp' ) {
  6247. _fnReDraw( settings, holdPosition );
  6248. }
  6249. else {
  6250. _fnProcessingDisplay( settings, true );
  6251. // Cancel an existing request
  6252. var xhr = settings.jqXHR;
  6253. if ( xhr && xhr.readyState !== 4 ) {
  6254. xhr.abort();
  6255. }
  6256. // Trigger xhr
  6257. _fnBuildAjax( settings, {}, function( json ) {
  6258. _fnClearTable( settings );
  6259. var data = _fnAjaxDataSrc( settings, json );
  6260. for ( var i=0, ien=data.length ; i<ien ; i++ ) {
  6261. _fnAddData( settings, data[i] );
  6262. }
  6263. _fnReDraw( settings, holdPosition );
  6264. _fnInitComplete( settings );
  6265. _fnProcessingDisplay( settings, false );
  6266. } );
  6267. }
  6268. };
  6269. /**
  6270. * Get the JSON response from the last Ajax request that DataTables made to the
  6271. * server. Note that this returns the JSON from the first table in the current
  6272. * context.
  6273. *
  6274. * @return {object} JSON received from the server.
  6275. */
  6276. _api_register( 'ajax.json()', function () {
  6277. var ctx = this.context;
  6278. if ( ctx.length > 0 ) {
  6279. return ctx[0].json;
  6280. }
  6281. // else return undefined;
  6282. } );
  6283. /**
  6284. * Get the data submitted in the last Ajax request
  6285. */
  6286. _api_register( 'ajax.params()', function () {
  6287. var ctx = this.context;
  6288. if ( ctx.length > 0 ) {
  6289. return ctx[0].oAjaxData;
  6290. }
  6291. // else return undefined;
  6292. } );
  6293. /**
  6294. * Reload tables from the Ajax data source. Note that this function will
  6295. * automatically re-draw the table when the remote data has been loaded.
  6296. *
  6297. * @param {boolean} [reset=true] Reset (default) or hold the current paging
  6298. * position. A full re-sort and re-filter is performed when this method is
  6299. * called, which is why the pagination reset is the default action.
  6300. * @returns {DataTables.Api} this
  6301. */
  6302. _api_register( 'ajax.reload()', function ( callback, resetPaging ) {
  6303. return this.iterator( 'table', function (settings) {
  6304. __reload( settings, resetPaging===false, callback );
  6305. } );
  6306. } );
  6307. /**
  6308. * Get the current Ajax URL. Note that this returns the URL from the first
  6309. * table in the current context.
  6310. *
  6311. * @return {string} Current Ajax source URL
  6312. *//**
  6313. * Set the Ajax URL. Note that this will set the URL for all tables in the
  6314. * current context.
  6315. *
  6316. * @param {string} url URL to set.
  6317. * @returns {DataTables.Api} this
  6318. */
  6319. _api_register( 'ajax.url()', function ( url ) {
  6320. var ctx = this.context;
  6321. if ( url === undefined ) {
  6322. // get
  6323. if ( ctx.length === 0 ) {
  6324. return undefined;
  6325. }
  6326. ctx = ctx[0];
  6327. return $.isPlainObject( ctx.ajax ) ?
  6328. ctx.ajax.url :
  6329. ctx.ajax;
  6330. }
  6331. // set
  6332. return this.iterator( 'table', function ( settings ) {
  6333. if ( $.isPlainObject( settings.ajax ) ) {
  6334. settings.ajax.url = url;
  6335. }
  6336. else {
  6337. settings.ajax = url;
  6338. }
  6339. } );
  6340. } );
  6341. /**
  6342. * Load data from the newly set Ajax URL. Note that this method is only
  6343. * available when `ajax.url()` is used to set a URL. Additionally, this method
  6344. * has the same effect as calling `ajax.reload()` but is provided for
  6345. * convenience when setting a new URL. Like `ajax.reload()` it will
  6346. * automatically redraw the table once the remote data has been loaded.
  6347. *
  6348. * @returns {DataTables.Api} this
  6349. */
  6350. _api_register( 'ajax.url().load()', function ( callback, resetPaging ) {
  6351. // Same as a reload, but makes sense to present it for easy access after a
  6352. // url change
  6353. return this.iterator( 'table', function ( ctx ) {
  6354. __reload( ctx, resetPaging===false, callback );
  6355. } );
  6356. } );
  6357. var _selector_run = function ( type, selector, selectFn, settings, opts )
  6358. {
  6359. var
  6360. out = [], res,
  6361. a, i, ien, j, jen,
  6362. selectorType = typeof selector;
  6363. // Can't just check for isArray here, as an API or jQuery instance might be
  6364. // given with their array like look
  6365. if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {
  6366. selector = [ selector ];
  6367. }
  6368. for ( i=0, ien=selector.length ; i<ien ; i++ ) {
  6369. // Only split on simple strings - complex expressions will be jQuery selectors
  6370. a = selector[i] && selector[i].split && ! selector[i].match(/[[(:]/) ?
  6371. selector[i].split(',') :
  6372. [ selector[i] ];
  6373. for ( j=0, jen=a.length ; j<jen ; j++ ) {
  6374. res = selectFn( typeof a[j] === 'string' ? (a[j]).trim() : a[j] );
  6375. // Remove empty items
  6376. res = res.filter( function (item) {
  6377. return item !== null && item !== undefined;
  6378. });
  6379. if ( res && res.length ) {
  6380. out = out.concat( res );
  6381. }
  6382. }
  6383. }
  6384. // selector extensions
  6385. var ext = _ext.selector[ type ];
  6386. if ( ext.length ) {
  6387. for ( i=0, ien=ext.length ; i<ien ; i++ ) {
  6388. out = ext[i]( settings, opts, out );
  6389. }
  6390. }
  6391. return _unique( out );
  6392. };
  6393. var _selector_opts = function ( opts )
  6394. {
  6395. if ( ! opts ) {
  6396. opts = {};
  6397. }
  6398. // Backwards compatibility for 1.9- which used the terminology filter rather
  6399. // than search
  6400. if ( opts.filter && opts.search === undefined ) {
  6401. opts.search = opts.filter;
  6402. }
  6403. return $.extend( {
  6404. search: 'none',
  6405. order: 'current',
  6406. page: 'all'
  6407. }, opts );
  6408. };
  6409. // Reduce the API instance to the first item found
  6410. var _selector_first = function ( old )
  6411. {
  6412. let inst = new _Api(old.context[0]);
  6413. // Use a push rather than passing to the constructor, since it will
  6414. // merge arrays down automatically, which isn't what is wanted here
  6415. if (old.length) {
  6416. inst.push( old[0] );
  6417. }
  6418. inst.selector = old.selector;
  6419. // Limit to a single row / column / cell
  6420. if (inst.length && inst[0].length > 1) {
  6421. inst[0].splice(1);
  6422. }
  6423. return inst;
  6424. };
  6425. var _selector_row_indexes = function ( settings, opts )
  6426. {
  6427. var
  6428. i, ien, tmp, a=[],
  6429. displayFiltered = settings.aiDisplay,
  6430. displayMaster = settings.aiDisplayMaster;
  6431. var
  6432. search = opts.search, // none, applied, removed
  6433. order = opts.order, // applied, current, index (original - compatibility with 1.9)
  6434. page = opts.page; // all, current
  6435. if ( page == 'current' ) {
  6436. // Current page implies that order=current and filter=applied, since it is
  6437. // fairly senseless otherwise, regardless of what order and search actually
  6438. // are
  6439. for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) {
  6440. a.push( displayFiltered[i] );
  6441. }
  6442. }
  6443. else if ( order == 'current' || order == 'applied' ) {
  6444. if ( search == 'none') {
  6445. a = displayMaster.slice();
  6446. }
  6447. else if ( search == 'applied' ) {
  6448. a = displayFiltered.slice();
  6449. }
  6450. else if ( search == 'removed' ) {
  6451. // O(n+m) solution by creating a hash map
  6452. var displayFilteredMap = {};
  6453. for ( i=0, ien=displayFiltered.length ; i<ien ; i++ ) {
  6454. displayFilteredMap[displayFiltered[i]] = null;
  6455. }
  6456. displayMaster.forEach(function (item) {
  6457. if (! Object.prototype.hasOwnProperty.call(displayFilteredMap, item)) {
  6458. a.push(item);
  6459. }
  6460. });
  6461. }
  6462. }
  6463. else if ( order == 'index' || order == 'original' ) {
  6464. for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
  6465. if (! settings.aoData[i]) {
  6466. continue;
  6467. }
  6468. if ( search == 'none' ) {
  6469. a.push( i );
  6470. }
  6471. else { // applied | removed
  6472. tmp = displayFiltered.indexOf(i);
  6473. if ((tmp === -1 && search == 'removed') ||
  6474. (tmp >= 0 && search == 'applied') )
  6475. {
  6476. a.push( i );
  6477. }
  6478. }
  6479. }
  6480. }
  6481. else if ( typeof order === 'number' ) {
  6482. // Order the rows by the given column
  6483. var ordered = _fnSort(settings, order, 'asc');
  6484. if (search === 'none') {
  6485. a = ordered;
  6486. }
  6487. else { // applied | removed
  6488. for (i=0; i<ordered.length; i++) {
  6489. tmp = displayFiltered.indexOf(ordered[i]);
  6490. if ((tmp === -1 && search == 'removed') ||
  6491. (tmp >= 0 && search == 'applied') )
  6492. {
  6493. a.push( ordered[i] );
  6494. }
  6495. }
  6496. }
  6497. }
  6498. return a;
  6499. };
  6500. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  6501. * Rows
  6502. *
  6503. * {} - no selector - use all available rows
  6504. * {integer} - row aoData index
  6505. * {node} - TR node
  6506. * {string} - jQuery selector to apply to the TR elements
  6507. * {array} - jQuery array of nodes, or simply an array of TR nodes
  6508. *
  6509. */
  6510. var __row_selector = function ( settings, selector, opts )
  6511. {
  6512. var rows;
  6513. var run = function ( sel ) {
  6514. var selInt = _intVal( sel );
  6515. var aoData = settings.aoData;
  6516. // Short cut - selector is a number and no options provided (default is
  6517. // all records, so no need to check if the index is in there, since it
  6518. // must be - dev error if the index doesn't exist).
  6519. if ( selInt !== null && ! opts ) {
  6520. return [ selInt ];
  6521. }
  6522. if ( ! rows ) {
  6523. rows = _selector_row_indexes( settings, opts );
  6524. }
  6525. if ( selInt !== null && rows.indexOf(selInt) !== -1 ) {
  6526. // Selector - integer
  6527. return [ selInt ];
  6528. }
  6529. else if ( sel === null || sel === undefined || sel === '' ) {
  6530. // Selector - none
  6531. return rows;
  6532. }
  6533. // Selector - function
  6534. if ( typeof sel === 'function' ) {
  6535. return rows.map( function (idx) {
  6536. var row = aoData[ idx ];
  6537. return sel( idx, row._aData, row.nTr ) ? idx : null;
  6538. } );
  6539. }
  6540. // Selector - node
  6541. if ( sel.nodeName ) {
  6542. var rowIdx = sel._DT_RowIndex; // Property added by DT for fast lookup
  6543. var cellIdx = sel._DT_CellIndex;
  6544. if ( rowIdx !== undefined ) {
  6545. // Make sure that the row is actually still present in the table
  6546. return aoData[ rowIdx ] && aoData[ rowIdx ].nTr === sel ?
  6547. [ rowIdx ] :
  6548. [];
  6549. }
  6550. else if ( cellIdx ) {
  6551. return aoData[ cellIdx.row ] && aoData[ cellIdx.row ].nTr === sel.parentNode ?
  6552. [ cellIdx.row ] :
  6553. [];
  6554. }
  6555. else {
  6556. var host = $(sel).closest('*[data-dt-row]');
  6557. return host.length ?
  6558. [ host.data('dt-row') ] :
  6559. [];
  6560. }
  6561. }
  6562. // ID selector. Want to always be able to select rows by id, regardless
  6563. // of if the tr element has been created or not, so can't rely upon
  6564. // jQuery here - hence a custom implementation. This does not match
  6565. // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything,
  6566. // but to select it using a CSS selector engine (like Sizzle or
  6567. // querySelect) it would need to need to be escaped for some characters.
  6568. // DataTables simplifies this for row selectors since you can select
  6569. // only a row. A # indicates an id any anything that follows is the id -
  6570. // unescaped.
  6571. if ( typeof sel === 'string' && sel.charAt(0) === '#' ) {
  6572. // get row index from id
  6573. var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ];
  6574. if ( rowObj !== undefined ) {
  6575. return [ rowObj.idx ];
  6576. }
  6577. // need to fall through to jQuery in case there is DOM id that
  6578. // matches
  6579. }
  6580. // Get nodes in the order from the `rows` array with null values removed
  6581. var nodes = _removeEmpty(
  6582. _pluck_order( settings.aoData, rows, 'nTr' )
  6583. );
  6584. // Selector - jQuery selector string, array of nodes or jQuery object/
  6585. // As jQuery's .filter() allows jQuery objects to be passed in filter,
  6586. // it also allows arrays, so this will cope with all three options
  6587. return $(nodes)
  6588. .filter( sel )
  6589. .map( function () {
  6590. return this._DT_RowIndex;
  6591. } )
  6592. .toArray();
  6593. };
  6594. var matched = _selector_run( 'row', selector, run, settings, opts );
  6595. if (opts.order === 'current' || opts.order === 'applied') {
  6596. _fnSortDisplay(settings, matched);
  6597. }
  6598. return matched;
  6599. };
  6600. _api_register( 'rows()', function ( selector, opts ) {
  6601. // argument shifting
  6602. if ( selector === undefined ) {
  6603. selector = '';
  6604. }
  6605. else if ( $.isPlainObject( selector ) ) {
  6606. opts = selector;
  6607. selector = '';
  6608. }
  6609. opts = _selector_opts( opts );
  6610. var inst = this.iterator( 'table', function ( settings ) {
  6611. return __row_selector( settings, selector, opts );
  6612. }, 1 );
  6613. // Want argument shifting here and in __row_selector?
  6614. inst.selector.rows = selector;
  6615. inst.selector.opts = opts;
  6616. return inst;
  6617. } );
  6618. _api_register( 'rows().nodes()', function () {
  6619. return this.iterator( 'row', function ( settings, row ) {
  6620. return settings.aoData[ row ].nTr || undefined;
  6621. }, 1 );
  6622. } );
  6623. _api_register( 'rows().data()', function () {
  6624. return this.iterator( true, 'rows', function ( settings, rows ) {
  6625. return _pluck_order( settings.aoData, rows, '_aData' );
  6626. }, 1 );
  6627. } );
  6628. _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {
  6629. return this.iterator( 'row', function ( settings, row ) {
  6630. var r = settings.aoData[ row ];
  6631. return type === 'search' ? r._aFilterData : r._aSortData;
  6632. }, 1 );
  6633. } );
  6634. _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {
  6635. return this.iterator( 'row', function ( settings, row ) {
  6636. _fnInvalidate( settings, row, src );
  6637. } );
  6638. } );
  6639. _api_registerPlural( 'rows().indexes()', 'row().index()', function () {
  6640. return this.iterator( 'row', function ( settings, row ) {
  6641. return row;
  6642. }, 1 );
  6643. } );
  6644. _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) {
  6645. var a = [];
  6646. var context = this.context;
  6647. // `iterator` will drop undefined values, but in this case we want them
  6648. for ( var i=0, ien=context.length ; i<ien ; i++ ) {
  6649. for ( var j=0, jen=this[i].length ; j<jen ; j++ ) {
  6650. var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData );
  6651. a.push( (hash === true ? '#' : '' )+ id );
  6652. }
  6653. }
  6654. return new _Api( context, a );
  6655. } );
  6656. _api_registerPlural( 'rows().remove()', 'row().remove()', function () {
  6657. this.iterator( 'row', function ( settings, row ) {
  6658. var data = settings.aoData;
  6659. var rowData = data[ row ];
  6660. // Delete from the display arrays
  6661. var idx = settings.aiDisplayMaster.indexOf(row);
  6662. if (idx !== -1) {
  6663. settings.aiDisplayMaster.splice(idx, 1);
  6664. }
  6665. // For server-side processing tables - subtract the deleted row from the count
  6666. if ( settings._iRecordsDisplay > 0 ) {
  6667. settings._iRecordsDisplay--;
  6668. }
  6669. // Check for an 'overflow' they case for displaying the table
  6670. _fnLengthOverflow( settings );
  6671. // Remove the row's ID reference if there is one
  6672. var id = settings.rowIdFn( rowData._aData );
  6673. if ( id !== undefined ) {
  6674. delete settings.aIds[ id ];
  6675. }
  6676. data[row] = null;
  6677. } );
  6678. return this;
  6679. } );
  6680. _api_register( 'rows.add()', function ( rows ) {
  6681. var newRows = this.iterator( 'table', function ( settings ) {
  6682. var row, i, ien;
  6683. var out = [];
  6684. for ( i=0, ien=rows.length ; i<ien ; i++ ) {
  6685. row = rows[i];
  6686. if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
  6687. out.push( _fnAddTr( settings, row )[0] );
  6688. }
  6689. else {
  6690. out.push( _fnAddData( settings, row ) );
  6691. }
  6692. }
  6693. return out;
  6694. }, 1 );
  6695. // Return an Api.rows() extended instance, so rows().nodes() etc can be used
  6696. var modRows = this.rows( -1 );
  6697. modRows.pop();
  6698. modRows.push.apply(modRows, newRows);
  6699. return modRows;
  6700. } );
  6701. /**
  6702. *
  6703. */
  6704. _api_register( 'row()', function ( selector, opts ) {
  6705. return _selector_first( this.rows( selector, opts ) );
  6706. } );
  6707. _api_register( 'row().data()', function ( data ) {
  6708. var ctx = this.context;
  6709. if ( data === undefined ) {
  6710. // Get
  6711. return ctx.length && this.length && this[0].length ?
  6712. ctx[0].aoData[ this[0] ]._aData :
  6713. undefined;
  6714. }
  6715. // Set
  6716. var row = ctx[0].aoData[ this[0] ];
  6717. row._aData = data;
  6718. // If the DOM has an id, and the data source is an array
  6719. if ( Array.isArray( data ) && row.nTr && row.nTr.id ) {
  6720. _fnSetObjectDataFn( ctx[0].rowId )( data, row.nTr.id );
  6721. }
  6722. // Automatically invalidate
  6723. _fnInvalidate( ctx[0], this[0], 'data' );
  6724. return this;
  6725. } );
  6726. _api_register( 'row().node()', function () {
  6727. var ctx = this.context;
  6728. if (ctx.length && this.length && this[0].length) {
  6729. var row = ctx[0].aoData[ this[0] ];
  6730. if (row && row.nTr) {
  6731. return row.nTr;
  6732. }
  6733. }
  6734. return null;
  6735. } );
  6736. _api_register( 'row.add()', function ( row ) {
  6737. // Allow a jQuery object to be passed in - only a single row is added from
  6738. // it though - the first element in the set
  6739. if ( row instanceof $ && row.length ) {
  6740. row = row[0];
  6741. }
  6742. var rows = this.iterator( 'table', function ( settings ) {
  6743. if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
  6744. return _fnAddTr( settings, row )[0];
  6745. }
  6746. return _fnAddData( settings, row );
  6747. } );
  6748. // Return an Api.rows() extended instance, with the newly added row selected
  6749. return this.row( rows[0] );
  6750. } );
  6751. $(document).on('plugin-init.dt', function (e, context) {
  6752. var api = new _Api( context );
  6753. api.on( 'stateSaveParams.DT', function ( e, settings, d ) {
  6754. // This could be more compact with the API, but it is a lot faster as a simple
  6755. // internal loop
  6756. var idFn = settings.rowIdFn;
  6757. var rows = settings.aiDisplayMaster;
  6758. var ids = [];
  6759. for (var i=0 ; i<rows.length ; i++) {
  6760. var rowIdx = rows[i];
  6761. var data = settings.aoData[rowIdx];
  6762. if (data._detailsShow) {
  6763. ids.push( '#' + idFn(data._aData) );
  6764. }
  6765. }
  6766. d.childRows = ids;
  6767. });
  6768. // For future state loads (e.g. with StateRestore)
  6769. api.on( 'stateLoaded.DT', function (e, settings, state) {
  6770. __details_state_load( api, state );
  6771. });
  6772. // And the initial load state
  6773. __details_state_load( api, api.state.loaded() );
  6774. });
  6775. var __details_state_load = function (api, state)
  6776. {
  6777. if ( state && state.childRows ) {
  6778. api
  6779. .rows( state.childRows.map(function (id) {
  6780. // Escape any `:` characters from the row id. Accounts for
  6781. // already escaped characters.
  6782. return id.replace(/([^:\\]*(?:\\.[^:\\]*)*):/g, "$1\\:");
  6783. }) )
  6784. .every( function () {
  6785. _fnCallbackFire( api.settings()[0], null, 'requestChild', [ this ] )
  6786. });
  6787. }
  6788. }
  6789. var __details_add = function ( ctx, row, data, klass )
  6790. {
  6791. // Convert to array of TR elements
  6792. var rows = [];
  6793. var addRow = function ( r, k ) {
  6794. // Recursion to allow for arrays of jQuery objects
  6795. if ( Array.isArray( r ) || r instanceof $ ) {
  6796. for ( var i=0, ien=r.length ; i<ien ; i++ ) {
  6797. addRow( r[i], k );
  6798. }
  6799. return;
  6800. }
  6801. // If we get a TR element, then just add it directly - up to the dev
  6802. // to add the correct number of columns etc
  6803. if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {
  6804. r.setAttribute( 'data-dt-row', row.idx );
  6805. rows.push( r );
  6806. }
  6807. else {
  6808. // Otherwise create a row with a wrapper
  6809. var created = $('<tr><td></td></tr>')
  6810. .attr( 'data-dt-row', row.idx )
  6811. .addClass( k );
  6812. $('td', created)
  6813. .addClass( k )
  6814. .html( r )[0].colSpan = _fnVisbleColumns( ctx );
  6815. rows.push( created[0] );
  6816. }
  6817. };
  6818. addRow( data, klass );
  6819. if ( row._details ) {
  6820. row._details.detach();
  6821. }
  6822. row._details = $(rows);
  6823. // If the children were already shown, that state should be retained
  6824. if ( row._detailsShow ) {
  6825. row._details.insertAfter( row.nTr );
  6826. }
  6827. };
  6828. // Make state saving of child row details async to allow them to be batch processed
  6829. var __details_state = DataTable.util.throttle(
  6830. function (ctx) {
  6831. _fnSaveState( ctx[0] )
  6832. },
  6833. 500
  6834. );
  6835. var __details_remove = function ( api, idx )
  6836. {
  6837. var ctx = api.context;
  6838. if ( ctx.length ) {
  6839. var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
  6840. if ( row && row._details ) {
  6841. row._details.remove();
  6842. row._detailsShow = undefined;
  6843. row._details = undefined;
  6844. $( row.nTr ).removeClass( 'dt-hasChild' );
  6845. __details_state( ctx );
  6846. }
  6847. }
  6848. };
  6849. var __details_display = function ( api, show ) {
  6850. var ctx = api.context;
  6851. if ( ctx.length && api.length ) {
  6852. var row = ctx[0].aoData[ api[0] ];
  6853. if ( row._details ) {
  6854. row._detailsShow = show;
  6855. if ( show ) {
  6856. row._details.insertAfter( row.nTr );
  6857. $( row.nTr ).addClass( 'dt-hasChild' );
  6858. }
  6859. else {
  6860. row._details.detach();
  6861. $( row.nTr ).removeClass( 'dt-hasChild' );
  6862. }
  6863. _fnCallbackFire( ctx[0], null, 'childRow', [ show, api.row( api[0] ) ] )
  6864. __details_events( ctx[0] );
  6865. __details_state( ctx );
  6866. }
  6867. }
  6868. };
  6869. var __details_events = function ( settings )
  6870. {
  6871. var api = new _Api( settings );
  6872. var namespace = '.dt.DT_details';
  6873. var drawEvent = 'draw'+namespace;
  6874. var colvisEvent = 'column-sizing'+namespace;
  6875. var destroyEvent = 'destroy'+namespace;
  6876. var data = settings.aoData;
  6877. api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );
  6878. if ( _pluck( data, '_details' ).length > 0 ) {
  6879. // On each draw, insert the required elements into the document
  6880. api.on( drawEvent, function ( e, ctx ) {
  6881. if ( settings !== ctx ) {
  6882. return;
  6883. }
  6884. api.rows( {page:'current'} ).eq(0).each( function (idx) {
  6885. // Internal data grab
  6886. var row = data[ idx ];
  6887. if ( row._detailsShow ) {
  6888. row._details.insertAfter( row.nTr );
  6889. }
  6890. } );
  6891. } );
  6892. // Column visibility change - update the colspan
  6893. api.on( colvisEvent, function ( e, ctx ) {
  6894. if ( settings !== ctx ) {
  6895. return;
  6896. }
  6897. // Update the colspan for the details rows (note, only if it already has
  6898. // a colspan)
  6899. var row, visible = _fnVisbleColumns( ctx );
  6900. for ( var i=0, ien=data.length ; i<ien ; i++ ) {
  6901. row = data[i];
  6902. if ( row && row._details ) {
  6903. row._details.each(function () {
  6904. var el = $(this).children('td');
  6905. if (el.length == 1) {
  6906. el.attr('colspan', visible);
  6907. }
  6908. });
  6909. }
  6910. }
  6911. } );
  6912. // Table destroyed - nuke any child rows
  6913. api.on( destroyEvent, function ( e, ctx ) {
  6914. if ( settings !== ctx ) {
  6915. return;
  6916. }
  6917. for ( var i=0, ien=data.length ; i<ien ; i++ ) {
  6918. if ( data[i] && data[i]._details ) {
  6919. __details_remove( api, i );
  6920. }
  6921. }
  6922. } );
  6923. }
  6924. };
  6925. // Strings for the method names to help minification
  6926. var _emp = '';
  6927. var _child_obj = _emp+'row().child';
  6928. var _child_mth = _child_obj+'()';
  6929. // data can be:
  6930. // tr
  6931. // string
  6932. // jQuery or array of any of the above
  6933. _api_register( _child_mth, function ( data, klass ) {
  6934. var ctx = this.context;
  6935. if ( data === undefined ) {
  6936. // get
  6937. return ctx.length && this.length && ctx[0].aoData[ this[0] ]
  6938. ? ctx[0].aoData[ this[0] ]._details
  6939. : undefined;
  6940. }
  6941. else if ( data === true ) {
  6942. // show
  6943. this.child.show();
  6944. }
  6945. else if ( data === false ) {
  6946. // remove
  6947. __details_remove( this );
  6948. }
  6949. else if ( ctx.length && this.length ) {
  6950. // set
  6951. __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );
  6952. }
  6953. return this;
  6954. } );
  6955. _api_register( [
  6956. _child_obj+'.show()',
  6957. _child_mth+'.show()' // only when `child()` was called with parameters (without
  6958. ], function () { // it returns an object and this method is not executed)
  6959. __details_display( this, true );
  6960. return this;
  6961. } );
  6962. _api_register( [
  6963. _child_obj+'.hide()',
  6964. _child_mth+'.hide()' // only when `child()` was called with parameters (without
  6965. ], function () { // it returns an object and this method is not executed)
  6966. __details_display( this, false );
  6967. return this;
  6968. } );
  6969. _api_register( [
  6970. _child_obj+'.remove()',
  6971. _child_mth+'.remove()' // only when `child()` was called with parameters (without
  6972. ], function () { // it returns an object and this method is not executed)
  6973. __details_remove( this );
  6974. return this;
  6975. } );
  6976. _api_register( _child_obj+'.isShown()', function () {
  6977. var ctx = this.context;
  6978. if ( ctx.length && this.length ) {
  6979. // _detailsShown as false or undefined will fall through to return false
  6980. return ctx[0].aoData[ this[0] ]._detailsShow || false;
  6981. }
  6982. return false;
  6983. } );
  6984. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  6985. * Columns
  6986. *
  6987. * {integer} - column index (>=0 count from left, <0 count from right)
  6988. * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right)
  6989. * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right)
  6990. * "{string}:name" - column name
  6991. * "{string}" - jQuery selector on column header nodes
  6992. *
  6993. */
  6994. // can be an array of these items, comma separated list, or an array of comma
  6995. // separated lists
  6996. var __re_column_selector = /^([^:]+):(name|title|visIdx|visible)$/;
  6997. // r1 and r2 are redundant - but it means that the parameters match for the
  6998. // iterator callback in columns().data()
  6999. var __columnData = function ( settings, column, r1, r2, rows, type ) {
  7000. var a = [];
  7001. for ( var row=0, ien=rows.length ; row<ien ; row++ ) {
  7002. a.push( _fnGetCellData( settings, rows[row], column, type ) );
  7003. }
  7004. return a;
  7005. };
  7006. var __column_header = function ( settings, column, row ) {
  7007. var header = settings.aoHeader;
  7008. var target = row !== undefined
  7009. ? row
  7010. : settings.bSortCellsTop // legacy support
  7011. ? 0
  7012. : header.length - 1;
  7013. return header[target][column].cell;
  7014. };
  7015. var __column_selector = function ( settings, selector, opts )
  7016. {
  7017. var
  7018. columns = settings.aoColumns,
  7019. names = _pluck( columns, 'sName' ),
  7020. titles = _pluck( columns, 'sTitle' ),
  7021. cells = DataTable.util.get('[].[].cell')(settings.aoHeader),
  7022. nodes = _unique( _flatten([], cells) );
  7023. var run = function ( s ) {
  7024. var selInt = _intVal( s );
  7025. // Selector - all
  7026. if ( s === '' ) {
  7027. return _range( columns.length );
  7028. }
  7029. // Selector - index
  7030. if ( selInt !== null ) {
  7031. return [ selInt >= 0 ?
  7032. selInt : // Count from left
  7033. columns.length + selInt // Count from right (+ because its a negative value)
  7034. ];
  7035. }
  7036. // Selector = function
  7037. if ( typeof s === 'function' ) {
  7038. var rows = _selector_row_indexes( settings, opts );
  7039. return columns.map(function (col, idx) {
  7040. return s(
  7041. idx,
  7042. __columnData( settings, idx, 0, 0, rows ),
  7043. __column_header( settings, idx )
  7044. ) ? idx : null;
  7045. });
  7046. }
  7047. // jQuery or string selector
  7048. var match = typeof s === 'string' ?
  7049. s.match( __re_column_selector ) :
  7050. '';
  7051. if ( match ) {
  7052. switch( match[2] ) {
  7053. case 'visIdx':
  7054. case 'visible':
  7055. var idx = parseInt( match[1], 10 );
  7056. // Visible index given, convert to column index
  7057. if ( idx < 0 ) {
  7058. // Counting from the right
  7059. var visColumns = columns.map( function (col,i) {
  7060. return col.bVisible ? i : null;
  7061. } );
  7062. return [ visColumns[ visColumns.length + idx ] ];
  7063. }
  7064. // Counting from the left
  7065. return [ _fnVisibleToColumnIndex( settings, idx ) ];
  7066. case 'name':
  7067. // match by name. `names` is column index complete and in order
  7068. return names.map( function (name, i) {
  7069. return name === match[1] ? i : null;
  7070. } );
  7071. case 'title':
  7072. // match by column title
  7073. return titles.map( function (title, i) {
  7074. return title === match[1] ? i : null;
  7075. } );
  7076. default:
  7077. return [];
  7078. }
  7079. }
  7080. // Cell in the table body
  7081. if ( s.nodeName && s._DT_CellIndex ) {
  7082. return [ s._DT_CellIndex.column ];
  7083. }
  7084. // jQuery selector on the TH elements for the columns
  7085. var jqResult = $( nodes )
  7086. .filter( s )
  7087. .map( function () {
  7088. return _fnColumnsFromHeader( this ); // `nodes` is column index complete and in order
  7089. } )
  7090. .toArray();
  7091. if ( jqResult.length || ! s.nodeName ) {
  7092. return jqResult;
  7093. }
  7094. // Otherwise a node which might have a `dt-column` data attribute, or be
  7095. // a child or such an element
  7096. var host = $(s).closest('*[data-dt-column]');
  7097. return host.length ?
  7098. [ host.data('dt-column') ] :
  7099. [];
  7100. };
  7101. return _selector_run( 'column', selector, run, settings, opts );
  7102. };
  7103. var __setColumnVis = function ( settings, column, vis ) {
  7104. var
  7105. cols = settings.aoColumns,
  7106. col = cols[ column ],
  7107. data = settings.aoData,
  7108. cells, i, ien, tr;
  7109. // Get
  7110. if ( vis === undefined ) {
  7111. return col.bVisible;
  7112. }
  7113. // Set
  7114. // No change
  7115. if ( col.bVisible === vis ) {
  7116. return false;
  7117. }
  7118. if ( vis ) {
  7119. // Insert column
  7120. // Need to decide if we should use appendChild or insertBefore
  7121. var insertBefore = _pluck(cols, 'bVisible').indexOf(true, column+1);
  7122. for ( i=0, ien=data.length ; i<ien ; i++ ) {
  7123. if (data[i]) {
  7124. tr = data[i].nTr;
  7125. cells = data[i].anCells;
  7126. if ( tr ) {
  7127. // insertBefore can act like appendChild if 2nd arg is null
  7128. tr.insertBefore( cells[ column ], cells[ insertBefore ] || null );
  7129. }
  7130. }
  7131. }
  7132. }
  7133. else {
  7134. // Remove column
  7135. $( _pluck( settings.aoData, 'anCells', column ) ).detach();
  7136. }
  7137. // Common actions
  7138. col.bVisible = vis;
  7139. _colGroup(settings);
  7140. return true;
  7141. };
  7142. _api_register( 'columns()', function ( selector, opts ) {
  7143. // argument shifting
  7144. if ( selector === undefined ) {
  7145. selector = '';
  7146. }
  7147. else if ( $.isPlainObject( selector ) ) {
  7148. opts = selector;
  7149. selector = '';
  7150. }
  7151. opts = _selector_opts( opts );
  7152. var inst = this.iterator( 'table', function ( settings ) {
  7153. return __column_selector( settings, selector, opts );
  7154. }, 1 );
  7155. // Want argument shifting here and in _row_selector?
  7156. inst.selector.cols = selector;
  7157. inst.selector.opts = opts;
  7158. return inst;
  7159. } );
  7160. _api_registerPlural( 'columns().header()', 'column().header()', function ( row ) {
  7161. return this.iterator( 'column', function (settings, column) {
  7162. return __column_header(settings, column, row);
  7163. }, 1 );
  7164. } );
  7165. _api_registerPlural( 'columns().footer()', 'column().footer()', function ( row ) {
  7166. return this.iterator( 'column', function ( settings, column ) {
  7167. var footer = settings.aoFooter;
  7168. if (! footer.length) {
  7169. return null;
  7170. }
  7171. return settings.aoFooter[row !== undefined ? row : 0][column].cell;
  7172. }, 1 );
  7173. } );
  7174. _api_registerPlural( 'columns().data()', 'column().data()', function () {
  7175. return this.iterator( 'column-rows', __columnData, 1 );
  7176. } );
  7177. _api_registerPlural( 'columns().render()', 'column().render()', function ( type ) {
  7178. return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
  7179. return __columnData( settings, column, i, j, rows, type );
  7180. }, 1 );
  7181. } );
  7182. _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {
  7183. return this.iterator( 'column', function ( settings, column ) {
  7184. return settings.aoColumns[column].mData;
  7185. }, 1 );
  7186. } );
  7187. _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {
  7188. return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
  7189. return _pluck_order( settings.aoData, rows,
  7190. type === 'search' ? '_aFilterData' : '_aSortData', column
  7191. );
  7192. }, 1 );
  7193. } );
  7194. _api_registerPlural( 'columns().init()', 'column().init()', function () {
  7195. return this.iterator( 'column', function ( settings, column ) {
  7196. return settings.aoColumns[column];
  7197. }, 1 );
  7198. } );
  7199. _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
  7200. return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
  7201. return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
  7202. }, 1 );
  7203. } );
  7204. _api_registerPlural( 'columns().titles()', 'column().title()', function (title, row) {
  7205. return this.iterator( 'column', function ( settings, column ) {
  7206. // Argument shifting
  7207. if (typeof title === 'number') {
  7208. row = title;
  7209. title = undefined;
  7210. }
  7211. var span = $('span.dt-column-title', this.column(column).header(row));
  7212. if (title !== undefined) {
  7213. span.html(title);
  7214. return this;
  7215. }
  7216. return span.html();
  7217. }, 1 );
  7218. } );
  7219. _api_registerPlural( 'columns().types()', 'column().type()', function () {
  7220. return this.iterator( 'column', function ( settings, column ) {
  7221. var type = settings.aoColumns[column].sType;
  7222. // If the type was invalidated, then resolve it. This actually does
  7223. // all columns at the moment. Would only happen once if getting all
  7224. // column's data types.
  7225. if (! type) {
  7226. _fnColumnTypes(settings);
  7227. }
  7228. return type;
  7229. }, 1 );
  7230. } );
  7231. _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {
  7232. var that = this;
  7233. var changed = [];
  7234. var ret = this.iterator( 'column', function ( settings, column ) {
  7235. if ( vis === undefined ) {
  7236. return settings.aoColumns[ column ].bVisible;
  7237. } // else
  7238. if (__setColumnVis( settings, column, vis )) {
  7239. changed.push(column);
  7240. }
  7241. } );
  7242. // Group the column visibility changes
  7243. if ( vis !== undefined ) {
  7244. this.iterator( 'table', function ( settings ) {
  7245. // Redraw the header after changes
  7246. _fnDrawHead( settings, settings.aoHeader );
  7247. _fnDrawHead( settings, settings.aoFooter );
  7248. // Update colspan for no records display. Child rows and extensions will use their own
  7249. // listeners to do this - only need to update the empty table item here
  7250. if ( ! settings.aiDisplay.length ) {
  7251. $(settings.nTBody).find('td[colspan]').attr('colspan', _fnVisbleColumns(settings));
  7252. }
  7253. _fnSaveState( settings );
  7254. // Second loop once the first is done for events
  7255. that.iterator( 'column', function ( settings, column ) {
  7256. if (changed.includes(column)) {
  7257. _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] );
  7258. }
  7259. } );
  7260. if ( changed.length && (calc === undefined || calc) ) {
  7261. that.columns.adjust();
  7262. }
  7263. });
  7264. }
  7265. return ret;
  7266. } );
  7267. _api_registerPlural( 'columns().widths()', 'column().width()', function () {
  7268. // Injects a fake row into the table for just a moment so the widths can
  7269. // be read, regardless of colspan in the header and rows being present in
  7270. // the body
  7271. var columns = this.columns(':visible').count();
  7272. var row = $('<tr>').html('<td>' + Array(columns).join('</td><td>') + '</td>');
  7273. $(this.table().body()).append(row);
  7274. var widths = row.children().map(function () {
  7275. return $(this).outerWidth();
  7276. });
  7277. row.remove();
  7278. return this.iterator( 'column', function ( settings, column ) {
  7279. var visIdx = _fnColumnIndexToVisible( settings, column );
  7280. return visIdx !== null ? widths[visIdx] : 0;
  7281. }, 1);
  7282. } );
  7283. _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
  7284. return this.iterator( 'column', function ( settings, column ) {
  7285. return type === 'visible' ?
  7286. _fnColumnIndexToVisible( settings, column ) :
  7287. column;
  7288. }, 1 );
  7289. } );
  7290. _api_register( 'columns.adjust()', function () {
  7291. return this.iterator( 'table', function ( settings ) {
  7292. _fnAdjustColumnSizing( settings );
  7293. }, 1 );
  7294. } );
  7295. _api_register( 'column.index()', function ( type, idx ) {
  7296. if ( this.context.length !== 0 ) {
  7297. var ctx = this.context[0];
  7298. if ( type === 'fromVisible' || type === 'toData' ) {
  7299. return _fnVisibleToColumnIndex( ctx, idx );
  7300. }
  7301. else if ( type === 'fromData' || type === 'toVisible' ) {
  7302. return _fnColumnIndexToVisible( ctx, idx );
  7303. }
  7304. }
  7305. } );
  7306. _api_register( 'column()', function ( selector, opts ) {
  7307. return _selector_first( this.columns( selector, opts ) );
  7308. } );
  7309. var __cell_selector = function ( settings, selector, opts )
  7310. {
  7311. var data = settings.aoData;
  7312. var rows = _selector_row_indexes( settings, opts );
  7313. var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) );
  7314. var allCells = $(_flatten( [], cells ));
  7315. var row;
  7316. var columns = settings.aoColumns.length;
  7317. var a, i, ien, j, o, host;
  7318. var run = function ( s ) {
  7319. var fnSelector = typeof s === 'function';
  7320. if ( s === null || s === undefined || fnSelector ) {
  7321. // All cells and function selectors
  7322. a = [];
  7323. for ( i=0, ien=rows.length ; i<ien ; i++ ) {
  7324. row = rows[i];
  7325. for ( j=0 ; j<columns ; j++ ) {
  7326. o = {
  7327. row: row,
  7328. column: j
  7329. };
  7330. if ( fnSelector ) {
  7331. // Selector - function
  7332. host = data[ row ];
  7333. if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) {
  7334. a.push( o );
  7335. }
  7336. }
  7337. else {
  7338. // Selector - all
  7339. a.push( o );
  7340. }
  7341. }
  7342. }
  7343. return a;
  7344. }
  7345. // Selector - index
  7346. if ( $.isPlainObject( s ) ) {
  7347. // Valid cell index and its in the array of selectable rows
  7348. return s.column !== undefined && s.row !== undefined && rows.indexOf(s.row) !== -1 ?
  7349. [s] :
  7350. [];
  7351. }
  7352. // Selector - jQuery filtered cells
  7353. var jqResult = allCells
  7354. .filter( s )
  7355. .map( function (i, el) {
  7356. return { // use a new object, in case someone changes the values
  7357. row: el._DT_CellIndex.row,
  7358. column: el._DT_CellIndex.column
  7359. };
  7360. } )
  7361. .toArray();
  7362. if ( jqResult.length || ! s.nodeName ) {
  7363. return jqResult;
  7364. }
  7365. // Otherwise the selector is a node, and there is one last option - the
  7366. // element might be a child of an element which has dt-row and dt-column
  7367. // data attributes
  7368. host = $(s).closest('*[data-dt-row]');
  7369. return host.length ?
  7370. [ {
  7371. row: host.data('dt-row'),
  7372. column: host.data('dt-column')
  7373. } ] :
  7374. [];
  7375. };
  7376. return _selector_run( 'cell', selector, run, settings, opts );
  7377. };
  7378. _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {
  7379. // Argument shifting
  7380. if ( $.isPlainObject( rowSelector ) ) {
  7381. // Indexes
  7382. if ( rowSelector.row === undefined ) {
  7383. // Selector options in first parameter
  7384. opts = rowSelector;
  7385. rowSelector = null;
  7386. }
  7387. else {
  7388. // Cell index objects in first parameter
  7389. opts = columnSelector;
  7390. columnSelector = null;
  7391. }
  7392. }
  7393. if ( $.isPlainObject( columnSelector ) ) {
  7394. opts = columnSelector;
  7395. columnSelector = null;
  7396. }
  7397. // Cell selector
  7398. if ( columnSelector === null || columnSelector === undefined ) {
  7399. return this.iterator( 'table', function ( settings ) {
  7400. return __cell_selector( settings, rowSelector, _selector_opts( opts ) );
  7401. } );
  7402. }
  7403. // The default built in options need to apply to row and columns
  7404. var internalOpts = opts ? {
  7405. page: opts.page,
  7406. order: opts.order,
  7407. search: opts.search
  7408. } : {};
  7409. // Row + column selector
  7410. var columns = this.columns( columnSelector, internalOpts );
  7411. var rows = this.rows( rowSelector, internalOpts );
  7412. var i, ien, j, jen;
  7413. var cellsNoOpts = this.iterator( 'table', function ( settings, idx ) {
  7414. var a = [];
  7415. for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {
  7416. for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {
  7417. a.push( {
  7418. row: rows[idx][i],
  7419. column: columns[idx][j]
  7420. } );
  7421. }
  7422. }
  7423. return a;
  7424. }, 1 );
  7425. // There is currently only one extension which uses a cell selector extension
  7426. // It is a _major_ performance drag to run this if it isn't needed, so this is
  7427. // an extension specific check at the moment
  7428. var cells = opts && opts.selected ?
  7429. this.cells( cellsNoOpts, opts ) :
  7430. cellsNoOpts;
  7431. $.extend( cells.selector, {
  7432. cols: columnSelector,
  7433. rows: rowSelector,
  7434. opts: opts
  7435. } );
  7436. return cells;
  7437. } );
  7438. _api_registerPlural( 'cells().nodes()', 'cell().node()', function () {
  7439. return this.iterator( 'cell', function ( settings, row, column ) {
  7440. var data = settings.aoData[ row ];
  7441. return data && data.anCells ?
  7442. data.anCells[ column ] :
  7443. undefined;
  7444. }, 1 );
  7445. } );
  7446. _api_register( 'cells().data()', function () {
  7447. return this.iterator( 'cell', function ( settings, row, column ) {
  7448. return _fnGetCellData( settings, row, column );
  7449. }, 1 );
  7450. } );
  7451. _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {
  7452. type = type === 'search' ? '_aFilterData' : '_aSortData';
  7453. return this.iterator( 'cell', function ( settings, row, column ) {
  7454. return settings.aoData[ row ][ type ][ column ];
  7455. }, 1 );
  7456. } );
  7457. _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {
  7458. return this.iterator( 'cell', function ( settings, row, column ) {
  7459. return _fnGetCellData( settings, row, column, type );
  7460. }, 1 );
  7461. } );
  7462. _api_registerPlural( 'cells().indexes()', 'cell().index()', function () {
  7463. return this.iterator( 'cell', function ( settings, row, column ) {
  7464. return {
  7465. row: row,
  7466. column: column,
  7467. columnVisible: _fnColumnIndexToVisible( settings, column )
  7468. };
  7469. }, 1 );
  7470. } );
  7471. _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {
  7472. return this.iterator( 'cell', function ( settings, row, column ) {
  7473. _fnInvalidate( settings, row, src, column );
  7474. } );
  7475. } );
  7476. _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {
  7477. return _selector_first( this.cells( rowSelector, columnSelector, opts ) );
  7478. } );
  7479. _api_register( 'cell().data()', function ( data ) {
  7480. var ctx = this.context;
  7481. var cell = this[0];
  7482. if ( data === undefined ) {
  7483. // Get
  7484. return ctx.length && cell.length ?
  7485. _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) :
  7486. undefined;
  7487. }
  7488. // Set
  7489. _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
  7490. _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );
  7491. return this;
  7492. } );
  7493. /**
  7494. * Get current ordering (sorting) that has been applied to the table.
  7495. *
  7496. * @returns {array} 2D array containing the sorting information for the first
  7497. * table in the current context. Each element in the parent array represents
  7498. * a column being sorted upon (i.e. multi-sorting with two columns would have
  7499. * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is
  7500. * the column index that the sorting condition applies to, the second is the
  7501. * direction of the sort (`desc` or `asc`) and, optionally, the third is the
  7502. * index of the sorting order from the `column.sorting` initialisation array.
  7503. *//**
  7504. * Set the ordering for the table.
  7505. *
  7506. * @param {integer} order Column index to sort upon.
  7507. * @param {string} direction Direction of the sort to be applied (`asc` or `desc`)
  7508. * @returns {DataTables.Api} this
  7509. *//**
  7510. * Set the ordering for the table.
  7511. *
  7512. * @param {array} order 1D array of sorting information to be applied.
  7513. * @param {array} [...] Optional additional sorting conditions
  7514. * @returns {DataTables.Api} this
  7515. *//**
  7516. * Set the ordering for the table.
  7517. *
  7518. * @param {array} order 2D array of sorting information to be applied.
  7519. * @returns {DataTables.Api} this
  7520. */
  7521. _api_register( 'order()', function ( order, dir ) {
  7522. var ctx = this.context;
  7523. var args = Array.prototype.slice.call( arguments );
  7524. if ( order === undefined ) {
  7525. // get
  7526. return ctx.length !== 0 ?
  7527. ctx[0].aaSorting :
  7528. undefined;
  7529. }
  7530. // set
  7531. if ( typeof order === 'number' ) {
  7532. // Simple column / direction passed in
  7533. order = [ [ order, dir ] ];
  7534. }
  7535. else if ( args.length > 1 ) {
  7536. // Arguments passed in (list of 1D arrays)
  7537. order = args;
  7538. }
  7539. // otherwise a 2D array was passed in
  7540. return this.iterator( 'table', function ( settings ) {
  7541. settings.aaSorting = Array.isArray(order) ? order.slice() : order;
  7542. } );
  7543. } );
  7544. /**
  7545. * Attach a sort listener to an element for a given column
  7546. *
  7547. * @param {node|jQuery|string} node Identifier for the element(s) to attach the
  7548. * listener to. This can take the form of a single DOM node, a jQuery
  7549. * collection of nodes or a jQuery selector which will identify the node(s).
  7550. * @param {integer} column the column that a click on this node will sort on
  7551. * @param {function} [callback] callback function when sort is run
  7552. * @returns {DataTables.Api} this
  7553. */
  7554. _api_register( 'order.listener()', function ( node, column, callback ) {
  7555. return this.iterator( 'table', function ( settings ) {
  7556. _fnSortAttachListener(settings, node, {}, column, callback);
  7557. } );
  7558. } );
  7559. _api_register( 'order.fixed()', function ( set ) {
  7560. if ( ! set ) {
  7561. var ctx = this.context;
  7562. var fixed = ctx.length ?
  7563. ctx[0].aaSortingFixed :
  7564. undefined;
  7565. return Array.isArray( fixed ) ?
  7566. { pre: fixed } :
  7567. fixed;
  7568. }
  7569. return this.iterator( 'table', function ( settings ) {
  7570. settings.aaSortingFixed = $.extend( true, {}, set );
  7571. } );
  7572. } );
  7573. // Order by the selected column(s)
  7574. _api_register( [
  7575. 'columns().order()',
  7576. 'column().order()'
  7577. ], function ( dir ) {
  7578. var that = this;
  7579. if ( ! dir ) {
  7580. return this.iterator( 'column', function ( settings, idx ) {
  7581. var sort = _fnSortFlatten( settings );
  7582. for ( var i=0, ien=sort.length ; i<ien ; i++ ) {
  7583. if ( sort[i].col === idx ) {
  7584. return sort[i].dir;
  7585. }
  7586. }
  7587. return null;
  7588. }, 1 );
  7589. }
  7590. else {
  7591. return this.iterator( 'table', function ( settings, i ) {
  7592. settings.aaSorting = that[i].map( function (col) {
  7593. return [ col, dir ];
  7594. } );
  7595. } );
  7596. }
  7597. } );
  7598. _api_registerPlural('columns().orderable()', 'column().orderable()', function ( directions ) {
  7599. return this.iterator( 'column', function ( settings, idx ) {
  7600. var col = settings.aoColumns[idx];
  7601. return directions ?
  7602. col.asSorting :
  7603. col.bSortable;
  7604. }, 1 );
  7605. } );
  7606. _api_register( 'processing()', function ( show ) {
  7607. return this.iterator( 'table', function ( ctx ) {
  7608. _fnProcessingDisplay( ctx, show );
  7609. } );
  7610. } );
  7611. _api_register( 'search()', function ( input, regex, smart, caseInsen ) {
  7612. var ctx = this.context;
  7613. if ( input === undefined ) {
  7614. // get
  7615. return ctx.length !== 0 ?
  7616. ctx[0].oPreviousSearch.search :
  7617. undefined;
  7618. }
  7619. // set
  7620. return this.iterator( 'table', function ( settings ) {
  7621. if ( ! settings.oFeatures.bFilter ) {
  7622. return;
  7623. }
  7624. if (typeof regex === 'object') {
  7625. // New style options to pass to the search builder
  7626. _fnFilterComplete( settings, $.extend( settings.oPreviousSearch, regex, {
  7627. search: input
  7628. } ) );
  7629. }
  7630. else {
  7631. // Compat for the old options
  7632. _fnFilterComplete( settings, $.extend( settings.oPreviousSearch, {
  7633. search: input,
  7634. regex: regex === null ? false : regex,
  7635. smart: smart === null ? true : smart,
  7636. caseInsensitive: caseInsen === null ? true : caseInsen
  7637. } ) );
  7638. }
  7639. } );
  7640. } );
  7641. _api_register( 'search.fixed()', function ( name, search ) {
  7642. var ret = this.iterator( true, 'table', function ( settings ) {
  7643. var fixed = settings.searchFixed;
  7644. if (! name) {
  7645. return Object.keys(fixed)
  7646. }
  7647. else if (search === undefined) {
  7648. return fixed[name];
  7649. }
  7650. else if (search === null) {
  7651. delete fixed[name];
  7652. }
  7653. else {
  7654. fixed[name] = search;
  7655. }
  7656. return this;
  7657. } );
  7658. return name !== undefined && search === undefined
  7659. ? ret[0]
  7660. : ret;
  7661. } );
  7662. _api_registerPlural(
  7663. 'columns().search()',
  7664. 'column().search()',
  7665. function ( input, regex, smart, caseInsen ) {
  7666. return this.iterator( 'column', function ( settings, column ) {
  7667. var preSearch = settings.aoPreSearchCols;
  7668. if ( input === undefined ) {
  7669. // get
  7670. return preSearch[ column ].search;
  7671. }
  7672. // set
  7673. if ( ! settings.oFeatures.bFilter ) {
  7674. return;
  7675. }
  7676. if (typeof regex === 'object') {
  7677. // New style options to pass to the search builder
  7678. $.extend( preSearch[ column ], regex, {
  7679. search: input
  7680. } );
  7681. }
  7682. else {
  7683. // Old style (with not all options available)
  7684. $.extend( preSearch[ column ], {
  7685. search: input,
  7686. regex: regex === null ? false : regex,
  7687. smart: smart === null ? true : smart,
  7688. caseInsensitive: caseInsen === null ? true : caseInsen
  7689. } );
  7690. }
  7691. _fnFilterComplete( settings, settings.oPreviousSearch );
  7692. } );
  7693. }
  7694. );
  7695. _api_register([
  7696. 'columns().search.fixed()',
  7697. 'column().search.fixed()'
  7698. ],
  7699. function ( name, search ) {
  7700. var ret = this.iterator( true, 'column', function ( settings, colIdx ) {
  7701. var fixed = settings.aoColumns[colIdx].searchFixed;
  7702. if (! name) {
  7703. return Object.keys(fixed)
  7704. }
  7705. else if (search === undefined) {
  7706. return fixed[name];
  7707. }
  7708. else if (search === null) {
  7709. delete fixed[name];
  7710. }
  7711. else {
  7712. fixed[name] = search;
  7713. }
  7714. return this;
  7715. } );
  7716. return name !== undefined && search === undefined
  7717. ? ret[0]
  7718. : ret;
  7719. }
  7720. );
  7721. /*
  7722. * State API methods
  7723. */
  7724. _api_register( 'state()', function ( set, ignoreTime ) {
  7725. // getter
  7726. if ( ! set ) {
  7727. return this.context.length ?
  7728. this.context[0].oSavedState :
  7729. null;
  7730. }
  7731. var setMutate = $.extend( true, {}, set );
  7732. // setter
  7733. return this.iterator( 'table', function ( settings ) {
  7734. if ( ignoreTime !== false ) {
  7735. setMutate.time = +new Date() + 100;
  7736. }
  7737. _fnImplementState( settings, setMutate, function(){} );
  7738. } );
  7739. } );
  7740. _api_register( 'state.clear()', function () {
  7741. return this.iterator( 'table', function ( settings ) {
  7742. // Save an empty object
  7743. settings.fnStateSaveCallback.call( settings.oInstance, settings, {} );
  7744. } );
  7745. } );
  7746. _api_register( 'state.loaded()', function () {
  7747. return this.context.length ?
  7748. this.context[0].oLoadedState :
  7749. null;
  7750. } );
  7751. _api_register( 'state.save()', function () {
  7752. return this.iterator( 'table', function ( settings ) {
  7753. _fnSaveState( settings );
  7754. } );
  7755. } );
  7756. /**
  7757. * Set the jQuery or window object to be used by DataTables
  7758. *
  7759. * @param {*} module Library / container object
  7760. * @param {string} [type] Library or container type `lib`, `win` or `datetime`.
  7761. * If not provided, automatic detection is attempted.
  7762. */
  7763. DataTable.use = function (module, type) {
  7764. if (type === 'lib' || module.fn) {
  7765. $ = module;
  7766. }
  7767. else if (type == 'win' || module.document) {
  7768. window = module;
  7769. document = module.document;
  7770. }
  7771. else if (type === 'datetime' || module.type === 'DateTime') {
  7772. DataTable.DateTime = module;
  7773. }
  7774. }
  7775. /**
  7776. * CommonJS factory function pass through. This will check if the arguments
  7777. * given are a window object or a jQuery object. If so they are set
  7778. * accordingly.
  7779. * @param {*} root Window
  7780. * @param {*} jq jQUery
  7781. * @returns {boolean} Indicator
  7782. */
  7783. DataTable.factory = function (root, jq) {
  7784. var is = false;
  7785. // Test if the first parameter is a window object
  7786. if (root && root.document) {
  7787. window = root;
  7788. document = root.document;
  7789. }
  7790. // Test if the second parameter is a jQuery object
  7791. if (jq && jq.fn && jq.fn.jquery) {
  7792. $ = jq;
  7793. is = true;
  7794. }
  7795. return is;
  7796. }
  7797. /**
  7798. * Provide a common method for plug-ins to check the version of DataTables being
  7799. * used, in order to ensure compatibility.
  7800. *
  7801. * @param {string} version Version string to check for, in the format "X.Y.Z".
  7802. * Note that the formats "X" and "X.Y" are also acceptable.
  7803. * @param {string} [version2=current DataTables version] As above, but optional.
  7804. * If not given the current DataTables version will be used.
  7805. * @returns {boolean} true if this version of DataTables is greater or equal to
  7806. * the required version, or false if this version of DataTales is not
  7807. * suitable
  7808. * @static
  7809. * @dtopt API-Static
  7810. *
  7811. * @example
  7812. * alert( $.fn.dataTable.versionCheck( '1.9.0' ) );
  7813. */
  7814. DataTable.versionCheck = function( version, version2 )
  7815. {
  7816. var aThis = version2 ?
  7817. version2.split('.') :
  7818. DataTable.version.split('.');
  7819. var aThat = version.split('.');
  7820. var iThis, iThat;
  7821. for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) {
  7822. iThis = parseInt( aThis[i], 10 ) || 0;
  7823. iThat = parseInt( aThat[i], 10 ) || 0;
  7824. // Parts are the same, keep comparing
  7825. if (iThis === iThat) {
  7826. continue;
  7827. }
  7828. // Parts are different, return immediately
  7829. return iThis > iThat;
  7830. }
  7831. return true;
  7832. };
  7833. /**
  7834. * Check if a `<table>` node is a DataTable table already or not.
  7835. *
  7836. * @param {node|jquery|string} table Table node, jQuery object or jQuery
  7837. * selector for the table to test. Note that if more than more than one
  7838. * table is passed on, only the first will be checked
  7839. * @returns {boolean} true the table given is a DataTable, or false otherwise
  7840. * @static
  7841. * @dtopt API-Static
  7842. *
  7843. * @example
  7844. * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) {
  7845. * $('#example').dataTable();
  7846. * }
  7847. */
  7848. DataTable.isDataTable = function ( table )
  7849. {
  7850. var t = $(table).get(0);
  7851. var is = false;
  7852. if ( table instanceof DataTable.Api ) {
  7853. return true;
  7854. }
  7855. $.each( DataTable.settings, function (i, o) {
  7856. var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null;
  7857. var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null;
  7858. if ( o.nTable === t || head === t || foot === t ) {
  7859. is = true;
  7860. }
  7861. } );
  7862. return is;
  7863. };
  7864. /**
  7865. * Get all DataTable tables that have been initialised - optionally you can
  7866. * select to get only currently visible tables.
  7867. *
  7868. * @param {boolean} [visible=false] Flag to indicate if you want all (default)
  7869. * or visible tables only.
  7870. * @returns {array} Array of `table` nodes (not DataTable instances) which are
  7871. * DataTables
  7872. * @static
  7873. * @dtopt API-Static
  7874. *
  7875. * @example
  7876. * $.each( $.fn.dataTable.tables(true), function () {
  7877. * $(table).DataTable().columns.adjust();
  7878. * } );
  7879. */
  7880. DataTable.tables = function ( visible )
  7881. {
  7882. var api = false;
  7883. if ( $.isPlainObject( visible ) ) {
  7884. api = visible.api;
  7885. visible = visible.visible;
  7886. }
  7887. var a = DataTable.settings
  7888. .filter( function (o) {
  7889. return !visible || (visible && $(o.nTable).is(':visible'))
  7890. ? true
  7891. : false;
  7892. } )
  7893. .map( function (o) {
  7894. return o.nTable;
  7895. });
  7896. return api ?
  7897. new _Api( a ) :
  7898. a;
  7899. };
  7900. /**
  7901. * Convert from camel case parameters to Hungarian notation. This is made public
  7902. * for the extensions to provide the same ability as DataTables core to accept
  7903. * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase
  7904. * parameters.
  7905. *
  7906. * @param {object} src The model object which holds all parameters that can be
  7907. * mapped.
  7908. * @param {object} user The object to convert from camel case to Hungarian.
  7909. * @param {boolean} force When set to `true`, properties which already have a
  7910. * Hungarian value in the `user` object will be overwritten. Otherwise they
  7911. * won't be.
  7912. */
  7913. DataTable.camelToHungarian = _fnCamelToHungarian;
  7914. /**
  7915. *
  7916. */
  7917. _api_register( '$()', function ( selector, opts ) {
  7918. var
  7919. rows = this.rows( opts ).nodes(), // Get all rows
  7920. jqRows = $(rows);
  7921. return $( [].concat(
  7922. jqRows.filter( selector ).toArray(),
  7923. jqRows.find( selector ).toArray()
  7924. ) );
  7925. } );
  7926. // jQuery functions to operate on the tables
  7927. $.each( [ 'on', 'one', 'off' ], function (i, key) {
  7928. _api_register( key+'()', function ( /* event, handler */ ) {
  7929. var args = Array.prototype.slice.call(arguments);
  7930. // Add the `dt` namespace automatically if it isn't already present
  7931. args[0] = args[0].split( /\s/ ).map( function ( e ) {
  7932. return ! e.match(/\.dt\b/) ?
  7933. e+'.dt' :
  7934. e;
  7935. } ).join( ' ' );
  7936. var inst = $( this.tables().nodes() );
  7937. inst[key].apply( inst, args );
  7938. return this;
  7939. } );
  7940. } );
  7941. _api_register( 'clear()', function () {
  7942. return this.iterator( 'table', function ( settings ) {
  7943. _fnClearTable( settings );
  7944. } );
  7945. } );
  7946. _api_register( 'error()', function (msg) {
  7947. return this.iterator( 'table', function ( settings ) {
  7948. _fnLog( settings, 0, msg );
  7949. } );
  7950. } );
  7951. _api_register( 'settings()', function () {
  7952. return new _Api( this.context, this.context );
  7953. } );
  7954. _api_register( 'init()', function () {
  7955. var ctx = this.context;
  7956. return ctx.length ? ctx[0].oInit : null;
  7957. } );
  7958. _api_register( 'data()', function () {
  7959. return this.iterator( 'table', function ( settings ) {
  7960. return _pluck( settings.aoData, '_aData' );
  7961. } ).flatten();
  7962. } );
  7963. _api_register( 'trigger()', function ( name, args, bubbles ) {
  7964. return this.iterator( 'table', function ( settings ) {
  7965. return _fnCallbackFire( settings, null, name, args, bubbles );
  7966. } ).flatten();
  7967. } );
  7968. _api_register( 'ready()', function ( fn ) {
  7969. var ctx = this.context;
  7970. // Get status of first table
  7971. if (! fn) {
  7972. return ctx.length
  7973. ? (ctx[0]._bInitComplete || false)
  7974. : null;
  7975. }
  7976. // Function to run either once the table becomes ready or
  7977. // immediately if it is already ready.
  7978. return this.tables().every(function () {
  7979. if (this.context[0]._bInitComplete) {
  7980. fn.call(this);
  7981. }
  7982. else {
  7983. this.on('init', function () {
  7984. fn.call(this);
  7985. });
  7986. }
  7987. } );
  7988. } );
  7989. _api_register( 'destroy()', function ( remove ) {
  7990. remove = remove || false;
  7991. return this.iterator( 'table', function ( settings ) {
  7992. var classes = settings.oClasses;
  7993. var table = settings.nTable;
  7994. var tbody = settings.nTBody;
  7995. var thead = settings.nTHead;
  7996. var tfoot = settings.nTFoot;
  7997. var jqTable = $(table);
  7998. var jqTbody = $(tbody);
  7999. var jqWrapper = $(settings.nTableWrapper);
  8000. var rows = settings.aoData.map( function (r) { return r ? r.nTr : null; } );
  8001. var orderClasses = classes.order;
  8002. // Flag to note that the table is currently being destroyed - no action
  8003. // should be taken
  8004. settings.bDestroying = true;
  8005. // Fire off the destroy callbacks for plug-ins etc
  8006. _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings], true );
  8007. // If not being removed from the document, make all columns visible
  8008. if ( ! remove ) {
  8009. new _Api( settings ).columns().visible( true );
  8010. }
  8011. // Blitz all `DT` namespaced events (these are internal events, the
  8012. // lowercase, `dt` events are user subscribed and they are responsible
  8013. // for removing them
  8014. jqWrapper.off('.DT').find(':not(tbody *)').off('.DT');
  8015. $(window).off('.DT-'+settings.sInstance);
  8016. // When scrolling we had to break the table up - restore it
  8017. if ( table != thead.parentNode ) {
  8018. jqTable.children('thead').detach();
  8019. jqTable.append( thead );
  8020. }
  8021. if ( tfoot && table != tfoot.parentNode ) {
  8022. jqTable.children('tfoot').detach();
  8023. jqTable.append( tfoot );
  8024. }
  8025. settings.colgroup.remove();
  8026. settings.aaSorting = [];
  8027. settings.aaSortingFixed = [];
  8028. _fnSortingClasses( settings );
  8029. $('th, td', thead)
  8030. .removeClass(
  8031. orderClasses.canAsc + ' ' +
  8032. orderClasses.canDesc + ' ' +
  8033. orderClasses.isAsc + ' ' +
  8034. orderClasses.isDesc
  8035. )
  8036. .css('width', '');
  8037. // Add the TR elements back into the table in their original order
  8038. jqTbody.children().detach();
  8039. jqTbody.append( rows );
  8040. var orig = settings.nTableWrapper.parentNode;
  8041. var insertBefore = settings.nTableWrapper.nextSibling;
  8042. // Remove the DataTables generated nodes, events and classes
  8043. var removedMethod = remove ? 'remove' : 'detach';
  8044. jqTable[ removedMethod ]();
  8045. jqWrapper[ removedMethod ]();
  8046. // If we need to reattach the table to the document
  8047. if ( ! remove && orig ) {
  8048. // insertBefore acts like appendChild if !arg[1]
  8049. orig.insertBefore( table, insertBefore );
  8050. // Restore the width of the original table - was read from the style property,
  8051. // so we can restore directly to that
  8052. jqTable
  8053. .css( 'width', settings.sDestroyWidth )
  8054. .removeClass( classes.table );
  8055. }
  8056. /* Remove the settings object from the settings array */
  8057. var idx = DataTable.settings.indexOf(settings);
  8058. if ( idx !== -1 ) {
  8059. DataTable.settings.splice( idx, 1 );
  8060. }
  8061. } );
  8062. } );
  8063. // Add the `every()` method for rows, columns and cells in a compact form
  8064. $.each( [ 'column', 'row', 'cell' ], function ( i, type ) {
  8065. _api_register( type+'s().every()', function ( fn ) {
  8066. var opts = this.selector.opts;
  8067. var api = this;
  8068. var inst;
  8069. var counter = 0;
  8070. return this.iterator( 'every', function ( settings, selectedIdx, tableIdx ) {
  8071. inst = api[ type ](selectedIdx, opts);
  8072. if (type === 'cell') {
  8073. fn.call(inst, inst[0][0].row, inst[0][0].column, tableIdx, counter);
  8074. }
  8075. else {
  8076. fn.call(inst, selectedIdx, tableIdx, counter);
  8077. }
  8078. counter++;
  8079. } );
  8080. } );
  8081. } );
  8082. // i18n method for extensions to be able to use the language object from the
  8083. // DataTable
  8084. _api_register( 'i18n()', function ( token, def, plural ) {
  8085. var ctx = this.context[0];
  8086. var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage );
  8087. if ( resolved === undefined ) {
  8088. resolved = def;
  8089. }
  8090. if ( $.isPlainObject( resolved ) ) {
  8091. resolved = plural !== undefined && resolved[ plural ] !== undefined ?
  8092. resolved[ plural ] :
  8093. resolved._;
  8094. }
  8095. return typeof resolved === 'string'
  8096. ? resolved.replace( '%d', plural ) // nb: plural might be undefined,
  8097. : resolved;
  8098. } );
  8099. /**
  8100. * Version string for plug-ins to check compatibility. Allowed format is
  8101. * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used
  8102. * only for non-release builds. See https://semver.org/ for more information.
  8103. * @member
  8104. * @type string
  8105. * @default Version number
  8106. */
  8107. DataTable.version = "2.0.7";
  8108. /**
  8109. * Private data store, containing all of the settings objects that are
  8110. * created for the tables on a given page.
  8111. *
  8112. * Note that the `DataTable.settings` object is aliased to
  8113. * `jQuery.fn.dataTableExt` through which it may be accessed and
  8114. * manipulated, or `jQuery.fn.dataTable.settings`.
  8115. * @member
  8116. * @type array
  8117. * @default []
  8118. * @private
  8119. */
  8120. DataTable.settings = [];
  8121. /**
  8122. * Object models container, for the various models that DataTables has
  8123. * available to it. These models define the objects that are used to hold
  8124. * the active state and configuration of the table.
  8125. * @namespace
  8126. */
  8127. DataTable.models = {};
  8128. /**
  8129. * Template object for the way in which DataTables holds information about
  8130. * search information for the global filter and individual column filters.
  8131. * @namespace
  8132. */
  8133. DataTable.models.oSearch = {
  8134. /**
  8135. * Flag to indicate if the filtering should be case insensitive or not
  8136. */
  8137. "caseInsensitive": true,
  8138. /**
  8139. * Applied search term
  8140. */
  8141. "search": "",
  8142. /**
  8143. * Flag to indicate if the search term should be interpreted as a
  8144. * regular expression (true) or not (false) and therefore and special
  8145. * regex characters escaped.
  8146. */
  8147. "regex": false,
  8148. /**
  8149. * Flag to indicate if DataTables is to use its smart filtering or not.
  8150. */
  8151. "smart": true,
  8152. /**
  8153. * Flag to indicate if DataTables should only trigger a search when
  8154. * the return key is pressed.
  8155. */
  8156. "return": false
  8157. };
  8158. /**
  8159. * Template object for the way in which DataTables holds information about
  8160. * each individual row. This is the object format used for the settings
  8161. * aoData array.
  8162. * @namespace
  8163. */
  8164. DataTable.models.oRow = {
  8165. /**
  8166. * TR element for the row
  8167. */
  8168. "nTr": null,
  8169. /**
  8170. * Array of TD elements for each row. This is null until the row has been
  8171. * created.
  8172. */
  8173. "anCells": null,
  8174. /**
  8175. * Data object from the original data source for the row. This is either
  8176. * an array if using the traditional form of DataTables, or an object if
  8177. * using mData options. The exact type will depend on the passed in
  8178. * data from the data source, or will be an array if using DOM a data
  8179. * source.
  8180. */
  8181. "_aData": [],
  8182. /**
  8183. * Sorting data cache - this array is ostensibly the same length as the
  8184. * number of columns (although each index is generated only as it is
  8185. * needed), and holds the data that is used for sorting each column in the
  8186. * row. We do this cache generation at the start of the sort in order that
  8187. * the formatting of the sort data need be done only once for each cell
  8188. * per sort. This array should not be read from or written to by anything
  8189. * other than the master sorting methods.
  8190. */
  8191. "_aSortData": null,
  8192. /**
  8193. * Per cell filtering data cache. As per the sort data cache, used to
  8194. * increase the performance of the filtering in DataTables
  8195. */
  8196. "_aFilterData": null,
  8197. /**
  8198. * Filtering data cache. This is the same as the cell filtering cache, but
  8199. * in this case a string rather than an array. This is easily computed with
  8200. * a join on `_aFilterData`, but is provided as a cache so the join isn't
  8201. * needed on every search (memory traded for performance)
  8202. */
  8203. "_sFilterRow": null,
  8204. /**
  8205. * Denote if the original data source was from the DOM, or the data source
  8206. * object. This is used for invalidating data, so DataTables can
  8207. * automatically read data from the original source, unless uninstructed
  8208. * otherwise.
  8209. */
  8210. "src": null,
  8211. /**
  8212. * Index in the aoData array. This saves an indexOf lookup when we have the
  8213. * object, but want to know the index
  8214. */
  8215. "idx": -1,
  8216. /**
  8217. * Cached display value
  8218. */
  8219. displayData: null
  8220. };
  8221. /**
  8222. * Template object for the column information object in DataTables. This object
  8223. * is held in the settings aoColumns array and contains all the information that
  8224. * DataTables needs about each individual column.
  8225. *
  8226. * Note that this object is related to {@link DataTable.defaults.column}
  8227. * but this one is the internal data store for DataTables's cache of columns.
  8228. * It should NOT be manipulated outside of DataTables. Any configuration should
  8229. * be done through the initialisation options.
  8230. * @namespace
  8231. */
  8232. DataTable.models.oColumn = {
  8233. /**
  8234. * Column index.
  8235. */
  8236. "idx": null,
  8237. /**
  8238. * A list of the columns that sorting should occur on when this column
  8239. * is sorted. That this property is an array allows multi-column sorting
  8240. * to be defined for a column (for example first name / last name columns
  8241. * would benefit from this). The values are integers pointing to the
  8242. * columns to be sorted on (typically it will be a single integer pointing
  8243. * at itself, but that doesn't need to be the case).
  8244. */
  8245. "aDataSort": null,
  8246. /**
  8247. * Define the sorting directions that are applied to the column, in sequence
  8248. * as the column is repeatedly sorted upon - i.e. the first value is used
  8249. * as the sorting direction when the column if first sorted (clicked on).
  8250. * Sort it again (click again) and it will move on to the next index.
  8251. * Repeat until loop.
  8252. */
  8253. "asSorting": null,
  8254. /**
  8255. * Flag to indicate if the column is searchable, and thus should be included
  8256. * in the filtering or not.
  8257. */
  8258. "bSearchable": null,
  8259. /**
  8260. * Flag to indicate if the column is sortable or not.
  8261. */
  8262. "bSortable": null,
  8263. /**
  8264. * Flag to indicate if the column is currently visible in the table or not
  8265. */
  8266. "bVisible": null,
  8267. /**
  8268. * Store for manual type assignment using the `column.type` option. This
  8269. * is held in store so we can manipulate the column's `sType` property.
  8270. */
  8271. "_sManualType": null,
  8272. /**
  8273. * Flag to indicate if HTML5 data attributes should be used as the data
  8274. * source for filtering or sorting. True is either are.
  8275. */
  8276. "_bAttrSrc": false,
  8277. /**
  8278. * Developer definable function that is called whenever a cell is created (Ajax source,
  8279. * etc) or processed for input (DOM source). This can be used as a compliment to mRender
  8280. * allowing you to modify the DOM element (add background colour for example) when the
  8281. * element is available.
  8282. */
  8283. "fnCreatedCell": null,
  8284. /**
  8285. * Function to get data from a cell in a column. You should <b>never</b>
  8286. * access data directly through _aData internally in DataTables - always use
  8287. * the method attached to this property. It allows mData to function as
  8288. * required. This function is automatically assigned by the column
  8289. * initialisation method
  8290. */
  8291. "fnGetData": null,
  8292. /**
  8293. * Function to set data for a cell in the column. You should <b>never</b>
  8294. * set the data directly to _aData internally in DataTables - always use
  8295. * this method. It allows mData to function as required. This function
  8296. * is automatically assigned by the column initialisation method
  8297. */
  8298. "fnSetData": null,
  8299. /**
  8300. * Property to read the value for the cells in the column from the data
  8301. * source array / object. If null, then the default content is used, if a
  8302. * function is given then the return from the function is used.
  8303. */
  8304. "mData": null,
  8305. /**
  8306. * Partner property to mData which is used (only when defined) to get
  8307. * the data - i.e. it is basically the same as mData, but without the
  8308. * 'set' option, and also the data fed to it is the result from mData.
  8309. * This is the rendering method to match the data method of mData.
  8310. */
  8311. "mRender": null,
  8312. /**
  8313. * The class to apply to all TD elements in the table's TBODY for the column
  8314. */
  8315. "sClass": null,
  8316. /**
  8317. * When DataTables calculates the column widths to assign to each column,
  8318. * it finds the longest string in each column and then constructs a
  8319. * temporary table and reads the widths from that. The problem with this
  8320. * is that "mmm" is much wider then "iiii", but the latter is a longer
  8321. * string - thus the calculation can go wrong (doing it properly and putting
  8322. * it into an DOM object and measuring that is horribly(!) slow). Thus as
  8323. * a "work around" we provide this option. It will append its value to the
  8324. * text that is found to be the longest string for the column - i.e. padding.
  8325. */
  8326. "sContentPadding": null,
  8327. /**
  8328. * Allows a default value to be given for a column's data, and will be used
  8329. * whenever a null data source is encountered (this can be because mData
  8330. * is set to null, or because the data source itself is null).
  8331. */
  8332. "sDefaultContent": null,
  8333. /**
  8334. * Name for the column, allowing reference to the column by name as well as
  8335. * by index (needs a lookup to work by name).
  8336. */
  8337. "sName": null,
  8338. /**
  8339. * Custom sorting data type - defines which of the available plug-ins in
  8340. * afnSortData the custom sorting will use - if any is defined.
  8341. */
  8342. "sSortDataType": 'std',
  8343. /**
  8344. * Class to be applied to the header element when sorting on this column
  8345. */
  8346. "sSortingClass": null,
  8347. /**
  8348. * Title of the column - what is seen in the TH element (nTh).
  8349. */
  8350. "sTitle": null,
  8351. /**
  8352. * Column sorting and filtering type
  8353. */
  8354. "sType": null,
  8355. /**
  8356. * Width of the column
  8357. */
  8358. "sWidth": null,
  8359. /**
  8360. * Width of the column when it was first "encountered"
  8361. */
  8362. "sWidthOrig": null,
  8363. /** Cached string which is the longest in the column */
  8364. maxLenString: null,
  8365. /**
  8366. * Store for named searches
  8367. */
  8368. searchFixed: null
  8369. };
  8370. /*
  8371. * Developer note: The properties of the object below are given in Hungarian
  8372. * notation, that was used as the interface for DataTables prior to v1.10, however
  8373. * from v1.10 onwards the primary interface is camel case. In order to avoid
  8374. * breaking backwards compatibility utterly with this change, the Hungarian
  8375. * version is still, internally the primary interface, but is is not documented
  8376. * - hence the @name tags in each doc comment. This allows a Javascript function
  8377. * to create a map from Hungarian notation to camel case (going the other direction
  8378. * would require each property to be listed, which would add around 3K to the size
  8379. * of DataTables, while this method is about a 0.5K hit).
  8380. *
  8381. * Ultimately this does pave the way for Hungarian notation to be dropped
  8382. * completely, but that is a massive amount of work and will break current
  8383. * installs (therefore is on-hold until v2).
  8384. */
  8385. /**
  8386. * Initialisation options that can be given to DataTables at initialisation
  8387. * time.
  8388. * @namespace
  8389. */
  8390. DataTable.defaults = {
  8391. /**
  8392. * An array of data to use for the table, passed in at initialisation which
  8393. * will be used in preference to any data which is already in the DOM. This is
  8394. * particularly useful for constructing tables purely in Javascript, for
  8395. * example with a custom Ajax call.
  8396. */
  8397. "aaData": null,
  8398. /**
  8399. * If ordering is enabled, then DataTables will perform a first pass sort on
  8400. * initialisation. You can define which column(s) the sort is performed
  8401. * upon, and the sorting direction, with this variable. The `sorting` array
  8402. * should contain an array for each column to be sorted initially containing
  8403. * the column's index and a direction string ('asc' or 'desc').
  8404. */
  8405. "aaSorting": [[0,'asc']],
  8406. /**
  8407. * This parameter is basically identical to the `sorting` parameter, but
  8408. * cannot be overridden by user interaction with the table. What this means
  8409. * is that you could have a column (visible or hidden) which the sorting
  8410. * will always be forced on first - any sorting after that (from the user)
  8411. * will then be performed as required. This can be useful for grouping rows
  8412. * together.
  8413. */
  8414. "aaSortingFixed": [],
  8415. /**
  8416. * DataTables can be instructed to load data to display in the table from a
  8417. * Ajax source. This option defines how that Ajax call is made and where to.
  8418. *
  8419. * The `ajax` property has three different modes of operation, depending on
  8420. * how it is defined. These are:
  8421. *
  8422. * * `string` - Set the URL from where the data should be loaded from.
  8423. * * `object` - Define properties for `jQuery.ajax`.
  8424. * * `function` - Custom data get function
  8425. *
  8426. * `string`
  8427. * --------
  8428. *
  8429. * As a string, the `ajax` property simply defines the URL from which
  8430. * DataTables will load data.
  8431. *
  8432. * `object`
  8433. * --------
  8434. *
  8435. * As an object, the parameters in the object are passed to
  8436. * [jQuery.ajax](https://api.jquery.com/jQuery.ajax/) allowing fine control
  8437. * of the Ajax request. DataTables has a number of default parameters which
  8438. * you can override using this option. Please refer to the jQuery
  8439. * documentation for a full description of the options available, although
  8440. * the following parameters provide additional options in DataTables or
  8441. * require special consideration:
  8442. *
  8443. * * `data` - As with jQuery, `data` can be provided as an object, but it
  8444. * can also be used as a function to manipulate the data DataTables sends
  8445. * to the server. The function takes a single parameter, an object of
  8446. * parameters with the values that DataTables has readied for sending. An
  8447. * object may be returned which will be merged into the DataTables
  8448. * defaults, or you can add the items to the object that was passed in and
  8449. * not return anything from the function. This supersedes `fnServerParams`
  8450. * from DataTables 1.9-.
  8451. *
  8452. * * `dataSrc` - By default DataTables will look for the property `data` (or
  8453. * `aaData` for compatibility with DataTables 1.9-) when obtaining data
  8454. * from an Ajax source or for server-side processing - this parameter
  8455. * allows that property to be changed. You can use Javascript dotted
  8456. * object notation to get a data source for multiple levels of nesting, or
  8457. * it my be used as a function. As a function it takes a single parameter,
  8458. * the JSON returned from the server, which can be manipulated as
  8459. * required, with the returned value being that used by DataTables as the
  8460. * data source for the table.
  8461. *
  8462. * * `success` - Should not be overridden it is used internally in
  8463. * DataTables. To manipulate / transform the data returned by the server
  8464. * use `ajax.dataSrc`, or use `ajax` as a function (see below).
  8465. *
  8466. * `function`
  8467. * ----------
  8468. *
  8469. * As a function, making the Ajax call is left up to yourself allowing
  8470. * complete control of the Ajax request. Indeed, if desired, a method other
  8471. * than Ajax could be used to obtain the required data, such as Web storage
  8472. * or an AIR database.
  8473. *
  8474. * The function is given four parameters and no return is required. The
  8475. * parameters are:
  8476. *
  8477. * 1. _object_ - Data to send to the server
  8478. * 2. _function_ - Callback function that must be executed when the required
  8479. * data has been obtained. That data should be passed into the callback
  8480. * as the only parameter
  8481. * 3. _object_ - DataTables settings object for the table
  8482. */
  8483. "ajax": null,
  8484. /**
  8485. * This parameter allows you to readily specify the entries in the length drop
  8486. * down menu that DataTables shows when pagination is enabled. It can be
  8487. * either a 1D array of options which will be used for both the displayed
  8488. * option and the value, or a 2D array which will use the array in the first
  8489. * position as the value, and the array in the second position as the
  8490. * displayed options (useful for language strings such as 'All').
  8491. *
  8492. * Note that the `pageLength` property will be automatically set to the
  8493. * first value given in this array, unless `pageLength` is also provided.
  8494. */
  8495. "aLengthMenu": [ 10, 25, 50, 100 ],
  8496. /**
  8497. * The `columns` option in the initialisation parameter allows you to define
  8498. * details about the way individual columns behave. For a full list of
  8499. * column options that can be set, please see
  8500. * {@link DataTable.defaults.column}. Note that if you use `columns` to
  8501. * define your columns, you must have an entry in the array for every single
  8502. * column that you have in your table (these can be null if you don't which
  8503. * to specify any options).
  8504. */
  8505. "aoColumns": null,
  8506. /**
  8507. * Very similar to `columns`, `columnDefs` allows you to target a specific
  8508. * column, multiple columns, or all columns, using the `targets` property of
  8509. * each object in the array. This allows great flexibility when creating
  8510. * tables, as the `columnDefs` arrays can be of any length, targeting the
  8511. * columns you specifically want. `columnDefs` may use any of the column
  8512. * options available: {@link DataTable.defaults.column}, but it _must_
  8513. * have `targets` defined in each object in the array. Values in the `targets`
  8514. * array may be:
  8515. * <ul>
  8516. * <li>a string - class name will be matched on the TH for the column</li>
  8517. * <li>0 or a positive integer - column index counting from the left</li>
  8518. * <li>a negative integer - column index counting from the right</li>
  8519. * <li>the string "_all" - all columns (i.e. assign a default)</li>
  8520. * </ul>
  8521. */
  8522. "aoColumnDefs": null,
  8523. /**
  8524. * Basically the same as `search`, this parameter defines the individual column
  8525. * filtering state at initialisation time. The array must be of the same size
  8526. * as the number of columns, and each element be an object with the parameters
  8527. * `search` and `escapeRegex` (the latter is optional). 'null' is also
  8528. * accepted and the default will be used.
  8529. */
  8530. "aoSearchCols": [],
  8531. /**
  8532. * Enable or disable automatic column width calculation. This can be disabled
  8533. * as an optimisation (it takes some time to calculate the widths) if the
  8534. * tables widths are passed in using `columns`.
  8535. */
  8536. "bAutoWidth": true,
  8537. /**
  8538. * Deferred rendering can provide DataTables with a huge speed boost when you
  8539. * are using an Ajax or JS data source for the table. This option, when set to
  8540. * true, will cause DataTables to defer the creation of the table elements for
  8541. * each row until they are needed for a draw - saving a significant amount of
  8542. * time.
  8543. */
  8544. "bDeferRender": true,
  8545. /**
  8546. * Replace a DataTable which matches the given selector and replace it with
  8547. * one which has the properties of the new initialisation object passed. If no
  8548. * table matches the selector, then the new DataTable will be constructed as
  8549. * per normal.
  8550. */
  8551. "bDestroy": false,
  8552. /**
  8553. * Enable or disable filtering of data. Filtering in DataTables is "smart" in
  8554. * that it allows the end user to input multiple words (space separated) and
  8555. * will match a row containing those words, even if not in the order that was
  8556. * specified (this allow matching across multiple columns). Note that if you
  8557. * wish to use filtering in DataTables this must remain 'true' - to remove the
  8558. * default filtering input box and retain filtering abilities, please use
  8559. * {@link DataTable.defaults.dom}.
  8560. */
  8561. "bFilter": true,
  8562. /**
  8563. * Used only for compatiblity with DT1
  8564. * @deprecated
  8565. */
  8566. "bInfo": true,
  8567. /**
  8568. * Used only for compatiblity with DT1
  8569. * @deprecated
  8570. */
  8571. "bLengthChange": true,
  8572. /**
  8573. * Enable or disable pagination.
  8574. */
  8575. "bPaginate": true,
  8576. /**
  8577. * Enable or disable the display of a 'processing' indicator when the table is
  8578. * being processed (e.g. a sort). This is particularly useful for tables with
  8579. * large amounts of data where it can take a noticeable amount of time to sort
  8580. * the entries.
  8581. */
  8582. "bProcessing": false,
  8583. /**
  8584. * Retrieve the DataTables object for the given selector. Note that if the
  8585. * table has already been initialised, this parameter will cause DataTables
  8586. * to simply return the object that has already been set up - it will not take
  8587. * account of any changes you might have made to the initialisation object
  8588. * passed to DataTables (setting this parameter to true is an acknowledgement
  8589. * that you understand this). `destroy` can be used to reinitialise a table if
  8590. * you need.
  8591. */
  8592. "bRetrieve": false,
  8593. /**
  8594. * When vertical (y) scrolling is enabled, DataTables will force the height of
  8595. * the table's viewport to the given height at all times (useful for layout).
  8596. * However, this can look odd when filtering data down to a small data set,
  8597. * and the footer is left "floating" further down. This parameter (when
  8598. * enabled) will cause DataTables to collapse the table's viewport down when
  8599. * the result set will fit within the given Y height.
  8600. */
  8601. "bScrollCollapse": false,
  8602. /**
  8603. * Configure DataTables to use server-side processing. Note that the
  8604. * `ajax` parameter must also be given in order to give DataTables a
  8605. * source to obtain the required data for each draw.
  8606. */
  8607. "bServerSide": false,
  8608. /**
  8609. * Enable or disable sorting of columns. Sorting of individual columns can be
  8610. * disabled by the `sortable` option for each column.
  8611. */
  8612. "bSort": true,
  8613. /**
  8614. * Enable or display DataTables' ability to sort multiple columns at the
  8615. * same time (activated by shift-click by the user).
  8616. */
  8617. "bSortMulti": true,
  8618. /**
  8619. * Allows control over whether DataTables should use the top (true) unique
  8620. * cell that is found for a single column, or the bottom (false - default).
  8621. * This is useful when using complex headers.
  8622. */
  8623. "bSortCellsTop": null,
  8624. /**
  8625. * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
  8626. * `sorting\_3` to the columns which are currently being sorted on. This is
  8627. * presented as a feature switch as it can increase processing time (while
  8628. * classes are removed and added) so for large data sets you might want to
  8629. * turn this off.
  8630. */
  8631. "bSortClasses": true,
  8632. /**
  8633. * Enable or disable state saving. When enabled HTML5 `localStorage` will be
  8634. * used to save table display information such as pagination information,
  8635. * display length, filtering and sorting. As such when the end user reloads
  8636. * the page the display display will match what thy had previously set up.
  8637. */
  8638. "bStateSave": false,
  8639. /**
  8640. * This function is called when a TR element is created (and all TD child
  8641. * elements have been inserted), or registered if using a DOM source, allowing
  8642. * manipulation of the TR element (adding classes etc).
  8643. */
  8644. "fnCreatedRow": null,
  8645. /**
  8646. * This function is called on every 'draw' event, and allows you to
  8647. * dynamically modify any aspect you want about the created DOM.
  8648. */
  8649. "fnDrawCallback": null,
  8650. /**
  8651. * Identical to fnHeaderCallback() but for the table footer this function
  8652. * allows you to modify the table footer on every 'draw' event.
  8653. */
  8654. "fnFooterCallback": null,
  8655. /**
  8656. * When rendering large numbers in the information element for the table
  8657. * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers
  8658. * to have a comma separator for the 'thousands' units (e.g. 1 million is
  8659. * rendered as "1,000,000") to help readability for the end user. This
  8660. * function will override the default method DataTables uses.
  8661. */
  8662. "fnFormatNumber": function ( toFormat ) {
  8663. return toFormat.toString().replace(
  8664. /\B(?=(\d{3})+(?!\d))/g,
  8665. this.oLanguage.sThousands
  8666. );
  8667. },
  8668. /**
  8669. * This function is called on every 'draw' event, and allows you to
  8670. * dynamically modify the header row. This can be used to calculate and
  8671. * display useful information about the table.
  8672. */
  8673. "fnHeaderCallback": null,
  8674. /**
  8675. * The information element can be used to convey information about the current
  8676. * state of the table. Although the internationalisation options presented by
  8677. * DataTables are quite capable of dealing with most customisations, there may
  8678. * be times where you wish to customise the string further. This callback
  8679. * allows you to do exactly that.
  8680. */
  8681. "fnInfoCallback": null,
  8682. /**
  8683. * Called when the table has been initialised. Normally DataTables will
  8684. * initialise sequentially and there will be no need for this function,
  8685. * however, this does not hold true when using external language information
  8686. * since that is obtained using an async XHR call.
  8687. */
  8688. "fnInitComplete": null,
  8689. /**
  8690. * Called at the very start of each table draw and can be used to cancel the
  8691. * draw by returning false, any other return (including undefined) results in
  8692. * the full draw occurring).
  8693. */
  8694. "fnPreDrawCallback": null,
  8695. /**
  8696. * This function allows you to 'post process' each row after it have been
  8697. * generated for each table draw, but before it is rendered on screen. This
  8698. * function might be used for setting the row class name etc.
  8699. */
  8700. "fnRowCallback": null,
  8701. /**
  8702. * Load the table state. With this function you can define from where, and how, the
  8703. * state of a table is loaded. By default DataTables will load from `localStorage`
  8704. * but you might wish to use a server-side database or cookies.
  8705. */
  8706. "fnStateLoadCallback": function ( settings ) {
  8707. try {
  8708. return JSON.parse(
  8709. (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
  8710. 'DataTables_'+settings.sInstance+'_'+location.pathname
  8711. )
  8712. );
  8713. } catch (e) {
  8714. return {};
  8715. }
  8716. },
  8717. /**
  8718. * Callback which allows modification of the saved state prior to loading that state.
  8719. * This callback is called when the table is loading state from the stored data, but
  8720. * prior to the settings object being modified by the saved state. Note that for
  8721. * plug-in authors, you should use the `stateLoadParams` event to load parameters for
  8722. * a plug-in.
  8723. */
  8724. "fnStateLoadParams": null,
  8725. /**
  8726. * Callback that is called when the state has been loaded from the state saving method
  8727. * and the DataTables settings object has been modified as a result of the loaded state.
  8728. */
  8729. "fnStateLoaded": null,
  8730. /**
  8731. * Save the table state. This function allows you to define where and how the state
  8732. * information for the table is stored By default DataTables will use `localStorage`
  8733. * but you might wish to use a server-side database or cookies.
  8734. */
  8735. "fnStateSaveCallback": function ( settings, data ) {
  8736. try {
  8737. (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
  8738. 'DataTables_'+settings.sInstance+'_'+location.pathname,
  8739. JSON.stringify( data )
  8740. );
  8741. } catch (e) {
  8742. // noop
  8743. }
  8744. },
  8745. /**
  8746. * Callback which allows modification of the state to be saved. Called when the table
  8747. * has changed state a new state save is required. This method allows modification of
  8748. * the state saving object prior to actually doing the save, including addition or
  8749. * other state properties or modification. Note that for plug-in authors, you should
  8750. * use the `stateSaveParams` event to save parameters for a plug-in.
  8751. */
  8752. "fnStateSaveParams": null,
  8753. /**
  8754. * Duration for which the saved state information is considered valid. After this period
  8755. * has elapsed the state will be returned to the default.
  8756. * Value is given in seconds.
  8757. */
  8758. "iStateDuration": 7200,
  8759. /**
  8760. * Number of rows to display on a single page when using pagination. If
  8761. * feature enabled (`lengthChange`) then the end user will be able to override
  8762. * this to a custom setting using a pop-up menu.
  8763. */
  8764. "iDisplayLength": 10,
  8765. /**
  8766. * Define the starting point for data display when using DataTables with
  8767. * pagination. Note that this parameter is the number of records, rather than
  8768. * the page number, so if you have 10 records per page and want to start on
  8769. * the third page, it should be "20".
  8770. */
  8771. "iDisplayStart": 0,
  8772. /**
  8773. * By default DataTables allows keyboard navigation of the table (sorting, paging,
  8774. * and filtering) by adding a `tabindex` attribute to the required elements. This
  8775. * allows you to tab through the controls and press the enter key to activate them.
  8776. * The tabindex is default 0, meaning that the tab follows the flow of the document.
  8777. * You can overrule this using this parameter if you wish. Use a value of -1 to
  8778. * disable built-in keyboard navigation.
  8779. */
  8780. "iTabIndex": 0,
  8781. /**
  8782. * Classes that DataTables assigns to the various components and features
  8783. * that it adds to the HTML table. This allows classes to be configured
  8784. * during initialisation in addition to through the static
  8785. * {@link DataTable.ext.oStdClasses} object).
  8786. */
  8787. "oClasses": {},
  8788. /**
  8789. * All strings that DataTables uses in the user interface that it creates
  8790. * are defined in this object, allowing you to modified them individually or
  8791. * completely replace them all as required.
  8792. */
  8793. "oLanguage": {
  8794. /**
  8795. * Strings that are used for WAI-ARIA labels and controls only (these are not
  8796. * actually visible on the page, but will be read by screenreaders, and thus
  8797. * must be internationalised as well).
  8798. */
  8799. "oAria": {
  8800. /**
  8801. * ARIA label that is added to the table headers when the column may be sorted
  8802. */
  8803. "orderable": ": Activate to sort",
  8804. /**
  8805. * ARIA label that is added to the table headers when the column is currently being sorted
  8806. */
  8807. "orderableReverse": ": Activate to invert sorting",
  8808. /**
  8809. * ARIA label that is added to the table headers when the column is currently being
  8810. * sorted and next step is to remove sorting
  8811. */
  8812. "orderableRemove": ": Activate to remove sorting",
  8813. paginate: {
  8814. first: 'First',
  8815. last: 'Last',
  8816. next: 'Next',
  8817. previous: 'Previous'
  8818. }
  8819. },
  8820. /**
  8821. * Pagination string used by DataTables for the built-in pagination
  8822. * control types.
  8823. */
  8824. "oPaginate": {
  8825. /**
  8826. * Label and character for first page button («)
  8827. */
  8828. "sFirst": "\u00AB",
  8829. /**
  8830. * Last page button (»)
  8831. */
  8832. "sLast": "\u00BB",
  8833. /**
  8834. * Next page button (›)
  8835. */
  8836. "sNext": "\u203A",
  8837. /**
  8838. * Previous page button (‹)
  8839. */
  8840. "sPrevious": "\u2039",
  8841. },
  8842. /**
  8843. * Plural object for the data type the table is showing
  8844. */
  8845. entries: {
  8846. _: "entries",
  8847. 1: "entry"
  8848. },
  8849. /**
  8850. * This string is shown in preference to `zeroRecords` when the table is
  8851. * empty of data (regardless of filtering). Note that this is an optional
  8852. * parameter - if it is not given, the value of `zeroRecords` will be used
  8853. * instead (either the default or given value).
  8854. */
  8855. "sEmptyTable": "No data available in table",
  8856. /**
  8857. * This string gives information to the end user about the information
  8858. * that is current on display on the page. The following tokens can be
  8859. * used in the string and will be dynamically replaced as the table
  8860. * display updates. This tokens can be placed anywhere in the string, or
  8861. * removed as needed by the language requires:
  8862. *
  8863. * * `\_START\_` - Display index of the first record on the current page
  8864. * * `\_END\_` - Display index of the last record on the current page
  8865. * * `\_TOTAL\_` - Number of records in the table after filtering
  8866. * * `\_MAX\_` - Number of records in the table without filtering
  8867. * * `\_PAGE\_` - Current page number
  8868. * * `\_PAGES\_` - Total number of pages of data in the table
  8869. */
  8870. "sInfo": "Showing _START_ to _END_ of _TOTAL_ _ENTRIES-TOTAL_",
  8871. /**
  8872. * Display information string for when the table is empty. Typically the
  8873. * format of this string should match `info`.
  8874. */
  8875. "sInfoEmpty": "Showing 0 to 0 of 0 _ENTRIES-TOTAL_",
  8876. /**
  8877. * When a user filters the information in a table, this string is appended
  8878. * to the information (`info`) to give an idea of how strong the filtering
  8879. * is. The variable _MAX_ is dynamically updated.
  8880. */
  8881. "sInfoFiltered": "(filtered from _MAX_ total _ENTRIES-MAX_)",
  8882. /**
  8883. * If can be useful to append extra information to the info string at times,
  8884. * and this variable does exactly that. This information will be appended to
  8885. * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are
  8886. * being used) at all times.
  8887. */
  8888. "sInfoPostFix": "",
  8889. /**
  8890. * This decimal place operator is a little different from the other
  8891. * language options since DataTables doesn't output floating point
  8892. * numbers, so it won't ever use this for display of a number. Rather,
  8893. * what this parameter does is modify the sort methods of the table so
  8894. * that numbers which are in a format which has a character other than
  8895. * a period (`.`) as a decimal place will be sorted numerically.
  8896. *
  8897. * Note that numbers with different decimal places cannot be shown in
  8898. * the same table and still be sortable, the table must be consistent.
  8899. * However, multiple different tables on the page can use different
  8900. * decimal place characters.
  8901. */
  8902. "sDecimal": "",
  8903. /**
  8904. * DataTables has a build in number formatter (`formatNumber`) which is
  8905. * used to format large numbers that are used in the table information.
  8906. * By default a comma is used, but this can be trivially changed to any
  8907. * character you wish with this parameter.
  8908. */
  8909. "sThousands": ",",
  8910. /**
  8911. * Detail the action that will be taken when the drop down menu for the
  8912. * pagination length option is changed. The '_MENU_' variable is replaced
  8913. * with a default select list of 10, 25, 50 and 100, and can be replaced
  8914. * with a custom select box if required.
  8915. */
  8916. "sLengthMenu": "_MENU_ _ENTRIES_ per page",
  8917. /**
  8918. * When using Ajax sourced data and during the first draw when DataTables is
  8919. * gathering the data, this message is shown in an empty row in the table to
  8920. * indicate to the end user the the data is being loaded. Note that this
  8921. * parameter is not used when loading data by server-side processing, just
  8922. * Ajax sourced data with client-side processing.
  8923. */
  8924. "sLoadingRecords": "Loading...",
  8925. /**
  8926. * Text which is displayed when the table is processing a user action
  8927. * (usually a sort command or similar).
  8928. */
  8929. "sProcessing": "",
  8930. /**
  8931. * Details the actions that will be taken when the user types into the
  8932. * filtering input text box. The variable "_INPUT_", if used in the string,
  8933. * is replaced with the HTML text box for the filtering input allowing
  8934. * control over where it appears in the string. If "_INPUT_" is not given
  8935. * then the input box is appended to the string automatically.
  8936. */
  8937. "sSearch": "Search:",
  8938. /**
  8939. * Assign a `placeholder` attribute to the search `input` element
  8940. * @type string
  8941. * @default
  8942. *
  8943. * @dtopt Language
  8944. * @name DataTable.defaults.language.searchPlaceholder
  8945. */
  8946. "sSearchPlaceholder": "",
  8947. /**
  8948. * All of the language information can be stored in a file on the
  8949. * server-side, which DataTables will look up if this parameter is passed.
  8950. * It must store the URL of the language file, which is in a JSON format,
  8951. * and the object has the same properties as the oLanguage object in the
  8952. * initialiser object (i.e. the above parameters). Please refer to one of
  8953. * the example language files to see how this works in action.
  8954. */
  8955. "sUrl": "",
  8956. /**
  8957. * Text shown inside the table records when the is no information to be
  8958. * displayed after filtering. `emptyTable` is shown when there is simply no
  8959. * information in the table at all (regardless of filtering).
  8960. */
  8961. "sZeroRecords": "No matching records found"
  8962. },
  8963. /**
  8964. * This parameter allows you to have define the global filtering state at
  8965. * initialisation time. As an object the `search` parameter must be
  8966. * defined, but all other parameters are optional. When `regex` is true,
  8967. * the search string will be treated as a regular expression, when false
  8968. * (default) it will be treated as a straight string. When `smart`
  8969. * DataTables will use it's smart filtering methods (to word match at
  8970. * any point in the data), when false this will not be done.
  8971. */
  8972. "oSearch": $.extend( {}, DataTable.models.oSearch ),
  8973. /**
  8974. * Table and control layout. This replaces the legacy `dom` option.
  8975. */
  8976. layout: {
  8977. topStart: 'pageLength',
  8978. topEnd: 'search',
  8979. bottomStart: 'info',
  8980. bottomEnd: 'paging'
  8981. },
  8982. /**
  8983. * Legacy DOM layout option
  8984. */
  8985. "sDom": null,
  8986. /**
  8987. * Search delay option. This will throttle full table searches that use the
  8988. * DataTables provided search input element (it does not effect calls to
  8989. * `dt-api search()`, providing a delay before the search is made.
  8990. */
  8991. "searchDelay": null,
  8992. /**
  8993. * DataTables features six different built-in options for the buttons to
  8994. * display for pagination control:
  8995. *
  8996. * * `numbers` - Page number buttons only
  8997. * * `simple` - 'Previous' and 'Next' buttons only
  8998. * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
  8999. * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons
  9000. * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
  9001. * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers
  9002. */
  9003. "sPaginationType": "full_numbers",
  9004. /**
  9005. * Enable horizontal scrolling. When a table is too wide to fit into a
  9006. * certain layout, or you have a large number of columns in the table, you
  9007. * can enable x-scrolling to show the table in a viewport, which can be
  9008. * scrolled. This property can be `true` which will allow the table to
  9009. * scroll horizontally when needed, or any CSS unit, or a number (in which
  9010. * case it will be treated as a pixel measurement). Setting as simply `true`
  9011. * is recommended.
  9012. */
  9013. "sScrollX": "",
  9014. /**
  9015. * This property can be used to force a DataTable to use more width than it
  9016. * might otherwise do when x-scrolling is enabled. For example if you have a
  9017. * table which requires to be well spaced, this parameter is useful for
  9018. * "over-sizing" the table, and thus forcing scrolling. This property can by
  9019. * any CSS unit, or a number (in which case it will be treated as a pixel
  9020. * measurement).
  9021. */
  9022. "sScrollXInner": "",
  9023. /**
  9024. * Enable vertical scrolling. Vertical scrolling will constrain the DataTable
  9025. * to the given height, and enable scrolling for any data which overflows the
  9026. * current viewport. This can be used as an alternative to paging to display
  9027. * a lot of data in a small area (although paging and scrolling can both be
  9028. * enabled at the same time). This property can be any CSS unit, or a number
  9029. * (in which case it will be treated as a pixel measurement).
  9030. */
  9031. "sScrollY": "",
  9032. /**
  9033. * __Deprecated__ The functionality provided by this parameter has now been
  9034. * superseded by that provided through `ajax`, which should be used instead.
  9035. *
  9036. * Set the HTTP method that is used to make the Ajax call for server-side
  9037. * processing or Ajax sourced data.
  9038. */
  9039. "sServerMethod": "GET",
  9040. /**
  9041. * DataTables makes use of renderers when displaying HTML elements for
  9042. * a table. These renderers can be added or modified by plug-ins to
  9043. * generate suitable mark-up for a site. For example the Bootstrap
  9044. * integration plug-in for DataTables uses a paging button renderer to
  9045. * display pagination buttons in the mark-up required by Bootstrap.
  9046. *
  9047. * For further information about the renderers available see
  9048. * DataTable.ext.renderer
  9049. */
  9050. "renderer": null,
  9051. /**
  9052. * Set the data property name that DataTables should use to get a row's id
  9053. * to set as the `id` property in the node.
  9054. */
  9055. "rowId": "DT_RowId",
  9056. /**
  9057. * Caption value
  9058. */
  9059. "caption": null
  9060. };
  9061. _fnHungarianMap( DataTable.defaults );
  9062. /*
  9063. * Developer note - See note in model.defaults.js about the use of Hungarian
  9064. * notation and camel case.
  9065. */
  9066. /**
  9067. * Column options that can be given to DataTables at initialisation time.
  9068. * @namespace
  9069. */
  9070. DataTable.defaults.column = {
  9071. /**
  9072. * Define which column(s) an order will occur on for this column. This
  9073. * allows a column's ordering to take multiple columns into account when
  9074. * doing a sort or use the data from a different column. For example first
  9075. * name / last name columns make sense to do a multi-column sort over the
  9076. * two columns.
  9077. */
  9078. "aDataSort": null,
  9079. "iDataSort": -1,
  9080. ariaTitle: '',
  9081. /**
  9082. * You can control the default ordering direction, and even alter the
  9083. * behaviour of the sort handler (i.e. only allow ascending ordering etc)
  9084. * using this parameter.
  9085. */
  9086. "asSorting": [ 'asc', 'desc', '' ],
  9087. /**
  9088. * Enable or disable filtering on the data in this column.
  9089. */
  9090. "bSearchable": true,
  9091. /**
  9092. * Enable or disable ordering on this column.
  9093. */
  9094. "bSortable": true,
  9095. /**
  9096. * Enable or disable the display of this column.
  9097. */
  9098. "bVisible": true,
  9099. /**
  9100. * Developer definable function that is called whenever a cell is created (Ajax source,
  9101. * etc) or processed for input (DOM source). This can be used as a compliment to mRender
  9102. * allowing you to modify the DOM element (add background colour for example) when the
  9103. * element is available.
  9104. */
  9105. "fnCreatedCell": null,
  9106. /**
  9107. * This property can be used to read data from any data source property,
  9108. * including deeply nested objects / properties. `data` can be given in a
  9109. * number of different ways which effect its behaviour:
  9110. *
  9111. * * `integer` - treated as an array index for the data source. This is the
  9112. * default that DataTables uses (incrementally increased for each column).
  9113. * * `string` - read an object property from the data source. There are
  9114. * three 'special' options that can be used in the string to alter how
  9115. * DataTables reads the data from the source object:
  9116. * * `.` - Dotted Javascript notation. Just as you use a `.` in
  9117. * Javascript to read from nested objects, so to can the options
  9118. * specified in `data`. For example: `browser.version` or
  9119. * `browser.name`. If your object parameter name contains a period, use
  9120. * `\\` to escape it - i.e. `first\\.name`.
  9121. * * `[]` - Array notation. DataTables can automatically combine data
  9122. * from and array source, joining the data with the characters provided
  9123. * between the two brackets. For example: `name[, ]` would provide a
  9124. * comma-space separated list from the source array. If no characters
  9125. * are provided between the brackets, the original array source is
  9126. * returned.
  9127. * * `()` - Function notation. Adding `()` to the end of a parameter will
  9128. * execute a function of the name given. For example: `browser()` for a
  9129. * simple function on the data source, `browser.version()` for a
  9130. * function in a nested property or even `browser().version` to get an
  9131. * object property if the function called returns an object. Note that
  9132. * function notation is recommended for use in `render` rather than
  9133. * `data` as it is much simpler to use as a renderer.
  9134. * * `null` - use the original data source for the row rather than plucking
  9135. * data directly from it. This action has effects on two other
  9136. * initialisation options:
  9137. * * `defaultContent` - When null is given as the `data` option and
  9138. * `defaultContent` is specified for the column, the value defined by
  9139. * `defaultContent` will be used for the cell.
  9140. * * `render` - When null is used for the `data` option and the `render`
  9141. * option is specified for the column, the whole data source for the
  9142. * row is used for the renderer.
  9143. * * `function` - the function given will be executed whenever DataTables
  9144. * needs to set or get the data for a cell in the column. The function
  9145. * takes three parameters:
  9146. * * Parameters:
  9147. * * `{array|object}` The data source for the row
  9148. * * `{string}` The type call data requested - this will be 'set' when
  9149. * setting data or 'filter', 'display', 'type', 'sort' or undefined
  9150. * when gathering data. Note that when `undefined` is given for the
  9151. * type DataTables expects to get the raw data for the object back<
  9152. * * `{*}` Data to set when the second parameter is 'set'.
  9153. * * Return:
  9154. * * The return value from the function is not required when 'set' is
  9155. * the type of call, but otherwise the return is what will be used
  9156. * for the data requested.
  9157. *
  9158. * Note that `data` is a getter and setter option. If you just require
  9159. * formatting of data for output, you will likely want to use `render` which
  9160. * is simply a getter and thus simpler to use.
  9161. *
  9162. * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The
  9163. * name change reflects the flexibility of this property and is consistent
  9164. * with the naming of mRender. If 'mDataProp' is given, then it will still
  9165. * be used by DataTables, as it automatically maps the old name to the new
  9166. * if required.
  9167. */
  9168. "mData": null,
  9169. /**
  9170. * This property is the rendering partner to `data` and it is suggested that
  9171. * when you want to manipulate data for display (including filtering,
  9172. * sorting etc) without altering the underlying data for the table, use this
  9173. * property. `render` can be considered to be the the read only companion to
  9174. * `data` which is read / write (then as such more complex). Like `data`
  9175. * this option can be given in a number of different ways to effect its
  9176. * behaviour:
  9177. *
  9178. * * `integer` - treated as an array index for the data source. This is the
  9179. * default that DataTables uses (incrementally increased for each column).
  9180. * * `string` - read an object property from the data source. There are
  9181. * three 'special' options that can be used in the string to alter how
  9182. * DataTables reads the data from the source object:
  9183. * * `.` - Dotted Javascript notation. Just as you use a `.` in
  9184. * Javascript to read from nested objects, so to can the options
  9185. * specified in `data`. For example: `browser.version` or
  9186. * `browser.name`. If your object parameter name contains a period, use
  9187. * `\\` to escape it - i.e. `first\\.name`.
  9188. * * `[]` - Array notation. DataTables can automatically combine data
  9189. * from and array source, joining the data with the characters provided
  9190. * between the two brackets. For example: `name[, ]` would provide a
  9191. * comma-space separated list from the source array. If no characters
  9192. * are provided between the brackets, the original array source is
  9193. * returned.
  9194. * * `()` - Function notation. Adding `()` to the end of a parameter will
  9195. * execute a function of the name given. For example: `browser()` for a
  9196. * simple function on the data source, `browser.version()` for a
  9197. * function in a nested property or even `browser().version` to get an
  9198. * object property if the function called returns an object.
  9199. * * `object` - use different data for the different data types requested by
  9200. * DataTables ('filter', 'display', 'type' or 'sort'). The property names
  9201. * of the object is the data type the property refers to and the value can
  9202. * defined using an integer, string or function using the same rules as
  9203. * `render` normally does. Note that an `_` option _must_ be specified.
  9204. * This is the default value to use if you haven't specified a value for
  9205. * the data type requested by DataTables.
  9206. * * `function` - the function given will be executed whenever DataTables
  9207. * needs to set or get the data for a cell in the column. The function
  9208. * takes three parameters:
  9209. * * Parameters:
  9210. * * {array|object} The data source for the row (based on `data`)
  9211. * * {string} The type call data requested - this will be 'filter',
  9212. * 'display', 'type' or 'sort'.
  9213. * * {array|object} The full data source for the row (not based on
  9214. * `data`)
  9215. * * Return:
  9216. * * The return value from the function is what will be used for the
  9217. * data requested.
  9218. */
  9219. "mRender": null,
  9220. /**
  9221. * Change the cell type created for the column - either TD cells or TH cells. This
  9222. * can be useful as TH cells have semantic meaning in the table body, allowing them
  9223. * to act as a header for a row (you may wish to add scope='row' to the TH elements).
  9224. */
  9225. "sCellType": "td",
  9226. /**
  9227. * Class to give to each cell in this column.
  9228. */
  9229. "sClass": "",
  9230. /**
  9231. * When DataTables calculates the column widths to assign to each column,
  9232. * it finds the longest string in each column and then constructs a
  9233. * temporary table and reads the widths from that. The problem with this
  9234. * is that "mmm" is much wider then "iiii", but the latter is a longer
  9235. * string - thus the calculation can go wrong (doing it properly and putting
  9236. * it into an DOM object and measuring that is horribly(!) slow). Thus as
  9237. * a "work around" we provide this option. It will append its value to the
  9238. * text that is found to be the longest string for the column - i.e. padding.
  9239. * Generally you shouldn't need this!
  9240. */
  9241. "sContentPadding": "",
  9242. /**
  9243. * Allows a default value to be given for a column's data, and will be used
  9244. * whenever a null data source is encountered (this can be because `data`
  9245. * is set to null, or because the data source itself is null).
  9246. */
  9247. "sDefaultContent": null,
  9248. /**
  9249. * This parameter is only used in DataTables' server-side processing. It can
  9250. * be exceptionally useful to know what columns are being displayed on the
  9251. * client side, and to map these to database fields. When defined, the names
  9252. * also allow DataTables to reorder information from the server if it comes
  9253. * back in an unexpected order (i.e. if you switch your columns around on the
  9254. * client-side, your server-side code does not also need updating).
  9255. */
  9256. "sName": "",
  9257. /**
  9258. * Defines a data source type for the ordering which can be used to read
  9259. * real-time information from the table (updating the internally cached
  9260. * version) prior to ordering. This allows ordering to occur on user
  9261. * editable elements such as form inputs.
  9262. */
  9263. "sSortDataType": "std",
  9264. /**
  9265. * The title of this column.
  9266. */
  9267. "sTitle": null,
  9268. /**
  9269. * The type allows you to specify how the data for this column will be
  9270. * ordered. Four types (string, numeric, date and html (which will strip
  9271. * HTML tags before ordering)) are currently available. Note that only date
  9272. * formats understood by Javascript's Date() object will be accepted as type
  9273. * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string',
  9274. * 'numeric', 'date' or 'html' (by default). Further types can be adding
  9275. * through plug-ins.
  9276. */
  9277. "sType": null,
  9278. /**
  9279. * Defining the width of the column, this parameter may take any CSS value
  9280. * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not
  9281. * been given a specific width through this interface ensuring that the table
  9282. * remains readable.
  9283. */
  9284. "sWidth": null
  9285. };
  9286. _fnHungarianMap( DataTable.defaults.column );
  9287. /**
  9288. * DataTables settings object - this holds all the information needed for a
  9289. * given table, including configuration, data and current application of the
  9290. * table options. DataTables does not have a single instance for each DataTable
  9291. * with the settings attached to that instance, but rather instances of the
  9292. * DataTable "class" are created on-the-fly as needed (typically by a
  9293. * $().dataTable() call) and the settings object is then applied to that
  9294. * instance.
  9295. *
  9296. * Note that this object is related to {@link DataTable.defaults} but this
  9297. * one is the internal data store for DataTables's cache of columns. It should
  9298. * NOT be manipulated outside of DataTables. Any configuration should be done
  9299. * through the initialisation options.
  9300. */
  9301. DataTable.models.oSettings = {
  9302. /**
  9303. * Primary features of DataTables and their enablement state.
  9304. */
  9305. "oFeatures": {
  9306. /**
  9307. * Flag to say if DataTables should automatically try to calculate the
  9308. * optimum table and columns widths (true) or not (false).
  9309. * Note that this parameter will be set by the initialisation routine. To
  9310. * set a default use {@link DataTable.defaults}.
  9311. */
  9312. "bAutoWidth": null,
  9313. /**
  9314. * Delay the creation of TR and TD elements until they are actually
  9315. * needed by a driven page draw. This can give a significant speed
  9316. * increase for Ajax source and Javascript source data, but makes no
  9317. * difference at all for DOM and server-side processing tables.
  9318. * Note that this parameter will be set by the initialisation routine. To
  9319. * set a default use {@link DataTable.defaults}.
  9320. */
  9321. "bDeferRender": null,
  9322. /**
  9323. * Enable filtering on the table or not. Note that if this is disabled
  9324. * then there is no filtering at all on the table, including fnFilter.
  9325. * To just remove the filtering input use sDom and remove the 'f' option.
  9326. * Note that this parameter will be set by the initialisation routine. To
  9327. * set a default use {@link DataTable.defaults}.
  9328. */
  9329. "bFilter": null,
  9330. /**
  9331. * Used only for compatiblity with DT1
  9332. * @deprecated
  9333. */
  9334. "bInfo": true,
  9335. /**
  9336. * Used only for compatiblity with DT1
  9337. * @deprecated
  9338. */
  9339. "bLengthChange": true,
  9340. /**
  9341. * Pagination enabled or not. Note that if this is disabled then length
  9342. * changing must also be disabled.
  9343. * Note that this parameter will be set by the initialisation routine. To
  9344. * set a default use {@link DataTable.defaults}.
  9345. */
  9346. "bPaginate": null,
  9347. /**
  9348. * Processing indicator enable flag whenever DataTables is enacting a
  9349. * user request - typically an Ajax request for server-side processing.
  9350. * Note that this parameter will be set by the initialisation routine. To
  9351. * set a default use {@link DataTable.defaults}.
  9352. */
  9353. "bProcessing": null,
  9354. /**
  9355. * Server-side processing enabled flag - when enabled DataTables will
  9356. * get all data from the server for every draw - there is no filtering,
  9357. * sorting or paging done on the client-side.
  9358. * Note that this parameter will be set by the initialisation routine. To
  9359. * set a default use {@link DataTable.defaults}.
  9360. */
  9361. "bServerSide": null,
  9362. /**
  9363. * Sorting enablement flag.
  9364. * Note that this parameter will be set by the initialisation routine. To
  9365. * set a default use {@link DataTable.defaults}.
  9366. */
  9367. "bSort": null,
  9368. /**
  9369. * Multi-column sorting
  9370. * Note that this parameter will be set by the initialisation routine. To
  9371. * set a default use {@link DataTable.defaults}.
  9372. */
  9373. "bSortMulti": null,
  9374. /**
  9375. * Apply a class to the columns which are being sorted to provide a
  9376. * visual highlight or not. This can slow things down when enabled since
  9377. * there is a lot of DOM interaction.
  9378. * Note that this parameter will be set by the initialisation routine. To
  9379. * set a default use {@link DataTable.defaults}.
  9380. */
  9381. "bSortClasses": null,
  9382. /**
  9383. * State saving enablement flag.
  9384. * Note that this parameter will be set by the initialisation routine. To
  9385. * set a default use {@link DataTable.defaults}.
  9386. */
  9387. "bStateSave": null
  9388. },
  9389. /**
  9390. * Scrolling settings for a table.
  9391. */
  9392. "oScroll": {
  9393. /**
  9394. * When the table is shorter in height than sScrollY, collapse the
  9395. * table container down to the height of the table (when true).
  9396. * Note that this parameter will be set by the initialisation routine. To
  9397. * set a default use {@link DataTable.defaults}.
  9398. */
  9399. "bCollapse": null,
  9400. /**
  9401. * Width of the scrollbar for the web-browser's platform. Calculated
  9402. * during table initialisation.
  9403. */
  9404. "iBarWidth": 0,
  9405. /**
  9406. * Viewport width for horizontal scrolling. Horizontal scrolling is
  9407. * disabled if an empty string.
  9408. * Note that this parameter will be set by the initialisation routine. To
  9409. * set a default use {@link DataTable.defaults}.
  9410. */
  9411. "sX": null,
  9412. /**
  9413. * Width to expand the table to when using x-scrolling. Typically you
  9414. * should not need to use this.
  9415. * Note that this parameter will be set by the initialisation routine. To
  9416. * set a default use {@link DataTable.defaults}.
  9417. * @deprecated
  9418. */
  9419. "sXInner": null,
  9420. /**
  9421. * Viewport height for vertical scrolling. Vertical scrolling is disabled
  9422. * if an empty string.
  9423. * Note that this parameter will be set by the initialisation routine. To
  9424. * set a default use {@link DataTable.defaults}.
  9425. */
  9426. "sY": null
  9427. },
  9428. /**
  9429. * Language information for the table.
  9430. */
  9431. "oLanguage": {
  9432. /**
  9433. * Information callback function. See
  9434. * {@link DataTable.defaults.fnInfoCallback}
  9435. */
  9436. "fnInfoCallback": null
  9437. },
  9438. /**
  9439. * Browser support parameters
  9440. */
  9441. "oBrowser": {
  9442. /**
  9443. * Determine if the vertical scrollbar is on the right or left of the
  9444. * scrolling container - needed for rtl language layout, although not
  9445. * all browsers move the scrollbar (Safari).
  9446. */
  9447. "bScrollbarLeft": false,
  9448. /**
  9449. * Browser scrollbar width
  9450. */
  9451. "barWidth": 0
  9452. },
  9453. "ajax": null,
  9454. /**
  9455. * Array referencing the nodes which are used for the features. The
  9456. * parameters of this object match what is allowed by sDom - i.e.
  9457. * <ul>
  9458. * <li>'l' - Length changing</li>
  9459. * <li>'f' - Filtering input</li>
  9460. * <li>'t' - The table!</li>
  9461. * <li>'i' - Information</li>
  9462. * <li>'p' - Pagination</li>
  9463. * <li>'r' - pRocessing</li>
  9464. * </ul>
  9465. */
  9466. "aanFeatures": [],
  9467. /**
  9468. * Store data information - see {@link DataTable.models.oRow} for detailed
  9469. * information.
  9470. */
  9471. "aoData": [],
  9472. /**
  9473. * Array of indexes which are in the current display (after filtering etc)
  9474. */
  9475. "aiDisplay": [],
  9476. /**
  9477. * Array of indexes for display - no filtering
  9478. */
  9479. "aiDisplayMaster": [],
  9480. /**
  9481. * Map of row ids to data indexes
  9482. */
  9483. "aIds": {},
  9484. /**
  9485. * Store information about each column that is in use
  9486. */
  9487. "aoColumns": [],
  9488. /**
  9489. * Store information about the table's header
  9490. */
  9491. "aoHeader": [],
  9492. /**
  9493. * Store information about the table's footer
  9494. */
  9495. "aoFooter": [],
  9496. /**
  9497. * Store the applied global search information in case we want to force a
  9498. * research or compare the old search to a new one.
  9499. * Note that this parameter will be set by the initialisation routine. To
  9500. * set a default use {@link DataTable.defaults}.
  9501. */
  9502. "oPreviousSearch": {},
  9503. /**
  9504. * Store for named searches
  9505. */
  9506. searchFixed: {},
  9507. /**
  9508. * Store the applied search for each column - see
  9509. * {@link DataTable.models.oSearch} for the format that is used for the
  9510. * filtering information for each column.
  9511. */
  9512. "aoPreSearchCols": [],
  9513. /**
  9514. * Sorting that is applied to the table. Note that the inner arrays are
  9515. * used in the following manner:
  9516. * <ul>
  9517. * <li>Index 0 - column number</li>
  9518. * <li>Index 1 - current sorting direction</li>
  9519. * </ul>
  9520. * Note that this parameter will be set by the initialisation routine. To
  9521. * set a default use {@link DataTable.defaults}.
  9522. */
  9523. "aaSorting": null,
  9524. /**
  9525. * Sorting that is always applied to the table (i.e. prefixed in front of
  9526. * aaSorting).
  9527. * Note that this parameter will be set by the initialisation routine. To
  9528. * set a default use {@link DataTable.defaults}.
  9529. */
  9530. "aaSortingFixed": [],
  9531. /**
  9532. * If restoring a table - we should restore its width
  9533. */
  9534. "sDestroyWidth": 0,
  9535. /**
  9536. * Callback functions array for every time a row is inserted (i.e. on a draw).
  9537. */
  9538. "aoRowCallback": [],
  9539. /**
  9540. * Callback functions for the header on each draw.
  9541. */
  9542. "aoHeaderCallback": [],
  9543. /**
  9544. * Callback function for the footer on each draw.
  9545. */
  9546. "aoFooterCallback": [],
  9547. /**
  9548. * Array of callback functions for draw callback functions
  9549. */
  9550. "aoDrawCallback": [],
  9551. /**
  9552. * Array of callback functions for row created function
  9553. */
  9554. "aoRowCreatedCallback": [],
  9555. /**
  9556. * Callback functions for just before the table is redrawn. A return of
  9557. * false will be used to cancel the draw.
  9558. */
  9559. "aoPreDrawCallback": [],
  9560. /**
  9561. * Callback functions for when the table has been initialised.
  9562. */
  9563. "aoInitComplete": [],
  9564. /**
  9565. * Callbacks for modifying the settings to be stored for state saving, prior to
  9566. * saving state.
  9567. */
  9568. "aoStateSaveParams": [],
  9569. /**
  9570. * Callbacks for modifying the settings that have been stored for state saving
  9571. * prior to using the stored values to restore the state.
  9572. */
  9573. "aoStateLoadParams": [],
  9574. /**
  9575. * Callbacks for operating on the settings object once the saved state has been
  9576. * loaded
  9577. */
  9578. "aoStateLoaded": [],
  9579. /**
  9580. * Cache the table ID for quick access
  9581. */
  9582. "sTableId": "",
  9583. /**
  9584. * The TABLE node for the main table
  9585. */
  9586. "nTable": null,
  9587. /**
  9588. * Permanent ref to the thead element
  9589. */
  9590. "nTHead": null,
  9591. /**
  9592. * Permanent ref to the tfoot element - if it exists
  9593. */
  9594. "nTFoot": null,
  9595. /**
  9596. * Permanent ref to the tbody element
  9597. */
  9598. "nTBody": null,
  9599. /**
  9600. * Cache the wrapper node (contains all DataTables controlled elements)
  9601. */
  9602. "nTableWrapper": null,
  9603. /**
  9604. * Indicate if all required information has been read in
  9605. */
  9606. "bInitialised": false,
  9607. /**
  9608. * Information about open rows. Each object in the array has the parameters
  9609. * 'nTr' and 'nParent'
  9610. */
  9611. "aoOpenRows": [],
  9612. /**
  9613. * Dictate the positioning of DataTables' control elements - see
  9614. * {@link DataTable.model.oInit.sDom}.
  9615. * Note that this parameter will be set by the initialisation routine. To
  9616. * set a default use {@link DataTable.defaults}.
  9617. */
  9618. "sDom": null,
  9619. /**
  9620. * Search delay (in mS)
  9621. */
  9622. "searchDelay": null,
  9623. /**
  9624. * Which type of pagination should be used.
  9625. * Note that this parameter will be set by the initialisation routine. To
  9626. * set a default use {@link DataTable.defaults}.
  9627. */
  9628. "sPaginationType": "two_button",
  9629. /**
  9630. * Number of paging controls on the page. Only used for backwards compatibility
  9631. */
  9632. pagingControls: 0,
  9633. /**
  9634. * The state duration (for `stateSave`) in seconds.
  9635. * Note that this parameter will be set by the initialisation routine. To
  9636. * set a default use {@link DataTable.defaults}.
  9637. */
  9638. "iStateDuration": 0,
  9639. /**
  9640. * Array of callback functions for state saving. Each array element is an
  9641. * object with the following parameters:
  9642. * <ul>
  9643. * <li>function:fn - function to call. Takes two parameters, oSettings
  9644. * and the JSON string to save that has been thus far created. Returns
  9645. * a JSON string to be inserted into a json object
  9646. * (i.e. '"param": [ 0, 1, 2]')</li>
  9647. * <li>string:sName - name of callback</li>
  9648. * </ul>
  9649. */
  9650. "aoStateSave": [],
  9651. /**
  9652. * Array of callback functions for state loading. Each array element is an
  9653. * object with the following parameters:
  9654. * <ul>
  9655. * <li>function:fn - function to call. Takes two parameters, oSettings
  9656. * and the object stored. May return false to cancel state loading</li>
  9657. * <li>string:sName - name of callback</li>
  9658. * </ul>
  9659. */
  9660. "aoStateLoad": [],
  9661. /**
  9662. * State that was saved. Useful for back reference
  9663. */
  9664. "oSavedState": null,
  9665. /**
  9666. * State that was loaded. Useful for back reference
  9667. */
  9668. "oLoadedState": null,
  9669. /**
  9670. * Note if draw should be blocked while getting data
  9671. */
  9672. "bAjaxDataGet": true,
  9673. /**
  9674. * The last jQuery XHR object that was used for server-side data gathering.
  9675. * This can be used for working with the XHR information in one of the
  9676. * callbacks
  9677. */
  9678. "jqXHR": null,
  9679. /**
  9680. * JSON returned from the server in the last Ajax request
  9681. */
  9682. "json": undefined,
  9683. /**
  9684. * Data submitted as part of the last Ajax request
  9685. */
  9686. "oAjaxData": undefined,
  9687. /**
  9688. * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
  9689. * required).
  9690. * Note that this parameter will be set by the initialisation routine. To
  9691. * set a default use {@link DataTable.defaults}.
  9692. */
  9693. "sServerMethod": null,
  9694. /**
  9695. * Format numbers for display.
  9696. * Note that this parameter will be set by the initialisation routine. To
  9697. * set a default use {@link DataTable.defaults}.
  9698. */
  9699. "fnFormatNumber": null,
  9700. /**
  9701. * List of options that can be used for the user selectable length menu.
  9702. * Note that this parameter will be set by the initialisation routine. To
  9703. * set a default use {@link DataTable.defaults}.
  9704. */
  9705. "aLengthMenu": null,
  9706. /**
  9707. * Counter for the draws that the table does. Also used as a tracker for
  9708. * server-side processing
  9709. */
  9710. "iDraw": 0,
  9711. /**
  9712. * Indicate if a redraw is being done - useful for Ajax
  9713. */
  9714. "bDrawing": false,
  9715. /**
  9716. * Draw index (iDraw) of the last error when parsing the returned data
  9717. */
  9718. "iDrawError": -1,
  9719. /**
  9720. * Paging display length
  9721. */
  9722. "_iDisplayLength": 10,
  9723. /**
  9724. * Paging start point - aiDisplay index
  9725. */
  9726. "_iDisplayStart": 0,
  9727. /**
  9728. * Server-side processing - number of records in the result set
  9729. * (i.e. before filtering), Use fnRecordsTotal rather than
  9730. * this property to get the value of the number of records, regardless of
  9731. * the server-side processing setting.
  9732. */
  9733. "_iRecordsTotal": 0,
  9734. /**
  9735. * Server-side processing - number of records in the current display set
  9736. * (i.e. after filtering). Use fnRecordsDisplay rather than
  9737. * this property to get the value of the number of records, regardless of
  9738. * the server-side processing setting.
  9739. */
  9740. "_iRecordsDisplay": 0,
  9741. /**
  9742. * The classes to use for the table
  9743. */
  9744. "oClasses": {},
  9745. /**
  9746. * Flag attached to the settings object so you can check in the draw
  9747. * callback if filtering has been done in the draw. Deprecated in favour of
  9748. * events.
  9749. * @deprecated
  9750. */
  9751. "bFiltered": false,
  9752. /**
  9753. * Flag attached to the settings object so you can check in the draw
  9754. * callback if sorting has been done in the draw. Deprecated in favour of
  9755. * events.
  9756. * @deprecated
  9757. */
  9758. "bSorted": false,
  9759. /**
  9760. * Indicate that if multiple rows are in the header and there is more than
  9761. * one unique cell per column, if the top one (true) or bottom one (false)
  9762. * should be used for sorting / title by DataTables.
  9763. * Note that this parameter will be set by the initialisation routine. To
  9764. * set a default use {@link DataTable.defaults}.
  9765. */
  9766. "bSortCellsTop": null,
  9767. /**
  9768. * Initialisation object that is used for the table
  9769. */
  9770. "oInit": null,
  9771. /**
  9772. * Destroy callback functions - for plug-ins to attach themselves to the
  9773. * destroy so they can clean up markup and events.
  9774. */
  9775. "aoDestroyCallback": [],
  9776. /**
  9777. * Get the number of records in the current record set, before filtering
  9778. */
  9779. "fnRecordsTotal": function ()
  9780. {
  9781. return _fnDataSource( this ) == 'ssp' ?
  9782. this._iRecordsTotal * 1 :
  9783. this.aiDisplayMaster.length;
  9784. },
  9785. /**
  9786. * Get the number of records in the current record set, after filtering
  9787. */
  9788. "fnRecordsDisplay": function ()
  9789. {
  9790. return _fnDataSource( this ) == 'ssp' ?
  9791. this._iRecordsDisplay * 1 :
  9792. this.aiDisplay.length;
  9793. },
  9794. /**
  9795. * Get the display end point - aiDisplay index
  9796. */
  9797. "fnDisplayEnd": function ()
  9798. {
  9799. var
  9800. len = this._iDisplayLength,
  9801. start = this._iDisplayStart,
  9802. calc = start + len,
  9803. records = this.aiDisplay.length,
  9804. features = this.oFeatures,
  9805. paginate = features.bPaginate;
  9806. if ( features.bServerSide ) {
  9807. return paginate === false || len === -1 ?
  9808. start + records :
  9809. Math.min( start+len, this._iRecordsDisplay );
  9810. }
  9811. else {
  9812. return ! paginate || calc>records || len===-1 ?
  9813. records :
  9814. calc;
  9815. }
  9816. },
  9817. /**
  9818. * The DataTables object for this table
  9819. */
  9820. "oInstance": null,
  9821. /**
  9822. * Unique identifier for each instance of the DataTables object. If there
  9823. * is an ID on the table node, then it takes that value, otherwise an
  9824. * incrementing internal counter is used.
  9825. */
  9826. "sInstance": null,
  9827. /**
  9828. * tabindex attribute value that is added to DataTables control elements, allowing
  9829. * keyboard navigation of the table and its controls.
  9830. */
  9831. "iTabIndex": 0,
  9832. /**
  9833. * DIV container for the footer scrolling table if scrolling
  9834. */
  9835. "nScrollHead": null,
  9836. /**
  9837. * DIV container for the footer scrolling table if scrolling
  9838. */
  9839. "nScrollFoot": null,
  9840. /**
  9841. * Last applied sort
  9842. */
  9843. "aLastSort": [],
  9844. /**
  9845. * Stored plug-in instances
  9846. */
  9847. "oPlugins": {},
  9848. /**
  9849. * Function used to get a row's id from the row's data
  9850. */
  9851. "rowIdFn": null,
  9852. /**
  9853. * Data location where to store a row's id
  9854. */
  9855. "rowId": null,
  9856. caption: '',
  9857. captionNode: null,
  9858. colgroup: null
  9859. };
  9860. /**
  9861. * Extension object for DataTables that is used to provide all extension
  9862. * options.
  9863. *
  9864. * Note that the `DataTable.ext` object is available through
  9865. * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is
  9866. * also aliased to `jQuery.fn.dataTableExt` for historic reasons.
  9867. * @namespace
  9868. * @extends DataTable.models.ext
  9869. */
  9870. var extPagination = DataTable.ext.pager;
  9871. // Paging buttons configuration
  9872. $.extend( extPagination, {
  9873. simple: function () {
  9874. return [ 'previous', 'next' ];
  9875. },
  9876. full: function () {
  9877. return [ 'first', 'previous', 'next', 'last' ];
  9878. },
  9879. numbers: function () {
  9880. return [ 'numbers' ];
  9881. },
  9882. simple_numbers: function () {
  9883. return [ 'previous', 'numbers', 'next' ];
  9884. },
  9885. full_numbers: function () {
  9886. return [ 'first', 'previous', 'numbers', 'next', 'last' ];
  9887. },
  9888. first_last: function () {
  9889. return ['first', 'last'];
  9890. },
  9891. first_last_numbers: function () {
  9892. return ['first', 'numbers', 'last'];
  9893. },
  9894. // For testing and plug-ins to use
  9895. _numbers: _pagingNumbers,
  9896. // Number of number buttons - legacy, use `numbers` option for paging feature
  9897. numbers_length: 7
  9898. } );
  9899. $.extend( true, DataTable.ext.renderer, {
  9900. pagingButton: {
  9901. _: function (settings, buttonType, content, active, disabled) {
  9902. var classes = settings.oClasses.paging;
  9903. var btnClasses = [classes.button];
  9904. var btn;
  9905. if (active) {
  9906. btnClasses.push(classes.active);
  9907. }
  9908. if (disabled) {
  9909. btnClasses.push(classes.disabled)
  9910. }
  9911. if (buttonType === 'ellipsis') {
  9912. btn = $('<span class="ellipsis"></span>').html(content)[0];
  9913. }
  9914. else {
  9915. btn = $('<button>', {
  9916. class: btnClasses.join(' '),
  9917. role: 'link',
  9918. type: 'button'
  9919. }).html(content);
  9920. }
  9921. return {
  9922. display: btn,
  9923. clicker: btn
  9924. }
  9925. }
  9926. },
  9927. pagingContainer: {
  9928. _: function (settings, buttons) {
  9929. // No wrapping element - just append directly to the host
  9930. return buttons;
  9931. }
  9932. }
  9933. } );
  9934. // Common function to remove new lines, strip HTML and diacritic control
  9935. var _filterString = function (stripHtml, normalize) {
  9936. return function (str) {
  9937. if (_empty(str) || typeof str !== 'string') {
  9938. return str;
  9939. }
  9940. str = str.replace( _re_new_lines, " " );
  9941. if (stripHtml) {
  9942. str = _stripHtml(str);
  9943. }
  9944. if (normalize) {
  9945. str = _normalize(str, false);
  9946. }
  9947. return str;
  9948. };
  9949. }
  9950. /*
  9951. * Public helper functions. These aren't used internally by DataTables, or
  9952. * called by any of the options passed into DataTables, but they can be used
  9953. * externally by developers working with DataTables. They are helper functions
  9954. * to make working with DataTables a little bit easier.
  9955. */
  9956. function __mldFnName(name) {
  9957. return name.replace(/[\W]/g, '_')
  9958. }
  9959. // Common logic for moment, luxon or a date action
  9960. function __mld( dt, momentFn, luxonFn, dateFn, arg1 ) {
  9961. if (window.moment) {
  9962. return dt[momentFn]( arg1 );
  9963. }
  9964. else if (window.luxon) {
  9965. return dt[luxonFn]( arg1 );
  9966. }
  9967. return dateFn ? dt[dateFn]( arg1 ) : dt;
  9968. }
  9969. var __mlWarning = false;
  9970. function __mldObj (d, format, locale) {
  9971. var dt;
  9972. if (window.moment) {
  9973. dt = window.moment.utc( d, format, locale, true );
  9974. if (! dt.isValid()) {
  9975. return null;
  9976. }
  9977. }
  9978. else if (window.luxon) {
  9979. dt = format && typeof d === 'string'
  9980. ? window.luxon.DateTime.fromFormat( d, format )
  9981. : window.luxon.DateTime.fromISO( d );
  9982. if (! dt.isValid) {
  9983. return null;
  9984. }
  9985. dt.setLocale(locale);
  9986. }
  9987. else if (! format) {
  9988. // No format given, must be ISO
  9989. dt = new Date(d);
  9990. }
  9991. else {
  9992. if (! __mlWarning) {
  9993. alert('DataTables warning: Formatted date without Moment.js or Luxon - https://datatables.net/tn/17');
  9994. }
  9995. __mlWarning = true;
  9996. }
  9997. return dt;
  9998. }
  9999. // Wrapper for date, datetime and time which all operate the same way with the exception of
  10000. // the output string for auto locale support
  10001. function __mlHelper (localeString) {
  10002. return function ( from, to, locale, def ) {
  10003. // Luxon and Moment support
  10004. // Argument shifting
  10005. if ( arguments.length === 0 ) {
  10006. locale = 'en';
  10007. to = null; // means toLocaleString
  10008. from = null; // means iso8601
  10009. }
  10010. else if ( arguments.length === 1 ) {
  10011. locale = 'en';
  10012. to = from;
  10013. from = null;
  10014. }
  10015. else if ( arguments.length === 2 ) {
  10016. locale = to;
  10017. to = from;
  10018. from = null;
  10019. }
  10020. var typeName = 'datetime' + (to ? '-' + __mldFnName(to) : '');
  10021. // Add type detection and sorting specific to this date format - we need to be able to identify
  10022. // date type columns as such, rather than as numbers in extensions. Hence the need for this.
  10023. if (! DataTable.ext.type.order[typeName]) {
  10024. DataTable.type(typeName, {
  10025. detect: function (d) {
  10026. // The renderer will give the value to type detect as the type!
  10027. return d === typeName ? typeName : false;
  10028. },
  10029. order: {
  10030. pre: function (d) {
  10031. // The renderer gives us Moment, Luxon or Date obects for the sorting, all of which have a
  10032. // `valueOf` which gives milliseconds epoch
  10033. return d.valueOf();
  10034. }
  10035. },
  10036. className: 'dt-right'
  10037. });
  10038. }
  10039. return function ( d, type ) {
  10040. // Allow for a default value
  10041. if (d === null || d === undefined) {
  10042. if (def === '--now') {
  10043. // We treat everything as UTC further down, so no changes are
  10044. // made, as such need to get the local date / time as if it were
  10045. // UTC
  10046. var local = new Date();
  10047. d = new Date( Date.UTC(
  10048. local.getFullYear(), local.getMonth(), local.getDate(),
  10049. local.getHours(), local.getMinutes(), local.getSeconds()
  10050. ) );
  10051. }
  10052. else {
  10053. d = '';
  10054. }
  10055. }
  10056. if (type === 'type') {
  10057. // Typing uses the type name for fast matching
  10058. return typeName;
  10059. }
  10060. if (d === '') {
  10061. return type !== 'sort'
  10062. ? ''
  10063. : __mldObj('0000-01-01 00:00:00', null, locale);
  10064. }
  10065. // Shortcut. If `from` and `to` are the same, we are using the renderer to
  10066. // format for ordering, not display - its already in the display format.
  10067. if ( to !== null && from === to && type !== 'sort' && type !== 'type' && ! (d instanceof Date) ) {
  10068. return d;
  10069. }
  10070. var dt = __mldObj(d, from, locale);
  10071. if (dt === null) {
  10072. return d;
  10073. }
  10074. if (type === 'sort') {
  10075. return dt;
  10076. }
  10077. var formatted = to === null
  10078. ? __mld(dt, 'toDate', 'toJSDate', '')[localeString]()
  10079. : __mld(dt, 'format', 'toFormat', 'toISOString', to);
  10080. // XSS protection
  10081. return type === 'display' ?
  10082. _escapeHtml( formatted ) :
  10083. formatted;
  10084. };
  10085. }
  10086. }
  10087. // Based on locale, determine standard number formatting
  10088. // Fallback for legacy browsers is US English
  10089. var __thousands = ',';
  10090. var __decimal = '.';
  10091. if (window.Intl !== undefined) {
  10092. try {
  10093. var num = new Intl.NumberFormat().formatToParts(100000.1);
  10094. for (var i=0 ; i<num.length ; i++) {
  10095. if (num[i].type === 'group') {
  10096. __thousands = num[i].value;
  10097. }
  10098. else if (num[i].type === 'decimal') {
  10099. __decimal = num[i].value;
  10100. }
  10101. }
  10102. }
  10103. catch (e) {
  10104. // noop
  10105. }
  10106. }
  10107. // Formatted date time detection - use by declaring the formats you are going to use
  10108. DataTable.datetime = function ( format, locale ) {
  10109. var typeName = 'datetime-detect-' + __mldFnName(format);
  10110. if (! locale) {
  10111. locale = 'en';
  10112. }
  10113. if (! DataTable.ext.type.order[typeName]) {
  10114. DataTable.type(typeName, {
  10115. detect: function (d) {
  10116. var dt = __mldObj(d, format, locale);
  10117. return d === '' || dt ? typeName : false;
  10118. },
  10119. order: {
  10120. pre: function (d) {
  10121. return __mldObj(d, format, locale) || 0;
  10122. }
  10123. },
  10124. className: 'dt-right'
  10125. });
  10126. }
  10127. }
  10128. /**
  10129. * Helpers for `columns.render`.
  10130. *
  10131. * The options defined here can be used with the `columns.render` initialisation
  10132. * option to provide a display renderer. The following functions are defined:
  10133. *
  10134. * * `moment` - Uses the MomentJS library to convert from a given format into another.
  10135. * This renderer has three overloads:
  10136. * * 1 parameter:
  10137. * * `string` - Format to convert to (assumes input is ISO8601 and locale is `en`)
  10138. * * 2 parameters:
  10139. * * `string` - Format to convert from
  10140. * * `string` - Format to convert to. Assumes `en` locale
  10141. * * 3 parameters:
  10142. * * `string` - Format to convert from
  10143. * * `string` - Format to convert to
  10144. * * `string` - Locale
  10145. * * `number` - Will format numeric data (defined by `columns.data`) for
  10146. * display, retaining the original unformatted data for sorting and filtering.
  10147. * It takes 5 parameters:
  10148. * * `string` - Thousands grouping separator
  10149. * * `string` - Decimal point indicator
  10150. * * `integer` - Number of decimal points to show
  10151. * * `string` (optional) - Prefix.
  10152. * * `string` (optional) - Postfix (/suffix).
  10153. * * `text` - Escape HTML to help prevent XSS attacks. It has no optional
  10154. * parameters.
  10155. *
  10156. * @example
  10157. * // Column definition using the number renderer
  10158. * {
  10159. * data: "salary",
  10160. * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' )
  10161. * }
  10162. *
  10163. * @namespace
  10164. */
  10165. DataTable.render = {
  10166. date: __mlHelper('toLocaleDateString'),
  10167. datetime: __mlHelper('toLocaleString'),
  10168. time: __mlHelper('toLocaleTimeString'),
  10169. number: function ( thousands, decimal, precision, prefix, postfix ) {
  10170. // Auto locale detection
  10171. if (thousands === null || thousands === undefined) {
  10172. thousands = __thousands;
  10173. }
  10174. if (decimal === null || decimal === undefined) {
  10175. decimal = __decimal;
  10176. }
  10177. return {
  10178. display: function ( d ) {
  10179. if ( typeof d !== 'number' && typeof d !== 'string' ) {
  10180. return d;
  10181. }
  10182. if (d === '' || d === null) {
  10183. return d;
  10184. }
  10185. var negative = d < 0 ? '-' : '';
  10186. var flo = parseFloat( d );
  10187. var abs = Math.abs(flo);
  10188. // Scientific notation for large and small numbers
  10189. if (abs >= 100000000000 || (abs < 0.0001 && abs !== 0) ) {
  10190. var exp = flo.toExponential(precision).split(/e\+?/);
  10191. return exp[0] + ' x 10<sup>' + exp[1] + '</sup>';
  10192. }
  10193. // If NaN then there isn't much formatting that we can do - just
  10194. // return immediately, escaping any HTML (this was supposed to
  10195. // be a number after all)
  10196. if ( isNaN( flo ) ) {
  10197. return _escapeHtml( d );
  10198. }
  10199. flo = flo.toFixed( precision );
  10200. d = Math.abs( flo );
  10201. var intPart = parseInt( d, 10 );
  10202. var floatPart = precision ?
  10203. decimal+(d - intPart).toFixed( precision ).substring( 2 ):
  10204. '';
  10205. // If zero, then can't have a negative prefix
  10206. if (intPart === 0 && parseFloat(floatPart) === 0) {
  10207. negative = '';
  10208. }
  10209. return negative + (prefix||'') +
  10210. intPart.toString().replace(
  10211. /\B(?=(\d{3})+(?!\d))/g, thousands
  10212. ) +
  10213. floatPart +
  10214. (postfix||'');
  10215. }
  10216. };
  10217. },
  10218. text: function () {
  10219. return {
  10220. display: _escapeHtml,
  10221. filter: _escapeHtml
  10222. };
  10223. }
  10224. };
  10225. var _extTypes = DataTable.ext.type;
  10226. // Get / set type
  10227. DataTable.type = function (name, prop, val) {
  10228. if (! prop) {
  10229. return {
  10230. className: _extTypes.className[name],
  10231. detect: _extTypes.detect.find(function (fn) {
  10232. return fn.name === name;
  10233. }),
  10234. order: {
  10235. pre: _extTypes.order[name + '-pre'],
  10236. asc: _extTypes.order[name + '-asc'],
  10237. desc: _extTypes.order[name + '-desc']
  10238. },
  10239. render: _extTypes.render[name],
  10240. search: _extTypes.search[name]
  10241. };
  10242. }
  10243. var setProp = function(prop, propVal) {
  10244. _extTypes[prop][name] = propVal;
  10245. };
  10246. var setDetect = function (fn) {
  10247. // Wrap to allow the function to return `true` rather than
  10248. // specifying the type name.
  10249. var cb = function (d, s) {
  10250. var ret = fn(d, s);
  10251. return ret === true
  10252. ? name
  10253. : ret;
  10254. };
  10255. Object.defineProperty(cb, "name", {value: name});
  10256. var idx = _extTypes.detect.findIndex(function (fn) {
  10257. return fn.name === name;
  10258. });
  10259. if (idx === -1) {
  10260. _extTypes.detect.unshift(cb);
  10261. }
  10262. else {
  10263. _extTypes.detect.splice(idx, 1, cb);
  10264. }
  10265. };
  10266. var setOrder = function (obj) {
  10267. _extTypes.order[name + '-pre'] = obj.pre; // can be undefined
  10268. _extTypes.order[name + '-asc'] = obj.asc; // can be undefined
  10269. _extTypes.order[name + '-desc'] = obj.desc; // can be undefined
  10270. };
  10271. // prop is optional
  10272. if (val === undefined) {
  10273. val = prop;
  10274. prop = null;
  10275. }
  10276. if (prop === 'className') {
  10277. setProp('className', val);
  10278. }
  10279. else if (prop === 'detect') {
  10280. setDetect(val);
  10281. }
  10282. else if (prop === 'order') {
  10283. setOrder(val);
  10284. }
  10285. else if (prop === 'render') {
  10286. setProp('render', val);
  10287. }
  10288. else if (prop === 'search') {
  10289. setProp('search', val);
  10290. }
  10291. else if (! prop) {
  10292. if (val.className) {
  10293. setProp('className', val.className);
  10294. }
  10295. if (val.detect !== undefined) {
  10296. setDetect(val.detect);
  10297. }
  10298. if (val.order) {
  10299. setOrder(val.order);
  10300. }
  10301. if (val.render !== undefined) {
  10302. setProp('render', val.render);
  10303. }
  10304. if (val.search !== undefined) {
  10305. setProp('search', val.search);
  10306. }
  10307. }
  10308. }
  10309. // Get a list of types
  10310. DataTable.types = function () {
  10311. return _extTypes.detect.map(function (fn) {
  10312. return fn.name;
  10313. });
  10314. };
  10315. //
  10316. // Built in data types
  10317. //
  10318. DataTable.type('string', {
  10319. detect: function () {
  10320. return 'string';
  10321. },
  10322. order: {
  10323. pre: function ( a ) {
  10324. // This is a little complex, but faster than always calling toString,
  10325. // http://jsperf.com/tostring-v-check
  10326. return _empty(a) ?
  10327. '' :
  10328. typeof a === 'string' ?
  10329. a.toLowerCase() :
  10330. ! a.toString ?
  10331. '' :
  10332. a.toString();
  10333. }
  10334. },
  10335. search: _filterString(false, true)
  10336. });
  10337. DataTable.type('html', {
  10338. detect: function ( d ) {
  10339. return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
  10340. 'html' : null;
  10341. },
  10342. order: {
  10343. pre: function ( a ) {
  10344. return _empty(a) ?
  10345. '' :
  10346. a.replace ?
  10347. _stripHtml(a).trim().toLowerCase() :
  10348. a+'';
  10349. }
  10350. },
  10351. search: _filterString(true, true)
  10352. });
  10353. DataTable.type('date', {
  10354. className: 'dt-type-date',
  10355. detect: function ( d )
  10356. {
  10357. // V8 tries _very_ hard to make a string passed into `Date.parse()`
  10358. // valid, so we need to use a regex to restrict date formats. Use a
  10359. // plug-in for anything other than ISO8601 style strings
  10360. if ( d && !(d instanceof Date) && ! _re_date.test(d) ) {
  10361. return null;
  10362. }
  10363. var parsed = Date.parse(d);
  10364. return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
  10365. },
  10366. order: {
  10367. pre: function ( d ) {
  10368. var ts = Date.parse( d );
  10369. return isNaN(ts) ? -Infinity : ts;
  10370. }
  10371. }
  10372. });
  10373. DataTable.type('html-num-fmt', {
  10374. className: 'dt-type-numeric',
  10375. detect: function ( d, settings )
  10376. {
  10377. var decimal = settings.oLanguage.sDecimal;
  10378. return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt' : null;
  10379. },
  10380. order: {
  10381. pre: function ( d, s ) {
  10382. var dp = s.oLanguage.sDecimal;
  10383. return __numericReplace( d, dp, _re_html, _re_formatted_numeric );
  10384. }
  10385. },
  10386. search: _filterString(true, true)
  10387. });
  10388. DataTable.type('html-num', {
  10389. className: 'dt-type-numeric',
  10390. detect: function ( d, settings )
  10391. {
  10392. var decimal = settings.oLanguage.sDecimal;
  10393. return _htmlNumeric( d, decimal ) ? 'html-num' : null;
  10394. },
  10395. order: {
  10396. pre: function ( d, s ) {
  10397. var dp = s.oLanguage.sDecimal;
  10398. return __numericReplace( d, dp, _re_html );
  10399. }
  10400. },
  10401. search: _filterString(true, true)
  10402. });
  10403. DataTable.type('num-fmt', {
  10404. className: 'dt-type-numeric',
  10405. detect: function ( d, settings )
  10406. {
  10407. var decimal = settings.oLanguage.sDecimal;
  10408. return _isNumber( d, decimal, true ) ? 'num-fmt' : null;
  10409. },
  10410. order: {
  10411. pre: function ( d, s ) {
  10412. var dp = s.oLanguage.sDecimal;
  10413. return __numericReplace( d, dp, _re_formatted_numeric );
  10414. }
  10415. }
  10416. });
  10417. DataTable.type('num', {
  10418. className: 'dt-type-numeric',
  10419. detect: function ( d, settings )
  10420. {
  10421. var decimal = settings.oLanguage.sDecimal;
  10422. return _isNumber( d, decimal ) ? 'num' : null;
  10423. },
  10424. order: {
  10425. pre: function (d, s) {
  10426. var dp = s.oLanguage.sDecimal;
  10427. return __numericReplace( d, dp );
  10428. }
  10429. }
  10430. });
  10431. var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
  10432. if ( d !== 0 && (!d || d === '-') ) {
  10433. return -Infinity;
  10434. }
  10435. var type = typeof d;
  10436. if (type === 'number' || type === 'bigint') {
  10437. return d;
  10438. }
  10439. // If a decimal place other than `.` is used, it needs to be given to the
  10440. // function so we can detect it and replace with a `.` which is the only
  10441. // decimal place Javascript recognises - it is not locale aware.
  10442. if ( decimalPlace ) {
  10443. d = _numToDecimal( d, decimalPlace );
  10444. }
  10445. if ( d.replace ) {
  10446. if ( re1 ) {
  10447. d = d.replace( re1, '' );
  10448. }
  10449. if ( re2 ) {
  10450. d = d.replace( re2, '' );
  10451. }
  10452. }
  10453. return d * 1;
  10454. };
  10455. $.extend( true, DataTable.ext.renderer, {
  10456. footer: {
  10457. _: function ( settings, cell, classes ) {
  10458. cell.addClass(classes.tfoot.cell);
  10459. }
  10460. },
  10461. header: {
  10462. _: function ( settings, cell, classes ) {
  10463. cell.addClass(classes.thead.cell);
  10464. if (! settings.oFeatures.bSort) {
  10465. cell.addClass(classes.order.none);
  10466. }
  10467. var legacyTop = settings.bSortCellsTop;
  10468. var headerRows = cell.closest('thead').find('tr');
  10469. var rowIdx = cell.parent().index();
  10470. // Conditions to not apply the ordering icons
  10471. if (
  10472. // Cells and rows which have the attribute to disable the icons
  10473. cell.attr('data-dt-order') === 'disable' ||
  10474. cell.parent().attr('data-dt-order') === 'disable' ||
  10475. // Legacy support for `orderCellsTop`. If it is set, then cells
  10476. // which are not in the top or bottom row of the header (depending
  10477. // on the value) do not get the sorting classes applied to them
  10478. (legacyTop === true && rowIdx !== 0) ||
  10479. (legacyTop === false && rowIdx !== headerRows.length - 1)
  10480. ) {
  10481. return;
  10482. }
  10483. // No additional mark-up required
  10484. // Attach a sort listener to update on sort - note that using the
  10485. // `DT` namespace will allow the event to be removed automatically
  10486. // on destroy, while the `dt` namespaced event is the one we are
  10487. // listening for
  10488. $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting ) {
  10489. if ( settings !== ctx ) { // need to check this this is the host
  10490. return; // table, not a nested one
  10491. }
  10492. var orderClasses = classes.order;
  10493. var columns = ctx.api.columns( cell );
  10494. var col = settings.aoColumns[columns.flatten()[0]];
  10495. var orderable = columns.orderable().includes(true);
  10496. var ariaType = '';
  10497. var indexes = columns.indexes();
  10498. var sortDirs = columns.orderable(true).flatten();
  10499. var orderedColumns = ',' + sorting.map( function (val) {
  10500. return val.col;
  10501. } ).join(',') + ',';
  10502. cell
  10503. .removeClass(
  10504. orderClasses.isAsc +' '+
  10505. orderClasses.isDesc
  10506. )
  10507. .toggleClass( orderClasses.none, ! orderable )
  10508. .toggleClass( orderClasses.canAsc, orderable && sortDirs.includes('asc') )
  10509. .toggleClass( orderClasses.canDesc, orderable && sortDirs.includes('desc') );
  10510. var sortIdx = orderedColumns.indexOf( ',' + indexes.toArray().join(',') + ',' );
  10511. if ( sortIdx !== -1 ) {
  10512. // Get the ordering direction for the columns under this cell
  10513. // Note that it is possible for a cell to be asc and desc sorting
  10514. // (column spanning cells)
  10515. var orderDirs = columns.order();
  10516. cell.addClass(
  10517. orderDirs.includes('asc') ? orderClasses.isAsc : '' +
  10518. orderDirs.includes('desc') ? orderClasses.isDesc : ''
  10519. );
  10520. }
  10521. // The ARIA spec says that only one column should be marked with aria-sort
  10522. if ( sortIdx === 0 ) {
  10523. var firstSort = sorting[0];
  10524. var sortOrder = col.asSorting;
  10525. cell.attr('aria-sort', firstSort.dir === 'asc' ? 'ascending' : 'descending');
  10526. // Determine if the next click will remove sorting or change the sort
  10527. ariaType = ! sortOrder[firstSort.index + 1] ? 'Remove' : 'Reverse';
  10528. }
  10529. else {
  10530. cell.removeAttr('aria-sort');
  10531. }
  10532. cell.attr('aria-label', orderable
  10533. ? col.ariaTitle + ctx.api.i18n('oAria.orderable' + ariaType)
  10534. : col.ariaTitle
  10535. );
  10536. if (orderable) {
  10537. cell.find('.dt-column-title').attr('role', 'button');
  10538. cell.attr('tabindex', 0)
  10539. }
  10540. } );
  10541. }
  10542. },
  10543. layout: {
  10544. _: function ( settings, container, items ) {
  10545. var row = $('<div/>')
  10546. .addClass('dt-layout-row')
  10547. .appendTo( container );
  10548. $.each( items, function (key, val) {
  10549. var klass = ! val.table ?
  10550. 'dt-'+key+' ' :
  10551. '';
  10552. if (val.table) {
  10553. row.addClass('dt-layout-table');
  10554. }
  10555. $('<div/>')
  10556. .attr({
  10557. id: val.id || null,
  10558. "class": 'dt-layout-cell '+klass+(val.className || '')
  10559. })
  10560. .append( val.contents )
  10561. .appendTo( row );
  10562. } );
  10563. }
  10564. }
  10565. } );
  10566. DataTable.feature = {};
  10567. // Third parameter is internal only!
  10568. DataTable.feature.register = function ( name, cb, legacy ) {
  10569. DataTable.ext.features[ name ] = cb;
  10570. if (legacy) {
  10571. _ext.feature.push({
  10572. cFeature: legacy,
  10573. fnInit: cb
  10574. });
  10575. }
  10576. };
  10577. DataTable.feature.register( 'info', function ( settings, opts ) {
  10578. // For compatibility with the legacy `info` top level option
  10579. if (! settings.oFeatures.bInfo) {
  10580. return null;
  10581. }
  10582. var
  10583. lang = settings.oLanguage,
  10584. tid = settings.sTableId,
  10585. n = $('<div/>', {
  10586. 'class': settings.oClasses.info.container,
  10587. } );
  10588. opts = $.extend({
  10589. callback: lang.fnInfoCallback,
  10590. empty: lang.sInfoEmpty,
  10591. postfix: lang.sInfoPostFix,
  10592. search: lang.sInfoFiltered,
  10593. text: lang.sInfo,
  10594. }, opts);
  10595. // Update display on each draw
  10596. settings.aoDrawCallback.push(function (s) {
  10597. _fnUpdateInfo(s, opts, n);
  10598. });
  10599. // For the first info display in the table, we add a callback and aria information.
  10600. if (! settings._infoEl) {
  10601. n.attr({
  10602. 'aria-live': 'polite',
  10603. id: tid+'_info',
  10604. role: 'status'
  10605. });
  10606. // Table is described by our info div
  10607. $(settings.nTable).attr( 'aria-describedby', tid+'_info' );
  10608. settings._infoEl = n;
  10609. }
  10610. return n;
  10611. }, 'i' );
  10612. /**
  10613. * Update the information elements in the display
  10614. * @param {object} settings dataTables settings object
  10615. * @memberof DataTable#oApi
  10616. */
  10617. function _fnUpdateInfo ( settings, opts, node )
  10618. {
  10619. var
  10620. start = settings._iDisplayStart+1,
  10621. end = settings.fnDisplayEnd(),
  10622. max = settings.fnRecordsTotal(),
  10623. total = settings.fnRecordsDisplay(),
  10624. out = total
  10625. ? opts.text
  10626. : opts.empty;
  10627. if ( total !== max ) {
  10628. // Record set after filtering
  10629. out += ' ' + opts.search;
  10630. }
  10631. // Convert the macros
  10632. out += opts.postfix;
  10633. out = _fnMacros( settings, out );
  10634. if ( opts.callback ) {
  10635. out = opts.callback.call( settings.oInstance,
  10636. settings, start, end, max, total, out
  10637. );
  10638. }
  10639. node.html( out );
  10640. _fnCallbackFire(settings, null, 'info', [settings, node[0], out]);
  10641. }
  10642. var __searchCounter = 0;
  10643. // opts
  10644. // - text
  10645. // - placeholder
  10646. DataTable.feature.register( 'search', function ( settings, opts ) {
  10647. // Don't show the input if filtering isn't available on the table
  10648. if (! settings.oFeatures.bFilter) {
  10649. return null;
  10650. }
  10651. var classes = settings.oClasses.search;
  10652. var tableId = settings.sTableId;
  10653. var language = settings.oLanguage;
  10654. var previousSearch = settings.oPreviousSearch;
  10655. var input = '<input type="search" class="'+classes.input+'"/>';
  10656. opts = $.extend({
  10657. placeholder: language.sSearchPlaceholder,
  10658. text: language.sSearch
  10659. }, opts);
  10660. // The _INPUT_ is optional - is appended if not present
  10661. if (opts.text.indexOf('_INPUT_') === -1) {
  10662. opts.text += '_INPUT_';
  10663. }
  10664. opts.text = _fnMacros(settings, opts.text);
  10665. // We can put the <input> outside of the label if it is at the start or end
  10666. // which helps improve accessability (not all screen readers like implicit
  10667. // for elements).
  10668. var end = opts.text.match(/_INPUT_$/);
  10669. var start = opts.text.match(/^_INPUT_/);
  10670. var removed = opts.text.replace(/_INPUT_/, '');
  10671. var str = '<label>' + opts.text + '</label>';
  10672. if (start) {
  10673. str = '_INPUT_<label>' + removed + '</label>';
  10674. }
  10675. else if (end) {
  10676. str = '<label>' + removed + '</label>_INPUT_';
  10677. }
  10678. var filter = $('<div>')
  10679. .addClass(classes.container)
  10680. .append(str.replace(/_INPUT_/, input));
  10681. // add for and id to label and input
  10682. filter.find('label').attr('for', 'dt-search-' + __searchCounter);
  10683. filter.find('input').attr('id', 'dt-search-' + __searchCounter);
  10684. __searchCounter++;
  10685. var searchFn = function(event) {
  10686. var val = this.value;
  10687. if(previousSearch.return && event.key !== "Enter") {
  10688. return;
  10689. }
  10690. /* Now do the filter */
  10691. if ( val != previousSearch.search ) {
  10692. previousSearch.search = val;
  10693. _fnFilterComplete( settings, previousSearch );
  10694. // Need to redraw, without resorting
  10695. settings._iDisplayStart = 0;
  10696. _fnDraw( settings );
  10697. }
  10698. };
  10699. var searchDelay = settings.searchDelay !== null ?
  10700. settings.searchDelay :
  10701. 0;
  10702. var jqFilter = $('input', filter)
  10703. .val( previousSearch.search )
  10704. .attr( 'placeholder', opts.placeholder )
  10705. .on(
  10706. 'keyup.DT search.DT input.DT paste.DT cut.DT',
  10707. searchDelay ?
  10708. DataTable.util.debounce( searchFn, searchDelay ) :
  10709. searchFn
  10710. )
  10711. .on( 'mouseup.DT', function(e) {
  10712. // Edge fix! Edge 17 does not trigger anything other than mouse events when clicking
  10713. // on the clear icon (Edge bug 17584515). This is safe in other browsers as `searchFn`
  10714. // checks the value to see if it has changed. In other browsers it won't have.
  10715. setTimeout( function () {
  10716. searchFn.call(jqFilter[0], e);
  10717. }, 10);
  10718. } )
  10719. .on( 'keypress.DT', function(e) {
  10720. /* Prevent form submission */
  10721. if ( e.keyCode == 13 ) {
  10722. return false;
  10723. }
  10724. } )
  10725. .attr('aria-controls', tableId);
  10726. // Update the input elements whenever the table is filtered
  10727. $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {
  10728. if ( settings === s && jqFilter[0] !== document.activeElement ) {
  10729. jqFilter.val( typeof previousSearch.search !== 'function'
  10730. ? previousSearch.search
  10731. : ''
  10732. );
  10733. }
  10734. } );
  10735. return filter;
  10736. }, 'f' );
  10737. // opts
  10738. // - type - button configuration
  10739. // - buttons - number of buttons to show - must be odd
  10740. DataTable.feature.register( 'paging', function ( settings, opts ) {
  10741. // Don't show the paging input if the table doesn't have paging enabled
  10742. if (! settings.oFeatures.bPaginate) {
  10743. return null;
  10744. }
  10745. opts = $.extend({
  10746. buttons: DataTable.ext.pager.numbers_length,
  10747. type: settings.sPaginationType,
  10748. boundaryNumbers: true
  10749. }, opts);
  10750. // To be removed in 2.1
  10751. if (opts.numbers) {
  10752. opts.buttons = opts.numbers;
  10753. }
  10754. var host = $('<div/>').addClass( settings.oClasses.paging.container + ' paging_' + opts.type );
  10755. var draw = function () {
  10756. _pagingDraw(settings, host, opts);
  10757. };
  10758. settings.aoDrawCallback.push(draw);
  10759. // Responsive redraw of paging control
  10760. $(settings.nTable).on('column-sizing.dt.DT', draw);
  10761. return host;
  10762. }, 'p' );
  10763. function _pagingDraw(settings, host, opts) {
  10764. if (! settings._bInitComplete) {
  10765. return;
  10766. }
  10767. var
  10768. plugin = DataTable.ext.pager[ opts.type ],
  10769. aria = settings.oLanguage.oAria.paginate || {},
  10770. start = settings._iDisplayStart,
  10771. len = settings._iDisplayLength,
  10772. visRecords = settings.fnRecordsDisplay(),
  10773. all = len === -1,
  10774. page = all ? 0 : Math.ceil( start / len ),
  10775. pages = all ? 1 : Math.ceil( visRecords / len ),
  10776. buttons = plugin()
  10777. .map(function (val) {
  10778. return val === 'numbers'
  10779. ? _pagingNumbers(page, pages, opts.buttons, opts.boundaryNumbers)
  10780. : val;
  10781. })
  10782. .flat();
  10783. var buttonEls = [];
  10784. for (var i=0 ; i<buttons.length ; i++) {
  10785. var button = buttons[i];
  10786. var btnInfo = _pagingButtonInfo(settings, button, page, pages);
  10787. var btn = _fnRenderer( settings, 'pagingButton' )(
  10788. settings,
  10789. button,
  10790. btnInfo.display,
  10791. btnInfo.active,
  10792. btnInfo.disabled
  10793. );
  10794. // Common attributes
  10795. $(btn.clicker).attr({
  10796. 'aria-controls': settings.sTableId,
  10797. 'aria-disabled': btnInfo.disabled ? 'true' : null,
  10798. 'aria-current': btnInfo.active ? 'page' : null,
  10799. 'aria-label': aria[ button ],
  10800. 'data-dt-idx': button,
  10801. 'tabIndex': btnInfo.disabled ? -1 : settings.iTabIndex,
  10802. });
  10803. if (typeof button !== 'number') {
  10804. $(btn.clicker).addClass(button);
  10805. }
  10806. _fnBindAction(
  10807. btn.clicker, {action: button}, function(e) {
  10808. e.preventDefault();
  10809. _fnPageChange( settings, e.data.action, true );
  10810. }
  10811. );
  10812. buttonEls.push(btn.display);
  10813. }
  10814. var wrapped = _fnRenderer(settings, 'pagingContainer')(
  10815. settings, buttonEls
  10816. );
  10817. var activeEl = host.find(document.activeElement).data('dt-idx');
  10818. host.empty().append(wrapped);
  10819. if ( activeEl !== undefined ) {
  10820. host.find( '[data-dt-idx='+activeEl+']' ).trigger('focus');
  10821. }
  10822. // Responsive - check if the buttons are over two lines based on the
  10823. // height of the buttons and the container.
  10824. if (
  10825. buttonEls.length && // any buttons
  10826. opts.numbers > 1 && // prevent infinite
  10827. $(host).height() >= ($(buttonEls[0]).outerHeight() * 2) - 10
  10828. ) {
  10829. _pagingDraw(settings, host, $.extend({}, opts, { numbers: opts.numbers - 2 }));
  10830. }
  10831. }
  10832. /**
  10833. * Get properties for a button based on the current paging state of the table
  10834. *
  10835. * @param {*} settings DT settings object
  10836. * @param {*} button The button type in question
  10837. * @param {*} page Table's current page
  10838. * @param {*} pages Number of pages
  10839. * @returns Info object
  10840. */
  10841. function _pagingButtonInfo(settings, button, page, pages) {
  10842. var lang = settings.oLanguage.oPaginate;
  10843. var o = {
  10844. display: '',
  10845. active: false,
  10846. disabled: false
  10847. };
  10848. switch ( button ) {
  10849. case 'ellipsis':
  10850. o.display = '&#x2026;';
  10851. o.disabled = true;
  10852. break;
  10853. case 'first':
  10854. o.display = lang.sFirst;
  10855. if (page === 0) {
  10856. o.disabled = true;
  10857. }
  10858. break;
  10859. case 'previous':
  10860. o.display = lang.sPrevious;
  10861. if ( page === 0 ) {
  10862. o.disabled = true;
  10863. }
  10864. break;
  10865. case 'next':
  10866. o.display = lang.sNext;
  10867. if ( pages === 0 || page === pages-1 ) {
  10868. o.disabled = true;
  10869. }
  10870. break;
  10871. case 'last':
  10872. o.display = lang.sLast;
  10873. if ( pages === 0 || page === pages-1 ) {
  10874. o.disabled = true;
  10875. }
  10876. break;
  10877. default:
  10878. if ( typeof button === 'number' ) {
  10879. o.display = settings.fnFormatNumber( button + 1 );
  10880. if (page === button) {
  10881. o.active = true;
  10882. }
  10883. }
  10884. break;
  10885. }
  10886. return o;
  10887. }
  10888. /**
  10889. * Compute what number buttons to show in the paging control
  10890. *
  10891. * @param {*} page Current page
  10892. * @param {*} pages Total number of pages
  10893. * @param {*} buttons Target number of number buttons
  10894. * @param {boolean} addFirstLast Indicate if page 1 and end should be included
  10895. * @returns Buttons to show
  10896. */
  10897. function _pagingNumbers ( page, pages, buttons, addFirstLast ) {
  10898. var
  10899. numbers = [],
  10900. half = Math.floor(buttons / 2),
  10901. before = addFirstLast ? 2 : 1,
  10902. after = addFirstLast ? 1 : 0;
  10903. if ( pages <= buttons ) {
  10904. numbers = _range(0, pages);
  10905. }
  10906. else if (buttons === 1) {
  10907. // Single button - current page only
  10908. numbers = [page];
  10909. }
  10910. else if (buttons === 3) {
  10911. // Special logic for just three buttons
  10912. if (page <= 1) {
  10913. numbers = [0, 1, 'ellipsis'];
  10914. }
  10915. else if (page >= pages - 2) {
  10916. numbers = _range(pages-2, pages);
  10917. numbers.unshift('ellipsis');
  10918. }
  10919. else {
  10920. numbers = ['ellipsis', page, 'ellipsis'];
  10921. }
  10922. }
  10923. else if ( page <= half ) {
  10924. numbers = _range(0, buttons-before);
  10925. numbers.push('ellipsis');
  10926. if (addFirstLast) {
  10927. numbers.push(pages-1);
  10928. }
  10929. }
  10930. else if ( page >= pages - 1 - half ) {
  10931. numbers = _range(pages-(buttons-before), pages);
  10932. numbers.unshift('ellipsis');
  10933. if (addFirstLast) {
  10934. numbers.unshift(0);
  10935. }
  10936. }
  10937. else {
  10938. numbers = _range(page-half+before, page+half-after);
  10939. numbers.push('ellipsis');
  10940. numbers.unshift('ellipsis');
  10941. if (addFirstLast) {
  10942. numbers.push(pages-1);
  10943. numbers.unshift(0);
  10944. }
  10945. }
  10946. return numbers;
  10947. }
  10948. var __lengthCounter = 0;
  10949. // opts
  10950. // - menu
  10951. // - text
  10952. DataTable.feature.register( 'pageLength', function ( settings, opts ) {
  10953. var features = settings.oFeatures;
  10954. // For compatibility with the legacy `pageLength` top level option
  10955. if (! features.bPaginate || ! features.bLengthChange) {
  10956. return null;
  10957. }
  10958. opts = $.extend({
  10959. menu: settings.aLengthMenu,
  10960. text: settings.oLanguage.sLengthMenu
  10961. }, opts);
  10962. var
  10963. classes = settings.oClasses.length,
  10964. tableId = settings.sTableId,
  10965. menu = opts.menu,
  10966. lengths = [],
  10967. language = [],
  10968. i;
  10969. // Options can be given in a number of ways
  10970. if (Array.isArray( menu[0] )) {
  10971. // Old 1.x style - 2D array
  10972. lengths = menu[0];
  10973. language = menu[1];
  10974. }
  10975. else {
  10976. for ( i=0 ; i<menu.length ; i++ ) {
  10977. // An object with different label and value
  10978. if ($.isPlainObject(menu[i])) {
  10979. lengths.push(menu[i].value);
  10980. language.push(menu[i].label);
  10981. }
  10982. else {
  10983. // Or just a number to display and use
  10984. lengths.push(menu[i]);
  10985. language.push(menu[i]);
  10986. }
  10987. }
  10988. }
  10989. // We can put the <select> outside of the label if it is at the start or
  10990. // end which helps improve accessability (not all screen readers like
  10991. // implicit for elements).
  10992. var end = opts.text.match(/_MENU_$/);
  10993. var start = opts.text.match(/^_MENU_/);
  10994. var removed = opts.text.replace(/_MENU_/, '');
  10995. var str = '<label>' + opts.text + '</label>';
  10996. if (start) {
  10997. str = '_MENU_<label>' + removed + '</label>';
  10998. }
  10999. else if (end) {
  11000. str = '<label>' + removed + '</label>_MENU_';
  11001. }
  11002. // Wrapper element - use a span as a holder for where the select will go
  11003. var div = $('<div/>')
  11004. .addClass( classes.container )
  11005. .append(
  11006. str.replace( '_MENU_', '<span></span>' )
  11007. );
  11008. // Save text node content for macro updating
  11009. var textNodes = [];
  11010. div.find('label')[0].childNodes.forEach(function (el) {
  11011. if (el.nodeType === Node.TEXT_NODE) {
  11012. textNodes.push({
  11013. el: el,
  11014. text: el.textContent
  11015. });
  11016. }
  11017. })
  11018. // Update the label text in case it has an entries value
  11019. var updateEntries = function (len) {
  11020. textNodes.forEach(function (node) {
  11021. node.el.textContent = _fnMacros(settings, node.text, len);
  11022. });
  11023. }
  11024. // Next, the select itself, along with the options
  11025. var select = $('<select/>', {
  11026. 'name': tableId+'_length',
  11027. 'aria-controls': tableId,
  11028. 'class': classes.select
  11029. } );
  11030. for ( i=0 ; i<lengths.length ; i++ ) {
  11031. select[0][ i ] = new Option(
  11032. typeof language[i] === 'number' ?
  11033. settings.fnFormatNumber( language[i] ) :
  11034. language[i],
  11035. lengths[i]
  11036. );
  11037. }
  11038. // add for and id to label and input
  11039. div.find('label').attr('for', 'dt-length-' + __lengthCounter);
  11040. select.attr('id', 'dt-length-' + __lengthCounter);
  11041. __lengthCounter++;
  11042. // Swap in the select list
  11043. div.find('span').replaceWith(select);
  11044. // Can't use `select` variable as user might provide their own and the
  11045. // reference is broken by the use of outerHTML
  11046. $('select', div)
  11047. .val( settings._iDisplayLength )
  11048. .on( 'change.DT', function() {
  11049. _fnLengthChange( settings, $(this).val() );
  11050. _fnDraw( settings );
  11051. } );
  11052. // Update node value whenever anything changes the table's length
  11053. $(settings.nTable).on( 'length.dt.DT', function (e, s, len) {
  11054. if ( settings === s ) {
  11055. $('select', div).val( len );
  11056. // Resolve plurals in the text for the new length
  11057. updateEntries(len);
  11058. }
  11059. } );
  11060. updateEntries(settings._iDisplayLength);
  11061. return div;
  11062. }, 'l' );
  11063. // jQuery access
  11064. $.fn.dataTable = DataTable;
  11065. // Provide access to the host jQuery object (circular reference)
  11066. DataTable.$ = $;
  11067. // Legacy aliases
  11068. $.fn.dataTableSettings = DataTable.settings;
  11069. $.fn.dataTableExt = DataTable.ext;
  11070. // With a capital `D` we return a DataTables API instance rather than a
  11071. // jQuery object
  11072. $.fn.DataTable = function ( opts ) {
  11073. return $(this).dataTable( opts ).api();
  11074. };
  11075. // All properties that are available to $.fn.dataTable should also be
  11076. // available on $.fn.DataTable
  11077. $.each( DataTable, function ( prop, val ) {
  11078. $.fn.DataTable[ prop ] = val;
  11079. } );
  11080. return DataTable;
  11081. }));
  11082. /*! DataTables Bootstrap 5 integration
  11083. * © SpryMedia Ltd - datatables.net/license
  11084. */
  11085. (function( factory ){
  11086. if ( typeof define === 'function' && define.amd ) {
  11087. // AMD
  11088. define( ['jquery', 'datatables.net'], function ( $ ) {
  11089. return factory( $, window, document );
  11090. } );
  11091. }
  11092. else if ( typeof exports === 'object' ) {
  11093. // CommonJS
  11094. var jq = require('jquery');
  11095. var cjsRequires = function (root, $) {
  11096. if ( ! $.fn.dataTable ) {
  11097. require('datatables.net')(root, $);
  11098. }
  11099. };
  11100. if (typeof window === 'undefined') {
  11101. module.exports = function (root, $) {
  11102. if ( ! root ) {
  11103. // CommonJS environments without a window global must pass a
  11104. // root. This will give an error otherwise
  11105. root = window;
  11106. }
  11107. if ( ! $ ) {
  11108. $ = jq( root );
  11109. }
  11110. cjsRequires( root, $ );
  11111. return factory( $, root, root.document );
  11112. };
  11113. }
  11114. else {
  11115. cjsRequires( window, jq );
  11116. module.exports = factory( jq, window, window.document );
  11117. }
  11118. }
  11119. else {
  11120. // Browser
  11121. factory( jQuery, window, document );
  11122. }
  11123. }(function( $, window, document ) {
  11124. 'use strict';
  11125. var DataTable = $.fn.dataTable;
  11126. /**
  11127. * DataTables integration for Bootstrap 5.
  11128. *
  11129. * This file sets the defaults and adds options to DataTables to style its
  11130. * controls using Bootstrap. See https://datatables.net/manual/styling/bootstrap
  11131. * for further information.
  11132. */
  11133. /* Set the defaults for DataTables initialisation */
  11134. $.extend( true, DataTable.defaults, {
  11135. renderer: 'bootstrap'
  11136. } );
  11137. /* Default class modification */
  11138. $.extend( true, DataTable.ext.classes, {
  11139. container: "dt-container dt-bootstrap5",
  11140. search: {
  11141. input: "form-control form-control-sm"
  11142. },
  11143. length: {
  11144. select: "form-select form-select-sm"
  11145. },
  11146. processing: {
  11147. container: "dt-processing card"
  11148. }
  11149. } );
  11150. /* Bootstrap paging button renderer */
  11151. DataTable.ext.renderer.pagingButton.bootstrap = function (settings, buttonType, content, active, disabled) {
  11152. var btnClasses = ['dt-paging-button', 'page-item'];
  11153. if (active) {
  11154. btnClasses.push('active');
  11155. }
  11156. if (disabled) {
  11157. btnClasses.push('disabled')
  11158. }
  11159. var li = $('<li>').addClass(btnClasses.join(' '));
  11160. var a = $('<a>', {
  11161. 'href': disabled ? null : '#',
  11162. 'class': 'page-link'
  11163. })
  11164. .html(content)
  11165. .appendTo(li);
  11166. return {
  11167. display: li,
  11168. clicker: a
  11169. };
  11170. };
  11171. DataTable.ext.renderer.pagingContainer.bootstrap = function (settings, buttonEls) {
  11172. return $('<ul/>').addClass('pagination').append(buttonEls);
  11173. };
  11174. DataTable.ext.renderer.layout.bootstrap = function ( settings, container, items ) {
  11175. var row = $( '<div/>', {
  11176. "class": items.full ?
  11177. 'row mt-2 justify-content-md-center mb-3' :
  11178. 'row mt-2 justify-content-between mb-3'
  11179. } )
  11180. .appendTo( container );
  11181. $.each( items, function (key, val) {
  11182. var klass;
  11183. // Apply start / end (left / right when ltr) margins
  11184. if (val.table) {
  11185. klass = 'col-12';
  11186. }
  11187. else if (key === 'start') {
  11188. klass = 'col-md-auto me-auto';
  11189. }
  11190. else if (key === 'end') {
  11191. klass = 'col-md-auto ms-auto';
  11192. }
  11193. else {
  11194. klass = 'col-md';
  11195. }
  11196. $( '<div/>', {
  11197. id: val.id || null,
  11198. "class": klass + ' ' + (val.className || '')
  11199. } )
  11200. .append( val.contents )
  11201. .appendTo( row );
  11202. } );
  11203. };
  11204. return DataTable;
  11205. }));
  11206. /*! Buttons for DataTables 3.0.2
  11207. * © SpryMedia Ltd - datatables.net/license
  11208. */
  11209. (function( factory ){
  11210. if ( typeof define === 'function' && define.amd ) {
  11211. // AMD
  11212. define( ['jquery', 'datatables.net'], function ( $ ) {
  11213. return factory( $, window, document );
  11214. } );
  11215. }
  11216. else if ( typeof exports === 'object' ) {
  11217. // CommonJS
  11218. var jq = require('jquery');
  11219. var cjsRequires = function (root, $) {
  11220. if ( ! $.fn.dataTable ) {
  11221. require('datatables.net')(root, $);
  11222. }
  11223. };
  11224. if (typeof window === 'undefined') {
  11225. module.exports = function (root, $) {
  11226. if ( ! root ) {
  11227. // CommonJS environments without a window global must pass a
  11228. // root. This will give an error otherwise
  11229. root = window;
  11230. }
  11231. if ( ! $ ) {
  11232. $ = jq( root );
  11233. }
  11234. cjsRequires( root, $ );
  11235. return factory( $, root, root.document );
  11236. };
  11237. }
  11238. else {
  11239. cjsRequires( window, jq );
  11240. module.exports = factory( jq, window, window.document );
  11241. }
  11242. }
  11243. else {
  11244. // Browser
  11245. factory( jQuery, window, document );
  11246. }
  11247. }(function( $, window, document ) {
  11248. 'use strict';
  11249. var DataTable = $.fn.dataTable;
  11250. // Used for namespacing events added to the document by each instance, so they
  11251. // can be removed on destroy
  11252. var _instCounter = 0;
  11253. // Button namespacing counter for namespacing events on individual buttons
  11254. var _buttonCounter = 0;
  11255. var _dtButtons = DataTable.ext.buttons;
  11256. // Custom entity decoder for data export
  11257. var _entityDecoder = null;
  11258. // Allow for jQuery slim
  11259. function _fadeIn(el, duration, fn) {
  11260. if ($.fn.animate) {
  11261. el.stop().fadeIn(duration, fn);
  11262. }
  11263. else {
  11264. el.css('display', 'block');
  11265. if (fn) {
  11266. fn.call(el);
  11267. }
  11268. }
  11269. }
  11270. function _fadeOut(el, duration, fn) {
  11271. if ($.fn.animate) {
  11272. el.stop().fadeOut(duration, fn);
  11273. }
  11274. else {
  11275. el.css('display', 'none');
  11276. if (fn) {
  11277. fn.call(el);
  11278. }
  11279. }
  11280. }
  11281. /**
  11282. * [Buttons description]
  11283. * @param {[type]}
  11284. * @param {[type]}
  11285. */
  11286. var Buttons = function (dt, config) {
  11287. if (!DataTable.versionCheck('2')) {
  11288. throw 'Warning: Buttons requires DataTables 2 or newer';
  11289. }
  11290. // If not created with a `new` keyword then we return a wrapper function that
  11291. // will take the settings object for a DT. This allows easy use of new instances
  11292. // with the `layout` option - e.g. `topLeft: $.fn.dataTable.Buttons( ... )`.
  11293. if (!(this instanceof Buttons)) {
  11294. return function (settings) {
  11295. return new Buttons(settings, dt).container();
  11296. };
  11297. }
  11298. // If there is no config set it to an empty object
  11299. if (typeof config === 'undefined') {
  11300. config = {};
  11301. }
  11302. // Allow a boolean true for defaults
  11303. if (config === true) {
  11304. config = {};
  11305. }
  11306. // For easy configuration of buttons an array can be given
  11307. if (Array.isArray(config)) {
  11308. config = { buttons: config };
  11309. }
  11310. this.c = $.extend(true, {}, Buttons.defaults, config);
  11311. // Don't want a deep copy for the buttons
  11312. if (config.buttons) {
  11313. this.c.buttons = config.buttons;
  11314. }
  11315. this.s = {
  11316. dt: new DataTable.Api(dt),
  11317. buttons: [],
  11318. listenKeys: '',
  11319. namespace: 'dtb' + _instCounter++
  11320. };
  11321. this.dom = {
  11322. container: $('<' + this.c.dom.container.tag + '/>').addClass(
  11323. this.c.dom.container.className
  11324. )
  11325. };
  11326. this._constructor();
  11327. };
  11328. $.extend(Buttons.prototype, {
  11329. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  11330. * Public methods
  11331. */
  11332. /**
  11333. * Get the action of a button
  11334. * @param {int|string} Button index
  11335. * @return {function}
  11336. */ /**
  11337. * Set the action of a button
  11338. * @param {node} node Button element
  11339. * @param {function} action Function to set
  11340. * @return {Buttons} Self for chaining
  11341. */
  11342. action: function (node, action) {
  11343. var button = this._nodeToButton(node);
  11344. if (action === undefined) {
  11345. return button.conf.action;
  11346. }
  11347. button.conf.action = action;
  11348. return this;
  11349. },
  11350. /**
  11351. * Add an active class to the button to make to look active or get current
  11352. * active state.
  11353. * @param {node} node Button element
  11354. * @param {boolean} [flag] Enable / disable flag
  11355. * @return {Buttons} Self for chaining or boolean for getter
  11356. */
  11357. active: function (node, flag) {
  11358. var button = this._nodeToButton(node);
  11359. var klass = this.c.dom.button.active;
  11360. var jqNode = $(button.node);
  11361. if (
  11362. button.inCollection &&
  11363. this.c.dom.collection.button &&
  11364. this.c.dom.collection.button.active !== undefined
  11365. ) {
  11366. klass = this.c.dom.collection.button.active;
  11367. }
  11368. if (flag === undefined) {
  11369. return jqNode.hasClass(klass);
  11370. }
  11371. jqNode.toggleClass(klass, flag === undefined ? true : flag);
  11372. return this;
  11373. },
  11374. /**
  11375. * Add a new button
  11376. * @param {object} config Button configuration object, base string name or function
  11377. * @param {int|string} [idx] Button index for where to insert the button
  11378. * @param {boolean} [draw=true] Trigger a draw. Set a false when adding
  11379. * lots of buttons, until the last button.
  11380. * @return {Buttons} Self for chaining
  11381. */
  11382. add: function (config, idx, draw) {
  11383. var buttons = this.s.buttons;
  11384. if (typeof idx === 'string') {
  11385. var split = idx.split('-');
  11386. var base = this.s;
  11387. for (var i = 0, ien = split.length - 1; i < ien; i++) {
  11388. base = base.buttons[split[i] * 1];
  11389. }
  11390. buttons = base.buttons;
  11391. idx = split[split.length - 1] * 1;
  11392. }
  11393. this._expandButton(
  11394. buttons,
  11395. config,
  11396. config !== undefined ? config.split : undefined,
  11397. (config === undefined ||
  11398. config.split === undefined ||
  11399. config.split.length === 0) &&
  11400. base !== undefined,
  11401. false,
  11402. idx
  11403. );
  11404. if (draw === undefined || draw === true) {
  11405. this._draw();
  11406. }
  11407. return this;
  11408. },
  11409. /**
  11410. * Clear buttons from a collection and then insert new buttons
  11411. */
  11412. collectionRebuild: function (node, newButtons) {
  11413. var button = this._nodeToButton(node);
  11414. if (newButtons !== undefined) {
  11415. var i;
  11416. // Need to reverse the array
  11417. for (i = button.buttons.length - 1; i >= 0; i--) {
  11418. this.remove(button.buttons[i].node);
  11419. }
  11420. // If the collection has prefix and / or postfix buttons we need to add them in
  11421. if (button.conf.prefixButtons) {
  11422. newButtons.unshift.apply(newButtons, button.conf.prefixButtons);
  11423. }
  11424. if (button.conf.postfixButtons) {
  11425. newButtons.push.apply(newButtons, button.conf.postfixButtons);
  11426. }
  11427. for (i = 0; i < newButtons.length; i++) {
  11428. var newBtn = newButtons[i];
  11429. this._expandButton(
  11430. button.buttons,
  11431. newBtn,
  11432. newBtn !== undefined &&
  11433. newBtn.config !== undefined &&
  11434. newBtn.config.split !== undefined,
  11435. true,
  11436. newBtn.parentConf !== undefined &&
  11437. newBtn.parentConf.split !== undefined,
  11438. null,
  11439. newBtn.parentConf
  11440. );
  11441. }
  11442. }
  11443. this._draw(button.collection, button.buttons);
  11444. },
  11445. /**
  11446. * Get the container node for the buttons
  11447. * @return {jQuery} Buttons node
  11448. */
  11449. container: function () {
  11450. return this.dom.container;
  11451. },
  11452. /**
  11453. * Disable a button
  11454. * @param {node} node Button node
  11455. * @return {Buttons} Self for chaining
  11456. */
  11457. disable: function (node) {
  11458. var button = this._nodeToButton(node);
  11459. $(button.node)
  11460. .addClass(this.c.dom.button.disabled)
  11461. .prop('disabled', true);
  11462. return this;
  11463. },
  11464. /**
  11465. * Destroy the instance, cleaning up event handlers and removing DOM
  11466. * elements
  11467. * @return {Buttons} Self for chaining
  11468. */
  11469. destroy: function () {
  11470. // Key event listener
  11471. $('body').off('keyup.' + this.s.namespace);
  11472. // Individual button destroy (so they can remove their own events if
  11473. // needed). Take a copy as the array is modified by `remove`
  11474. var buttons = this.s.buttons.slice();
  11475. var i, ien;
  11476. for (i = 0, ien = buttons.length; i < ien; i++) {
  11477. this.remove(buttons[i].node);
  11478. }
  11479. // Container
  11480. this.dom.container.remove();
  11481. // Remove from the settings object collection
  11482. var buttonInsts = this.s.dt.settings()[0];
  11483. for (i = 0, ien = buttonInsts.length; i < ien; i++) {
  11484. if (buttonInsts.inst === this) {
  11485. buttonInsts.splice(i, 1);
  11486. break;
  11487. }
  11488. }
  11489. return this;
  11490. },
  11491. /**
  11492. * Enable / disable a button
  11493. * @param {node} node Button node
  11494. * @param {boolean} [flag=true] Enable / disable flag
  11495. * @return {Buttons} Self for chaining
  11496. */
  11497. enable: function (node, flag) {
  11498. if (flag === false) {
  11499. return this.disable(node);
  11500. }
  11501. var button = this._nodeToButton(node);
  11502. $(button.node)
  11503. .removeClass(this.c.dom.button.disabled)
  11504. .prop('disabled', false);
  11505. return this;
  11506. },
  11507. /**
  11508. * Get a button's index
  11509. *
  11510. * This is internally recursive
  11511. * @param {element} node Button to get the index of
  11512. * @return {string} Button index
  11513. */
  11514. index: function (node, nested, buttons) {
  11515. if (!nested) {
  11516. nested = '';
  11517. buttons = this.s.buttons;
  11518. }
  11519. for (var i = 0, ien = buttons.length; i < ien; i++) {
  11520. var inner = buttons[i].buttons;
  11521. if (buttons[i].node === node) {
  11522. return nested + i;
  11523. }
  11524. if (inner && inner.length) {
  11525. var match = this.index(node, i + '-', inner);
  11526. if (match !== null) {
  11527. return match;
  11528. }
  11529. }
  11530. }
  11531. return null;
  11532. },
  11533. /**
  11534. * Get the instance name for the button set selector
  11535. * @return {string} Instance name
  11536. */
  11537. name: function () {
  11538. return this.c.name;
  11539. },
  11540. /**
  11541. * Get a button's node of the buttons container if no button is given
  11542. * @param {node} [node] Button node
  11543. * @return {jQuery} Button element, or container
  11544. */
  11545. node: function (node) {
  11546. if (!node) {
  11547. return this.dom.container;
  11548. }
  11549. var button = this._nodeToButton(node);
  11550. return $(button.node);
  11551. },
  11552. /**
  11553. * Set / get a processing class on the selected button
  11554. * @param {element} node Triggering button node
  11555. * @param {boolean} flag true to add, false to remove, undefined to get
  11556. * @return {boolean|Buttons} Getter value or this if a setter.
  11557. */
  11558. processing: function (node, flag) {
  11559. var dt = this.s.dt;
  11560. var button = this._nodeToButton(node);
  11561. if (flag === undefined) {
  11562. return $(button.node).hasClass('processing');
  11563. }
  11564. $(button.node).toggleClass('processing', flag);
  11565. $(dt.table().node()).triggerHandler('buttons-processing.dt', [
  11566. flag,
  11567. dt.button(node),
  11568. dt,
  11569. $(node),
  11570. button.conf
  11571. ]);
  11572. return this;
  11573. },
  11574. /**
  11575. * Remove a button.
  11576. * @param {node} node Button node
  11577. * @return {Buttons} Self for chaining
  11578. */
  11579. remove: function (node) {
  11580. var button = this._nodeToButton(node);
  11581. var host = this._nodeToHost(node);
  11582. var dt = this.s.dt;
  11583. // Remove any child buttons first
  11584. if (button.buttons.length) {
  11585. for (var i = button.buttons.length - 1; i >= 0; i--) {
  11586. this.remove(button.buttons[i].node);
  11587. }
  11588. }
  11589. button.conf.destroying = true;
  11590. // Allow the button to remove event handlers, etc
  11591. if (button.conf.destroy) {
  11592. button.conf.destroy.call(dt.button(node), dt, $(node), button.conf);
  11593. }
  11594. this._removeKey(button.conf);
  11595. $(button.node).remove();
  11596. var idx = $.inArray(button, host);
  11597. host.splice(idx, 1);
  11598. return this;
  11599. },
  11600. /**
  11601. * Get the text for a button
  11602. * @param {int|string} node Button index
  11603. * @return {string} Button text
  11604. */ /**
  11605. * Set the text for a button
  11606. * @param {int|string|function} node Button index
  11607. * @param {string} label Text
  11608. * @return {Buttons} Self for chaining
  11609. */
  11610. text: function (node, label) {
  11611. var button = this._nodeToButton(node);
  11612. var textNode = button.textNode;
  11613. var dt = this.s.dt;
  11614. var jqNode = $(button.node);
  11615. var text = function (opt) {
  11616. return typeof opt === 'function'
  11617. ? opt(dt, jqNode, button.conf)
  11618. : opt;
  11619. };
  11620. if (label === undefined) {
  11621. return text(button.conf.text);
  11622. }
  11623. button.conf.text = label;
  11624. textNode.html(text(label));
  11625. return this;
  11626. },
  11627. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  11628. * Constructor
  11629. */
  11630. /**
  11631. * Buttons constructor
  11632. * @private
  11633. */
  11634. _constructor: function () {
  11635. var that = this;
  11636. var dt = this.s.dt;
  11637. var dtSettings = dt.settings()[0];
  11638. var buttons = this.c.buttons;
  11639. if (!dtSettings._buttons) {
  11640. dtSettings._buttons = [];
  11641. }
  11642. dtSettings._buttons.push({
  11643. inst: this,
  11644. name: this.c.name
  11645. });
  11646. for (var i = 0, ien = buttons.length; i < ien; i++) {
  11647. this.add(buttons[i]);
  11648. }
  11649. dt.on('destroy', function (e, settings) {
  11650. if (settings === dtSettings) {
  11651. that.destroy();
  11652. }
  11653. });
  11654. // Global key event binding to listen for button keys
  11655. $('body').on('keyup.' + this.s.namespace, function (e) {
  11656. if (
  11657. !document.activeElement ||
  11658. document.activeElement === document.body
  11659. ) {
  11660. // SUse a string of characters for fast lookup of if we need to
  11661. // handle this
  11662. var character = String.fromCharCode(e.keyCode).toLowerCase();
  11663. if (that.s.listenKeys.toLowerCase().indexOf(character) !== -1) {
  11664. that._keypress(character, e);
  11665. }
  11666. }
  11667. });
  11668. },
  11669. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  11670. * Private methods
  11671. */
  11672. /**
  11673. * Add a new button to the key press listener
  11674. * @param {object} conf Resolved button configuration object
  11675. * @private
  11676. */
  11677. _addKey: function (conf) {
  11678. if (conf.key) {
  11679. this.s.listenKeys += $.isPlainObject(conf.key)
  11680. ? conf.key.key
  11681. : conf.key;
  11682. }
  11683. },
  11684. /**
  11685. * Insert the buttons into the container. Call without parameters!
  11686. * @param {node} [container] Recursive only - Insert point
  11687. * @param {array} [buttons] Recursive only - Buttons array
  11688. * @private
  11689. */
  11690. _draw: function (container, buttons) {
  11691. if (!container) {
  11692. container = this.dom.container;
  11693. buttons = this.s.buttons;
  11694. }
  11695. container.children().detach();
  11696. for (var i = 0, ien = buttons.length; i < ien; i++) {
  11697. container.append(buttons[i].inserter);
  11698. container.append(' ');
  11699. if (buttons[i].buttons && buttons[i].buttons.length) {
  11700. this._draw(buttons[i].collection, buttons[i].buttons);
  11701. }
  11702. }
  11703. },
  11704. /**
  11705. * Create buttons from an array of buttons
  11706. * @param {array} attachTo Buttons array to attach to
  11707. * @param {object} button Button definition
  11708. * @param {boolean} inCollection true if the button is in a collection
  11709. * @private
  11710. */
  11711. _expandButton: function (
  11712. attachTo,
  11713. button,
  11714. split,
  11715. inCollection,
  11716. inSplit,
  11717. attachPoint,
  11718. parentConf
  11719. ) {
  11720. var dt = this.s.dt;
  11721. var isSplit = false;
  11722. var domCollection = this.c.dom.collection;
  11723. var buttons = !Array.isArray(button) ? [button] : button;
  11724. if (button === undefined) {
  11725. buttons = !Array.isArray(split) ? [split] : split;
  11726. }
  11727. for (var i = 0, ien = buttons.length; i < ien; i++) {
  11728. var conf = this._resolveExtends(buttons[i]);
  11729. if (!conf) {
  11730. continue;
  11731. }
  11732. isSplit = conf.config && conf.config.split ? true : false;
  11733. // If the configuration is an array, then expand the buttons at this
  11734. // point
  11735. if (Array.isArray(conf)) {
  11736. this._expandButton(
  11737. attachTo,
  11738. conf,
  11739. built !== undefined && built.conf !== undefined
  11740. ? built.conf.split
  11741. : undefined,
  11742. inCollection,
  11743. parentConf !== undefined && parentConf.split !== undefined,
  11744. attachPoint,
  11745. parentConf
  11746. );
  11747. continue;
  11748. }
  11749. var built = this._buildButton(
  11750. conf,
  11751. inCollection,
  11752. conf.split !== undefined ||
  11753. (conf.config !== undefined &&
  11754. conf.config.split !== undefined),
  11755. inSplit
  11756. );
  11757. if (!built) {
  11758. continue;
  11759. }
  11760. if (attachPoint !== undefined && attachPoint !== null) {
  11761. attachTo.splice(attachPoint, 0, built);
  11762. attachPoint++;
  11763. }
  11764. else {
  11765. attachTo.push(built);
  11766. }
  11767. // Create the dropdown for a collection
  11768. if (built.conf.buttons) {
  11769. built.collection = $(
  11770. '<' + domCollection.container.content.tag + '/>'
  11771. );
  11772. built.conf._collection = built.collection;
  11773. $(built.node).append(domCollection.action.dropHtml);
  11774. this._expandButton(
  11775. built.buttons,
  11776. built.conf.buttons,
  11777. built.conf.split,
  11778. !isSplit,
  11779. isSplit,
  11780. attachPoint,
  11781. built.conf
  11782. );
  11783. }
  11784. // And the split collection
  11785. if (built.conf.split) {
  11786. built.collection = $('<' + domCollection.container.tag + '/>');
  11787. built.conf._collection = built.collection;
  11788. for (var j = 0; j < built.conf.split.length; j++) {
  11789. var item = built.conf.split[j];
  11790. if (typeof item === 'object') {
  11791. item.parent = parentConf;
  11792. if (item.collectionLayout === undefined) {
  11793. item.collectionLayout = built.conf.collectionLayout;
  11794. }
  11795. if (item.dropup === undefined) {
  11796. item.dropup = built.conf.dropup;
  11797. }
  11798. if (item.fade === undefined) {
  11799. item.fade = built.conf.fade;
  11800. }
  11801. }
  11802. }
  11803. this._expandButton(
  11804. built.buttons,
  11805. built.conf.buttons,
  11806. built.conf.split,
  11807. !isSplit,
  11808. isSplit,
  11809. attachPoint,
  11810. built.conf
  11811. );
  11812. }
  11813. built.conf.parent = parentConf;
  11814. // init call is made here, rather than buildButton as it needs to
  11815. // be selectable, and for that it needs to be in the buttons array
  11816. if (conf.init) {
  11817. conf.init.call(dt.button(built.node), dt, $(built.node), conf);
  11818. }
  11819. }
  11820. },
  11821. /**
  11822. * Create an individual button
  11823. * @param {object} config Resolved button configuration
  11824. * @param {boolean} inCollection `true` if a collection button
  11825. * @return {object} Completed button description object
  11826. * @private
  11827. */
  11828. _buildButton: function (config, inCollection, isSplit, inSplit) {
  11829. var that = this;
  11830. var configDom = this.c.dom;
  11831. var textNode;
  11832. var dt = this.s.dt;
  11833. var text = function (opt) {
  11834. return typeof opt === 'function' ? opt(dt, button, config) : opt;
  11835. };
  11836. // Create an object that describes the button which can be in `dom.button`, or
  11837. // `dom.collection.button` or `dom.split.button` or `dom.collection.split.button`!
  11838. // Each should extend from `dom.button`.
  11839. var dom = $.extend(true, {}, configDom.button);
  11840. if (inCollection && isSplit && configDom.collection.split) {
  11841. $.extend(true, dom, configDom.collection.split.action);
  11842. }
  11843. else if (inSplit || inCollection) {
  11844. $.extend(true, dom, configDom.collection.button);
  11845. }
  11846. else if (isSplit) {
  11847. $.extend(true, dom, configDom.split.button);
  11848. }
  11849. // Spacers don't do much other than insert an element into the DOM
  11850. if (config.spacer) {
  11851. var spacer = $('<' + dom.spacer.tag + '/>')
  11852. .addClass(
  11853. 'dt-button-spacer ' +
  11854. config.style +
  11855. ' ' +
  11856. dom.spacer.className
  11857. )
  11858. .html(text(config.text));
  11859. return {
  11860. conf: config,
  11861. node: spacer,
  11862. inserter: spacer,
  11863. buttons: [],
  11864. inCollection: inCollection,
  11865. isSplit: isSplit,
  11866. collection: null,
  11867. textNode: spacer
  11868. };
  11869. }
  11870. // Make sure that the button is available based on whatever requirements
  11871. // it has. For example, PDF button require pdfmake
  11872. if (
  11873. config.available &&
  11874. !config.available(dt, config) &&
  11875. !config.html
  11876. ) {
  11877. return false;
  11878. }
  11879. var button;
  11880. if (!config.html) {
  11881. var run = function (e, dt, button, config, done) {
  11882. config.action.call(dt.button(button), e, dt, button, config, done);
  11883. $(dt.table().node()).triggerHandler('buttons-action.dt', [
  11884. dt.button(button),
  11885. dt,
  11886. button,
  11887. config
  11888. ]);
  11889. };
  11890. var action = function(e, dt, button, config) {
  11891. if (config.async) {
  11892. that.processing(button[0], true);
  11893. setTimeout(function () {
  11894. run(e, dt, button, config, function () {
  11895. that.processing(button[0], false);
  11896. });
  11897. }, config.async);
  11898. }
  11899. else {
  11900. run(e, dt, button, config, function () {});
  11901. }
  11902. }
  11903. var tag = config.tag || dom.tag;
  11904. var clickBlurs =
  11905. config.clickBlurs === undefined ? true : config.clickBlurs;
  11906. button = $('<' + tag + '/>')
  11907. .addClass(dom.className)
  11908. .attr('tabindex', this.s.dt.settings()[0].iTabIndex)
  11909. .attr('aria-controls', this.s.dt.table().node().id)
  11910. .on('click.dtb', function (e) {
  11911. e.preventDefault();
  11912. if (!button.hasClass(dom.disabled) && config.action) {
  11913. action(e, dt, button, config);
  11914. }
  11915. if (clickBlurs) {
  11916. button.trigger('blur');
  11917. }
  11918. })
  11919. .on('keypress.dtb', function (e) {
  11920. if (e.keyCode === 13) {
  11921. e.preventDefault();
  11922. if (!button.hasClass(dom.disabled) && config.action) {
  11923. action(e, dt, button, config);
  11924. }
  11925. }
  11926. });
  11927. // Make `a` tags act like a link
  11928. if (tag.toLowerCase() === 'a') {
  11929. button.attr('href', '#');
  11930. }
  11931. // Button tags should have `type=button` so they don't have any default behaviour
  11932. if (tag.toLowerCase() === 'button') {
  11933. button.attr('type', 'button');
  11934. }
  11935. if (dom.liner.tag) {
  11936. var liner = $('<' + dom.liner.tag + '/>')
  11937. .html(text(config.text))
  11938. .addClass(dom.liner.className);
  11939. if (dom.liner.tag.toLowerCase() === 'a') {
  11940. liner.attr('href', '#');
  11941. }
  11942. button.append(liner);
  11943. textNode = liner;
  11944. }
  11945. else {
  11946. button.html(text(config.text));
  11947. textNode = button;
  11948. }
  11949. if (config.enabled === false) {
  11950. button.addClass(dom.disabled);
  11951. }
  11952. if (config.className) {
  11953. button.addClass(config.className);
  11954. }
  11955. if (config.titleAttr) {
  11956. button.attr('title', text(config.titleAttr));
  11957. }
  11958. if (config.attr) {
  11959. button.attr(config.attr);
  11960. }
  11961. if (!config.namespace) {
  11962. config.namespace = '.dt-button-' + _buttonCounter++;
  11963. }
  11964. if (config.config !== undefined && config.config.split) {
  11965. config.split = config.config.split;
  11966. }
  11967. }
  11968. else {
  11969. button = $(config.html);
  11970. }
  11971. var buttonContainer = this.c.dom.buttonContainer;
  11972. var inserter;
  11973. if (buttonContainer && buttonContainer.tag) {
  11974. inserter = $('<' + buttonContainer.tag + '/>')
  11975. .addClass(buttonContainer.className)
  11976. .append(button);
  11977. }
  11978. else {
  11979. inserter = button;
  11980. }
  11981. this._addKey(config);
  11982. // Style integration callback for DOM manipulation
  11983. // Note that this is _not_ documented. It is currently
  11984. // for style integration only
  11985. if (this.c.buttonCreated) {
  11986. inserter = this.c.buttonCreated(config, inserter);
  11987. }
  11988. var splitDiv;
  11989. if (isSplit) {
  11990. var dropdownConf = inCollection
  11991. ? $.extend(true, this.c.dom.split, this.c.dom.collection.split)
  11992. : this.c.dom.split;
  11993. var wrapperConf = dropdownConf.wrapper;
  11994. splitDiv = $('<' + wrapperConf.tag + '/>')
  11995. .addClass(wrapperConf.className)
  11996. .append(button);
  11997. var dropButtonConfig = $.extend(config, {
  11998. align: dropdownConf.dropdown.align,
  11999. attr: {
  12000. 'aria-haspopup': 'dialog',
  12001. 'aria-expanded': false
  12002. },
  12003. className: dropdownConf.dropdown.className,
  12004. closeButton: false,
  12005. splitAlignClass: dropdownConf.dropdown.splitAlignClass,
  12006. text: dropdownConf.dropdown.text
  12007. });
  12008. this._addKey(dropButtonConfig);
  12009. var splitAction = function (e, dt, button, config) {
  12010. _dtButtons.split.action.call(
  12011. dt.button(splitDiv),
  12012. e,
  12013. dt,
  12014. button,
  12015. config
  12016. );
  12017. $(dt.table().node()).triggerHandler('buttons-action.dt', [
  12018. dt.button(button),
  12019. dt,
  12020. button,
  12021. config
  12022. ]);
  12023. button.attr('aria-expanded', true);
  12024. };
  12025. var dropButton = $(
  12026. '<button class="' +
  12027. dropdownConf.dropdown.className +
  12028. ' dt-button"></button>'
  12029. )
  12030. .html(dropdownConf.dropdown.dropHtml)
  12031. .on('click.dtb', function (e) {
  12032. e.preventDefault();
  12033. e.stopPropagation();
  12034. if (!dropButton.hasClass(dom.disabled)) {
  12035. splitAction(e, dt, dropButton, dropButtonConfig);
  12036. }
  12037. if (clickBlurs) {
  12038. dropButton.trigger('blur');
  12039. }
  12040. })
  12041. .on('keypress.dtb', function (e) {
  12042. if (e.keyCode === 13) {
  12043. e.preventDefault();
  12044. if (!dropButton.hasClass(dom.disabled)) {
  12045. splitAction(e, dt, dropButton, dropButtonConfig);
  12046. }
  12047. }
  12048. });
  12049. if (config.split.length === 0) {
  12050. dropButton.addClass('dtb-hide-drop');
  12051. }
  12052. splitDiv.append(dropButton).attr(dropButtonConfig.attr);
  12053. }
  12054. return {
  12055. conf: config,
  12056. node: isSplit ? splitDiv.get(0) : button.get(0),
  12057. inserter: isSplit ? splitDiv : inserter,
  12058. buttons: [],
  12059. inCollection: inCollection,
  12060. isSplit: isSplit,
  12061. inSplit: inSplit,
  12062. collection: null,
  12063. textNode: textNode
  12064. };
  12065. },
  12066. /**
  12067. * Get the button object from a node (recursive)
  12068. * @param {node} node Button node
  12069. * @param {array} [buttons] Button array, uses base if not defined
  12070. * @return {object} Button object
  12071. * @private
  12072. */
  12073. _nodeToButton: function (node, buttons) {
  12074. if (!buttons) {
  12075. buttons = this.s.buttons;
  12076. }
  12077. for (var i = 0, ien = buttons.length; i < ien; i++) {
  12078. if (buttons[i].node === node) {
  12079. return buttons[i];
  12080. }
  12081. if (buttons[i].buttons.length) {
  12082. var ret = this._nodeToButton(node, buttons[i].buttons);
  12083. if (ret) {
  12084. return ret;
  12085. }
  12086. }
  12087. }
  12088. },
  12089. /**
  12090. * Get container array for a button from a button node (recursive)
  12091. * @param {node} node Button node
  12092. * @param {array} [buttons] Button array, uses base if not defined
  12093. * @return {array} Button's host array
  12094. * @private
  12095. */
  12096. _nodeToHost: function (node, buttons) {
  12097. if (!buttons) {
  12098. buttons = this.s.buttons;
  12099. }
  12100. for (var i = 0, ien = buttons.length; i < ien; i++) {
  12101. if (buttons[i].node === node) {
  12102. return buttons;
  12103. }
  12104. if (buttons[i].buttons.length) {
  12105. var ret = this._nodeToHost(node, buttons[i].buttons);
  12106. if (ret) {
  12107. return ret;
  12108. }
  12109. }
  12110. }
  12111. },
  12112. /**
  12113. * Handle a key press - determine if any button's key configured matches
  12114. * what was typed and trigger the action if so.
  12115. * @param {string} character The character pressed
  12116. * @param {object} e Key event that triggered this call
  12117. * @private
  12118. */
  12119. _keypress: function (character, e) {
  12120. // Check if this button press already activated on another instance of Buttons
  12121. if (e._buttonsHandled) {
  12122. return;
  12123. }
  12124. var run = function (conf, node) {
  12125. if (!conf.key) {
  12126. return;
  12127. }
  12128. if (conf.key === character) {
  12129. e._buttonsHandled = true;
  12130. $(node).click();
  12131. }
  12132. else if ($.isPlainObject(conf.key)) {
  12133. if (conf.key.key !== character) {
  12134. return;
  12135. }
  12136. if (conf.key.shiftKey && !e.shiftKey) {
  12137. return;
  12138. }
  12139. if (conf.key.altKey && !e.altKey) {
  12140. return;
  12141. }
  12142. if (conf.key.ctrlKey && !e.ctrlKey) {
  12143. return;
  12144. }
  12145. if (conf.key.metaKey && !e.metaKey) {
  12146. return;
  12147. }
  12148. // Made it this far - it is good
  12149. e._buttonsHandled = true;
  12150. $(node).click();
  12151. }
  12152. };
  12153. var recurse = function (a) {
  12154. for (var i = 0, ien = a.length; i < ien; i++) {
  12155. run(a[i].conf, a[i].node);
  12156. if (a[i].buttons.length) {
  12157. recurse(a[i].buttons);
  12158. }
  12159. }
  12160. };
  12161. recurse(this.s.buttons);
  12162. },
  12163. /**
  12164. * Remove a key from the key listener for this instance (to be used when a
  12165. * button is removed)
  12166. * @param {object} conf Button configuration
  12167. * @private
  12168. */
  12169. _removeKey: function (conf) {
  12170. if (conf.key) {
  12171. var character = $.isPlainObject(conf.key) ? conf.key.key : conf.key;
  12172. // Remove only one character, as multiple buttons could have the
  12173. // same listening key
  12174. var a = this.s.listenKeys.split('');
  12175. var idx = $.inArray(character, a);
  12176. a.splice(idx, 1);
  12177. this.s.listenKeys = a.join('');
  12178. }
  12179. },
  12180. /**
  12181. * Resolve a button configuration
  12182. * @param {string|function|object} conf Button config to resolve
  12183. * @return {object} Button configuration
  12184. * @private
  12185. */
  12186. _resolveExtends: function (conf) {
  12187. var that = this;
  12188. var dt = this.s.dt;
  12189. var i, ien;
  12190. var toConfObject = function (base) {
  12191. var loop = 0;
  12192. // Loop until we have resolved to a button configuration, or an
  12193. // array of button configurations (which will be iterated
  12194. // separately)
  12195. while (!$.isPlainObject(base) && !Array.isArray(base)) {
  12196. if (base === undefined) {
  12197. return;
  12198. }
  12199. if (typeof base === 'function') {
  12200. base = base.call(that, dt, conf);
  12201. if (!base) {
  12202. return false;
  12203. }
  12204. }
  12205. else if (typeof base === 'string') {
  12206. if (!_dtButtons[base]) {
  12207. return { html: base };
  12208. }
  12209. base = _dtButtons[base];
  12210. }
  12211. loop++;
  12212. if (loop > 30) {
  12213. // Protect against misconfiguration killing the browser
  12214. throw 'Buttons: Too many iterations';
  12215. }
  12216. }
  12217. return Array.isArray(base) ? base : $.extend({}, base);
  12218. };
  12219. conf = toConfObject(conf);
  12220. while (conf && conf.extend) {
  12221. // Use `toConfObject` in case the button definition being extended
  12222. // is itself a string or a function
  12223. if (!_dtButtons[conf.extend]) {
  12224. throw 'Cannot extend unknown button type: ' + conf.extend;
  12225. }
  12226. var objArray = toConfObject(_dtButtons[conf.extend]);
  12227. if (Array.isArray(objArray)) {
  12228. return objArray;
  12229. }
  12230. else if (!objArray) {
  12231. // This is a little brutal as it might be possible to have a
  12232. // valid button without the extend, but if there is no extend
  12233. // then the host button would be acting in an undefined state
  12234. return false;
  12235. }
  12236. // Stash the current class name
  12237. var originalClassName = objArray.className;
  12238. if (conf.config !== undefined && objArray.config !== undefined) {
  12239. conf.config = $.extend({}, objArray.config, conf.config);
  12240. }
  12241. conf = $.extend({}, objArray, conf);
  12242. // The extend will have overwritten the original class name if the
  12243. // `conf` object also assigned a class, but we want to concatenate
  12244. // them so they are list that is combined from all extended buttons
  12245. if (originalClassName && conf.className !== originalClassName) {
  12246. conf.className = originalClassName + ' ' + conf.className;
  12247. }
  12248. // Although we want the `conf` object to overwrite almost all of
  12249. // the properties of the object being extended, the `extend`
  12250. // property should come from the object being extended
  12251. conf.extend = objArray.extend;
  12252. }
  12253. // Buttons to be added to a collection -gives the ability to define
  12254. // if buttons should be added to the start or end of a collection
  12255. var postfixButtons = conf.postfixButtons;
  12256. if (postfixButtons) {
  12257. if (!conf.buttons) {
  12258. conf.buttons = [];
  12259. }
  12260. for (i = 0, ien = postfixButtons.length; i < ien; i++) {
  12261. conf.buttons.push(postfixButtons[i]);
  12262. }
  12263. }
  12264. var prefixButtons = conf.prefixButtons;
  12265. if (prefixButtons) {
  12266. if (!conf.buttons) {
  12267. conf.buttons = [];
  12268. }
  12269. for (i = 0, ien = prefixButtons.length; i < ien; i++) {
  12270. conf.buttons.splice(i, 0, prefixButtons[i]);
  12271. }
  12272. }
  12273. return conf;
  12274. },
  12275. /**
  12276. * Display (and replace if there is an existing one) a popover attached to a button
  12277. * @param {string|node} content Content to show
  12278. * @param {DataTable.Api} hostButton DT API instance of the button
  12279. * @param {object} inOpts Options (see object below for all options)
  12280. */
  12281. _popover: function (content, hostButton, inOpts) {
  12282. var dt = hostButton;
  12283. var c = this.c;
  12284. var closed = false;
  12285. var options = $.extend(
  12286. {
  12287. align: 'button-left', // button-right, dt-container, split-left, split-right
  12288. autoClose: false,
  12289. background: true,
  12290. backgroundClassName: 'dt-button-background',
  12291. closeButton: true,
  12292. containerClassName: c.dom.collection.container.className,
  12293. contentClassName: c.dom.collection.container.content.className,
  12294. collectionLayout: '',
  12295. collectionTitle: '',
  12296. dropup: false,
  12297. fade: 400,
  12298. popoverTitle: '',
  12299. rightAlignClassName: 'dt-button-right',
  12300. tag: c.dom.collection.container.tag
  12301. },
  12302. inOpts
  12303. );
  12304. var containerSelector =
  12305. options.tag + '.' + options.containerClassName.replace(/ /g, '.');
  12306. var hostNode = hostButton.node();
  12307. var close = function () {
  12308. closed = true;
  12309. _fadeOut($(containerSelector), options.fade, function () {
  12310. $(this).detach();
  12311. });
  12312. $(
  12313. dt
  12314. .buttons('[aria-haspopup="dialog"][aria-expanded="true"]')
  12315. .nodes()
  12316. ).attr('aria-expanded', 'false');
  12317. $('div.dt-button-background').off('click.dtb-collection');
  12318. Buttons.background(
  12319. false,
  12320. options.backgroundClassName,
  12321. options.fade,
  12322. hostNode
  12323. );
  12324. $(window).off('resize.resize.dtb-collection');
  12325. $('body').off('.dtb-collection');
  12326. dt.off('buttons-action.b-internal');
  12327. dt.off('destroy');
  12328. };
  12329. if (content === false) {
  12330. close();
  12331. return;
  12332. }
  12333. var existingExpanded = $(
  12334. dt.buttons('[aria-haspopup="dialog"][aria-expanded="true"]').nodes()
  12335. );
  12336. if (existingExpanded.length) {
  12337. // Reuse the current position if the button that was triggered is inside an existing collection
  12338. if (hostNode.closest(containerSelector).length) {
  12339. hostNode = existingExpanded.eq(0);
  12340. }
  12341. close();
  12342. }
  12343. // Try to be smart about the layout
  12344. var cnt = $('.dt-button', content).length;
  12345. var mod = '';
  12346. if (cnt === 3) {
  12347. mod = 'dtb-b3';
  12348. }
  12349. else if (cnt === 2) {
  12350. mod = 'dtb-b2';
  12351. }
  12352. else if (cnt === 1) {
  12353. mod = 'dtb-b1';
  12354. }
  12355. var display = $('<' + options.tag + '/>')
  12356. .addClass(options.containerClassName)
  12357. .addClass(options.collectionLayout)
  12358. .addClass(options.splitAlignClass)
  12359. .addClass(mod)
  12360. .css('display', 'none')
  12361. .attr({
  12362. 'aria-modal': true,
  12363. role: 'dialog'
  12364. });
  12365. content = $(content)
  12366. .addClass(options.contentClassName)
  12367. .attr('role', 'menu')
  12368. .appendTo(display);
  12369. hostNode.attr('aria-expanded', 'true');
  12370. if (hostNode.parents('body')[0] !== document.body) {
  12371. hostNode = document.body.lastChild;
  12372. }
  12373. if (options.popoverTitle) {
  12374. display.prepend(
  12375. '<div class="dt-button-collection-title">' +
  12376. options.popoverTitle +
  12377. '</div>'
  12378. );
  12379. }
  12380. else if (options.collectionTitle) {
  12381. display.prepend(
  12382. '<div class="dt-button-collection-title">' +
  12383. options.collectionTitle +
  12384. '</div>'
  12385. );
  12386. }
  12387. if (options.closeButton) {
  12388. display
  12389. .prepend('<div class="dtb-popover-close">&times;</div>')
  12390. .addClass('dtb-collection-closeable');
  12391. }
  12392. _fadeIn(display.insertAfter(hostNode), options.fade);
  12393. var tableContainer = $(hostButton.table().container());
  12394. var position = display.css('position');
  12395. if (options.span === 'container' || options.align === 'dt-container') {
  12396. hostNode = hostNode.parent();
  12397. display.css('width', tableContainer.width());
  12398. }
  12399. // Align the popover relative to the DataTables container
  12400. // Useful for wide popovers such as SearchPanes
  12401. if (position === 'absolute') {
  12402. // Align relative to the host button
  12403. var offsetParent = $(hostNode[0].offsetParent);
  12404. var buttonPosition = hostNode.position();
  12405. var buttonOffset = hostNode.offset();
  12406. var tableSizes = offsetParent.offset();
  12407. var containerPosition = offsetParent.position();
  12408. var computed = window.getComputedStyle(offsetParent[0]);
  12409. tableSizes.height = offsetParent.outerHeight();
  12410. tableSizes.width =
  12411. offsetParent.width() + parseFloat(computed.paddingLeft);
  12412. tableSizes.right = tableSizes.left + tableSizes.width;
  12413. tableSizes.bottom = tableSizes.top + tableSizes.height;
  12414. // Set the initial position so we can read height / width
  12415. var top = buttonPosition.top + hostNode.outerHeight();
  12416. var left = buttonPosition.left;
  12417. display.css({
  12418. top: top,
  12419. left: left
  12420. });
  12421. // Get the popover position
  12422. computed = window.getComputedStyle(display[0]);
  12423. var popoverSizes = display.offset();
  12424. popoverSizes.height = display.outerHeight();
  12425. popoverSizes.width = display.outerWidth();
  12426. popoverSizes.right = popoverSizes.left + popoverSizes.width;
  12427. popoverSizes.bottom = popoverSizes.top + popoverSizes.height;
  12428. popoverSizes.marginTop = parseFloat(computed.marginTop);
  12429. popoverSizes.marginBottom = parseFloat(computed.marginBottom);
  12430. // First position per the class requirements - pop up and right align
  12431. if (options.dropup) {
  12432. top =
  12433. buttonPosition.top -
  12434. popoverSizes.height -
  12435. popoverSizes.marginTop -
  12436. popoverSizes.marginBottom;
  12437. }
  12438. if (
  12439. options.align === 'button-right' ||
  12440. display.hasClass(options.rightAlignClassName)
  12441. ) {
  12442. left =
  12443. buttonPosition.left -
  12444. popoverSizes.width +
  12445. hostNode.outerWidth();
  12446. }
  12447. // Container alignment - make sure it doesn't overflow the table container
  12448. if (
  12449. options.align === 'dt-container' ||
  12450. options.align === 'container'
  12451. ) {
  12452. if (left < buttonPosition.left) {
  12453. left = -buttonPosition.left;
  12454. }
  12455. }
  12456. // Window adjustment
  12457. if (
  12458. containerPosition.left + left + popoverSizes.width >
  12459. $(window).width()
  12460. ) {
  12461. // Overflowing the document to the right
  12462. left =
  12463. $(window).width() -
  12464. popoverSizes.width -
  12465. containerPosition.left;
  12466. }
  12467. if (buttonOffset.left + left < 0) {
  12468. // Off to the left of the document
  12469. left = -buttonOffset.left;
  12470. }
  12471. if (
  12472. containerPosition.top + top + popoverSizes.height >
  12473. $(window).height() + $(window).scrollTop()
  12474. ) {
  12475. // Pop up if otherwise we'd need the user to scroll down
  12476. top =
  12477. buttonPosition.top -
  12478. popoverSizes.height -
  12479. popoverSizes.marginTop -
  12480. popoverSizes.marginBottom;
  12481. }
  12482. if (containerPosition.top + top < $(window).scrollTop()) {
  12483. // Correction for when the top is beyond the top of the page
  12484. top = buttonPosition.top + hostNode.outerHeight();
  12485. }
  12486. // Calculations all done - now set it
  12487. display.css({
  12488. top: top,
  12489. left: left
  12490. });
  12491. }
  12492. else {
  12493. // Fix position - centre on screen
  12494. var place = function () {
  12495. var half = $(window).height() / 2;
  12496. var top = display.height() / 2;
  12497. if (top > half) {
  12498. top = half;
  12499. }
  12500. display.css('marginTop', top * -1);
  12501. };
  12502. place();
  12503. $(window).on('resize.dtb-collection', function () {
  12504. place();
  12505. });
  12506. }
  12507. if (options.background) {
  12508. Buttons.background(
  12509. true,
  12510. options.backgroundClassName,
  12511. options.fade,
  12512. options.backgroundHost || hostNode
  12513. );
  12514. }
  12515. // This is bonkers, but if we don't have a click listener on the
  12516. // background element, iOS Safari will ignore the body click
  12517. // listener below. An empty function here is all that is
  12518. // required to make it work...
  12519. $('div.dt-button-background').on(
  12520. 'click.dtb-collection',
  12521. function () {}
  12522. );
  12523. if (options.autoClose) {
  12524. setTimeout(function () {
  12525. dt.on('buttons-action.b-internal', function (e, btn, dt, node) {
  12526. if (node[0] === hostNode[0]) {
  12527. return;
  12528. }
  12529. close();
  12530. });
  12531. }, 0);
  12532. }
  12533. $(display).trigger('buttons-popover.dt');
  12534. dt.on('destroy', close);
  12535. setTimeout(function () {
  12536. closed = false;
  12537. $('body')
  12538. .on('click.dtb-collection', function (e) {
  12539. if (closed) {
  12540. return;
  12541. }
  12542. // andSelf is deprecated in jQ1.8, but we want 1.7 compat
  12543. var back = $.fn.addBack ? 'addBack' : 'andSelf';
  12544. var parent = $(e.target).parent()[0];
  12545. if (
  12546. (!$(e.target).parents()[back]().filter(content)
  12547. .length &&
  12548. !$(parent).hasClass('dt-buttons')) ||
  12549. $(e.target).hasClass('dt-button-background')
  12550. ) {
  12551. close();
  12552. }
  12553. })
  12554. .on('keyup.dtb-collection', function (e) {
  12555. if (e.keyCode === 27) {
  12556. close();
  12557. }
  12558. })
  12559. .on('keydown.dtb-collection', function (e) {
  12560. // Focus trap for tab key
  12561. var elements = $('a, button', content);
  12562. var active = document.activeElement;
  12563. if (e.keyCode !== 9) {
  12564. // tab
  12565. return;
  12566. }
  12567. if (elements.index(active) === -1) {
  12568. // If current focus is not inside the popover
  12569. elements.first().focus();
  12570. e.preventDefault();
  12571. }
  12572. else if (e.shiftKey) {
  12573. // Reverse tabbing order when shift key is pressed
  12574. if (active === elements[0]) {
  12575. elements.last().focus();
  12576. e.preventDefault();
  12577. }
  12578. }
  12579. else {
  12580. if (active === elements.last()[0]) {
  12581. elements.first().focus();
  12582. e.preventDefault();
  12583. }
  12584. }
  12585. });
  12586. }, 0);
  12587. }
  12588. });
  12589. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  12590. * Statics
  12591. */
  12592. /**
  12593. * Show / hide a background layer behind a collection
  12594. * @param {boolean} Flag to indicate if the background should be shown or
  12595. * hidden
  12596. * @param {string} Class to assign to the background
  12597. * @static
  12598. */
  12599. Buttons.background = function (show, className, fade, insertPoint) {
  12600. if (fade === undefined) {
  12601. fade = 400;
  12602. }
  12603. if (!insertPoint) {
  12604. insertPoint = document.body;
  12605. }
  12606. if (show) {
  12607. _fadeIn(
  12608. $('<div/>')
  12609. .addClass(className)
  12610. .css('display', 'none')
  12611. .insertAfter(insertPoint),
  12612. fade
  12613. );
  12614. }
  12615. else {
  12616. _fadeOut($('div.' + className), fade, function () {
  12617. $(this).removeClass(className).remove();
  12618. });
  12619. }
  12620. };
  12621. /**
  12622. * Instance selector - select Buttons instances based on an instance selector
  12623. * value from the buttons assigned to a DataTable. This is only useful if
  12624. * multiple instances are attached to a DataTable.
  12625. * @param {string|int|array} Instance selector - see `instance-selector`
  12626. * documentation on the DataTables site
  12627. * @param {array} Button instance array that was attached to the DataTables
  12628. * settings object
  12629. * @return {array} Buttons instances
  12630. * @static
  12631. */
  12632. Buttons.instanceSelector = function (group, buttons) {
  12633. if (group === undefined || group === null) {
  12634. return $.map(buttons, function (v) {
  12635. return v.inst;
  12636. });
  12637. }
  12638. var ret = [];
  12639. var names = $.map(buttons, function (v) {
  12640. return v.name;
  12641. });
  12642. // Flatten the group selector into an array of single options
  12643. var process = function (input) {
  12644. if (Array.isArray(input)) {
  12645. for (var i = 0, ien = input.length; i < ien; i++) {
  12646. process(input[i]);
  12647. }
  12648. return;
  12649. }
  12650. if (typeof input === 'string') {
  12651. if (input.indexOf(',') !== -1) {
  12652. // String selector, list of names
  12653. process(input.split(','));
  12654. }
  12655. else {
  12656. // String selector individual name
  12657. var idx = $.inArray(input.trim(), names);
  12658. if (idx !== -1) {
  12659. ret.push(buttons[idx].inst);
  12660. }
  12661. }
  12662. }
  12663. else if (typeof input === 'number') {
  12664. // Index selector
  12665. ret.push(buttons[input].inst);
  12666. }
  12667. else if (typeof input === 'object' && input.nodeName) {
  12668. // Element selector
  12669. for (var j = 0; j < buttons.length; j++) {
  12670. if (buttons[j].inst.dom.container[0] === input) {
  12671. ret.push(buttons[j].inst);
  12672. }
  12673. }
  12674. }
  12675. else if (typeof input === 'object') {
  12676. // Actual instance selector
  12677. ret.push(input);
  12678. }
  12679. };
  12680. process(group);
  12681. return ret;
  12682. };
  12683. /**
  12684. * Button selector - select one or more buttons from a selector input so some
  12685. * operation can be performed on them.
  12686. * @param {array} Button instances array that the selector should operate on
  12687. * @param {string|int|node|jQuery|array} Button selector - see
  12688. * `button-selector` documentation on the DataTables site
  12689. * @return {array} Array of objects containing `inst` and `idx` properties of
  12690. * the selected buttons so you know which instance each button belongs to.
  12691. * @static
  12692. */
  12693. Buttons.buttonSelector = function (insts, selector) {
  12694. var ret = [];
  12695. var nodeBuilder = function (a, buttons, baseIdx) {
  12696. var button;
  12697. var idx;
  12698. for (var i = 0, ien = buttons.length; i < ien; i++) {
  12699. button = buttons[i];
  12700. if (button) {
  12701. idx = baseIdx !== undefined ? baseIdx + i : i + '';
  12702. a.push({
  12703. node: button.node,
  12704. name: button.conf.name,
  12705. idx: idx
  12706. });
  12707. if (button.buttons) {
  12708. nodeBuilder(a, button.buttons, idx + '-');
  12709. }
  12710. }
  12711. }
  12712. };
  12713. var run = function (selector, inst) {
  12714. var i, ien;
  12715. var buttons = [];
  12716. nodeBuilder(buttons, inst.s.buttons);
  12717. var nodes = $.map(buttons, function (v) {
  12718. return v.node;
  12719. });
  12720. if (Array.isArray(selector) || selector instanceof $) {
  12721. for (i = 0, ien = selector.length; i < ien; i++) {
  12722. run(selector[i], inst);
  12723. }
  12724. return;
  12725. }
  12726. if (selector === null || selector === undefined || selector === '*') {
  12727. // Select all
  12728. for (i = 0, ien = buttons.length; i < ien; i++) {
  12729. ret.push({
  12730. inst: inst,
  12731. node: buttons[i].node
  12732. });
  12733. }
  12734. }
  12735. else if (typeof selector === 'number') {
  12736. // Main button index selector
  12737. if (inst.s.buttons[selector]) {
  12738. ret.push({
  12739. inst: inst,
  12740. node: inst.s.buttons[selector].node
  12741. });
  12742. }
  12743. }
  12744. else if (typeof selector === 'string') {
  12745. if (selector.indexOf(',') !== -1) {
  12746. // Split
  12747. var a = selector.split(',');
  12748. for (i = 0, ien = a.length; i < ien; i++) {
  12749. run(a[i].trim(), inst);
  12750. }
  12751. }
  12752. else if (selector.match(/^\d+(\-\d+)*$/)) {
  12753. // Sub-button index selector
  12754. var indexes = $.map(buttons, function (v) {
  12755. return v.idx;
  12756. });
  12757. ret.push({
  12758. inst: inst,
  12759. node: buttons[$.inArray(selector, indexes)].node
  12760. });
  12761. }
  12762. else if (selector.indexOf(':name') !== -1) {
  12763. // Button name selector
  12764. var name = selector.replace(':name', '');
  12765. for (i = 0, ien = buttons.length; i < ien; i++) {
  12766. if (buttons[i].name === name) {
  12767. ret.push({
  12768. inst: inst,
  12769. node: buttons[i].node
  12770. });
  12771. }
  12772. }
  12773. }
  12774. else {
  12775. // jQuery selector on the nodes
  12776. $(nodes)
  12777. .filter(selector)
  12778. .each(function () {
  12779. ret.push({
  12780. inst: inst,
  12781. node: this
  12782. });
  12783. });
  12784. }
  12785. }
  12786. else if (typeof selector === 'object' && selector.nodeName) {
  12787. // Node selector
  12788. var idx = $.inArray(selector, nodes);
  12789. if (idx !== -1) {
  12790. ret.push({
  12791. inst: inst,
  12792. node: nodes[idx]
  12793. });
  12794. }
  12795. }
  12796. };
  12797. for (var i = 0, ien = insts.length; i < ien; i++) {
  12798. var inst = insts[i];
  12799. run(selector, inst);
  12800. }
  12801. return ret;
  12802. };
  12803. /**
  12804. * Default function used for formatting output data.
  12805. * @param {*} str Data to strip
  12806. */
  12807. Buttons.stripData = function (str, config) {
  12808. if (typeof str !== 'string') {
  12809. return str;
  12810. }
  12811. // Always remove script tags
  12812. str = Buttons.stripHtmlScript(str);
  12813. // Always remove comments
  12814. str = Buttons.stripHtmlComments(str);
  12815. if (!config || config.stripHtml) {
  12816. str = DataTable.util.stripHtml(str);
  12817. }
  12818. if (!config || config.trim) {
  12819. str = str.trim();
  12820. }
  12821. if (!config || config.stripNewlines) {
  12822. str = str.replace(/\n/g, ' ');
  12823. }
  12824. if (!config || config.decodeEntities) {
  12825. if (_entityDecoder) {
  12826. str = _entityDecoder(str);
  12827. }
  12828. else {
  12829. _exportTextarea.innerHTML = str;
  12830. str = _exportTextarea.value;
  12831. }
  12832. }
  12833. return str;
  12834. };
  12835. /**
  12836. * Provide a custom entity decoding function - e.g. a regex one, which can be
  12837. * much faster than the built in DOM option, but also larger code size.
  12838. * @param {function} fn
  12839. */
  12840. Buttons.entityDecoder = function (fn) {
  12841. _entityDecoder = fn;
  12842. };
  12843. /**
  12844. * Common function for stripping HTML comments
  12845. *
  12846. * @param {*} input
  12847. * @returns
  12848. */
  12849. Buttons.stripHtmlComments = function (input) {
  12850. var previous;
  12851. do {
  12852. previous = input;
  12853. input = input.replace(/(<!--.*?--!?>)|(<!--[\S\s]+?--!?>)|(<!--[\S\s]*?$)/g, '');
  12854. } while (input !== previous);
  12855. return input;
  12856. };
  12857. /**
  12858. * Common function for stripping HTML script tags
  12859. *
  12860. * @param {*} input
  12861. * @returns
  12862. */
  12863. Buttons.stripHtmlScript = function (input) {
  12864. var previous;
  12865. do {
  12866. previous = input;
  12867. input = input.replace(/<script\b[^<]*(?:(?!<\/script[^>]*>)<[^<]*)*<\/script[^>]*>/gi, '');
  12868. } while (input !== previous);
  12869. return input;
  12870. };
  12871. /**
  12872. * Buttons defaults. For full documentation, please refer to the docs/option
  12873. * directory or the DataTables site.
  12874. * @type {Object}
  12875. * @static
  12876. */
  12877. Buttons.defaults = {
  12878. buttons: ['copy', 'excel', 'csv', 'pdf', 'print'],
  12879. name: 'main',
  12880. tabIndex: 0,
  12881. dom: {
  12882. container: {
  12883. tag: 'div',
  12884. className: 'dt-buttons'
  12885. },
  12886. collection: {
  12887. action: {
  12888. // action button
  12889. dropHtml: '<span class="dt-button-down-arrow">&#x25BC;</span>'
  12890. },
  12891. container: {
  12892. // The element used for the dropdown
  12893. className: 'dt-button-collection',
  12894. content: {
  12895. className: '',
  12896. tag: 'div'
  12897. },
  12898. tag: 'div'
  12899. }
  12900. // optionally
  12901. // , button: IButton - buttons inside the collection container
  12902. // , split: ISplit - splits inside the collection container
  12903. },
  12904. button: {
  12905. tag: 'button',
  12906. className: 'dt-button',
  12907. active: 'dt-button-active', // class name
  12908. disabled: 'disabled', // class name
  12909. spacer: {
  12910. className: 'dt-button-spacer',
  12911. tag: 'span'
  12912. },
  12913. liner: {
  12914. tag: 'span',
  12915. className: ''
  12916. }
  12917. },
  12918. split: {
  12919. action: {
  12920. // action button
  12921. className: 'dt-button-split-drop-button dt-button',
  12922. tag: 'button'
  12923. },
  12924. dropdown: {
  12925. // button to trigger the dropdown
  12926. align: 'split-right',
  12927. className: 'dt-button-split-drop',
  12928. dropHtml: '<span class="dt-button-down-arrow">&#x25BC;</span>',
  12929. splitAlignClass: 'dt-button-split-left',
  12930. tag: 'button'
  12931. },
  12932. wrapper: {
  12933. // wrap around both
  12934. className: 'dt-button-split',
  12935. tag: 'div'
  12936. }
  12937. }
  12938. }
  12939. };
  12940. /**
  12941. * Version information
  12942. * @type {string}
  12943. * @static
  12944. */
  12945. Buttons.version = '3.0.2';
  12946. $.extend(_dtButtons, {
  12947. collection: {
  12948. text: function (dt) {
  12949. return dt.i18n('buttons.collection', 'Collection');
  12950. },
  12951. className: 'buttons-collection',
  12952. closeButton: false,
  12953. init: function (dt, button) {
  12954. button.attr('aria-expanded', false);
  12955. },
  12956. action: function (e, dt, button, config) {
  12957. if (config._collection.parents('body').length) {
  12958. this.popover(false, config);
  12959. }
  12960. else {
  12961. this.popover(config._collection, config);
  12962. }
  12963. // When activated using a key - auto focus on the
  12964. // first item in the popover
  12965. if (e.type === 'keypress') {
  12966. $('a, button', config._collection).eq(0).focus();
  12967. }
  12968. },
  12969. attr: {
  12970. 'aria-haspopup': 'dialog'
  12971. }
  12972. // Also the popover options, defined in Buttons.popover
  12973. },
  12974. split: {
  12975. text: function (dt) {
  12976. return dt.i18n('buttons.split', 'Split');
  12977. },
  12978. className: 'buttons-split',
  12979. closeButton: false,
  12980. init: function (dt, button) {
  12981. return button.attr('aria-expanded', false);
  12982. },
  12983. action: function (e, dt, button, config) {
  12984. this.popover(config._collection, config);
  12985. },
  12986. attr: {
  12987. 'aria-haspopup': 'dialog'
  12988. }
  12989. // Also the popover options, defined in Buttons.popover
  12990. },
  12991. copy: function () {
  12992. if (_dtButtons.copyHtml5) {
  12993. return 'copyHtml5';
  12994. }
  12995. },
  12996. csv: function (dt, conf) {
  12997. if (_dtButtons.csvHtml5 && _dtButtons.csvHtml5.available(dt, conf)) {
  12998. return 'csvHtml5';
  12999. }
  13000. },
  13001. excel: function (dt, conf) {
  13002. if (
  13003. _dtButtons.excelHtml5 &&
  13004. _dtButtons.excelHtml5.available(dt, conf)
  13005. ) {
  13006. return 'excelHtml5';
  13007. }
  13008. },
  13009. pdf: function (dt, conf) {
  13010. if (_dtButtons.pdfHtml5 && _dtButtons.pdfHtml5.available(dt, conf)) {
  13011. return 'pdfHtml5';
  13012. }
  13013. },
  13014. pageLength: function (dt) {
  13015. var lengthMenu = dt.settings()[0].aLengthMenu;
  13016. var vals = [];
  13017. var lang = [];
  13018. var text = function (dt) {
  13019. return dt.i18n(
  13020. 'buttons.pageLength',
  13021. {
  13022. '-1': 'Show all rows',
  13023. _: 'Show %d rows'
  13024. },
  13025. dt.page.len()
  13026. );
  13027. };
  13028. // Support for DataTables 1.x 2D array
  13029. if (Array.isArray(lengthMenu[0])) {
  13030. vals = lengthMenu[0];
  13031. lang = lengthMenu[1];
  13032. }
  13033. else {
  13034. for (var i = 0; i < lengthMenu.length; i++) {
  13035. var option = lengthMenu[i];
  13036. // Support for DataTables 2 object in the array
  13037. if ($.isPlainObject(option)) {
  13038. vals.push(option.value);
  13039. lang.push(option.label);
  13040. }
  13041. else {
  13042. vals.push(option);
  13043. lang.push(option);
  13044. }
  13045. }
  13046. }
  13047. return {
  13048. extend: 'collection',
  13049. text: text,
  13050. className: 'buttons-page-length',
  13051. autoClose: true,
  13052. buttons: $.map(vals, function (val, i) {
  13053. return {
  13054. text: lang[i],
  13055. className: 'button-page-length',
  13056. action: function (e, dt) {
  13057. dt.page.len(val).draw();
  13058. },
  13059. init: function (dt, node, conf) {
  13060. var that = this;
  13061. var fn = function () {
  13062. that.active(dt.page.len() === val);
  13063. };
  13064. dt.on('length.dt' + conf.namespace, fn);
  13065. fn();
  13066. },
  13067. destroy: function (dt, node, conf) {
  13068. dt.off('length.dt' + conf.namespace);
  13069. }
  13070. };
  13071. }),
  13072. init: function (dt, node, conf) {
  13073. var that = this;
  13074. dt.on('length.dt' + conf.namespace, function () {
  13075. that.text(conf.text);
  13076. });
  13077. },
  13078. destroy: function (dt, node, conf) {
  13079. dt.off('length.dt' + conf.namespace);
  13080. }
  13081. };
  13082. },
  13083. spacer: {
  13084. style: 'empty',
  13085. spacer: true,
  13086. text: function (dt) {
  13087. return dt.i18n('buttons.spacer', '');
  13088. }
  13089. }
  13090. });
  13091. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  13092. * DataTables API
  13093. *
  13094. * For complete documentation, please refer to the docs/api directory or the
  13095. * DataTables site
  13096. */
  13097. // Buttons group and individual button selector
  13098. DataTable.Api.register('buttons()', function (group, selector) {
  13099. // Argument shifting
  13100. if (selector === undefined) {
  13101. selector = group;
  13102. group = undefined;
  13103. }
  13104. this.selector.buttonGroup = group;
  13105. var res = this.iterator(
  13106. true,
  13107. 'table',
  13108. function (ctx) {
  13109. if (ctx._buttons) {
  13110. return Buttons.buttonSelector(
  13111. Buttons.instanceSelector(group, ctx._buttons),
  13112. selector
  13113. );
  13114. }
  13115. },
  13116. true
  13117. );
  13118. res._groupSelector = group;
  13119. return res;
  13120. });
  13121. // Individual button selector
  13122. DataTable.Api.register('button()', function (group, selector) {
  13123. // just run buttons() and truncate
  13124. var buttons = this.buttons(group, selector);
  13125. if (buttons.length > 1) {
  13126. buttons.splice(1, buttons.length);
  13127. }
  13128. return buttons;
  13129. });
  13130. // Active buttons
  13131. DataTable.Api.registerPlural(
  13132. 'buttons().active()',
  13133. 'button().active()',
  13134. function (flag) {
  13135. if (flag === undefined) {
  13136. return this.map(function (set) {
  13137. return set.inst.active(set.node);
  13138. });
  13139. }
  13140. return this.each(function (set) {
  13141. set.inst.active(set.node, flag);
  13142. });
  13143. }
  13144. );
  13145. // Get / set button action
  13146. DataTable.Api.registerPlural(
  13147. 'buttons().action()',
  13148. 'button().action()',
  13149. function (action) {
  13150. if (action === undefined) {
  13151. return this.map(function (set) {
  13152. return set.inst.action(set.node);
  13153. });
  13154. }
  13155. return this.each(function (set) {
  13156. set.inst.action(set.node, action);
  13157. });
  13158. }
  13159. );
  13160. // Collection control
  13161. DataTable.Api.registerPlural(
  13162. 'buttons().collectionRebuild()',
  13163. 'button().collectionRebuild()',
  13164. function (buttons) {
  13165. return this.each(function (set) {
  13166. for (var i = 0; i < buttons.length; i++) {
  13167. if (typeof buttons[i] === 'object') {
  13168. buttons[i].parentConf = set;
  13169. }
  13170. }
  13171. set.inst.collectionRebuild(set.node, buttons);
  13172. });
  13173. }
  13174. );
  13175. // Enable / disable buttons
  13176. DataTable.Api.register(
  13177. ['buttons().enable()', 'button().enable()'],
  13178. function (flag) {
  13179. return this.each(function (set) {
  13180. set.inst.enable(set.node, flag);
  13181. });
  13182. }
  13183. );
  13184. // Disable buttons
  13185. DataTable.Api.register(
  13186. ['buttons().disable()', 'button().disable()'],
  13187. function () {
  13188. return this.each(function (set) {
  13189. set.inst.disable(set.node);
  13190. });
  13191. }
  13192. );
  13193. // Button index
  13194. DataTable.Api.register('button().index()', function () {
  13195. var idx = null;
  13196. this.each(function (set) {
  13197. var res = set.inst.index(set.node);
  13198. if (res !== null) {
  13199. idx = res;
  13200. }
  13201. });
  13202. return idx;
  13203. });
  13204. // Get button nodes
  13205. DataTable.Api.registerPlural(
  13206. 'buttons().nodes()',
  13207. 'button().node()',
  13208. function () {
  13209. var jq = $();
  13210. // jQuery will automatically reduce duplicates to a single entry
  13211. $(
  13212. this.each(function (set) {
  13213. jq = jq.add(set.inst.node(set.node));
  13214. })
  13215. );
  13216. return jq;
  13217. }
  13218. );
  13219. // Get / set button processing state
  13220. DataTable.Api.registerPlural(
  13221. 'buttons().processing()',
  13222. 'button().processing()',
  13223. function (flag) {
  13224. if (flag === undefined) {
  13225. return this.map(function (set) {
  13226. return set.inst.processing(set.node);
  13227. });
  13228. }
  13229. return this.each(function (set) {
  13230. set.inst.processing(set.node, flag);
  13231. });
  13232. }
  13233. );
  13234. // Get / set button text (i.e. the button labels)
  13235. DataTable.Api.registerPlural(
  13236. 'buttons().text()',
  13237. 'button().text()',
  13238. function (label) {
  13239. if (label === undefined) {
  13240. return this.map(function (set) {
  13241. return set.inst.text(set.node);
  13242. });
  13243. }
  13244. return this.each(function (set) {
  13245. set.inst.text(set.node, label);
  13246. });
  13247. }
  13248. );
  13249. // Trigger a button's action
  13250. DataTable.Api.registerPlural(
  13251. 'buttons().trigger()',
  13252. 'button().trigger()',
  13253. function () {
  13254. return this.each(function (set) {
  13255. set.inst.node(set.node).trigger('click');
  13256. });
  13257. }
  13258. );
  13259. // Button resolver to the popover
  13260. DataTable.Api.register('button().popover()', function (content, options) {
  13261. return this.map(function (set) {
  13262. return set.inst._popover(content, this.button(this[0].node), options);
  13263. });
  13264. });
  13265. // Get the container elements
  13266. DataTable.Api.register('buttons().containers()', function () {
  13267. var jq = $();
  13268. var groupSelector = this._groupSelector;
  13269. // We need to use the group selector directly, since if there are no buttons
  13270. // the result set will be empty
  13271. this.iterator(true, 'table', function (ctx) {
  13272. if (ctx._buttons) {
  13273. var insts = Buttons.instanceSelector(groupSelector, ctx._buttons);
  13274. for (var i = 0, ien = insts.length; i < ien; i++) {
  13275. jq = jq.add(insts[i].container());
  13276. }
  13277. }
  13278. });
  13279. return jq;
  13280. });
  13281. DataTable.Api.register('buttons().container()', function () {
  13282. // API level of nesting is `buttons()` so we can zip into the containers method
  13283. return this.containers().eq(0);
  13284. });
  13285. // Add a new button
  13286. DataTable.Api.register('button().add()', function (idx, conf, draw) {
  13287. var ctx = this.context;
  13288. // Don't use `this` as it could be empty - select the instances directly
  13289. if (ctx.length) {
  13290. var inst = Buttons.instanceSelector(
  13291. this._groupSelector,
  13292. ctx[0]._buttons
  13293. );
  13294. if (inst.length) {
  13295. inst[0].add(conf, idx, draw);
  13296. }
  13297. }
  13298. return this.button(this._groupSelector, idx);
  13299. });
  13300. // Destroy the button sets selected
  13301. DataTable.Api.register('buttons().destroy()', function () {
  13302. this.pluck('inst')
  13303. .unique()
  13304. .each(function (inst) {
  13305. inst.destroy();
  13306. });
  13307. return this;
  13308. });
  13309. // Remove a button
  13310. DataTable.Api.registerPlural(
  13311. 'buttons().remove()',
  13312. 'buttons().remove()',
  13313. function () {
  13314. this.each(function (set) {
  13315. set.inst.remove(set.node);
  13316. });
  13317. return this;
  13318. }
  13319. );
  13320. // Information box that can be used by buttons
  13321. var _infoTimer;
  13322. DataTable.Api.register('buttons.info()', function (title, message, time) {
  13323. var that = this;
  13324. if (title === false) {
  13325. this.off('destroy.btn-info');
  13326. _fadeOut($('#datatables_buttons_info'), 400, function () {
  13327. $(this).remove();
  13328. });
  13329. clearTimeout(_infoTimer);
  13330. _infoTimer = null;
  13331. return this;
  13332. }
  13333. if (_infoTimer) {
  13334. clearTimeout(_infoTimer);
  13335. }
  13336. if ($('#datatables_buttons_info').length) {
  13337. $('#datatables_buttons_info').remove();
  13338. }
  13339. title = title ? '<h2>' + title + '</h2>' : '';
  13340. _fadeIn(
  13341. $('<div id="datatables_buttons_info" class="dt-button-info"/>')
  13342. .html(title)
  13343. .append(
  13344. $('<div/>')[typeof message === 'string' ? 'html' : 'append'](
  13345. message
  13346. )
  13347. )
  13348. .css('display', 'none')
  13349. .appendTo('body')
  13350. );
  13351. if (time !== undefined && time !== 0) {
  13352. _infoTimer = setTimeout(function () {
  13353. that.buttons.info(false);
  13354. }, time);
  13355. }
  13356. this.on('destroy.btn-info', function () {
  13357. that.buttons.info(false);
  13358. });
  13359. return this;
  13360. });
  13361. // Get data from the table for export - this is common to a number of plug-in
  13362. // buttons so it is included in the Buttons core library
  13363. DataTable.Api.register('buttons.exportData()', function (options) {
  13364. if (this.context.length) {
  13365. return _exportData(new DataTable.Api(this.context[0]), options);
  13366. }
  13367. });
  13368. // Get information about the export that is common to many of the export data
  13369. // types (DRY)
  13370. DataTable.Api.register('buttons.exportInfo()', function (conf) {
  13371. if (!conf) {
  13372. conf = {};
  13373. }
  13374. return {
  13375. filename: _filename(conf, this),
  13376. title: _title(conf, this),
  13377. messageTop: _message(this, conf, conf.message || conf.messageTop, 'top'),
  13378. messageBottom: _message(this, conf, conf.messageBottom, 'bottom')
  13379. };
  13380. });
  13381. /**
  13382. * Get the file name for an exported file.
  13383. *
  13384. * @param {object} config Button configuration
  13385. * @param {object} dt DataTable instance
  13386. */
  13387. var _filename = function (config, dt) {
  13388. // Backwards compatibility
  13389. var filename =
  13390. config.filename === '*' &&
  13391. config.title !== '*' &&
  13392. config.title !== undefined &&
  13393. config.title !== null &&
  13394. config.title !== ''
  13395. ? config.title
  13396. : config.filename;
  13397. if (typeof filename === 'function') {
  13398. filename = filename(config, dt);
  13399. }
  13400. if (filename === undefined || filename === null) {
  13401. return null;
  13402. }
  13403. if (filename.indexOf('*') !== -1) {
  13404. filename = filename.replace(/\*/g, $('head > title').text()).trim();
  13405. }
  13406. // Strip characters which the OS will object to
  13407. filename = filename.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, '');
  13408. var extension = _stringOrFunction(config.extension, config, dt);
  13409. if (!extension) {
  13410. extension = '';
  13411. }
  13412. return filename + extension;
  13413. };
  13414. /**
  13415. * Simply utility method to allow parameters to be given as a function
  13416. *
  13417. * @param {undefined|string|function} option Option
  13418. * @return {null|string} Resolved value
  13419. */
  13420. var _stringOrFunction = function (option, config, dt) {
  13421. if (option === null || option === undefined) {
  13422. return null;
  13423. }
  13424. else if (typeof option === 'function') {
  13425. return option(config, dt);
  13426. }
  13427. return option;
  13428. };
  13429. /**
  13430. * Get the title for an exported file.
  13431. *
  13432. * @param {object} config Button configuration
  13433. */
  13434. var _title = function (config, dt) {
  13435. var title = _stringOrFunction(config.title, config, dt);
  13436. return title === null
  13437. ? null
  13438. : title.indexOf('*') !== -1
  13439. ? title.replace(/\*/g, $('head > title').text() || 'Exported data')
  13440. : title;
  13441. };
  13442. var _message = function (dt, config, option, position) {
  13443. var message = _stringOrFunction(option, config, dt);
  13444. if (message === null) {
  13445. return null;
  13446. }
  13447. var caption = $('caption', dt.table().container()).eq(0);
  13448. if (message === '*') {
  13449. var side = caption.css('caption-side');
  13450. if (side !== position) {
  13451. return null;
  13452. }
  13453. return caption.length ? caption.text() : '';
  13454. }
  13455. return message;
  13456. };
  13457. var _exportTextarea = $('<textarea/>')[0];
  13458. var _exportData = function (dt, inOpts) {
  13459. var config = $.extend(
  13460. true,
  13461. {},
  13462. {
  13463. rows: null,
  13464. columns: '',
  13465. modifier: {
  13466. search: 'applied',
  13467. order: 'applied'
  13468. },
  13469. orthogonal: 'display',
  13470. stripHtml: true,
  13471. stripNewlines: true,
  13472. decodeEntities: true,
  13473. trim: true,
  13474. format: {
  13475. header: function (d) {
  13476. return Buttons.stripData(d, config);
  13477. },
  13478. footer: function (d) {
  13479. return Buttons.stripData(d, config);
  13480. },
  13481. body: function (d) {
  13482. return Buttons.stripData(d, config);
  13483. }
  13484. },
  13485. customizeData: null,
  13486. customizeZip: null
  13487. },
  13488. inOpts
  13489. );
  13490. var header = dt
  13491. .columns(config.columns)
  13492. .indexes()
  13493. .map(function (idx) {
  13494. var col = dt.column(idx);
  13495. return config.format.header(col.title(), idx, col.header());
  13496. })
  13497. .toArray();
  13498. var footer = dt.table().footer()
  13499. ? dt
  13500. .columns(config.columns)
  13501. .indexes()
  13502. .map(function (idx) {
  13503. var el = dt.column(idx).footer();
  13504. var val = '';
  13505. if (el) {
  13506. var inner = $('.dt-column-title', el);
  13507. val = inner.length
  13508. ? inner.html()
  13509. : $(el).html();
  13510. }
  13511. return config.format.footer(val, idx, el);
  13512. })
  13513. .toArray()
  13514. : null;
  13515. // If Select is available on this table, and any rows are selected, limit the export
  13516. // to the selected rows. If no rows are selected, all rows will be exported. Specify
  13517. // a `selected` modifier to control directly.
  13518. var modifier = $.extend({}, config.modifier);
  13519. if (
  13520. dt.select &&
  13521. typeof dt.select.info === 'function' &&
  13522. modifier.selected === undefined
  13523. ) {
  13524. if (
  13525. dt.rows(config.rows, $.extend({ selected: true }, modifier)).any()
  13526. ) {
  13527. $.extend(modifier, { selected: true });
  13528. }
  13529. }
  13530. var rowIndexes = dt.rows(config.rows, modifier).indexes().toArray();
  13531. var selectedCells = dt.cells(rowIndexes, config.columns, {
  13532. order: modifier.order
  13533. });
  13534. var cells = selectedCells.render(config.orthogonal).toArray();
  13535. var cellNodes = selectedCells.nodes().toArray();
  13536. var cellIndexes = selectedCells.indexes().toArray();
  13537. var columns = dt.columns(config.columns).count();
  13538. var rows = columns > 0 ? cells.length / columns : 0;
  13539. var body = [];
  13540. var cellCounter = 0;
  13541. for (var i = 0, ien = rows; i < ien; i++) {
  13542. var row = [columns];
  13543. for (var j = 0; j < columns; j++) {
  13544. row[j] = config.format.body(
  13545. cells[cellCounter],
  13546. cellIndexes[cellCounter].row,
  13547. cellIndexes[cellCounter].column,
  13548. cellNodes[cellCounter]
  13549. );
  13550. cellCounter++;
  13551. }
  13552. body[i] = row;
  13553. }
  13554. var data = {
  13555. header: header,
  13556. headerStructure: _headerFormatter(
  13557. config.format.header,
  13558. dt.table().header.structure(config.columns)
  13559. ),
  13560. footer: footer,
  13561. footerStructure: _headerFormatter(
  13562. config.format.footer,
  13563. dt.table().footer.structure(config.columns)
  13564. ),
  13565. body: body
  13566. };
  13567. if (config.customizeData) {
  13568. config.customizeData(data);
  13569. }
  13570. return data;
  13571. };
  13572. function _headerFormatter(formatter, struct) {
  13573. for (var i=0 ; i<struct.length ; i++) {
  13574. for (var j=0 ; j<struct[i].length ; j++) {
  13575. var item = struct[i][j];
  13576. if (item) {
  13577. item.title = formatter(
  13578. item.title,
  13579. j,
  13580. item.cell
  13581. );
  13582. }
  13583. }
  13584. }
  13585. return struct;
  13586. }
  13587. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  13588. * DataTables interface
  13589. */
  13590. // Attach to DataTables objects for global access
  13591. $.fn.dataTable.Buttons = Buttons;
  13592. $.fn.DataTable.Buttons = Buttons;
  13593. // DataTables creation - check if the buttons have been defined for this table,
  13594. // they will have been if the `B` option was used in `dom`, otherwise we should
  13595. // create the buttons instance here so they can be inserted into the document
  13596. // using the API. Listen for `init` for compatibility with pre 1.10.10, but to
  13597. // be removed in future.
  13598. $(document).on('init.dt plugin-init.dt', function (e, settings) {
  13599. if (e.namespace !== 'dt') {
  13600. return;
  13601. }
  13602. var opts = settings.oInit.buttons || DataTable.defaults.buttons;
  13603. if (opts && !settings._buttons) {
  13604. new Buttons(settings, opts).container();
  13605. }
  13606. });
  13607. function _init(settings, options) {
  13608. var api = new DataTable.Api(settings);
  13609. var opts = options
  13610. ? options
  13611. : api.init().buttons || DataTable.defaults.buttons;
  13612. return new Buttons(api, opts).container();
  13613. }
  13614. // DataTables 1 `dom` feature option
  13615. DataTable.ext.feature.push({
  13616. fnInit: _init,
  13617. cFeature: 'B'
  13618. });
  13619. // DataTables 2 layout feature
  13620. if (DataTable.feature) {
  13621. DataTable.feature.register('buttons', _init);
  13622. }
  13623. return DataTable;
  13624. }));
  13625. /*! Bootstrap integration for DataTables' Buttons
  13626. * © SpryMedia Ltd - datatables.net/license
  13627. */
  13628. (function( factory ){
  13629. if ( typeof define === 'function' && define.amd ) {
  13630. // AMD
  13631. define( ['jquery', 'datatables.net-bs5', 'datatables.net-buttons'], function ( $ ) {
  13632. return factory( $, window, document );
  13633. } );
  13634. }
  13635. else if ( typeof exports === 'object' ) {
  13636. // CommonJS
  13637. var jq = require('jquery');
  13638. var cjsRequires = function (root, $) {
  13639. if ( ! $.fn.dataTable ) {
  13640. require('datatables.net-bs5')(root, $);
  13641. }
  13642. if ( ! $.fn.dataTable.Buttons ) {
  13643. require('datatables.net-buttons')(root, $);
  13644. }
  13645. };
  13646. if (typeof window === 'undefined') {
  13647. module.exports = function (root, $) {
  13648. if ( ! root ) {
  13649. // CommonJS environments without a window global must pass a
  13650. // root. This will give an error otherwise
  13651. root = window;
  13652. }
  13653. if ( ! $ ) {
  13654. $ = jq( root );
  13655. }
  13656. cjsRequires( root, $ );
  13657. return factory( $, root, root.document );
  13658. };
  13659. }
  13660. else {
  13661. cjsRequires( window, jq );
  13662. module.exports = factory( jq, window, window.document );
  13663. }
  13664. }
  13665. else {
  13666. // Browser
  13667. factory( jQuery, window, document );
  13668. }
  13669. }(function( $, window, document ) {
  13670. 'use strict';
  13671. var DataTable = $.fn.dataTable;
  13672. $.extend(true, DataTable.Buttons.defaults, {
  13673. dom: {
  13674. container: {
  13675. className: 'dt-buttons btn-group flex-wrap'
  13676. },
  13677. button: {
  13678. className: 'btn btn-secondary',
  13679. active: 'active'
  13680. },
  13681. collection: {
  13682. action: {
  13683. dropHtml: ''
  13684. },
  13685. container: {
  13686. tag: 'div',
  13687. className: 'dropdown-menu dt-button-collection'
  13688. },
  13689. closeButton: false,
  13690. button: {
  13691. tag: 'a',
  13692. className: 'dt-button dropdown-item',
  13693. active: 'dt-button-active',
  13694. disabled: 'disabled',
  13695. spacer: {
  13696. className: 'dropdown-divider',
  13697. tag: 'hr'
  13698. }
  13699. }
  13700. },
  13701. split: {
  13702. action: {
  13703. tag: 'a',
  13704. className: 'btn btn-secondary dt-button-split-drop-button',
  13705. closeButton: false
  13706. },
  13707. dropdown: {
  13708. tag: 'button',
  13709. dropHtml: '',
  13710. className:
  13711. 'btn btn-secondary dt-button-split-drop dropdown-toggle dropdown-toggle-split',
  13712. closeButton: false,
  13713. align: 'split-left',
  13714. splitAlignClass: 'dt-button-split-left'
  13715. },
  13716. wrapper: {
  13717. tag: 'div',
  13718. className: 'dt-button-split btn-group',
  13719. closeButton: false
  13720. }
  13721. }
  13722. },
  13723. buttonCreated: function (config, button) {
  13724. return config.buttons ? $('<div class="btn-group"/>').append(button) : button;
  13725. }
  13726. });
  13727. DataTable.ext.buttons.collection.className += ' dropdown-toggle';
  13728. DataTable.ext.buttons.collection.rightAlignClassName = 'dropdown-menu-right';
  13729. return DataTable;
  13730. }));
  13731. /*!
  13732. * HTML5 export buttons for Buttons and DataTables.
  13733. * © SpryMedia Ltd - datatables.net/license
  13734. *
  13735. * FileSaver.js (1.3.3) - MIT license
  13736. * Copyright © 2016 Eli Grey - http://eligrey.com
  13737. */
  13738. (function( factory ){
  13739. if ( typeof define === 'function' && define.amd ) {
  13740. // AMD
  13741. define( ['jquery', 'datatables.net', 'datatables.net-buttons'], function ( $ ) {
  13742. return factory( $, window, document );
  13743. } );
  13744. }
  13745. else if ( typeof exports === 'object' ) {
  13746. // CommonJS
  13747. var jq = require('jquery');
  13748. var cjsRequires = function (root, $) {
  13749. if ( ! $.fn.dataTable ) {
  13750. require('datatables.net')(root, $);
  13751. }
  13752. if ( ! $.fn.dataTable.Buttons ) {
  13753. require('datatables.net-buttons')(root, $);
  13754. }
  13755. };
  13756. if (typeof window === 'undefined') {
  13757. module.exports = function (root, $) {
  13758. if ( ! root ) {
  13759. // CommonJS environments without a window global must pass a
  13760. // root. This will give an error otherwise
  13761. root = window;
  13762. }
  13763. if ( ! $ ) {
  13764. $ = jq( root );
  13765. }
  13766. cjsRequires( root, $ );
  13767. return factory( $, root, root.document );
  13768. };
  13769. }
  13770. else {
  13771. cjsRequires( window, jq );
  13772. module.exports = factory( jq, window, window.document );
  13773. }
  13774. }
  13775. else {
  13776. // Browser
  13777. factory( jQuery, window, document );
  13778. }
  13779. }(function( $, window, document ) {
  13780. 'use strict';
  13781. var DataTable = $.fn.dataTable;
  13782. // Allow the constructor to pass in JSZip and PDFMake from external requires.
  13783. // Otherwise, use globally defined variables, if they are available.
  13784. var useJszip;
  13785. var usePdfmake;
  13786. function _jsZip() {
  13787. return useJszip || window.JSZip;
  13788. }
  13789. function _pdfMake() {
  13790. return usePdfmake || window.pdfMake;
  13791. }
  13792. DataTable.Buttons.pdfMake = function (_) {
  13793. if (!_) {
  13794. return _pdfMake();
  13795. }
  13796. usePdfmake = _;
  13797. };
  13798. DataTable.Buttons.jszip = function (_) {
  13799. if (!_) {
  13800. return _jsZip();
  13801. }
  13802. useJszip = _;
  13803. };
  13804. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  13805. * FileSaver.js dependency
  13806. */
  13807. /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
  13808. var _saveAs = (function (view) {
  13809. 'use strict';
  13810. // IE <10 is explicitly unsupported
  13811. if (
  13812. typeof view === 'undefined' ||
  13813. (typeof navigator !== 'undefined' &&
  13814. /MSIE [1-9]\./.test(navigator.userAgent))
  13815. ) {
  13816. return;
  13817. }
  13818. var doc = view.document,
  13819. // only get URL when necessary in case Blob.js hasn't overridden it yet
  13820. get_URL = function () {
  13821. return view.URL || view.webkitURL || view;
  13822. },
  13823. save_link = doc.createElementNS('http://www.w3.org/1999/xhtml', 'a'),
  13824. can_use_save_link = 'download' in save_link,
  13825. click = function (node) {
  13826. var event = new MouseEvent('click');
  13827. node.dispatchEvent(event);
  13828. },
  13829. is_safari = /constructor/i.test(view.HTMLElement) || view.safari,
  13830. is_chrome_ios = /CriOS\/[\d]+/.test(navigator.userAgent),
  13831. throw_outside = function (ex) {
  13832. (view.setImmediate || view.setTimeout)(function () {
  13833. throw ex;
  13834. }, 0);
  13835. },
  13836. force_saveable_type = 'application/octet-stream',
  13837. // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
  13838. arbitrary_revoke_timeout = 1000 * 40, // in ms
  13839. revoke = function (file) {
  13840. var revoker = function () {
  13841. if (typeof file === 'string') {
  13842. // file is an object URL
  13843. get_URL().revokeObjectURL(file);
  13844. }
  13845. else {
  13846. // file is a File
  13847. file.remove();
  13848. }
  13849. };
  13850. setTimeout(revoker, arbitrary_revoke_timeout);
  13851. },
  13852. dispatch = function (filesaver, event_types, event) {
  13853. event_types = [].concat(event_types);
  13854. var i = event_types.length;
  13855. while (i--) {
  13856. var listener = filesaver['on' + event_types[i]];
  13857. if (typeof listener === 'function') {
  13858. try {
  13859. listener.call(filesaver, event || filesaver);
  13860. } catch (ex) {
  13861. throw_outside(ex);
  13862. }
  13863. }
  13864. }
  13865. },
  13866. auto_bom = function (blob) {
  13867. // prepend BOM for UTF-8 XML and text/* types (including HTML)
  13868. // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
  13869. if (
  13870. /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(
  13871. blob.type
  13872. )
  13873. ) {
  13874. return new Blob([String.fromCharCode(0xfeff), blob], {
  13875. type: blob.type
  13876. });
  13877. }
  13878. return blob;
  13879. },
  13880. FileSaver = function (blob, name, no_auto_bom) {
  13881. if (!no_auto_bom) {
  13882. blob = auto_bom(blob);
  13883. }
  13884. // First try a.download, then web filesystem, then object URLs
  13885. var filesaver = this,
  13886. type = blob.type,
  13887. force = type === force_saveable_type,
  13888. object_url,
  13889. dispatch_all = function () {
  13890. dispatch(
  13891. filesaver,
  13892. 'writestart progress write writeend'.split(' ')
  13893. );
  13894. },
  13895. // on any filesys errors revert to saving with object URLs
  13896. fs_error = function () {
  13897. if (
  13898. (is_chrome_ios || (force && is_safari)) &&
  13899. view.FileReader
  13900. ) {
  13901. // Safari doesn't allow downloading of blob urls
  13902. var reader = new FileReader();
  13903. reader.onloadend = function () {
  13904. var url = is_chrome_ios
  13905. ? reader.result
  13906. : reader.result.replace(
  13907. /^data:[^;]*;/,
  13908. 'data:attachment/file;'
  13909. );
  13910. var popup = view.open(url, '_blank');
  13911. if (!popup) view.location.href = url;
  13912. url = undefined; // release reference before dispatching
  13913. filesaver.readyState = filesaver.DONE;
  13914. dispatch_all();
  13915. };
  13916. reader.readAsDataURL(blob);
  13917. filesaver.readyState = filesaver.INIT;
  13918. return;
  13919. }
  13920. // don't create more object URLs than needed
  13921. if (!object_url) {
  13922. object_url = get_URL().createObjectURL(blob);
  13923. }
  13924. if (force) {
  13925. view.location.href = object_url;
  13926. }
  13927. else {
  13928. var opened = view.open(object_url, '_blank');
  13929. if (!opened) {
  13930. // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
  13931. view.location.href = object_url;
  13932. }
  13933. }
  13934. filesaver.readyState = filesaver.DONE;
  13935. dispatch_all();
  13936. revoke(object_url);
  13937. };
  13938. filesaver.readyState = filesaver.INIT;
  13939. if (can_use_save_link) {
  13940. object_url = get_URL().createObjectURL(blob);
  13941. setTimeout(function () {
  13942. save_link.href = object_url;
  13943. save_link.download = name;
  13944. click(save_link);
  13945. dispatch_all();
  13946. revoke(object_url);
  13947. filesaver.readyState = filesaver.DONE;
  13948. });
  13949. return;
  13950. }
  13951. fs_error();
  13952. },
  13953. FS_proto = FileSaver.prototype,
  13954. saveAs = function (blob, name, no_auto_bom) {
  13955. return new FileSaver(
  13956. blob,
  13957. name || blob.name || 'download',
  13958. no_auto_bom
  13959. );
  13960. };
  13961. // IE 10+ (native saveAs)
  13962. if (typeof navigator !== 'undefined' && navigator.msSaveOrOpenBlob) {
  13963. return function (blob, name, no_auto_bom) {
  13964. name = name || blob.name || 'download';
  13965. if (!no_auto_bom) {
  13966. blob = auto_bom(blob);
  13967. }
  13968. return navigator.msSaveOrOpenBlob(blob, name);
  13969. };
  13970. }
  13971. FS_proto.abort = function () {};
  13972. FS_proto.readyState = FS_proto.INIT = 0;
  13973. FS_proto.WRITING = 1;
  13974. FS_proto.DONE = 2;
  13975. FS_proto.error =
  13976. FS_proto.onwritestart =
  13977. FS_proto.onprogress =
  13978. FS_proto.onwrite =
  13979. FS_proto.onabort =
  13980. FS_proto.onerror =
  13981. FS_proto.onwriteend =
  13982. null;
  13983. return saveAs;
  13984. })(
  13985. (typeof self !== 'undefined' && self) ||
  13986. (typeof window !== 'undefined' && window) ||
  13987. this.content
  13988. );
  13989. // Expose file saver on the DataTables API. Can't attach to `DataTables.Buttons`
  13990. // since this file can be loaded before Button's core!
  13991. DataTable.fileSave = _saveAs;
  13992. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  13993. * Local (private) functions
  13994. */
  13995. /**
  13996. * Get the sheet name for Excel exports.
  13997. *
  13998. * @param {object} config Button configuration
  13999. */
  14000. var _sheetname = function (config) {
  14001. var sheetName = 'Sheet1';
  14002. if (config.sheetName) {
  14003. sheetName = config.sheetName.replace(/[\[\]\*\/\\\?\:]/g, '');
  14004. }
  14005. return sheetName;
  14006. };
  14007. /**
  14008. * Get the newline character(s)
  14009. *
  14010. * @param {object} config Button configuration
  14011. * @return {string} Newline character
  14012. */
  14013. var _newLine = function (config) {
  14014. return config.newline
  14015. ? config.newline
  14016. : navigator.userAgent.match(/Windows/)
  14017. ? '\r\n'
  14018. : '\n';
  14019. };
  14020. /**
  14021. * Combine the data from the `buttons.exportData` method into a string that
  14022. * will be used in the export file.
  14023. *
  14024. * @param {DataTable.Api} dt DataTables API instance
  14025. * @param {object} config Button configuration
  14026. * @return {object} The data to export
  14027. */
  14028. var _exportData = function (dt, config) {
  14029. var newLine = _newLine(config);
  14030. var data = dt.buttons.exportData(config.exportOptions);
  14031. var boundary = config.fieldBoundary;
  14032. var separator = config.fieldSeparator;
  14033. var reBoundary = new RegExp(boundary, 'g');
  14034. var escapeChar = config.escapeChar !== undefined ? config.escapeChar : '\\';
  14035. var join = function (a) {
  14036. var s = '';
  14037. // If there is a field boundary, then we might need to escape it in
  14038. // the source data
  14039. for (var i = 0, ien = a.length; i < ien; i++) {
  14040. if (i > 0) {
  14041. s += separator;
  14042. }
  14043. s += boundary
  14044. ? boundary +
  14045. ('' + a[i]).replace(reBoundary, escapeChar + boundary) +
  14046. boundary
  14047. : a[i];
  14048. }
  14049. return s;
  14050. };
  14051. var header = '';
  14052. var footer = '';
  14053. var body = [];
  14054. if (config.header) {
  14055. header =
  14056. data.headerStructure
  14057. .map(function (row) {
  14058. return join(
  14059. row.map(function (cell) {
  14060. return cell ? cell.title : '';
  14061. })
  14062. );
  14063. })
  14064. .join(newLine) + newLine;
  14065. }
  14066. if (config.footer && data.footer) {
  14067. footer =
  14068. data.footerStructure
  14069. .map(function (row) {
  14070. return join(
  14071. row.map(function (cell) {
  14072. return cell ? cell.title : '';
  14073. })
  14074. );
  14075. })
  14076. .join(newLine) + newLine;
  14077. }
  14078. for (var i = 0, ien = data.body.length; i < ien; i++) {
  14079. body.push(join(data.body[i]));
  14080. }
  14081. return {
  14082. str: header + body.join(newLine) + newLine + footer,
  14083. rows: body.length
  14084. };
  14085. };
  14086. /**
  14087. * Older versions of Safari (prior to tech preview 18) don't support the
  14088. * download option required.
  14089. *
  14090. * @return {Boolean} `true` if old Safari
  14091. */
  14092. var _isDuffSafari = function () {
  14093. var safari =
  14094. navigator.userAgent.indexOf('Safari') !== -1 &&
  14095. navigator.userAgent.indexOf('Chrome') === -1 &&
  14096. navigator.userAgent.indexOf('Opera') === -1;
  14097. if (!safari) {
  14098. return false;
  14099. }
  14100. var version = navigator.userAgent.match(/AppleWebKit\/(\d+\.\d+)/);
  14101. if (version && version.length > 1 && version[1] * 1 < 603.1) {
  14102. return true;
  14103. }
  14104. return false;
  14105. };
  14106. /**
  14107. * Convert from numeric position to letter for column names in Excel
  14108. * @param {int} n Column number
  14109. * @return {string} Column letter(s) name
  14110. */
  14111. function createCellPos(n) {
  14112. var ordA = 'A'.charCodeAt(0);
  14113. var ordZ = 'Z'.charCodeAt(0);
  14114. var len = ordZ - ordA + 1;
  14115. var s = '';
  14116. while (n >= 0) {
  14117. s = String.fromCharCode((n % len) + ordA) + s;
  14118. n = Math.floor(n / len) - 1;
  14119. }
  14120. return s;
  14121. }
  14122. try {
  14123. var _serialiser = new XMLSerializer();
  14124. var _ieExcel;
  14125. } catch (t) {
  14126. // noop
  14127. }
  14128. /**
  14129. * Recursively add XML files from an object's structure to a ZIP file. This
  14130. * allows the XSLX file to be easily defined with an object's structure matching
  14131. * the files structure.
  14132. *
  14133. * @param {JSZip} zip ZIP package
  14134. * @param {object} obj Object to add (recursive)
  14135. */
  14136. function _addToZip(zip, obj) {
  14137. if (_ieExcel === undefined) {
  14138. // Detect if we are dealing with IE's _awful_ serialiser by seeing if it
  14139. // drop attributes
  14140. _ieExcel =
  14141. _serialiser
  14142. .serializeToString(
  14143. new window.DOMParser().parseFromString(
  14144. excelStrings['xl/worksheets/sheet1.xml'],
  14145. 'text/xml'
  14146. )
  14147. )
  14148. .indexOf('xmlns:r') === -1;
  14149. }
  14150. $.each(obj, function (name, val) {
  14151. if ($.isPlainObject(val)) {
  14152. var newDir = zip.folder(name);
  14153. _addToZip(newDir, val);
  14154. }
  14155. else {
  14156. if (_ieExcel) {
  14157. // IE's XML serialiser will drop some name space attributes from
  14158. // from the root node, so we need to save them. Do this by
  14159. // replacing the namespace nodes with a regular attribute that
  14160. // we convert back when serialised. Edge does not have this
  14161. // issue
  14162. var worksheet = val.childNodes[0];
  14163. var i, ien;
  14164. var attrs = [];
  14165. for (i = worksheet.attributes.length - 1; i >= 0; i--) {
  14166. var attrName = worksheet.attributes[i].nodeName;
  14167. var attrValue = worksheet.attributes[i].nodeValue;
  14168. if (attrName.indexOf(':') !== -1) {
  14169. attrs.push({ name: attrName, value: attrValue });
  14170. worksheet.removeAttribute(attrName);
  14171. }
  14172. }
  14173. for (i = 0, ien = attrs.length; i < ien; i++) {
  14174. var attr = val.createAttribute(
  14175. attrs[i].name.replace(':', '_dt_b_namespace_token_')
  14176. );
  14177. attr.value = attrs[i].value;
  14178. worksheet.setAttributeNode(attr);
  14179. }
  14180. }
  14181. var str = _serialiser.serializeToString(val);
  14182. // Fix IE's XML
  14183. if (_ieExcel) {
  14184. // IE doesn't include the XML declaration
  14185. if (str.indexOf('<?xml') === -1) {
  14186. str =
  14187. '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
  14188. str;
  14189. }
  14190. // Return namespace attributes to being as such
  14191. str = str.replace(/_dt_b_namespace_token_/g, ':');
  14192. // Remove testing name space that IE puts into the space preserve attr
  14193. str = str.replace(/xmlns:NS[\d]+="" NS[\d]+:/g, '');
  14194. }
  14195. // Safari, IE and Edge will put empty name space attributes onto
  14196. // various elements making them useless. This strips them out
  14197. str = str.replace(/<([^<>]*?) xmlns=""([^<>]*?)>/g, '<$1 $2>');
  14198. zip.file(name, str);
  14199. }
  14200. });
  14201. }
  14202. /**
  14203. * Create an XML node and add any children, attributes, etc without needing to
  14204. * be verbose in the DOM.
  14205. *
  14206. * @param {object} doc XML document
  14207. * @param {string} nodeName Node name
  14208. * @param {object} opts Options - can be `attr` (attributes), `children`
  14209. * (child nodes) and `text` (text content)
  14210. * @return {node} Created node
  14211. */
  14212. function _createNode(doc, nodeName, opts) {
  14213. var tempNode = doc.createElement(nodeName);
  14214. if (opts) {
  14215. if (opts.attr) {
  14216. $(tempNode).attr(opts.attr);
  14217. }
  14218. if (opts.children) {
  14219. $.each(opts.children, function (key, value) {
  14220. tempNode.appendChild(value);
  14221. });
  14222. }
  14223. if (opts.text !== null && opts.text !== undefined) {
  14224. tempNode.appendChild(doc.createTextNode(opts.text));
  14225. }
  14226. }
  14227. return tempNode;
  14228. }
  14229. /**
  14230. * Get the width for an Excel column based on the contents of that column
  14231. * @param {object} data Data for export
  14232. * @param {int} col Column index
  14233. * @return {int} Column width
  14234. */
  14235. function _excelColWidth(data, col) {
  14236. var max = data.header[col].length;
  14237. var len, lineSplit, str;
  14238. if (data.footer && data.footer[col] && data.footer[col].length > max) {
  14239. max = data.footer[col].length;
  14240. }
  14241. for (var i = 0, ien = data.body.length; i < ien; i++) {
  14242. var point = data.body[i][col];
  14243. str = point !== null && point !== undefined ? point.toString() : '';
  14244. // If there is a newline character, workout the width of the column
  14245. // based on the longest line in the string
  14246. if (str.indexOf('\n') !== -1) {
  14247. lineSplit = str.split('\n');
  14248. lineSplit.sort(function (a, b) {
  14249. return b.length - a.length;
  14250. });
  14251. len = lineSplit[0].length;
  14252. }
  14253. else {
  14254. len = str.length;
  14255. }
  14256. if (len > max) {
  14257. max = len;
  14258. }
  14259. // Max width rather than having potentially massive column widths
  14260. if (max > 40) {
  14261. return 54; // 40 * 1.35
  14262. }
  14263. }
  14264. max *= 1.35;
  14265. // And a min width
  14266. return max > 6 ? max : 6;
  14267. }
  14268. // Excel - Pre-defined strings to build a basic XLSX file
  14269. var excelStrings = {
  14270. '_rels/.rels':
  14271. '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
  14272. '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' +
  14273. '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>' +
  14274. '</Relationships>',
  14275. 'xl/_rels/workbook.xml.rels':
  14276. '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
  14277. '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">' +
  14278. '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>' +
  14279. '<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>' +
  14280. '</Relationships>',
  14281. '[Content_Types].xml':
  14282. '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
  14283. '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">' +
  14284. '<Default Extension="xml" ContentType="application/xml" />' +
  14285. '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml" />' +
  14286. '<Default Extension="jpeg" ContentType="image/jpeg" />' +
  14287. '<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" />' +
  14288. '<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" />' +
  14289. '<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml" />' +
  14290. '</Types>',
  14291. 'xl/workbook.xml':
  14292. '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
  14293. '<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">' +
  14294. '<fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="24816"/>' +
  14295. '<workbookPr showInkAnnotation="0" autoCompressPictures="0"/>' +
  14296. '<bookViews>' +
  14297. '<workbookView xWindow="0" yWindow="0" windowWidth="25600" windowHeight="19020" tabRatio="500"/>' +
  14298. '</bookViews>' +
  14299. '<sheets>' +
  14300. '<sheet name="Sheet1" sheetId="1" r:id="rId1"/>' +
  14301. '</sheets>' +
  14302. '<definedNames/>' +
  14303. '</workbook>',
  14304. 'xl/worksheets/sheet1.xml':
  14305. '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>' +
  14306. '<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">' +
  14307. '<sheetData/>' +
  14308. '<mergeCells count="0"/>' +
  14309. '</worksheet>',
  14310. 'xl/styles.xml':
  14311. '<?xml version="1.0" encoding="UTF-8"?>' +
  14312. '<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">' +
  14313. '<numFmts count="6">' +
  14314. '<numFmt numFmtId="164" formatCode="[$$-409]#,##0.00;-[$$-409]#,##0.00"/>' +
  14315. '<numFmt numFmtId="165" formatCode="&quot;£&quot;#,##0.00"/>' +
  14316. '<numFmt numFmtId="166" formatCode="[$€-2] #,##0.00"/>' +
  14317. '<numFmt numFmtId="167" formatCode="0.0%"/>' +
  14318. '<numFmt numFmtId="168" formatCode="#,##0;(#,##0)"/>' +
  14319. '<numFmt numFmtId="169" formatCode="#,##0.00;(#,##0.00)"/>' +
  14320. '</numFmts>' +
  14321. '<fonts count="5" x14ac:knownFonts="1">' +
  14322. '<font>' +
  14323. '<sz val="11" />' +
  14324. '<name val="Calibri" />' +
  14325. '</font>' +
  14326. '<font>' +
  14327. '<sz val="11" />' +
  14328. '<name val="Calibri" />' +
  14329. '<color rgb="FFFFFFFF" />' +
  14330. '</font>' +
  14331. '<font>' +
  14332. '<sz val="11" />' +
  14333. '<name val="Calibri" />' +
  14334. '<b />' +
  14335. '</font>' +
  14336. '<font>' +
  14337. '<sz val="11" />' +
  14338. '<name val="Calibri" />' +
  14339. '<i />' +
  14340. '</font>' +
  14341. '<font>' +
  14342. '<sz val="11" />' +
  14343. '<name val="Calibri" />' +
  14344. '<u />' +
  14345. '</font>' +
  14346. '</fonts>' +
  14347. '<fills count="6">' +
  14348. '<fill>' +
  14349. '<patternFill patternType="none" />' +
  14350. '</fill>' +
  14351. '<fill>' + // Excel appears to use this as a dotted background regardless of values but
  14352. '<patternFill patternType="none" />' + // to be valid to the schema, use a patternFill
  14353. '</fill>' +
  14354. '<fill>' +
  14355. '<patternFill patternType="solid">' +
  14356. '<fgColor rgb="FFD9D9D9" />' +
  14357. '<bgColor indexed="64" />' +
  14358. '</patternFill>' +
  14359. '</fill>' +
  14360. '<fill>' +
  14361. '<patternFill patternType="solid">' +
  14362. '<fgColor rgb="FFD99795" />' +
  14363. '<bgColor indexed="64" />' +
  14364. '</patternFill>' +
  14365. '</fill>' +
  14366. '<fill>' +
  14367. '<patternFill patternType="solid">' +
  14368. '<fgColor rgb="ffc6efce" />' +
  14369. '<bgColor indexed="64" />' +
  14370. '</patternFill>' +
  14371. '</fill>' +
  14372. '<fill>' +
  14373. '<patternFill patternType="solid">' +
  14374. '<fgColor rgb="ffc6cfef" />' +
  14375. '<bgColor indexed="64" />' +
  14376. '</patternFill>' +
  14377. '</fill>' +
  14378. '</fills>' +
  14379. '<borders count="2">' +
  14380. '<border>' +
  14381. '<left />' +
  14382. '<right />' +
  14383. '<top />' +
  14384. '<bottom />' +
  14385. '<diagonal />' +
  14386. '</border>' +
  14387. '<border diagonalUp="false" diagonalDown="false">' +
  14388. '<left style="thin">' +
  14389. '<color auto="1" />' +
  14390. '</left>' +
  14391. '<right style="thin">' +
  14392. '<color auto="1" />' +
  14393. '</right>' +
  14394. '<top style="thin">' +
  14395. '<color auto="1" />' +
  14396. '</top>' +
  14397. '<bottom style="thin">' +
  14398. '<color auto="1" />' +
  14399. '</bottom>' +
  14400. '<diagonal />' +
  14401. '</border>' +
  14402. '</borders>' +
  14403. '<cellStyleXfs count="1">' +
  14404. '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" />' +
  14405. '</cellStyleXfs>' +
  14406. '<cellXfs count="68">' +
  14407. '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14408. '<xf numFmtId="0" fontId="1" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14409. '<xf numFmtId="0" fontId="2" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14410. '<xf numFmtId="0" fontId="3" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14411. '<xf numFmtId="0" fontId="4" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14412. '<xf numFmtId="0" fontId="0" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14413. '<xf numFmtId="0" fontId="1" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14414. '<xf numFmtId="0" fontId="2" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14415. '<xf numFmtId="0" fontId="3" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14416. '<xf numFmtId="0" fontId="4" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14417. '<xf numFmtId="0" fontId="0" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14418. '<xf numFmtId="0" fontId="1" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14419. '<xf numFmtId="0" fontId="2" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14420. '<xf numFmtId="0" fontId="3" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14421. '<xf numFmtId="0" fontId="4" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14422. '<xf numFmtId="0" fontId="0" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14423. '<xf numFmtId="0" fontId="1" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14424. '<xf numFmtId="0" fontId="2" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14425. '<xf numFmtId="0" fontId="3" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14426. '<xf numFmtId="0" fontId="4" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14427. '<xf numFmtId="0" fontId="0" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14428. '<xf numFmtId="0" fontId="1" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14429. '<xf numFmtId="0" fontId="2" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14430. '<xf numFmtId="0" fontId="3" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14431. '<xf numFmtId="0" fontId="4" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14432. '<xf numFmtId="0" fontId="0" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14433. '<xf numFmtId="0" fontId="1" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14434. '<xf numFmtId="0" fontId="2" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14435. '<xf numFmtId="0" fontId="3" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14436. '<xf numFmtId="0" fontId="4" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14437. '<xf numFmtId="0" fontId="0" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14438. '<xf numFmtId="0" fontId="1" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14439. '<xf numFmtId="0" fontId="2" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14440. '<xf numFmtId="0" fontId="3" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14441. '<xf numFmtId="0" fontId="4" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14442. '<xf numFmtId="0" fontId="0" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14443. '<xf numFmtId="0" fontId="1" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14444. '<xf numFmtId="0" fontId="2" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14445. '<xf numFmtId="0" fontId="3" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14446. '<xf numFmtId="0" fontId="4" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14447. '<xf numFmtId="0" fontId="0" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14448. '<xf numFmtId="0" fontId="1" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14449. '<xf numFmtId="0" fontId="2" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14450. '<xf numFmtId="0" fontId="3" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14451. '<xf numFmtId="0" fontId="4" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14452. '<xf numFmtId="0" fontId="0" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14453. '<xf numFmtId="0" fontId="1" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14454. '<xf numFmtId="0" fontId="2" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14455. '<xf numFmtId="0" fontId="3" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14456. '<xf numFmtId="0" fontId="4" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>' +
  14457. '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
  14458. '<alignment horizontal="left"/>' +
  14459. '</xf>' +
  14460. '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
  14461. '<alignment horizontal="center"/>' +
  14462. '</xf>' +
  14463. '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
  14464. '<alignment horizontal="right"/>' +
  14465. '</xf>' +
  14466. '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
  14467. '<alignment horizontal="fill"/>' +
  14468. '</xf>' +
  14469. '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
  14470. '<alignment textRotation="90"/>' +
  14471. '</xf>' +
  14472. '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">' +
  14473. '<alignment wrapText="1"/>' +
  14474. '</xf>' +
  14475. '<xf numFmtId="9" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
  14476. '<xf numFmtId="164" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
  14477. '<xf numFmtId="165" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
  14478. '<xf numFmtId="166" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
  14479. '<xf numFmtId="167" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
  14480. '<xf numFmtId="168" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
  14481. '<xf numFmtId="169" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
  14482. '<xf numFmtId="3" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
  14483. '<xf numFmtId="4" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
  14484. '<xf numFmtId="1" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
  14485. '<xf numFmtId="2" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
  14486. '<xf numFmtId="14" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>' +
  14487. '</cellXfs>' +
  14488. '<cellStyles count="1">' +
  14489. '<cellStyle name="Normal" xfId="0" builtinId="0" />' +
  14490. '</cellStyles>' +
  14491. '<dxfs count="0" />' +
  14492. '<tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleMedium4" />' +
  14493. '</styleSheet>'
  14494. };
  14495. // Note we could use 3 `for` loops for the styles, but when gzipped there is
  14496. // virtually no difference in size, since the above can be easily compressed
  14497. // Pattern matching for special number formats. Perhaps this should be exposed
  14498. // via an API in future?
  14499. // Ref: section 3.8.30 - built in formatters in open spreadsheet
  14500. // https://www.ecma-international.org/news/TC45_current_work/Office%20Open%20XML%20Part%204%20-%20Markup%20Language%20Reference.pdf
  14501. var _excelSpecials = [
  14502. {
  14503. match: /^\-?\d+\.\d%$/,
  14504. style: 60,
  14505. fmt: function (d) {
  14506. return d / 100;
  14507. }
  14508. }, // Percent with d.p.
  14509. {
  14510. match: /^\-?\d+\.?\d*%$/,
  14511. style: 56,
  14512. fmt: function (d) {
  14513. return d / 100;
  14514. }
  14515. }, // Percent
  14516. { match: /^\-?\$[\d,]+.?\d*$/, style: 57 }, // Dollars
  14517. { match: /^\-?£[\d,]+.?\d*$/, style: 58 }, // Pounds
  14518. { match: /^\-?€[\d,]+.?\d*$/, style: 59 }, // Euros
  14519. { match: /^\-?\d+$/, style: 65 }, // Numbers without thousand separators
  14520. { match: /^\-?\d+\.\d{2}$/, style: 66 }, // Numbers 2 d.p. without thousands separators
  14521. {
  14522. match: /^\([\d,]+\)$/,
  14523. style: 61,
  14524. fmt: function (d) {
  14525. return -1 * d.replace(/[\(\)]/g, '');
  14526. }
  14527. }, // Negative numbers indicated by brackets
  14528. {
  14529. match: /^\([\d,]+\.\d{2}\)$/,
  14530. style: 62,
  14531. fmt: function (d) {
  14532. return -1 * d.replace(/[\(\)]/g, '');
  14533. }
  14534. }, // Negative numbers indicated by brackets - 2d.p.
  14535. { match: /^\-?[\d,]+$/, style: 63 }, // Numbers with thousand separators
  14536. { match: /^\-?[\d,]+\.\d{2}$/, style: 64 },
  14537. {
  14538. match: /^[\d]{4}\-[01][\d]\-[0123][\d]$/,
  14539. style: 67,
  14540. fmt: function (d) {
  14541. return Math.round(25569 + Date.parse(d) / (86400 * 1000));
  14542. }
  14543. } //Date yyyy-mm-dd
  14544. ];
  14545. var _excelMergeCells = function (rels, row, column, rowspan, colspan) {
  14546. var mergeCells = $('mergeCells', rels);
  14547. mergeCells[0].appendChild(
  14548. _createNode(rels, 'mergeCell', {
  14549. attr: {
  14550. ref:
  14551. createCellPos(column) +
  14552. row +
  14553. ':' +
  14554. createCellPos(column + colspan - 1) +
  14555. (row + rowspan - 1)
  14556. }
  14557. })
  14558. );
  14559. mergeCells.attr('count', parseFloat(mergeCells.attr('count')) + 1);
  14560. };
  14561. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  14562. * Buttons
  14563. */
  14564. //
  14565. // Copy to clipboard
  14566. //
  14567. DataTable.ext.buttons.copyHtml5 = {
  14568. className: 'buttons-copy buttons-html5',
  14569. text: function (dt) {
  14570. return dt.i18n('buttons.copy', 'Copy');
  14571. },
  14572. action: function (e, dt, button, config, cb) {
  14573. var exportData = _exportData(dt, config);
  14574. var info = dt.buttons.exportInfo(config);
  14575. var newline = _newLine(config);
  14576. var output = exportData.str;
  14577. var hiddenDiv = $('<div/>').css({
  14578. height: 1,
  14579. width: 1,
  14580. overflow: 'hidden',
  14581. position: 'fixed',
  14582. top: 0,
  14583. left: 0
  14584. });
  14585. if (info.title) {
  14586. output = info.title + newline + newline + output;
  14587. }
  14588. if (info.messageTop) {
  14589. output = info.messageTop + newline + newline + output;
  14590. }
  14591. if (info.messageBottom) {
  14592. output = output + newline + newline + info.messageBottom;
  14593. }
  14594. if (config.customize) {
  14595. output = config.customize(output, config, dt);
  14596. }
  14597. var textarea = $('<textarea readonly/>')
  14598. .val(output)
  14599. .appendTo(hiddenDiv);
  14600. // For browsers that support the copy execCommand, try to use it
  14601. if (document.queryCommandSupported('copy')) {
  14602. hiddenDiv.appendTo(dt.table().container());
  14603. textarea[0].focus();
  14604. textarea[0].select();
  14605. try {
  14606. var successful = document.execCommand('copy');
  14607. hiddenDiv.remove();
  14608. if (successful) {
  14609. dt.buttons.info(
  14610. dt.i18n('buttons.copyTitle', 'Copy to clipboard'),
  14611. dt.i18n(
  14612. 'buttons.copySuccess',
  14613. {
  14614. 1: 'Copied one row to clipboard',
  14615. _: 'Copied %d rows to clipboard'
  14616. },
  14617. exportData.rows
  14618. ),
  14619. 2000
  14620. );
  14621. cb();
  14622. return;
  14623. }
  14624. } catch (t) {
  14625. // noop
  14626. }
  14627. }
  14628. // Otherwise we show the text box and instruct the user to use it
  14629. var message = $(
  14630. '<span>' +
  14631. dt.i18n(
  14632. 'buttons.copyKeys',
  14633. 'Press <i>ctrl</i> or <i>\u2318</i> + <i>C</i> to copy the table data<br>to your system clipboard.<br><br>' +
  14634. 'To cancel, click this message or press escape.'
  14635. ) +
  14636. '</span>'
  14637. ).append(hiddenDiv);
  14638. dt.buttons.info(
  14639. dt.i18n('buttons.copyTitle', 'Copy to clipboard'),
  14640. message,
  14641. 0
  14642. );
  14643. // Select the text so when the user activates their system clipboard
  14644. // it will copy that text
  14645. textarea[0].focus();
  14646. textarea[0].select();
  14647. // Event to hide the message when the user is done
  14648. var container = $(message).closest('.dt-button-info');
  14649. var close = function () {
  14650. container.off('click.buttons-copy');
  14651. $(document).off('.buttons-copy');
  14652. dt.buttons.info(false);
  14653. };
  14654. container.on('click.buttons-copy', close);
  14655. $(document)
  14656. .on('keydown.buttons-copy', function (e) {
  14657. if (e.keyCode === 27) {
  14658. // esc
  14659. close();
  14660. cb();
  14661. }
  14662. })
  14663. .on('copy.buttons-copy cut.buttons-copy', function () {
  14664. close();
  14665. cb();
  14666. });
  14667. },
  14668. async: 100,
  14669. exportOptions: {},
  14670. fieldSeparator: '\t',
  14671. fieldBoundary: '',
  14672. header: true,
  14673. footer: true,
  14674. title: '*',
  14675. messageTop: '*',
  14676. messageBottom: '*'
  14677. };
  14678. //
  14679. // CSV export
  14680. //
  14681. DataTable.ext.buttons.csvHtml5 = {
  14682. bom: false,
  14683. className: 'buttons-csv buttons-html5',
  14684. available: function () {
  14685. return window.FileReader !== undefined && window.Blob;
  14686. },
  14687. text: function (dt) {
  14688. return dt.i18n('buttons.csv', 'CSV');
  14689. },
  14690. action: function (e, dt, button, config, cb) {
  14691. // Set the text
  14692. var output = _exportData(dt, config).str;
  14693. var info = dt.buttons.exportInfo(config);
  14694. var charset = config.charset;
  14695. if (config.customize) {
  14696. output = config.customize(output, config, dt);
  14697. }
  14698. if (charset !== false) {
  14699. if (!charset) {
  14700. charset = document.characterSet || document.charset;
  14701. }
  14702. if (charset) {
  14703. charset = ';charset=' + charset;
  14704. }
  14705. }
  14706. else {
  14707. charset = '';
  14708. }
  14709. if (config.bom) {
  14710. output = String.fromCharCode(0xfeff) + output;
  14711. }
  14712. _saveAs(
  14713. new Blob([output], { type: 'text/csv' + charset }),
  14714. info.filename,
  14715. true
  14716. );
  14717. cb();
  14718. },
  14719. async: 100,
  14720. filename: '*',
  14721. extension: '.csv',
  14722. exportOptions: {},
  14723. fieldSeparator: ',',
  14724. fieldBoundary: '"',
  14725. escapeChar: '"',
  14726. charset: null,
  14727. header: true,
  14728. footer: true
  14729. };
  14730. //
  14731. // Excel (xlsx) export
  14732. //
  14733. DataTable.ext.buttons.excelHtml5 = {
  14734. className: 'buttons-excel buttons-html5',
  14735. available: function () {
  14736. return (
  14737. window.FileReader !== undefined &&
  14738. _jsZip() !== undefined &&
  14739. !_isDuffSafari() &&
  14740. _serialiser
  14741. );
  14742. },
  14743. text: function (dt) {
  14744. return dt.i18n('buttons.excel', 'Excel');
  14745. },
  14746. action: function (e, dt, button, config, cb) {
  14747. var rowPos = 0;
  14748. var dataStartRow, dataEndRow;
  14749. var getXml = function (type) {
  14750. var str = excelStrings[type];
  14751. //str = str.replace( /xmlns:/g, 'xmlns_' ).replace( /mc:/g, 'mc_' );
  14752. return $.parseXML(str);
  14753. };
  14754. var rels = getXml('xl/worksheets/sheet1.xml');
  14755. var relsGet = rels.getElementsByTagName('sheetData')[0];
  14756. var xlsx = {
  14757. _rels: {
  14758. '.rels': getXml('_rels/.rels')
  14759. },
  14760. xl: {
  14761. _rels: {
  14762. 'workbook.xml.rels': getXml('xl/_rels/workbook.xml.rels')
  14763. },
  14764. 'workbook.xml': getXml('xl/workbook.xml'),
  14765. 'styles.xml': getXml('xl/styles.xml'),
  14766. worksheets: {
  14767. 'sheet1.xml': rels
  14768. }
  14769. },
  14770. '[Content_Types].xml': getXml('[Content_Types].xml')
  14771. };
  14772. var data = dt.buttons.exportData(config.exportOptions);
  14773. var currentRow, rowNode;
  14774. var addRow = function (row) {
  14775. currentRow = rowPos + 1;
  14776. rowNode = _createNode(rels, 'row', { attr: { r: currentRow } });
  14777. for (var i = 0, ien = row.length; i < ien; i++) {
  14778. // Concat both the Cell Columns as a letter and the Row of the cell.
  14779. var cellId = createCellPos(i) + '' + currentRow;
  14780. var cell = null;
  14781. // For null, undefined of blank cell, continue so it doesn't create the _createNode
  14782. if (row[i] === null || row[i] === undefined || row[i] === '') {
  14783. if (config.createEmptyCells === true) {
  14784. row[i] = '';
  14785. }
  14786. else {
  14787. continue;
  14788. }
  14789. }
  14790. var originalContent = row[i];
  14791. row[i] =
  14792. typeof row[i].trim === 'function' ? row[i].trim() : row[i];
  14793. // Special number formatting options
  14794. for (var j = 0, jen = _excelSpecials.length; j < jen; j++) {
  14795. var special = _excelSpecials[j];
  14796. // TODO Need to provide the ability for the specials to say
  14797. // if they are returning a string, since at the moment it is
  14798. // assumed to be a number
  14799. if (
  14800. row[i].match &&
  14801. !row[i].match(/^0\d+/) &&
  14802. row[i].match(special.match)
  14803. ) {
  14804. var val = row[i].replace(/[^\d\.\-]/g, '');
  14805. if (special.fmt) {
  14806. val = special.fmt(val);
  14807. }
  14808. cell = _createNode(rels, 'c', {
  14809. attr: {
  14810. r: cellId,
  14811. s: special.style
  14812. },
  14813. children: [_createNode(rels, 'v', { text: val })]
  14814. });
  14815. break;
  14816. }
  14817. }
  14818. if (!cell) {
  14819. if (
  14820. typeof row[i] === 'number' ||
  14821. (row[i].match &&
  14822. row[i].match(/^-?\d+(\.\d+)?([eE]\-?\d+)?$/) && // Includes exponential format
  14823. !row[i].match(/^0\d+/))
  14824. ) {
  14825. // Detect numbers - don't match numbers with leading zeros
  14826. // or a negative anywhere but the start
  14827. cell = _createNode(rels, 'c', {
  14828. attr: {
  14829. t: 'n',
  14830. r: cellId
  14831. },
  14832. children: [_createNode(rels, 'v', { text: row[i] })]
  14833. });
  14834. }
  14835. else {
  14836. // String output - replace non standard characters for text output
  14837. /*eslint no-control-regex: "off"*/
  14838. var text = !originalContent.replace
  14839. ? originalContent
  14840. : originalContent.replace(
  14841. /[\x00-\x09\x0B\x0C\x0E-\x1F\x7F-\x9F]/g,
  14842. ''
  14843. );
  14844. cell = _createNode(rels, 'c', {
  14845. attr: {
  14846. t: 'inlineStr',
  14847. r: cellId
  14848. },
  14849. children: {
  14850. row: _createNode(rels, 'is', {
  14851. children: {
  14852. row: _createNode(rels, 't', {
  14853. text: text,
  14854. attr: {
  14855. 'xml:space': 'preserve'
  14856. }
  14857. })
  14858. }
  14859. })
  14860. }
  14861. });
  14862. }
  14863. }
  14864. rowNode.appendChild(cell);
  14865. }
  14866. relsGet.appendChild(rowNode);
  14867. rowPos++;
  14868. };
  14869. var addHeader = function (structure) {
  14870. structure.forEach(function (row) {
  14871. addRow(
  14872. row.map(function (cell) {
  14873. return cell ? cell.title : '';
  14874. }),
  14875. rowPos
  14876. );
  14877. $('row:last c', rels).attr('s', '2'); // bold
  14878. // Add any merge cells
  14879. row.forEach(function (cell, columnCounter) {
  14880. if (cell && (cell.colSpan > 1 || cell.rowSpan > 1)) {
  14881. _excelMergeCells(
  14882. rels,
  14883. rowPos,
  14884. columnCounter,
  14885. cell.rowSpan,
  14886. cell.colSpan
  14887. );
  14888. }
  14889. });
  14890. });
  14891. };
  14892. if (config.customizeData) {
  14893. config.customizeData(data);
  14894. }
  14895. // Title and top messages
  14896. var exportInfo = dt.buttons.exportInfo(config);
  14897. if (exportInfo.title) {
  14898. addRow([exportInfo.title], rowPos);
  14899. _excelMergeCells(rels, rowPos, 0, 1, data.header.length);
  14900. $('row:last c', rels).attr('s', '51'); // centre
  14901. }
  14902. if (exportInfo.messageTop) {
  14903. addRow([exportInfo.messageTop], rowPos);
  14904. _excelMergeCells(rels, rowPos, 0, 1, data.header.length);
  14905. }
  14906. // Table header
  14907. if (config.header) {
  14908. addHeader(data.headerStructure);
  14909. }
  14910. dataStartRow = rowPos;
  14911. // Table body
  14912. for (var n = 0, ie = data.body.length; n < ie; n++) {
  14913. addRow(data.body[n], rowPos);
  14914. }
  14915. dataEndRow = rowPos;
  14916. // Table footer
  14917. if (config.footer && data.footer) {
  14918. addHeader(data.footerStructure);
  14919. }
  14920. // Below the table
  14921. if (exportInfo.messageBottom) {
  14922. addRow([exportInfo.messageBottom], rowPos);
  14923. _excelMergeCells(rels, rowPos, 0, 1, data.header.length);
  14924. }
  14925. // Set column widths
  14926. var cols = _createNode(rels, 'cols');
  14927. $('worksheet', rels).prepend(cols);
  14928. for (var i = 0, ien = data.header.length; i < ien; i++) {
  14929. cols.appendChild(
  14930. _createNode(rels, 'col', {
  14931. attr: {
  14932. min: i + 1,
  14933. max: i + 1,
  14934. width: _excelColWidth(data, i),
  14935. customWidth: 1
  14936. }
  14937. })
  14938. );
  14939. }
  14940. // Workbook modifications
  14941. var workbook = xlsx.xl['workbook.xml'];
  14942. $('sheets sheet', workbook).attr('name', _sheetname(config));
  14943. // Auto filter for columns
  14944. if (config.autoFilter) {
  14945. $('mergeCells', rels).before(
  14946. _createNode(rels, 'autoFilter', {
  14947. attr: {
  14948. ref:
  14949. 'A' +
  14950. dataStartRow +
  14951. ':' +
  14952. createCellPos(data.header.length - 1) +
  14953. dataEndRow
  14954. }
  14955. })
  14956. );
  14957. $('definedNames', workbook).append(
  14958. _createNode(workbook, 'definedName', {
  14959. attr: {
  14960. name: '_xlnm._FilterDatabase',
  14961. localSheetId: '0',
  14962. hidden: 1
  14963. },
  14964. text:
  14965. '\'' +
  14966. _sheetname(config).replace(/'/g, '\'\'') +
  14967. '\'!$A$' +
  14968. dataStartRow +
  14969. ':' +
  14970. createCellPos(data.header.length - 1) +
  14971. dataEndRow
  14972. })
  14973. );
  14974. }
  14975. // Let the developer customise the document if they want to
  14976. if (config.customize) {
  14977. config.customize(xlsx, config, dt);
  14978. }
  14979. // Excel doesn't like an empty mergeCells tag
  14980. if ($('mergeCells', rels).children().length === 0) {
  14981. $('mergeCells', rels).remove();
  14982. }
  14983. var jszip = _jsZip();
  14984. var zip = new jszip();
  14985. var zipConfig = {
  14986. compression: 'DEFLATE',
  14987. type: 'blob',
  14988. mimeType:
  14989. 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
  14990. };
  14991. _addToZip(zip, xlsx);
  14992. // Modern Excel has a 218 character limit on the file name + path of the file (why!?)
  14993. // https://support.microsoft.com/en-us/office/excel-specifications-and-limits-1672b34d-7043-467e-8e27-269d656771c3
  14994. // So we truncate to allow for this.
  14995. var filename = exportInfo.filename;
  14996. if (filename > 175) {
  14997. filename = filename.substr(0, 175);
  14998. }
  14999. // Let the developer customize the final zip file if they want to before it is generated and sent to the browser
  15000. if (config.customizeZip) {
  15001. config.customizeZip(zip, data, filename);
  15002. }
  15003. if (zip.generateAsync) {
  15004. // JSZip 3+
  15005. zip.generateAsync(zipConfig).then(function (blob) {
  15006. _saveAs(blob, filename);
  15007. cb();
  15008. });
  15009. }
  15010. else {
  15011. // JSZip 2.5
  15012. _saveAs(zip.generate(zipConfig), filename);
  15013. cb();
  15014. }
  15015. },
  15016. async: 100,
  15017. filename: '*',
  15018. extension: '.xlsx',
  15019. exportOptions: {},
  15020. header: true,
  15021. footer: true,
  15022. title: '*',
  15023. messageTop: '*',
  15024. messageBottom: '*',
  15025. createEmptyCells: false,
  15026. autoFilter: false,
  15027. sheetName: ''
  15028. };
  15029. //
  15030. // PDF export - using pdfMake - http://pdfmake.org
  15031. //
  15032. DataTable.ext.buttons.pdfHtml5 = {
  15033. className: 'buttons-pdf buttons-html5',
  15034. available: function () {
  15035. return window.FileReader !== undefined && _pdfMake();
  15036. },
  15037. text: function (dt) {
  15038. return dt.i18n('buttons.pdf', 'PDF');
  15039. },
  15040. action: function (e, dt, button, config, cb) {
  15041. var data = dt.buttons.exportData(config.exportOptions);
  15042. var info = dt.buttons.exportInfo(config);
  15043. var rows = [];
  15044. if (config.header) {
  15045. data.headerStructure.forEach(function (row) {
  15046. rows.push(
  15047. row.map(function (cell) {
  15048. return cell
  15049. ? {
  15050. text: cell.title,
  15051. colSpan: cell.colspan,
  15052. rowSpan: cell.rowspan,
  15053. style: 'tableHeader'
  15054. }
  15055. : {};
  15056. })
  15057. );
  15058. });
  15059. }
  15060. for (var i = 0, ien = data.body.length; i < ien; i++) {
  15061. rows.push(
  15062. data.body[i].map(function (d) {
  15063. return {
  15064. text:
  15065. d === null || d === undefined
  15066. ? ''
  15067. : typeof d === 'string'
  15068. ? d
  15069. : d.toString()
  15070. };
  15071. })
  15072. );
  15073. }
  15074. if (config.footer) {
  15075. data.footerStructure.forEach(function (row) {
  15076. rows.push(
  15077. row.map(function (cell) {
  15078. return cell
  15079. ? {
  15080. text: cell.title,
  15081. colSpan: cell.colspan,
  15082. rowSpan: cell.rowspan,
  15083. style: 'tableHeader'
  15084. }
  15085. : {};
  15086. })
  15087. );
  15088. });
  15089. }
  15090. var doc = {
  15091. pageSize: config.pageSize,
  15092. pageOrientation: config.orientation,
  15093. content: [
  15094. {
  15095. style: 'table',
  15096. table: {
  15097. headerRows: data.headerStructure.length,
  15098. footerRows: data.footerStructure.length, // Used for styling, doesn't do anything in pdfmake
  15099. body: rows
  15100. },
  15101. layout: {
  15102. hLineWidth: function (i, node) {
  15103. if (i === 0 || i === node.table.body.length) {
  15104. return 0;
  15105. }
  15106. return 0.5;
  15107. },
  15108. vLineWidth: function () {
  15109. return 0;
  15110. },
  15111. hLineColor: function (i, node) {
  15112. return i === node.table.headerRows ||
  15113. i ===
  15114. node.table.body.length -
  15115. node.table.footerRows
  15116. ? '#333'
  15117. : '#ddd';
  15118. },
  15119. fillColor: function (rowIndex) {
  15120. if (rowIndex < data.headerStructure.length) {
  15121. return '#fff';
  15122. }
  15123. return rowIndex % 2 === 0 ? '#f3f3f3' : null;
  15124. },
  15125. paddingTop: function () {
  15126. return 5;
  15127. },
  15128. paddingBottom: function () {
  15129. return 5;
  15130. }
  15131. }
  15132. }
  15133. ],
  15134. styles: {
  15135. tableHeader: {
  15136. bold: true,
  15137. fontSize: 11,
  15138. alignment: 'center'
  15139. },
  15140. tableFooter: {
  15141. bold: true,
  15142. fontSize: 11
  15143. },
  15144. table: {
  15145. margin: [0, 5, 0, 5]
  15146. },
  15147. title: {
  15148. alignment: 'center',
  15149. fontSize: 13
  15150. },
  15151. message: {}
  15152. },
  15153. defaultStyle: {
  15154. fontSize: 10
  15155. }
  15156. };
  15157. if (info.messageTop) {
  15158. doc.content.unshift({
  15159. text: info.messageTop,
  15160. style: 'message',
  15161. margin: [0, 0, 0, 12]
  15162. });
  15163. }
  15164. if (info.messageBottom) {
  15165. doc.content.push({
  15166. text: info.messageBottom,
  15167. style: 'message',
  15168. margin: [0, 0, 0, 12]
  15169. });
  15170. }
  15171. if (info.title) {
  15172. doc.content.unshift({
  15173. text: info.title,
  15174. style: 'title',
  15175. margin: [0, 0, 0, 12]
  15176. });
  15177. }
  15178. if (config.customize) {
  15179. config.customize(doc, config, dt);
  15180. }
  15181. var pdf = _pdfMake().createPdf(doc);
  15182. if (config.download === 'open' && !_isDuffSafari()) {
  15183. pdf.open();
  15184. }
  15185. else {
  15186. pdf.download(info.filename);
  15187. }
  15188. cb();
  15189. },
  15190. async: 100,
  15191. title: '*',
  15192. filename: '*',
  15193. extension: '.pdf',
  15194. exportOptions: {},
  15195. orientation: 'portrait',
  15196. // This isn't perfect, but it is close
  15197. pageSize:
  15198. navigator.language === 'en-US' || navigator.language === 'en-CA'
  15199. ? 'LETTER'
  15200. : 'A4',
  15201. header: true,
  15202. footer: true,
  15203. messageTop: '*',
  15204. messageBottom: '*',
  15205. customize: null,
  15206. download: 'download'
  15207. };
  15208. return DataTable;
  15209. }));
  15210. /*!
  15211. * Print button for Buttons and DataTables.
  15212. * © SpryMedia Ltd - datatables.net/license
  15213. */
  15214. (function( factory ){
  15215. if ( typeof define === 'function' && define.amd ) {
  15216. // AMD
  15217. define( ['jquery', 'datatables.net', 'datatables.net-buttons'], function ( $ ) {
  15218. return factory( $, window, document );
  15219. } );
  15220. }
  15221. else if ( typeof exports === 'object' ) {
  15222. // CommonJS
  15223. var jq = require('jquery');
  15224. var cjsRequires = function (root, $) {
  15225. if ( ! $.fn.dataTable ) {
  15226. require('datatables.net')(root, $);
  15227. }
  15228. if ( ! $.fn.dataTable.Buttons ) {
  15229. require('datatables.net-buttons')(root, $);
  15230. }
  15231. };
  15232. if (typeof window === 'undefined') {
  15233. module.exports = function (root, $) {
  15234. if ( ! root ) {
  15235. // CommonJS environments without a window global must pass a
  15236. // root. This will give an error otherwise
  15237. root = window;
  15238. }
  15239. if ( ! $ ) {
  15240. $ = jq( root );
  15241. }
  15242. cjsRequires( root, $ );
  15243. return factory( $, root, root.document );
  15244. };
  15245. }
  15246. else {
  15247. cjsRequires( window, jq );
  15248. module.exports = factory( jq, window, window.document );
  15249. }
  15250. }
  15251. else {
  15252. // Browser
  15253. factory( jQuery, window, document );
  15254. }
  15255. }(function( $, window, document ) {
  15256. 'use strict';
  15257. var DataTable = $.fn.dataTable;
  15258. var _link = document.createElement('a');
  15259. /**
  15260. * Clone link and style tags, taking into account the need to change the source
  15261. * path.
  15262. *
  15263. * @param {node} el Element to convert
  15264. */
  15265. var _styleToAbs = function (el) {
  15266. var clone = $(el).clone()[0];
  15267. if (clone.nodeName.toLowerCase() === 'link') {
  15268. clone.href = _relToAbs(clone.href);
  15269. }
  15270. return clone.outerHTML;
  15271. };
  15272. /**
  15273. * Convert a URL from a relative to an absolute address so it will work
  15274. * correctly in the popup window which has no base URL.
  15275. *
  15276. * @param {string} href URL
  15277. */
  15278. var _relToAbs = function (href) {
  15279. // Assign to a link on the original page so the browser will do all the
  15280. // hard work of figuring out where the file actually is
  15281. _link.href = href;
  15282. var linkHost = _link.host;
  15283. // IE doesn't have a trailing slash on the host
  15284. // Chrome has it on the pathname
  15285. if (linkHost.indexOf('/') === -1 && _link.pathname.indexOf('/') !== 0) {
  15286. linkHost += '/';
  15287. }
  15288. return _link.protocol + '//' + linkHost + _link.pathname + _link.search;
  15289. };
  15290. DataTable.ext.buttons.print = {
  15291. className: 'buttons-print',
  15292. text: function (dt) {
  15293. return dt.i18n('buttons.print', 'Print');
  15294. },
  15295. action: function (e, dt, button, config, cb) {
  15296. var data = dt.buttons.exportData(
  15297. $.extend({ decodeEntities: false }, config.exportOptions) // XSS protection
  15298. );
  15299. var exportInfo = dt.buttons.exportInfo(config);
  15300. // Get the classes for the columns from the header cells
  15301. var columnClasses = dt
  15302. .columns(config.exportOptions.columns)
  15303. .nodes()
  15304. .map(function (n) {
  15305. return n.className;
  15306. })
  15307. .toArray();
  15308. var addRow = function (d, tag) {
  15309. var str = '<tr>';
  15310. for (var i = 0, ien = d.length; i < ien; i++) {
  15311. // null and undefined aren't useful in the print output
  15312. var dataOut = d[i] === null || d[i] === undefined ? '' : d[i];
  15313. var classAttr = columnClasses[i]
  15314. ? 'class="' + columnClasses[i] + '"'
  15315. : '';
  15316. str +=
  15317. '<' +
  15318. tag +
  15319. ' ' +
  15320. classAttr +
  15321. '>' +
  15322. dataOut +
  15323. '</' +
  15324. tag +
  15325. '>';
  15326. }
  15327. return str + '</tr>';
  15328. };
  15329. // Construct a table for printing
  15330. var html = '<table class="' + dt.table().node().className + '">';
  15331. if (config.header) {
  15332. var headerRows = data.headerStructure.map(function (row) {
  15333. return (
  15334. '<tr>' +
  15335. row
  15336. .map(function (cell) {
  15337. return cell
  15338. ? '<th colspan="' +
  15339. cell.colspan +
  15340. '" rowspan="' +
  15341. cell.rowspan +
  15342. '">' +
  15343. cell.title +
  15344. '</th>'
  15345. : '';
  15346. })
  15347. .join('') +
  15348. '</tr>'
  15349. );
  15350. });
  15351. html += '<thead>' + headerRows.join('') + '</thead>';
  15352. }
  15353. html += '<tbody>';
  15354. for (var i = 0, ien = data.body.length; i < ien; i++) {
  15355. html += addRow(data.body[i], 'td');
  15356. }
  15357. html += '</tbody>';
  15358. if (config.footer && data.footer) {
  15359. var footerRows = data.footerStructure.map(function (row) {
  15360. return (
  15361. '<tr>' +
  15362. row
  15363. .map(function (cell) {
  15364. return cell
  15365. ? '<th colspan="' +
  15366. cell.colspan +
  15367. '" rowspan="' +
  15368. cell.rowspan +
  15369. '">' +
  15370. cell.title +
  15371. '</th>'
  15372. : '';
  15373. })
  15374. .join('') +
  15375. '</tr>'
  15376. );
  15377. });
  15378. html += '<tfoot>' + footerRows.join('') + '</tfoot>';
  15379. }
  15380. html += '</table>';
  15381. // Open a new window for the printable table
  15382. var win = window.open('', '');
  15383. if (!win) {
  15384. dt.buttons.info(
  15385. dt.i18n('buttons.printErrorTitle', 'Unable to open print view'),
  15386. dt.i18n(
  15387. 'buttons.printErrorMsg',
  15388. 'Please allow popups in your browser for this site to be able to view the print view.'
  15389. ),
  15390. 5000
  15391. );
  15392. return;
  15393. }
  15394. win.document.close();
  15395. // Inject the title and also a copy of the style and link tags from this
  15396. // document so the table can retain its base styling. Note that we have
  15397. // to use string manipulation as IE won't allow elements to be created
  15398. // in the host document and then appended to the new window.
  15399. var head = '<title>' + exportInfo.title + '</title>';
  15400. $('style, link').each(function () {
  15401. head += _styleToAbs(this);
  15402. });
  15403. try {
  15404. win.document.head.innerHTML = head; // Work around for Edge
  15405. } catch (e) {
  15406. $(win.document.head).html(head); // Old IE
  15407. }
  15408. // Inject the table and other surrounding information
  15409. win.document.body.innerHTML =
  15410. '<h1>' +
  15411. exportInfo.title +
  15412. '</h1>' +
  15413. '<div>' +
  15414. (exportInfo.messageTop || '') +
  15415. '</div>' +
  15416. html +
  15417. '<div>' +
  15418. (exportInfo.messageBottom || '') +
  15419. '</div>';
  15420. $(win.document.body).addClass('dt-print-view');
  15421. $('img', win.document.body).each(function (i, img) {
  15422. img.setAttribute('src', _relToAbs(img.getAttribute('src')));
  15423. });
  15424. if (config.customize) {
  15425. config.customize(win, config, dt);
  15426. }
  15427. // Allow stylesheets time to load
  15428. var autoPrint = function () {
  15429. if (config.autoPrint) {
  15430. win.print(); // blocking - so close will not
  15431. win.close(); // execute until this is done
  15432. }
  15433. };
  15434. win.setTimeout(autoPrint, 1000);
  15435. cb();
  15436. },
  15437. async: 100,
  15438. title: '*',
  15439. messageTop: '*',
  15440. messageBottom: '*',
  15441. exportOptions: {},
  15442. header: true,
  15443. footer: true,
  15444. autoPrint: true,
  15445. customize: null
  15446. };
  15447. return DataTable;
  15448. }));
  15449. /*! FixedHeader 4.0.1
  15450. * © SpryMedia Ltd - datatables.net/license
  15451. */
  15452. (function( factory ){
  15453. if ( typeof define === 'function' && define.amd ) {
  15454. // AMD
  15455. define( ['jquery', 'datatables.net'], function ( $ ) {
  15456. return factory( $, window, document );
  15457. } );
  15458. }
  15459. else if ( typeof exports === 'object' ) {
  15460. // CommonJS
  15461. var jq = require('jquery');
  15462. var cjsRequires = function (root, $) {
  15463. if ( ! $.fn.dataTable ) {
  15464. require('datatables.net')(root, $);
  15465. }
  15466. };
  15467. if (typeof window === 'undefined') {
  15468. module.exports = function (root, $) {
  15469. if ( ! root ) {
  15470. // CommonJS environments without a window global must pass a
  15471. // root. This will give an error otherwise
  15472. root = window;
  15473. }
  15474. if ( ! $ ) {
  15475. $ = jq( root );
  15476. }
  15477. cjsRequires( root, $ );
  15478. return factory( $, root, root.document );
  15479. };
  15480. }
  15481. else {
  15482. cjsRequires( window, jq );
  15483. module.exports = factory( jq, window, window.document );
  15484. }
  15485. }
  15486. else {
  15487. // Browser
  15488. factory( jQuery, window, document );
  15489. }
  15490. }(function( $, window, document ) {
  15491. 'use strict';
  15492. var DataTable = $.fn.dataTable;
  15493. /**
  15494. * @summary FixedHeader
  15495. * @description Fix a table's header or footer, so it is always visible while
  15496. * scrolling
  15497. * @version 4.0.1
  15498. * @author SpryMedia Ltd
  15499. * @contact datatables.net
  15500. *
  15501. * This source file is free software, available under the following license:
  15502. * MIT license - http://datatables.net/license/mit
  15503. *
  15504. * This source file is distributed in the hope that it will be useful, but
  15505. * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  15506. * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
  15507. *
  15508. * For details please refer to: http://www.datatables.net
  15509. */
  15510. var _instCounter = 0;
  15511. var FixedHeader = function (dt, config) {
  15512. if (!DataTable.versionCheck('2')) {
  15513. throw 'Warning: FixedHeader requires DataTables 2 or newer';
  15514. }
  15515. // Sanity check - you just know it will happen
  15516. if (!(this instanceof FixedHeader)) {
  15517. throw "FixedHeader must be initialised with the 'new' keyword.";
  15518. }
  15519. // Allow a boolean true for defaults
  15520. if (config === true) {
  15521. config = {};
  15522. }
  15523. dt = new DataTable.Api(dt);
  15524. this.c = $.extend(true, {}, FixedHeader.defaults, config);
  15525. this.s = {
  15526. dt: dt,
  15527. position: {
  15528. theadTop: 0,
  15529. tbodyTop: 0,
  15530. tfootTop: 0,
  15531. tfootBottom: 0,
  15532. width: 0,
  15533. left: 0,
  15534. tfootHeight: 0,
  15535. theadHeight: 0,
  15536. windowHeight: $(window).height(),
  15537. visible: true
  15538. },
  15539. headerMode: null,
  15540. footerMode: null,
  15541. autoWidth: dt.settings()[0].oFeatures.bAutoWidth,
  15542. namespace: '.dtfc' + _instCounter++,
  15543. scrollLeft: {
  15544. header: -1,
  15545. footer: -1
  15546. },
  15547. enable: true,
  15548. autoDisable: false
  15549. };
  15550. this.dom = {
  15551. floatingHeader: null,
  15552. thead: $(dt.table().header()),
  15553. tbody: $(dt.table().body()),
  15554. tfoot: $(dt.table().footer()),
  15555. header: {
  15556. host: null,
  15557. floating: null,
  15558. floatingParent: $('<div class="dtfh-floatingparent"><div></div></div>'),
  15559. placeholder: null
  15560. },
  15561. footer: {
  15562. host: null,
  15563. floating: null,
  15564. floatingParent: $('<div class="dtfh-floatingparent"><div></div></div>'),
  15565. placeholder: null
  15566. }
  15567. };
  15568. this.dom.header.host = this.dom.thead.parent();
  15569. this.dom.footer.host = this.dom.tfoot.parent();
  15570. var dtSettings = dt.settings()[0];
  15571. if (dtSettings._fixedHeader) {
  15572. throw (
  15573. 'FixedHeader already initialised on table ' + dtSettings.nTable.id
  15574. );
  15575. }
  15576. dtSettings._fixedHeader = this;
  15577. this._constructor();
  15578. };
  15579. /*
  15580. * Variable: FixedHeader
  15581. * Purpose: Prototype for FixedHeader
  15582. * Scope: global
  15583. */
  15584. $.extend(FixedHeader.prototype, {
  15585. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  15586. * API methods
  15587. */
  15588. /**
  15589. * Kill off FH and any events
  15590. */
  15591. destroy: function () {
  15592. var dom = this.dom;
  15593. this.s.dt.off('.dtfc');
  15594. $(window).off(this.s.namespace);
  15595. // Remove clones of FC blockers
  15596. if (dom.header.rightBlocker) {
  15597. dom.header.rightBlocker.remove();
  15598. }
  15599. if (dom.header.leftBlocker) {
  15600. dom.header.leftBlocker.remove();
  15601. }
  15602. if (dom.footer.rightBlocker) {
  15603. dom.footer.rightBlocker.remove();
  15604. }
  15605. if (dom.footer.leftBlocker) {
  15606. dom.footer.leftBlocker.remove();
  15607. }
  15608. if (this.c.header) {
  15609. this._modeChange('in-place', 'header', true);
  15610. }
  15611. if (this.c.footer && dom.tfoot.length) {
  15612. this._modeChange('in-place', 'footer', true);
  15613. }
  15614. },
  15615. /**
  15616. * Enable / disable the fixed elements
  15617. *
  15618. * @param {boolean} enable `true` to enable, `false` to disable
  15619. */
  15620. enable: function (enable, update, type) {
  15621. this.s.enable = enable;
  15622. this.s.enableType = type;
  15623. if (update || update === undefined) {
  15624. this._positions();
  15625. this._scroll(true);
  15626. }
  15627. },
  15628. /**
  15629. * Get enabled status
  15630. */
  15631. enabled: function () {
  15632. return this.s.enable;
  15633. },
  15634. /**
  15635. * Set header offset
  15636. *
  15637. * @param {int} new value for headerOffset
  15638. */
  15639. headerOffset: function (offset) {
  15640. if (offset !== undefined) {
  15641. this.c.headerOffset = offset;
  15642. this.update();
  15643. }
  15644. return this.c.headerOffset;
  15645. },
  15646. /**
  15647. * Set footer offset
  15648. *
  15649. * @param {int} new value for footerOffset
  15650. */
  15651. footerOffset: function (offset) {
  15652. if (offset !== undefined) {
  15653. this.c.footerOffset = offset;
  15654. this.update();
  15655. }
  15656. return this.c.footerOffset;
  15657. },
  15658. /**
  15659. * Recalculate the position of the fixed elements and force them into place
  15660. */
  15661. update: function (force) {
  15662. var table = this.s.dt.table().node();
  15663. // Update should only do something if enabled by the dev.
  15664. if (!this.s.enable && !this.s.autoDisable) {
  15665. return;
  15666. }
  15667. if ($(table).is(':visible')) {
  15668. this.s.autoDisable = false;
  15669. this.enable(true, false);
  15670. }
  15671. else {
  15672. this.s.autoDisable = true;
  15673. this.enable(false, false);
  15674. }
  15675. // Don't update if header is not in the document atm (due to
  15676. // async events)
  15677. if ($(table).children('thead').length === 0) {
  15678. return;
  15679. }
  15680. this._positions();
  15681. this._scroll(force !== undefined ? force : true);
  15682. this._widths(this.dom.header);
  15683. this._widths(this.dom.footer);
  15684. },
  15685. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  15686. * Constructor
  15687. */
  15688. /**
  15689. * FixedHeader constructor - adding the required event listeners and
  15690. * simple initialisation
  15691. *
  15692. * @private
  15693. */
  15694. _constructor: function () {
  15695. var that = this;
  15696. var dt = this.s.dt;
  15697. $(window)
  15698. .on('scroll' + this.s.namespace, function () {
  15699. that._scroll();
  15700. })
  15701. .on(
  15702. 'resize' + this.s.namespace,
  15703. DataTable.util.throttle(function () {
  15704. that.s.position.windowHeight = $(window).height();
  15705. that.update();
  15706. }, 50)
  15707. );
  15708. var autoHeader = $('.fh-fixedHeader');
  15709. if (!this.c.headerOffset && autoHeader.length) {
  15710. this.c.headerOffset = autoHeader.outerHeight();
  15711. }
  15712. var autoFooter = $('.fh-fixedFooter');
  15713. if (!this.c.footerOffset && autoFooter.length) {
  15714. this.c.footerOffset = autoFooter.outerHeight();
  15715. }
  15716. dt.on(
  15717. 'column-reorder.dt.dtfc column-visibility.dt.dtfc column-sizing.dt.dtfc responsive-display.dt.dtfc',
  15718. function (e, ctx) {
  15719. that.update();
  15720. }
  15721. ).on('draw.dt.dtfc', function (e, ctx) {
  15722. // For updates from our own table, don't reclone, but for all others, do
  15723. that.update(ctx === dt.settings()[0] ? false : true);
  15724. });
  15725. dt.on('destroy.dtfc', function () {
  15726. that.destroy();
  15727. });
  15728. this._positions();
  15729. this._scroll();
  15730. },
  15731. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  15732. * Private methods
  15733. */
  15734. /**
  15735. * Clone a fixed item to act as a place holder for the original element
  15736. * which is moved into a clone of the table element, and moved around the
  15737. * document to give the fixed effect.
  15738. *
  15739. * @param {string} item 'header' or 'footer'
  15740. * @param {boolean} force Force the clone to happen, or allow automatic
  15741. * decision (reuse existing if available)
  15742. * @private
  15743. */
  15744. _clone: function (item, force) {
  15745. var that = this;
  15746. var dt = this.s.dt;
  15747. var itemDom = this.dom[item];
  15748. var itemElement = item === 'header' ? this.dom.thead : this.dom.tfoot;
  15749. // If footer and scrolling is enabled then we don't clone
  15750. // Instead the table's height is decreased accordingly - see `_scroll()`
  15751. if (item === 'footer' && this._scrollEnabled()) {
  15752. return;
  15753. }
  15754. if (!force && itemDom.floating) {
  15755. // existing floating element - reuse it
  15756. itemDom.floating.removeClass(
  15757. 'fixedHeader-floating fixedHeader-locked'
  15758. );
  15759. }
  15760. else {
  15761. if (itemDom.floating) {
  15762. if (itemDom.placeholder !== null) {
  15763. itemDom.placeholder.remove();
  15764. }
  15765. itemDom.floating.children().detach();
  15766. itemDom.floating.remove();
  15767. }
  15768. var tableNode = $(dt.table().node());
  15769. var scrollBody = $(tableNode.parent());
  15770. var scrollEnabled = this._scrollEnabled();
  15771. itemDom.floating = $(dt.table().node().cloneNode(false))
  15772. .attr('aria-hidden', 'true')
  15773. .css({
  15774. top: 0,
  15775. left: 0
  15776. })
  15777. .removeAttr('id');
  15778. itemDom.floatingParent
  15779. .css({
  15780. width: scrollBody[0].offsetWidth,
  15781. overflow: 'hidden',
  15782. height: 'fit-content',
  15783. position: 'fixed',
  15784. left: scrollEnabled
  15785. ? tableNode.offset().left + scrollBody.scrollLeft()
  15786. : 0
  15787. })
  15788. .css(
  15789. item === 'header'
  15790. ? {
  15791. top: this.c.headerOffset,
  15792. bottom: ''
  15793. }
  15794. : {
  15795. top: '',
  15796. bottom: this.c.footerOffset
  15797. }
  15798. )
  15799. .addClass(
  15800. item === 'footer'
  15801. ? 'dtfh-floatingparent-foot'
  15802. : 'dtfh-floatingparent-head'
  15803. )
  15804. .appendTo('body')
  15805. .children()
  15806. .eq(0)
  15807. .append(itemDom.floating);
  15808. this._stickyPosition(itemDom.floating, '-');
  15809. var scrollLeftUpdate = function () {
  15810. var scrollLeft = scrollBody.scrollLeft();
  15811. that.s.scrollLeft = { footer: scrollLeft, header: scrollLeft };
  15812. itemDom.floatingParent.scrollLeft(that.s.scrollLeft.header);
  15813. };
  15814. scrollLeftUpdate();
  15815. scrollBody.off('scroll.dtfh').on('scroll.dtfh', scrollLeftUpdate);
  15816. // Need padding on the header's container to allow for a scrollbar,
  15817. // just like how DataTables handles it
  15818. itemDom.floatingParent.children().css({
  15819. width: 'fit-content',
  15820. paddingRight: that.s.dt.settings()[0].oBrowser.barWidth
  15821. });
  15822. // Blocker to hide the table behind the scrollbar - this needs to use
  15823. // fixed positioning in the container since we don't have an outer wrapper
  15824. let blocker = $(
  15825. item === 'footer'
  15826. ? 'div.dtfc-bottom-blocker'
  15827. : 'div.dtfc-top-blocker',
  15828. dt.table().container()
  15829. );
  15830. if (blocker.length) {
  15831. blocker
  15832. .clone()
  15833. .appendTo(itemDom.floatingParent)
  15834. .css({
  15835. position: 'fixed',
  15836. right: blocker.width()
  15837. });
  15838. }
  15839. // Insert a fake thead/tfoot into the DataTable to stop it jumping around
  15840. itemDom.placeholder = itemElement.clone(false);
  15841. itemDom.placeholder.find('*[id]').removeAttr('id');
  15842. // Move the thead / tfoot elements around - original into the floating
  15843. // element and clone into the original table
  15844. itemDom.host.prepend(itemDom.placeholder);
  15845. itemDom.floating.append(itemElement);
  15846. this._widths(itemDom);
  15847. }
  15848. },
  15849. /**
  15850. * This method sets the sticky position of the header elements to match fixed columns
  15851. * @param {JQuery<HTMLElement>} el
  15852. * @param {string} sign
  15853. */
  15854. _stickyPosition: function (el, sign) {
  15855. if (this._scrollEnabled()) {
  15856. var that = this;
  15857. var rtl = $(that.s.dt.table().node()).css('direction') === 'rtl';
  15858. el.find('th').each(function () {
  15859. // Find out if fixed header has previously set this column
  15860. if ($(this).css('position') === 'sticky') {
  15861. var right = $(this).css('right');
  15862. var left = $(this).css('left');
  15863. var potential;
  15864. if (right !== 'auto' && !rtl) {
  15865. potential = +right.replace(/px/g, '')
  15866. $(this).css('right', potential > 0 ? potential : 0);
  15867. }
  15868. else if (left !== 'auto' && rtl) {
  15869. potential = +left.replace(/px/g, '');
  15870. $(this).css('left', potential > 0 ? potential : 0);
  15871. }
  15872. }
  15873. });
  15874. }
  15875. },
  15876. /**
  15877. * Reposition the floating elements to take account of horizontal page
  15878. * scroll
  15879. *
  15880. * @param {string} item The `header` or `footer`
  15881. * @param {int} scrollLeft Document scrollLeft
  15882. * @private
  15883. */
  15884. _horizontal: function (item, scrollLeft) {
  15885. var itemDom = this.dom[item];
  15886. var lastScrollLeft = this.s.scrollLeft;
  15887. if (itemDom.floating && lastScrollLeft[item] !== scrollLeft) {
  15888. // If scrolling is enabled we need to match the floating header to the body
  15889. if (this._scrollEnabled()) {
  15890. var newScrollLeft = $(
  15891. $(this.s.dt.table().node()).parent()
  15892. ).scrollLeft();
  15893. itemDom.floating.scrollLeft(newScrollLeft);
  15894. itemDom.floatingParent.scrollLeft(newScrollLeft);
  15895. }
  15896. lastScrollLeft[item] = scrollLeft;
  15897. }
  15898. },
  15899. /**
  15900. * Change from one display mode to another. Each fixed item can be in one
  15901. * of:
  15902. *
  15903. * * `in-place` - In the main DataTable
  15904. * * `in` - Floating over the DataTable
  15905. * * `below` - (Header only) Fixed to the bottom of the table body
  15906. * * `above` - (Footer only) Fixed to the top of the table body
  15907. *
  15908. * @param {string} mode Mode that the item should be shown in
  15909. * @param {string} item 'header' or 'footer'
  15910. * @param {boolean} forceChange Force a redraw of the mode, even if already
  15911. * in that mode.
  15912. * @private
  15913. */
  15914. _modeChange: function (mode, item, forceChange) {
  15915. var itemDom = this.dom[item];
  15916. var position = this.s.position;
  15917. // Just determine if scroll is enabled once
  15918. var scrollEnabled = this._scrollEnabled();
  15919. // If footer and scrolling is enabled then we don't clone
  15920. // Instead the table's height is decreased accordingly - see `_scroll()`
  15921. if (item === 'footer' && scrollEnabled) {
  15922. return;
  15923. }
  15924. // It isn't trivial to add a !important css attribute...
  15925. var importantWidth = function (w) {
  15926. itemDom.floating[0].style.setProperty('width', w + 'px', 'important');
  15927. // If not scrolling also have to update the floatingParent
  15928. if (!scrollEnabled) {
  15929. itemDom.floatingParent[0].style.setProperty('width', w + 'px', 'important');
  15930. }
  15931. };
  15932. // Record focus. Browser's will cause input elements to loose focus if
  15933. // they are inserted else where in the doc
  15934. var tablePart = this.dom[item === 'footer' ? 'tfoot' : 'thead'];
  15935. var focus = $.contains(tablePart[0], document.activeElement)
  15936. ? document.activeElement
  15937. : null;
  15938. var scrollBody = $($(this.s.dt.table().node()).parent());
  15939. if (mode === 'in-place') {
  15940. // Insert the header back into the table's real header
  15941. if (itemDom.placeholder) {
  15942. itemDom.placeholder.remove();
  15943. itemDom.placeholder = null;
  15944. }
  15945. if (item === 'header') {
  15946. itemDom.host.prepend(tablePart);
  15947. }
  15948. else {
  15949. itemDom.host.append(tablePart);
  15950. }
  15951. if (itemDom.floating) {
  15952. itemDom.floating.remove();
  15953. itemDom.floating = null;
  15954. this._stickyPosition(itemDom.host, '+');
  15955. }
  15956. if (itemDom.floatingParent) {
  15957. itemDom.floatingParent.find('div.dtfc-top-blocker').remove();
  15958. itemDom.floatingParent.remove();
  15959. }
  15960. $($(itemDom.host.parent()).parent()).scrollLeft(
  15961. scrollBody.scrollLeft()
  15962. );
  15963. }
  15964. else if (mode === 'in') {
  15965. // Remove the header from the real table and insert into a fixed
  15966. // positioned floating table clone
  15967. this._clone(item, forceChange);
  15968. // Get useful position values
  15969. var scrollOffset = scrollBody.offset();
  15970. var windowTop = $(document).scrollTop();
  15971. var windowHeight = $(window).height();
  15972. var windowBottom = windowTop + windowHeight;
  15973. var bodyTop = scrollEnabled ? scrollOffset.top : position.tbodyTop;
  15974. var bodyBottom = scrollEnabled
  15975. ? scrollOffset.top + scrollBody.outerHeight()
  15976. : position.tfootTop;
  15977. // Calculate the amount that the footer or header needs to be shuffled
  15978. var shuffle;
  15979. if (item === 'footer') {
  15980. shuffle =
  15981. bodyTop > windowBottom
  15982. ? position.tfootHeight // Yes - push the footer below
  15983. : bodyTop + position.tfootHeight - windowBottom; // No
  15984. }
  15985. else {
  15986. // Otherwise must be a header so get the difference from the bottom of the
  15987. // desired floating header and the bottom of the table body
  15988. shuffle =
  15989. windowTop +
  15990. this.c.headerOffset +
  15991. position.theadHeight -
  15992. bodyBottom;
  15993. }
  15994. // Set the top or bottom based off of the offset and the shuffle value
  15995. var prop = item === 'header' ? 'top' : 'bottom';
  15996. var val = this.c[item + 'Offset'] - (shuffle > 0 ? shuffle : 0);
  15997. itemDom.floating.addClass('fixedHeader-floating');
  15998. itemDom.floatingParent
  15999. .css(prop, val)
  16000. .css({
  16001. left: position.left,
  16002. 'z-index': 3
  16003. });
  16004. importantWidth(position.width);
  16005. if (item === 'footer') {
  16006. itemDom.floating.css('top', '');
  16007. }
  16008. }
  16009. else if (mode === 'below') {
  16010. // only used for the header
  16011. // Fix the position of the floating header at base of the table body
  16012. this._clone(item, forceChange);
  16013. itemDom.floating.addClass('fixedHeader-locked');
  16014. itemDom.floatingParent.css({
  16015. position: 'absolute',
  16016. top: position.tfootTop - position.theadHeight,
  16017. left: position.left + 'px'
  16018. });
  16019. importantWidth(position.width);
  16020. }
  16021. else if (mode === 'above') {
  16022. // only used for the footer
  16023. // Fix the position of the floating footer at top of the table body
  16024. this._clone(item, forceChange);
  16025. itemDom.floating.addClass('fixedHeader-locked');
  16026. itemDom.floatingParent.css({
  16027. position: 'absolute',
  16028. top: position.tbodyTop,
  16029. left: position.left + 'px'
  16030. });
  16031. importantWidth(position.width);
  16032. }
  16033. // Restore focus if it was lost
  16034. if (focus && focus !== document.activeElement) {
  16035. setTimeout(function () {
  16036. focus.focus();
  16037. }, 10);
  16038. }
  16039. this.s.scrollLeft.header = -1;
  16040. this.s.scrollLeft.footer = -1;
  16041. this.s[item + 'Mode'] = mode;
  16042. },
  16043. /**
  16044. * Cache the positional information that is required for the mode
  16045. * calculations that FixedHeader performs.
  16046. *
  16047. * @private
  16048. */
  16049. _positions: function () {
  16050. var dt = this.s.dt;
  16051. var table = dt.table();
  16052. var position = this.s.position;
  16053. var dom = this.dom;
  16054. var tableNode = $(table.node());
  16055. var scrollEnabled = this._scrollEnabled();
  16056. // Need to use the header and footer that are in the main table,
  16057. // regardless of if they are clones, since they hold the positions we
  16058. // want to measure from
  16059. var thead = $(dt.table().header());
  16060. var tfoot = $(dt.table().footer());
  16061. var tbody = dom.tbody;
  16062. var scrollBody = tableNode.parent();
  16063. position.visible = tableNode.is(':visible');
  16064. position.width = tableNode.outerWidth();
  16065. position.left = tableNode.offset().left;
  16066. position.theadTop = thead.offset().top;
  16067. position.tbodyTop = scrollEnabled
  16068. ? scrollBody.offset().top
  16069. : tbody.offset().top;
  16070. position.tbodyHeight = scrollEnabled
  16071. ? scrollBody.outerHeight()
  16072. : tbody.outerHeight();
  16073. position.theadHeight = thead.outerHeight();
  16074. position.theadBottom = position.theadTop + position.theadHeight;
  16075. position.tfootTop = position.tbodyTop + position.tbodyHeight; //tfoot.offset().top;
  16076. if (tfoot.length) {
  16077. position.tfootBottom = position.tfootTop + tfoot.outerHeight();
  16078. position.tfootHeight = tfoot.outerHeight();
  16079. }
  16080. else {
  16081. position.tfootBottom = position.tfootTop;
  16082. position.tfootHeight = 0;
  16083. }
  16084. },
  16085. /**
  16086. * Mode calculation - determine what mode the fixed items should be placed
  16087. * into.
  16088. *
  16089. * @param {boolean} forceChange Force a redraw of the mode, even if already
  16090. * in that mode.
  16091. * @private
  16092. */
  16093. _scroll: function (forceChange) {
  16094. if (this.s.dt.settings()[0].bDestroying) {
  16095. return;
  16096. }
  16097. // ScrollBody details
  16098. var scrollEnabled = this._scrollEnabled();
  16099. var scrollBody = $(this.s.dt.table().node()).parent();
  16100. var scrollOffset = scrollBody.offset();
  16101. var scrollHeight = scrollBody.outerHeight();
  16102. // Window details
  16103. var windowLeft = $(document).scrollLeft();
  16104. var windowTop = $(document).scrollTop();
  16105. var windowHeight = $(window).height();
  16106. var windowBottom = windowHeight + windowTop;
  16107. var position = this.s.position;
  16108. var headerMode, footerMode;
  16109. // Body Details
  16110. var bodyTop = scrollEnabled ? scrollOffset.top : position.tbodyTop;
  16111. var bodyLeft = scrollEnabled ? scrollOffset.left : position.left;
  16112. var bodyBottom = scrollEnabled
  16113. ? scrollOffset.top + scrollHeight
  16114. : position.tfootTop;
  16115. var bodyWidth = scrollEnabled
  16116. ? scrollBody.outerWidth()
  16117. : position.tbodyWidth;
  16118. if (this.c.header) {
  16119. if (!this.s.enable) {
  16120. headerMode = 'in-place';
  16121. }
  16122. // The header is in it's normal place if the body top is lower than
  16123. // the scroll of the window plus the headerOffset and the height of the header
  16124. else if (
  16125. !position.visible ||
  16126. windowTop + this.c.headerOffset + position.theadHeight <=
  16127. bodyTop
  16128. ) {
  16129. headerMode = 'in-place';
  16130. }
  16131. // The header should be floated if
  16132. else if (
  16133. // The scrolling plus the header offset plus the height of the header is lower than the top of the body
  16134. windowTop + this.c.headerOffset + position.theadHeight >
  16135. bodyTop &&
  16136. // And the scrolling at the top plus the header offset is above the bottom of the body
  16137. windowTop + this.c.headerOffset + position.theadHeight <
  16138. bodyBottom
  16139. ) {
  16140. headerMode = 'in';
  16141. // Further to the above, If the scrolling plus the header offset plus the header height is lower
  16142. // than the bottom of the table a shuffle is required so have to force the calculation
  16143. if (
  16144. windowTop + this.c.headerOffset + position.theadHeight >
  16145. bodyBottom ||
  16146. this.dom.header.floatingParent === undefined
  16147. ) {
  16148. forceChange = true;
  16149. }
  16150. else {
  16151. this.dom.header.floatingParent
  16152. .css({
  16153. top: this.c.headerOffset,
  16154. position: 'fixed'
  16155. })
  16156. .children()
  16157. .eq(0)
  16158. .append(this.dom.header.floating);
  16159. }
  16160. }
  16161. // Anything else and the view is below the table
  16162. else {
  16163. headerMode = 'below';
  16164. }
  16165. if (forceChange || headerMode !== this.s.headerMode) {
  16166. this._modeChange(headerMode, 'header', forceChange);
  16167. }
  16168. this._horizontal('header', windowLeft);
  16169. }
  16170. var header = {
  16171. offset: { top: 0, left: 0 },
  16172. height: 0
  16173. };
  16174. var footer = {
  16175. offset: { top: 0, left: 0 },
  16176. height: 0
  16177. };
  16178. if (
  16179. this.c.footer &&
  16180. this.dom.tfoot.length &&
  16181. this.dom.tfoot.find('th, td').length
  16182. ) {
  16183. if (!this.s.enable) {
  16184. footerMode = 'in-place';
  16185. }
  16186. else if (
  16187. !position.visible ||
  16188. position.tfootBottom + this.c.footerOffset <= windowBottom
  16189. ) {
  16190. footerMode = 'in-place';
  16191. }
  16192. else if (
  16193. bodyBottom + position.tfootHeight + this.c.footerOffset >
  16194. windowBottom &&
  16195. bodyTop + this.c.footerOffset < windowBottom
  16196. ) {
  16197. footerMode = 'in';
  16198. forceChange = true;
  16199. }
  16200. else {
  16201. footerMode = 'above';
  16202. }
  16203. if (forceChange || footerMode !== this.s.footerMode) {
  16204. this._modeChange(footerMode, 'footer', forceChange);
  16205. }
  16206. this._horizontal('footer', windowLeft);
  16207. var getOffsetHeight = function (el) {
  16208. return {
  16209. offset: el.offset(),
  16210. height: el.outerHeight()
  16211. };
  16212. };
  16213. header = this.dom.header.floating
  16214. ? getOffsetHeight(this.dom.header.floating)
  16215. : getOffsetHeight(this.dom.thead);
  16216. footer = this.dom.footer.floating
  16217. ? getOffsetHeight(this.dom.footer.floating)
  16218. : getOffsetHeight(this.dom.tfoot);
  16219. // If scrolling is enabled and the footer is off the screen
  16220. if (scrollEnabled && footer.offset.top > windowTop) {
  16221. // && footer.offset.top >= windowBottom) {
  16222. // Calculate the gap between the top of the scrollBody and the top of the window
  16223. var overlap = windowTop - scrollOffset.top;
  16224. // The new height is the bottom of the window
  16225. var newHeight =
  16226. windowBottom +
  16227. // If the gap between the top of the scrollbody and the window is more than
  16228. // the height of the header then the top of the table is still visible so add that gap
  16229. // Doing this has effectively calculated the height from the top of the table to the bottom of the current page
  16230. (overlap > -header.height ? overlap : 0) -
  16231. // Take from that
  16232. // The top of the header plus
  16233. (header.offset.top +
  16234. // The header height if the standard header is present
  16235. (overlap < -header.height ? header.height : 0) +
  16236. // And the height of the footer
  16237. footer.height);
  16238. // Don't want a negative height
  16239. if (newHeight < 0) {
  16240. newHeight = 0;
  16241. }
  16242. // At the end of the above calculation the space between the header (top of the page if floating)
  16243. // and the point just above the footer should be the new value for the height of the table.
  16244. scrollBody.outerHeight(newHeight);
  16245. // Need some rounding here as sometimes very small decimal places are encountered
  16246. // If the actual height is bigger or equal to the height we just applied then the footer is "Floating"
  16247. if (
  16248. Math.round(scrollBody.outerHeight()) >=
  16249. Math.round(newHeight)
  16250. ) {
  16251. $(this.dom.tfoot.parent()).addClass('fixedHeader-floating');
  16252. }
  16253. // Otherwise max-width has kicked in so it is not floating
  16254. else {
  16255. $(this.dom.tfoot.parent()).removeClass(
  16256. 'fixedHeader-floating'
  16257. );
  16258. }
  16259. }
  16260. }
  16261. if (this.dom.header.floating) {
  16262. this.dom.header.floatingParent.css('left', bodyLeft - windowLeft);
  16263. }
  16264. if (this.dom.footer.floating) {
  16265. this.dom.footer.floatingParent.css('left', bodyLeft - windowLeft);
  16266. }
  16267. // If fixed columns is being used on this table then the blockers need to be copied across
  16268. // Cloning these is cleaner than creating as our own as it will keep consistency with fixedColumns automatically
  16269. // ASSUMING that the class remains the same
  16270. if (this.s.dt.settings()[0]._fixedColumns !== undefined) {
  16271. var adjustBlocker = function (side, end, el) {
  16272. if (el === undefined) {
  16273. var blocker = $(
  16274. 'div.dtfc-' + side + '-' + end + '-blocker'
  16275. );
  16276. el =
  16277. blocker.length === 0
  16278. ? null
  16279. : blocker.clone().css('z-index', 1);
  16280. }
  16281. if (el !== null) {
  16282. if (headerMode === 'in' || headerMode === 'below') {
  16283. el.appendTo('body').css({
  16284. top:
  16285. end === 'top'
  16286. ? header.offset.top
  16287. : footer.offset.top,
  16288. left:
  16289. side === 'right'
  16290. ? bodyLeft + bodyWidth - el.width()
  16291. : bodyLeft
  16292. });
  16293. }
  16294. else {
  16295. el.detach();
  16296. }
  16297. }
  16298. return el;
  16299. };
  16300. // Adjust all blockers
  16301. this.dom.header.rightBlocker = adjustBlocker(
  16302. 'right',
  16303. 'top',
  16304. this.dom.header.rightBlocker
  16305. );
  16306. this.dom.header.leftBlocker = adjustBlocker(
  16307. 'left',
  16308. 'top',
  16309. this.dom.header.leftBlocker
  16310. );
  16311. this.dom.footer.rightBlocker = adjustBlocker(
  16312. 'right',
  16313. 'bottom',
  16314. this.dom.footer.rightBlocker
  16315. );
  16316. this.dom.footer.leftBlocker = adjustBlocker(
  16317. 'left',
  16318. 'bottom',
  16319. this.dom.footer.leftBlocker
  16320. );
  16321. }
  16322. },
  16323. /**
  16324. * Function to check if scrolling is enabled on the table or not
  16325. * @returns Boolean value indicating if scrolling on the table is enabled or not
  16326. */
  16327. _scrollEnabled: function () {
  16328. var oScroll = this.s.dt.settings()[0].oScroll;
  16329. if (oScroll.sY !== '' || oScroll.sX !== '') {
  16330. return true;
  16331. }
  16332. return false;
  16333. },
  16334. /**
  16335. * Realign columns by using the colgroup tag and
  16336. * checking column widths
  16337. */
  16338. _widths: function (itemDom) {
  16339. if (! itemDom || ! itemDom.placeholder) {
  16340. return;
  16341. }
  16342. // Match the table overall width
  16343. var tableNode = $(this.s.dt.table().node());
  16344. var scrollBody = $(tableNode.parent());
  16345. itemDom.floatingParent.css('width', scrollBody[0].offsetWidth);
  16346. itemDom.floating.css('width', tableNode[0].offsetWidth);
  16347. // Strip out the old colgroup
  16348. $('colgroup', itemDom.floating).remove();
  16349. // Copy the `colgroup` element to define the number of columns - needed
  16350. // for complex header cases where a column might not have a unique
  16351. // header
  16352. var cols = itemDom.placeholder
  16353. .parent()
  16354. .find('colgroup')
  16355. .clone()
  16356. .appendTo(itemDom.floating)
  16357. .find('col');
  16358. // However, the widths defined in the colgroup from the DataTable might
  16359. // not exactly reflect the actual widths of the columns (content can
  16360. // force it to stretch). So we need to copy the actual widths into the
  16361. // colgroup / col's used for the floating header.
  16362. var widths = this.s.dt.columns(':visible').widths();
  16363. for (var i=0 ; i<widths.length ; i++) {
  16364. cols.eq(i).css('width', widths[i]);
  16365. }
  16366. }
  16367. });
  16368. /**
  16369. * Version
  16370. * @type {String}
  16371. * @static
  16372. */
  16373. FixedHeader.version = '4.0.1';
  16374. /**
  16375. * Defaults
  16376. * @type {Object}
  16377. * @static
  16378. */
  16379. FixedHeader.defaults = {
  16380. header: true,
  16381. footer: false,
  16382. headerOffset: 0,
  16383. footerOffset: 0
  16384. };
  16385. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  16386. * DataTables interfaces
  16387. */
  16388. // Attach for constructor access
  16389. $.fn.dataTable.FixedHeader = FixedHeader;
  16390. $.fn.DataTable.FixedHeader = FixedHeader;
  16391. // DataTables creation - check if the FixedHeader option has been defined on the
  16392. // table and if so, initialise
  16393. $(document).on('init.dt.dtfh', function (e, settings, json) {
  16394. if (e.namespace !== 'dt') {
  16395. return;
  16396. }
  16397. var init = settings.oInit.fixedHeader;
  16398. var defaults = DataTable.defaults.fixedHeader;
  16399. if ((init || defaults) && !settings._fixedHeader) {
  16400. var opts = $.extend({}, defaults, init);
  16401. if (init !== false) {
  16402. new FixedHeader(settings, opts);
  16403. }
  16404. }
  16405. });
  16406. // DataTables API methods
  16407. DataTable.Api.register('fixedHeader()', function () { });
  16408. DataTable.Api.register('fixedHeader.adjust()', function () {
  16409. return this.iterator('table', function (ctx) {
  16410. var fh = ctx._fixedHeader;
  16411. if (fh) {
  16412. fh.update();
  16413. }
  16414. });
  16415. });
  16416. DataTable.Api.register('fixedHeader.enable()', function (flag) {
  16417. return this.iterator('table', function (ctx) {
  16418. var fh = ctx._fixedHeader;
  16419. flag = flag !== undefined ? flag : true;
  16420. if (fh && flag !== fh.enabled()) {
  16421. fh.enable(flag);
  16422. }
  16423. });
  16424. });
  16425. DataTable.Api.register('fixedHeader.enabled()', function () {
  16426. if (this.context.length) {
  16427. var fh = this.context[0]._fixedHeader;
  16428. if (fh) {
  16429. return fh.enabled();
  16430. }
  16431. }
  16432. return false;
  16433. });
  16434. DataTable.Api.register('fixedHeader.disable()', function () {
  16435. return this.iterator('table', function (ctx) {
  16436. var fh = ctx._fixedHeader;
  16437. if (fh && fh.enabled()) {
  16438. fh.enable(false);
  16439. }
  16440. });
  16441. });
  16442. $.each(['header', 'footer'], function (i, el) {
  16443. DataTable.Api.register('fixedHeader.' + el + 'Offset()', function (offset) {
  16444. var ctx = this.context;
  16445. if (offset === undefined) {
  16446. return ctx.length && ctx[0]._fixedHeader
  16447. ? ctx[0]._fixedHeader[el + 'Offset']()
  16448. : undefined;
  16449. }
  16450. return this.iterator('table', function (ctx) {
  16451. var fh = ctx._fixedHeader;
  16452. if (fh) {
  16453. fh[el + 'Offset'](offset);
  16454. }
  16455. });
  16456. });
  16457. });
  16458. return DataTable;
  16459. }));
  16460. /*! SearchBuilder 1.7.1
  16461. * ©SpryMedia Ltd - datatables.net/license/mit
  16462. */
  16463. (function( factory ){
  16464. if ( typeof define === 'function' && define.amd ) {
  16465. // AMD
  16466. define( ['jquery', 'datatables.net'], function ( $ ) {
  16467. return factory( $, window, document );
  16468. } );
  16469. }
  16470. else if ( typeof exports === 'object' ) {
  16471. // CommonJS
  16472. var jq = require('jquery');
  16473. var cjsRequires = function (root, $) {
  16474. if ( ! $.fn.dataTable ) {
  16475. require('datatables.net')(root, $);
  16476. }
  16477. };
  16478. if (typeof window === 'undefined') {
  16479. module.exports = function (root, $) {
  16480. if ( ! root ) {
  16481. // CommonJS environments without a window global must pass a
  16482. // root. This will give an error otherwise
  16483. root = window;
  16484. }
  16485. if ( ! $ ) {
  16486. $ = jq( root );
  16487. }
  16488. cjsRequires( root, $ );
  16489. return factory( $, root, root.document );
  16490. };
  16491. }
  16492. else {
  16493. cjsRequires( window, jq );
  16494. module.exports = factory( jq, window, window.document );
  16495. }
  16496. }
  16497. else {
  16498. // Browser
  16499. factory( jQuery, window, document );
  16500. }
  16501. }(function( $, window, document ) {
  16502. 'use strict';
  16503. var DataTable = $.fn.dataTable;
  16504. (function () {
  16505. 'use strict';
  16506. var $$3;
  16507. var dataTable$3;
  16508. function moment() {
  16509. return window.moment;
  16510. }
  16511. function luxon() {
  16512. return window.luxon;
  16513. }
  16514. /**
  16515. * Sets the value of jQuery for use in the file
  16516. *
  16517. * @param jq the instance of jQuery to be set
  16518. */
  16519. function setJQuery$2(jq) {
  16520. $$3 = jq;
  16521. dataTable$3 = jq.fn.dataTable;
  16522. }
  16523. /**
  16524. * The Criteria class is used within SearchBuilder to represent a search criteria
  16525. */
  16526. var Criteria = /** @class */ (function () {
  16527. function Criteria(table, opts, topGroup, index, depth, serverData, liveSearch) {
  16528. if (index === void 0) { index = 0; }
  16529. if (depth === void 0) { depth = 1; }
  16530. if (serverData === void 0) { serverData = undefined; }
  16531. if (liveSearch === void 0) { liveSearch = false; }
  16532. var _this = this;
  16533. // Check that the required version of DataTables is included
  16534. if (!dataTable$3 || !dataTable$3.versionCheck || !dataTable$3.versionCheck('1.10.0')) {
  16535. throw new Error('SearchPane requires DataTables 1.10 or newer');
  16536. }
  16537. this.classes = $$3.extend(true, {}, Criteria.classes);
  16538. // Get options from user and any extra conditions/column types defined by plug-ins
  16539. this.c = $$3.extend(true, {}, Criteria.defaults, $$3.fn.dataTable.ext.searchBuilder, opts);
  16540. var i18n = this.c.i18n;
  16541. this.s = {
  16542. condition: undefined,
  16543. conditions: {},
  16544. data: undefined,
  16545. dataIdx: -1,
  16546. dataPoints: [],
  16547. dateFormat: false,
  16548. depth: depth,
  16549. dt: table,
  16550. filled: false,
  16551. index: index,
  16552. liveSearch: liveSearch,
  16553. origData: undefined,
  16554. preventRedraw: false,
  16555. serverData: serverData,
  16556. topGroup: topGroup,
  16557. type: '',
  16558. value: []
  16559. };
  16560. this.dom = {
  16561. buttons: $$3('<div/>')
  16562. .addClass(this.classes.buttonContainer),
  16563. condition: $$3('<select disabled/>')
  16564. .addClass(this.classes.condition)
  16565. .addClass(this.classes.dropDown)
  16566. .addClass(this.classes.italic)
  16567. .attr('autocomplete', 'hacking'),
  16568. conditionTitle: $$3('<option value="" disabled selected hidden/>')
  16569. .html(this.s.dt.i18n('searchBuilder.condition', i18n.condition)),
  16570. container: $$3('<div/>')
  16571. .addClass(this.classes.container),
  16572. data: $$3('<select/>')
  16573. .addClass(this.classes.data)
  16574. .addClass(this.classes.dropDown)
  16575. .addClass(this.classes.italic),
  16576. dataTitle: $$3('<option value="" disabled selected hidden/>')
  16577. .html(this.s.dt.i18n('searchBuilder.data', i18n.data)),
  16578. defaultValue: $$3('<select disabled/>')
  16579. .addClass(this.classes.value)
  16580. .addClass(this.classes.dropDown)
  16581. .addClass(this.classes.select)
  16582. .addClass(this.classes.italic),
  16583. "delete": $$3('<button/>')
  16584. .html(this.s.dt.i18n('searchBuilder.delete', i18n["delete"]))
  16585. .addClass(this.classes["delete"])
  16586. .addClass(this.classes.button)
  16587. .attr('title', this.s.dt.i18n('searchBuilder.deleteTitle', i18n.deleteTitle))
  16588. .attr('type', 'button'),
  16589. inputCont: $$3('<div/>')
  16590. .addClass(this.classes.inputCont),
  16591. // eslint-disable-next-line no-useless-escape
  16592. left: $$3('<button/>')
  16593. .html(this.s.dt.i18n('searchBuilder.left', i18n.left))
  16594. .addClass(this.classes.left)
  16595. .addClass(this.classes.button)
  16596. .attr('title', this.s.dt.i18n('searchBuilder.leftTitle', i18n.leftTitle))
  16597. .attr('type', 'button'),
  16598. // eslint-disable-next-line no-useless-escape
  16599. right: $$3('<button/>')
  16600. .html(this.s.dt.i18n('searchBuilder.right', i18n.right))
  16601. .addClass(this.classes.right)
  16602. .addClass(this.classes.button)
  16603. .attr('title', this.s.dt.i18n('searchBuilder.rightTitle', i18n.rightTitle))
  16604. .attr('type', 'button'),
  16605. value: [
  16606. $$3('<select disabled/>')
  16607. .addClass(this.classes.value)
  16608. .addClass(this.classes.dropDown)
  16609. .addClass(this.classes.italic)
  16610. .addClass(this.classes.select)
  16611. ],
  16612. valueTitle: $$3('<option value="--valueTitle--" disabled selected hidden/>')
  16613. .html(this.s.dt.i18n('searchBuilder.value', i18n.value))
  16614. };
  16615. // If the greyscale option is selected then add the class to add the grey colour to SearchBuilder
  16616. if (this.c.greyscale) {
  16617. this.dom.data.addClass(this.classes.greyscale);
  16618. this.dom.condition.addClass(this.classes.greyscale);
  16619. this.dom.defaultValue.addClass(this.classes.greyscale);
  16620. for (var _i = 0, _a = this.dom.value; _i < _a.length; _i++) {
  16621. var val = _a[_i];
  16622. val.addClass(this.classes.greyscale);
  16623. }
  16624. }
  16625. $$3(window).on('resize.dtsb', dataTable$3.util.throttle(function () {
  16626. _this.s.topGroup.trigger('dtsb-redrawLogic');
  16627. }));
  16628. this._buildCriteria();
  16629. return this;
  16630. }
  16631. /**
  16632. * Escape html characters within a string
  16633. *
  16634. * @param txt the string to be escaped
  16635. * @returns the escaped string
  16636. */
  16637. Criteria._escapeHTML = function (txt) {
  16638. return txt
  16639. .toString()
  16640. .replace(/&lt;/g, '<')
  16641. .replace(/&gt;/g, '>')
  16642. .replace(/&quot;/g, '"')
  16643. .replace(/&amp;/g, '&');
  16644. };
  16645. /**
  16646. * Redraw the DataTable with the current search parameters
  16647. */
  16648. Criteria.prototype.doSearch = function () {
  16649. // Only do the search if live search is disabled, otherwise the search
  16650. // is triggered by the button at the top level group.
  16651. if (this.c.liveSearch) {
  16652. this.s.dt.draw();
  16653. }
  16654. };
  16655. /**
  16656. * Parses formatted numbers down to a form where they can be compared.
  16657. * Note that this does not account for different decimal characters. Use
  16658. * parseNumber instead on the instance.
  16659. *
  16660. * @param val the value to convert
  16661. * @returns the converted value
  16662. */
  16663. Criteria.parseNumFmt = function (val) {
  16664. return +val.replace(/(?!^-)[^0-9.]/g, '');
  16665. };
  16666. /**
  16667. * Adds the left button to the criteria
  16668. */
  16669. Criteria.prototype.updateArrows = function (hasSiblings) {
  16670. if (hasSiblings === void 0) { hasSiblings = false; }
  16671. // Empty the container and append all of the elements in the correct order
  16672. this.dom.container.children().detach();
  16673. this.dom.container
  16674. .append(this.dom.data)
  16675. .append(this.dom.condition)
  16676. .append(this.dom.inputCont);
  16677. this.setListeners();
  16678. // Trigger the inserted events for the value elements as they are inserted
  16679. if (this.dom.value[0] !== undefined) {
  16680. $$3(this.dom.value[0]).trigger('dtsb-inserted');
  16681. }
  16682. for (var i = 1; i < this.dom.value.length; i++) {
  16683. this.dom.inputCont.append(this.dom.value[i]);
  16684. $$3(this.dom.value[i]).trigger('dtsb-inserted');
  16685. }
  16686. // If this is a top level criteria then don't let it move left
  16687. if (this.s.depth > 1) {
  16688. this.dom.buttons.append(this.dom.left);
  16689. }
  16690. // If the depthLimit of the query has been hit then don't add the right button
  16691. if ((this.c.depthLimit === false || this.s.depth < this.c.depthLimit) && hasSiblings) {
  16692. this.dom.buttons.append(this.dom.right);
  16693. }
  16694. else {
  16695. this.dom.right.remove();
  16696. }
  16697. this.dom.buttons.append(this.dom["delete"]);
  16698. this.dom.container.append(this.dom.buttons);
  16699. };
  16700. /**
  16701. * Destroys the criteria, removing listeners and container from the dom
  16702. */
  16703. Criteria.prototype.destroy = function () {
  16704. // Turn off listeners
  16705. this.dom.data.off('.dtsb');
  16706. this.dom.condition.off('.dtsb');
  16707. this.dom["delete"].off('.dtsb');
  16708. for (var _i = 0, _a = this.dom.value; _i < _a.length; _i++) {
  16709. var val = _a[_i];
  16710. val.off('.dtsb');
  16711. }
  16712. // Remove container from the dom
  16713. this.dom.container.remove();
  16714. };
  16715. /**
  16716. * Passes in the data for the row and compares it against this single criteria
  16717. *
  16718. * @param rowData The data for the row to be compared
  16719. * @returns boolean Whether the criteria has passed
  16720. */
  16721. Criteria.prototype.search = function (rowData, rowIdx) {
  16722. var settings = this.s.dt.settings()[0];
  16723. var condition = this.s.conditions[this.s.condition];
  16724. if (this.s.condition !== undefined && condition !== undefined) {
  16725. var filter = rowData[this.s.dataIdx];
  16726. // This check is in place for if a custom decimal character is in place
  16727. if (this.s.type.includes('num') &&
  16728. (settings.oLanguage.sDecimal !== '' ||
  16729. settings.oLanguage.sThousands !== '')) {
  16730. var splitRD = [rowData[this.s.dataIdx]];
  16731. if (settings.oLanguage.sDecimal !== '') {
  16732. splitRD = rowData[this.s.dataIdx].split(settings.oLanguage.sDecimal);
  16733. }
  16734. if (settings.oLanguage.sThousands !== '') {
  16735. for (var i = 0; i < splitRD.length; i++) {
  16736. splitRD[i] = splitRD[i].replace(settings.oLanguage.sThousands, ',');
  16737. }
  16738. }
  16739. filter = splitRD.join('.');
  16740. }
  16741. // If orthogonal data is in place we need to get it's values for searching
  16742. if (this.c.orthogonal.search !== 'filter') {
  16743. filter = settings.fastData(rowIdx, this.s.dataIdx, typeof this.c.orthogonal === 'string' ?
  16744. this.c.orthogonal :
  16745. this.c.orthogonal.search);
  16746. }
  16747. if (this.s.type === 'array') {
  16748. // Make sure we are working with an array
  16749. if (!Array.isArray(filter)) {
  16750. filter = [filter];
  16751. }
  16752. filter.sort();
  16753. for (var _i = 0, filter_1 = filter; _i < filter_1.length; _i++) {
  16754. var filt = filter_1[_i];
  16755. if (filt && typeof filt === 'string') {
  16756. filt = filt.replace(/[\r\n\u2028]/g, ' ');
  16757. }
  16758. }
  16759. }
  16760. else if (filter !== null && typeof filter === 'string') {
  16761. filter = filter.replace(/[\r\n\u2028]/g, ' ');
  16762. }
  16763. if (this.s.type.includes('html') && typeof filter === 'string') {
  16764. filter = filter.replace(/(<([^>]+)>)/ig, '');
  16765. }
  16766. // Not ideal, but jqueries .val() returns an empty string even
  16767. // when the value set is null, so we shall assume the two are equal
  16768. if (filter === null) {
  16769. filter = '';
  16770. }
  16771. return condition.search(filter, this.s.value, this);
  16772. }
  16773. };
  16774. /**
  16775. * Gets the details required to rebuild the criteria
  16776. */
  16777. Criteria.prototype.getDetails = function (deFormatDates) {
  16778. if (deFormatDates === void 0) { deFormatDates = false; }
  16779. var i;
  16780. var settings = this.s.dt.settings()[0];
  16781. // This check is in place for if a custom decimal character is in place
  16782. if (this.s.type !== null &&
  16783. this.s.type.includes('num') &&
  16784. (settings.oLanguage.sDecimal !== '' || settings.oLanguage.sThousands !== '')) {
  16785. for (i = 0; i < this.s.value.length; i++) {
  16786. var splitRD = [this.s.value[i].toString()];
  16787. if (settings.oLanguage.sDecimal !== '') {
  16788. splitRD = this.s.value[i].split(settings.oLanguage.sDecimal);
  16789. }
  16790. if (settings.oLanguage.sThousands !== '') {
  16791. for (var j = 0; j < splitRD.length; j++) {
  16792. splitRD[j] = splitRD[j].replace(settings.oLanguage.sThousands, ',');
  16793. }
  16794. }
  16795. this.s.value[i] = splitRD.join('.');
  16796. }
  16797. }
  16798. else if (this.s.type !== null && deFormatDates) {
  16799. if (this.s.type.includes('date') ||
  16800. this.s.type.includes('time')) {
  16801. for (i = 0; i < this.s.value.length; i++) {
  16802. if (this.s.value[i].match(/^\d{4}-([0]\d|1[0-2])-([0-2]\d|3[01])$/g) === null) {
  16803. this.s.value[i] = '';
  16804. }
  16805. }
  16806. }
  16807. else if (this.s.type.includes('moment')) {
  16808. for (i = 0; i < this.s.value.length; i++) {
  16809. if (this.s.value[i] &&
  16810. this.s.value[i].length > 0 &&
  16811. moment()(this.s.value[i], this.s.dateFormat, true).isValid()) {
  16812. this.s.value[i] = moment()(this.s.value[i], this.s.dateFormat).format('YYYY-MM-DD HH:mm:ss');
  16813. }
  16814. }
  16815. }
  16816. else if (this.s.type.includes('luxon')) {
  16817. for (i = 0; i < this.s.value.length; i++) {
  16818. if (this.s.value[i] &&
  16819. this.s.value[i].length > 0 &&
  16820. luxon().DateTime.fromFormat(this.s.value[i], this.s.dateFormat).invalid === null) {
  16821. this.s.value[i] = luxon().DateTime.fromFormat(this.s.value[i], this.s.dateFormat).toFormat('yyyy-MM-dd HH:mm:ss');
  16822. }
  16823. }
  16824. }
  16825. }
  16826. if (this.s.type.includes('num') && this.s.dt.page.info().serverSide) {
  16827. for (i = 0; i < this.s.value.length; i++) {
  16828. this.s.value[i] = this.s.value[i].replace(/[^0-9.\-]/g, '');
  16829. }
  16830. }
  16831. return {
  16832. condition: this.s.condition,
  16833. data: this.s.data,
  16834. origData: this.s.origData,
  16835. type: this.s.type,
  16836. value: this.s.value.map(function (a) { return a !== null && a !== undefined ? a.toString() : a; })
  16837. };
  16838. };
  16839. /**
  16840. * Getter for the node for the container of the criteria
  16841. *
  16842. * @returns JQuery<HTMLElement> the node for the container
  16843. */
  16844. Criteria.prototype.getNode = function () {
  16845. return this.dom.container;
  16846. };
  16847. /**
  16848. * Parses formatted numbers down to a form where they can be compared
  16849. *
  16850. * @param val the value to convert
  16851. * @returns the converted value
  16852. */
  16853. Criteria.prototype.parseNumber = function (val) {
  16854. var decimal = this.s.dt.i18n('decimal');
  16855. // Remove any periods and then replace the decimal with a period
  16856. if (decimal && decimal !== '.') {
  16857. val = val.replace(/\./g, '').replace(decimal, '.');
  16858. }
  16859. return +val.replace(/(?!^-)[^0-9.]/g, '');
  16860. };
  16861. /**
  16862. * Populates the criteria data, condition and value(s) as far as has been selected
  16863. */
  16864. Criteria.prototype.populate = function () {
  16865. this._populateData();
  16866. // If the column index has been found attempt to select a condition
  16867. if (this.s.dataIdx !== -1) {
  16868. this._populateCondition();
  16869. // If the condittion has been found attempt to select the values
  16870. if (this.s.condition !== undefined) {
  16871. this._populateValue();
  16872. }
  16873. }
  16874. };
  16875. /**
  16876. * Rebuilds the criteria based upon the details passed in
  16877. *
  16878. * @param loadedCriteria the details required to rebuild the criteria
  16879. */
  16880. Criteria.prototype.rebuild = function (loadedCriteria) {
  16881. // Check to see if the previously selected data exists, if so select it
  16882. var foundData = false;
  16883. var dataIdx, i;
  16884. this._populateData();
  16885. // If a data selection has previously been made attempt to find and select it
  16886. if (loadedCriteria.data !== undefined) {
  16887. var italic_1 = this.classes.italic;
  16888. var data_1 = this.dom.data;
  16889. this.dom.data.children('option').each(function () {
  16890. if (!foundData &&
  16891. ($$3(this).text() === loadedCriteria.data ||
  16892. loadedCriteria.origData && $$3(this).prop('origData') === loadedCriteria.origData)) {
  16893. $$3(this).prop('selected', true);
  16894. data_1.removeClass(italic_1);
  16895. foundData = true;
  16896. dataIdx = parseInt($$3(this).val(), 10);
  16897. }
  16898. else {
  16899. $$3(this).removeProp('selected');
  16900. }
  16901. });
  16902. }
  16903. // If the data has been found and selected then the condition can be populated and searched
  16904. if (foundData) {
  16905. this.s.data = loadedCriteria.data;
  16906. this.s.origData = loadedCriteria.origData;
  16907. this.s.dataIdx = dataIdx;
  16908. this.c.orthogonal = this._getOptions().orthogonal;
  16909. this.dom.dataTitle.remove();
  16910. this._populateCondition();
  16911. this.dom.conditionTitle.remove();
  16912. var condition = void 0;
  16913. // Check to see if the previously selected condition exists, if so select it
  16914. var options = this.dom.condition.children('option');
  16915. for (i = 0; i < options.length; i++) {
  16916. var option = $$3(options[i]);
  16917. if (loadedCriteria.condition !== undefined &&
  16918. option.val() === loadedCriteria.condition &&
  16919. typeof loadedCriteria.condition === 'string') {
  16920. option.prop('selected', true);
  16921. condition = option.val();
  16922. }
  16923. else {
  16924. option.removeProp('selected');
  16925. }
  16926. }
  16927. this.s.condition = condition;
  16928. // If the condition has been found and selected then the value can be populated and searched
  16929. if (this.s.condition !== undefined) {
  16930. this.dom.conditionTitle.removeProp('selected');
  16931. this.dom.conditionTitle.remove();
  16932. this.dom.condition.removeClass(this.classes.italic);
  16933. for (i = 0; i < options.length; i++) {
  16934. var opt = $$3(options[i]);
  16935. if (opt.val() !== this.s.condition) {
  16936. opt.removeProp('selected');
  16937. }
  16938. }
  16939. this._populateValue(loadedCriteria);
  16940. }
  16941. else {
  16942. this.dom.conditionTitle.prependTo(this.dom.condition).prop('selected', true);
  16943. }
  16944. }
  16945. };
  16946. /**
  16947. * Sets the listeners for the criteria
  16948. */
  16949. Criteria.prototype.setListeners = function () {
  16950. var _this = this;
  16951. this.dom.data
  16952. .unbind('change')
  16953. .on('change.dtsb', function () {
  16954. _this.dom.dataTitle.removeProp('selected');
  16955. // Need to go over every option to identify the correct selection
  16956. var options = _this.dom.data.children('option.' + _this.classes.option);
  16957. for (var i = 0; i < options.length; i++) {
  16958. var option = $$3(options[i]);
  16959. if (option.val() === _this.dom.data.val()) {
  16960. _this.dom.data.removeClass(_this.classes.italic);
  16961. option.prop('selected', true);
  16962. _this.s.dataIdx = +option.val();
  16963. _this.s.data = option.text();
  16964. _this.s.origData = option.prop('origData');
  16965. _this.c.orthogonal = _this._getOptions().orthogonal;
  16966. // When the data is changed, the values in condition and
  16967. // value may also change so need to renew them
  16968. _this._clearCondition();
  16969. _this._clearValue();
  16970. _this._populateCondition();
  16971. // If this criteria was previously active in the search then
  16972. // remove it from the search and trigger a new search
  16973. if (_this.s.filled) {
  16974. _this.s.filled = false;
  16975. _this.doSearch();
  16976. _this.setListeners();
  16977. }
  16978. _this.s.dt.state.save();
  16979. }
  16980. else {
  16981. option.removeProp('selected');
  16982. }
  16983. }
  16984. });
  16985. this.dom.condition
  16986. .unbind('change')
  16987. .on('change.dtsb', function () {
  16988. _this.dom.conditionTitle.removeProp('selected');
  16989. // Need to go over every option to identify the correct selection
  16990. var options = _this.dom.condition.children('option.' + _this.classes.option);
  16991. for (var i = 0; i < options.length; i++) {
  16992. var option = $$3(options[i]);
  16993. if (option.val() === _this.dom.condition.val()) {
  16994. _this.dom.condition.removeClass(_this.classes.italic);
  16995. option.prop('selected', true);
  16996. var condDisp = option.val();
  16997. // Find the condition that has been selected and store it internally
  16998. for (var _i = 0, _a = Object.keys(_this.s.conditions); _i < _a.length; _i++) {
  16999. var cond = _a[_i];
  17000. if (cond === condDisp) {
  17001. _this.s.condition = condDisp;
  17002. break;
  17003. }
  17004. }
  17005. // When the condition is changed, the value selector may switch between
  17006. // a select element and an input element
  17007. _this._clearValue();
  17008. _this._populateValue();
  17009. for (var _b = 0, _c = _this.dom.value; _b < _c.length; _b++) {
  17010. var val = _c[_b];
  17011. // If this criteria was previously active in the search then remove
  17012. // it from the search and trigger a new search
  17013. if (_this.s.filled && val !== undefined && _this.dom.inputCont.has(val[0]).length !== 0) {
  17014. _this.s.filled = false;
  17015. _this.doSearch();
  17016. _this.setListeners();
  17017. }
  17018. }
  17019. if (_this.dom.value.length === 0 ||
  17020. _this.dom.value.length === 1 && _this.dom.value[0] === undefined) {
  17021. _this.doSearch();
  17022. }
  17023. }
  17024. else {
  17025. option.removeProp('selected');
  17026. }
  17027. }
  17028. });
  17029. };
  17030. Criteria.prototype.setupButtons = function () {
  17031. if (window.innerWidth > 550) {
  17032. this.dom.container.removeClass(this.classes.vertical);
  17033. this.dom.buttons.css('left', null);
  17034. this.dom.buttons.css('top', null);
  17035. return;
  17036. }
  17037. this.dom.container.addClass(this.classes.vertical);
  17038. this.dom.buttons.css('left', this.dom.data.innerWidth());
  17039. this.dom.buttons.css('top', this.dom.data.position().top);
  17040. };
  17041. /**
  17042. * Builds the elements of the dom together
  17043. */
  17044. Criteria.prototype._buildCriteria = function () {
  17045. // Append Titles for select elements
  17046. this.dom.data.append(this.dom.dataTitle);
  17047. this.dom.condition.append(this.dom.conditionTitle);
  17048. // Add elements to container
  17049. this.dom.container
  17050. .append(this.dom.data)
  17051. .append(this.dom.condition);
  17052. this.dom.inputCont.empty();
  17053. for (var _i = 0, _a = this.dom.value; _i < _a.length; _i++) {
  17054. var val = _a[_i];
  17055. val.append(this.dom.valueTitle);
  17056. this.dom.inputCont.append(val);
  17057. }
  17058. // Add buttons to container
  17059. this.dom.buttons
  17060. .append(this.dom["delete"])
  17061. .append(this.dom.right);
  17062. this.dom.container.append(this.dom.inputCont).append(this.dom.buttons);
  17063. this.setListeners();
  17064. };
  17065. /**
  17066. * Clears the condition select element
  17067. */
  17068. Criteria.prototype._clearCondition = function () {
  17069. this.dom.condition.empty();
  17070. this.dom.conditionTitle.prop('selected', true).attr('disabled', 'true');
  17071. this.dom.condition.prepend(this.dom.conditionTitle).prop('selectedIndex', 0);
  17072. this.s.conditions = {};
  17073. this.s.condition = undefined;
  17074. };
  17075. /**
  17076. * Clears the value elements
  17077. */
  17078. Criteria.prototype._clearValue = function () {
  17079. var val;
  17080. if (this.s.condition !== undefined) {
  17081. if (this.dom.value.length > 0 && this.dom.value[0] !== undefined) {
  17082. // Remove all of the value elements
  17083. for (var _i = 0, _a = this.dom.value; _i < _a.length; _i++) {
  17084. val = _a[_i];
  17085. if (val !== undefined) {
  17086. // Timeout is annoying but because of IOS
  17087. setTimeout(function () {
  17088. val.remove();
  17089. }, 50);
  17090. }
  17091. }
  17092. }
  17093. // Call the init function to get the value elements for this condition
  17094. this.dom.value = [].concat(this.s.conditions[this.s.condition].init(this, Criteria.updateListener));
  17095. if (this.dom.value.length > 0 && this.dom.value[0] !== undefined) {
  17096. this.dom.inputCont
  17097. .empty()
  17098. .append(this.dom.value[0])
  17099. .insertAfter(this.dom.condition);
  17100. $$3(this.dom.value[0]).trigger('dtsb-inserted');
  17101. // Insert all of the value elements
  17102. for (var i = 1; i < this.dom.value.length; i++) {
  17103. this.dom.inputCont.append(this.dom.value[i]);
  17104. $$3(this.dom.value[i]).trigger('dtsb-inserted');
  17105. }
  17106. }
  17107. }
  17108. else {
  17109. // Remove all of the value elements
  17110. for (var _b = 0, _c = this.dom.value; _b < _c.length; _b++) {
  17111. val = _c[_b];
  17112. if (val !== undefined) {
  17113. // Timeout is annoying but because of IOS
  17114. setTimeout(function () {
  17115. val.remove();
  17116. }, 50);
  17117. }
  17118. }
  17119. // Append the default valueTitle to the default select element
  17120. this.dom.valueTitle
  17121. .prop('selected', true);
  17122. this.dom.defaultValue
  17123. .append(this.dom.valueTitle)
  17124. .insertAfter(this.dom.condition);
  17125. }
  17126. this.s.value = [];
  17127. this.dom.value = [
  17128. $$3('<select disabled/>')
  17129. .addClass(this.classes.value)
  17130. .addClass(this.classes.dropDown)
  17131. .addClass(this.classes.italic)
  17132. .addClass(this.classes.select)
  17133. .append(this.dom.valueTitle.clone())
  17134. ];
  17135. };
  17136. /**
  17137. * Gets the options for the column
  17138. *
  17139. * @returns {object} The options for the column
  17140. */
  17141. Criteria.prototype._getOptions = function () {
  17142. var table = this.s.dt;
  17143. return $$3.extend(true, {}, Criteria.defaults, table.settings()[0].aoColumns[this.s.dataIdx].searchBuilder);
  17144. };
  17145. /**
  17146. * Populates the condition dropdown
  17147. */
  17148. Criteria.prototype._populateCondition = function () {
  17149. var conditionOpts = [];
  17150. var conditionsLength = Object.keys(this.s.conditions).length;
  17151. var colInits = this.s.dt.settings()[0].aoColumns;
  17152. var column = +this.dom.data.children('option:selected').val();
  17153. var condition, condName;
  17154. // If there are no conditions stored then we need to get them from the appropriate type
  17155. if (conditionsLength === 0) {
  17156. this.s.type = this.s.dt.column(column).type();
  17157. if (colInits !== undefined) {
  17158. var colInit = colInits[column];
  17159. if (colInit.searchBuilderType !== undefined && colInit.searchBuilderType !== null) {
  17160. this.s.type = colInit.searchBuilderType;
  17161. }
  17162. else if (this.s.type === undefined || this.s.type === null) {
  17163. this.s.type = colInit.sType;
  17164. }
  17165. }
  17166. // If the column type is still unknown use the internal API to detect type
  17167. if (this.s.type === null || this.s.type === undefined) {
  17168. // This can only happen in DT1 - DT2 will do the invalidation of the type itself
  17169. if ($$3.fn.dataTable.ext.oApi) {
  17170. $$3.fn.dataTable.ext.oApi._fnColumnTypes(this.s.dt.settings()[0]);
  17171. }
  17172. this.s.type = this.s.dt.column(column).type();
  17173. }
  17174. // Enable the condition element
  17175. this.dom.condition
  17176. .removeAttr('disabled')
  17177. .empty()
  17178. .append(this.dom.conditionTitle)
  17179. .addClass(this.classes.italic);
  17180. this.dom.conditionTitle
  17181. .prop('selected', true);
  17182. var decimal = this.s.dt.settings()[0].oLanguage.sDecimal;
  17183. // This check is in place for if a custom decimal character is in place
  17184. if (decimal !== '' && this.s.type.indexOf(decimal) === this.s.type.length - decimal.length) {
  17185. if (this.s.type.includes('num-fmt')) {
  17186. this.s.type = this.s.type.replace(decimal, '');
  17187. }
  17188. else if (this.s.type.includes('num')) {
  17189. this.s.type = this.s.type.replace(decimal, '');
  17190. }
  17191. }
  17192. // Select which conditions are going to be used based on the column type
  17193. var conditionObj = this.c.conditions[this.s.type] !== undefined ?
  17194. this.c.conditions[this.s.type] :
  17195. this.s.type.includes('moment') ?
  17196. this.c.conditions.moment :
  17197. this.s.type.includes('luxon') ?
  17198. this.c.conditions.luxon :
  17199. this.c.conditions.string;
  17200. // If it is a moment format then extract the date format
  17201. if (this.s.type.includes('moment')) {
  17202. this.s.dateFormat = this.s.type.replace(/moment-/g, '');
  17203. }
  17204. else if (this.s.type.includes('luxon')) {
  17205. this.s.dateFormat = this.s.type.replace(/luxon-/g, '');
  17206. }
  17207. // Add all of the conditions to the select element
  17208. for (var _i = 0, _a = Object.keys(conditionObj); _i < _a.length; _i++) {
  17209. condition = _a[_i];
  17210. if (conditionObj[condition] !== null) {
  17211. // Serverside processing does not supply the options for the select elements
  17212. // Instead input elements need to be used for these instead
  17213. if (this.s.dt.page.info().serverSide && conditionObj[condition].init === Criteria.initSelect) {
  17214. var col = colInits[column];
  17215. if (this.s.serverData && this.s.serverData[col.data]) {
  17216. conditionObj[condition].init = Criteria.initSelectSSP;
  17217. conditionObj[condition].inputValue = Criteria.inputValueSelect;
  17218. conditionObj[condition].isInputValid = Criteria.isInputValidSelect;
  17219. }
  17220. else {
  17221. conditionObj[condition].init = Criteria.initInput;
  17222. conditionObj[condition].inputValue = Criteria.inputValueInput;
  17223. conditionObj[condition].isInputValid = Criteria.isInputValidInput;
  17224. }
  17225. }
  17226. this.s.conditions[condition] = conditionObj[condition];
  17227. condName = conditionObj[condition].conditionName;
  17228. if (typeof condName === 'function') {
  17229. condName = condName(this.s.dt, this.c.i18n);
  17230. }
  17231. conditionOpts.push($$3('<option>', {
  17232. text: condName,
  17233. value: condition
  17234. })
  17235. .addClass(this.classes.option)
  17236. .addClass(this.classes.notItalic));
  17237. }
  17238. }
  17239. }
  17240. // Otherwise we can just load them in
  17241. else if (conditionsLength > 0) {
  17242. this.dom.condition.empty().removeAttr('disabled').addClass(this.classes.italic);
  17243. for (var _b = 0, _c = Object.keys(this.s.conditions); _b < _c.length; _b++) {
  17244. condition = _c[_b];
  17245. var name_1 = this.s.conditions[condition].conditionName;
  17246. if (typeof name_1 === 'function') {
  17247. name_1 = name_1(this.s.dt, this.c.i18n);
  17248. }
  17249. var newOpt = $$3('<option>', {
  17250. text: name_1,
  17251. value: condition
  17252. })
  17253. .addClass(this.classes.option)
  17254. .addClass(this.classes.notItalic);
  17255. if (this.s.condition !== undefined && this.s.condition === name_1) {
  17256. newOpt.prop('selected', true);
  17257. this.dom.condition.removeClass(this.classes.italic);
  17258. }
  17259. conditionOpts.push(newOpt);
  17260. }
  17261. }
  17262. else {
  17263. this.dom.condition
  17264. .attr('disabled', 'true')
  17265. .addClass(this.classes.italic);
  17266. return;
  17267. }
  17268. for (var _d = 0, conditionOpts_1 = conditionOpts; _d < conditionOpts_1.length; _d++) {
  17269. var opt = conditionOpts_1[_d];
  17270. this.dom.condition.append(opt);
  17271. }
  17272. // Selecting a default condition if one is set
  17273. if (colInits[column].searchBuilder && colInits[column].searchBuilder.defaultCondition) {
  17274. var defaultCondition = colInits[column].searchBuilder.defaultCondition;
  17275. // If it is a number just use it as an index
  17276. if (typeof defaultCondition === 'number') {
  17277. this.dom.condition.prop('selectedIndex', defaultCondition);
  17278. this.dom.condition.trigger('change');
  17279. }
  17280. // If it is a string then things get slightly more tricly
  17281. else if (typeof defaultCondition === 'string') {
  17282. // We need to check each condition option to see if any will match
  17283. for (var i = 0; i < conditionOpts.length; i++) {
  17284. // Need to check against the stored conditions so we can match the token "cond" to the option
  17285. for (var _e = 0, _f = Object.keys(this.s.conditions); _e < _f.length; _e++) {
  17286. var cond = _f[_e];
  17287. condName = this.s.conditions[cond].conditionName;
  17288. if (
  17289. // If the conditionName matches the text of the option
  17290. (typeof condName === 'string' ? condName : condName(this.s.dt, this.c.i18n)) ===
  17291. conditionOpts[i].text() &&
  17292. // and the tokens match
  17293. cond === defaultCondition) {
  17294. // Select that option
  17295. this.dom.condition
  17296. .prop('selectedIndex', this.dom.condition.children().toArray().indexOf(conditionOpts[i][0]))
  17297. .removeClass(this.classes.italic);
  17298. this.dom.condition.trigger('change');
  17299. i = conditionOpts.length;
  17300. break;
  17301. }
  17302. }
  17303. }
  17304. }
  17305. }
  17306. // If not default set then default to 0, the title
  17307. else {
  17308. this.dom.condition.prop('selectedIndex', 0);
  17309. }
  17310. };
  17311. /**
  17312. * Populates the data / column select element
  17313. */
  17314. Criteria.prototype._populateData = function () {
  17315. var columns = this.s.dt.settings()[0].aoColumns;
  17316. var includeColumns = this.s.dt.columns(this.c.columns).indexes().toArray();
  17317. this.dom.data.empty().append(this.dom.dataTitle);
  17318. for (var index = 0; index < columns.length; index++) {
  17319. // Need to check that the column can be filtered on before adding it
  17320. if (this.c.columns === true || includeColumns.includes(index)) {
  17321. var col = columns[index];
  17322. var opt = {
  17323. index: index,
  17324. origData: col.data,
  17325. text: (col.searchBuilderTitle || col.sTitle)
  17326. .replace(/(<([^>]+)>)/ig, '')
  17327. };
  17328. this.dom.data.append($$3('<option>', {
  17329. text: opt.text,
  17330. value: opt.index
  17331. })
  17332. .addClass(this.classes.option)
  17333. .addClass(this.classes.notItalic)
  17334. .prop('origData', col.data)
  17335. .prop('selected', this.s.dataIdx === opt.index ? true : false));
  17336. if (this.s.dataIdx === opt.index) {
  17337. this.dom.dataTitle.removeProp('selected');
  17338. }
  17339. }
  17340. }
  17341. };
  17342. /**
  17343. * Populates the Value select element
  17344. *
  17345. * @param loadedCriteria optional, used to reload criteria from predefined filters
  17346. */
  17347. Criteria.prototype._populateValue = function (loadedCriteria) {
  17348. var _this = this;
  17349. var prevFilled = this.s.filled;
  17350. var i;
  17351. this.s.filled = false;
  17352. // Remove any previous value elements
  17353. // Timeout is annoying but because of IOS
  17354. setTimeout(function () {
  17355. _this.dom.defaultValue.remove();
  17356. }, 50);
  17357. var _loop_1 = function (val) {
  17358. // Timeout is annoying but because of IOS
  17359. setTimeout(function () {
  17360. if (val !== undefined) {
  17361. val.remove();
  17362. }
  17363. }, 50);
  17364. };
  17365. for (var _i = 0, _a = this.dom.value; _i < _a.length; _i++) {
  17366. var val = _a[_i];
  17367. _loop_1(val);
  17368. }
  17369. var children = this.dom.inputCont.children();
  17370. if (children.length > 1) {
  17371. for (i = 0; i < children.length; i++) {
  17372. $$3(children[i]).remove();
  17373. }
  17374. }
  17375. // Find the column with the title matching the data for the criteria and take note of the index
  17376. if (loadedCriteria !== undefined) {
  17377. this.s.dt.columns().every(function (index) {
  17378. if (_this.s.dt.settings()[0].aoColumns[index].sTitle === loadedCriteria.data) {
  17379. _this.s.dataIdx = index;
  17380. }
  17381. });
  17382. }
  17383. // Initialise the value elements based on the condition
  17384. this.dom.value = [].concat(this.s.conditions[this.s.condition].init(this, Criteria.updateListener, loadedCriteria !== undefined ? loadedCriteria.value : undefined));
  17385. if (loadedCriteria !== undefined && loadedCriteria.value !== undefined) {
  17386. this.s.value = loadedCriteria.value;
  17387. }
  17388. this.dom.inputCont.empty();
  17389. // Insert value elements and trigger the inserted event
  17390. if (this.dom.value[0] !== undefined) {
  17391. $$3(this.dom.value[0])
  17392. .appendTo(this.dom.inputCont)
  17393. .trigger('dtsb-inserted');
  17394. }
  17395. for (i = 1; i < this.dom.value.length; i++) {
  17396. $$3(this.dom.value[i])
  17397. .insertAfter(this.dom.value[i - 1])
  17398. .trigger('dtsb-inserted');
  17399. }
  17400. // Check if the criteria can be used in a search
  17401. this.s.filled = this.s.conditions[this.s.condition].isInputValid(this.dom.value, this);
  17402. this.setListeners();
  17403. // If it can and this is different to before then trigger a draw
  17404. if (!this.s.preventRedraw && prevFilled !== this.s.filled) {
  17405. // If using SSP we want to restrict the amount of server calls that take place
  17406. // and this will already have taken place
  17407. if (!this.s.dt.page.info().serverSide) {
  17408. this.doSearch();
  17409. }
  17410. this.setListeners();
  17411. }
  17412. };
  17413. /**
  17414. * Provides throttling capabilities to SearchBuilder without having to use dt's _fnThrottle function
  17415. * This is because that function is not quite suitable for our needs as it runs initially rather than waiting
  17416. *
  17417. * @param args arguments supplied to the throttle function
  17418. * @returns Function that is to be run that implements the throttling
  17419. */
  17420. Criteria.prototype._throttle = function (fn, frequency) {
  17421. if (frequency === void 0) { frequency = 200; }
  17422. var last = null;
  17423. var timer = null;
  17424. var that = this;
  17425. if (frequency === null) {
  17426. frequency = 200;
  17427. }
  17428. return function () {
  17429. var args = [];
  17430. for (var _i = 0; _i < arguments.length; _i++) {
  17431. args[_i] = arguments[_i];
  17432. }
  17433. var now = +new Date();
  17434. if (last !== null && now < last + frequency) {
  17435. clearTimeout(timer);
  17436. }
  17437. else {
  17438. last = now;
  17439. }
  17440. timer = setTimeout(function () {
  17441. last = null;
  17442. fn.apply(that, args);
  17443. }, frequency);
  17444. };
  17445. };
  17446. Criteria.version = '1.1.0';
  17447. Criteria.classes = {
  17448. button: 'dtsb-button',
  17449. buttonContainer: 'dtsb-buttonContainer',
  17450. condition: 'dtsb-condition',
  17451. container: 'dtsb-criteria',
  17452. data: 'dtsb-data',
  17453. "delete": 'dtsb-delete',
  17454. dropDown: 'dtsb-dropDown',
  17455. greyscale: 'dtsb-greyscale',
  17456. input: 'dtsb-input',
  17457. inputCont: 'dtsb-inputCont',
  17458. italic: 'dtsb-italic',
  17459. joiner: 'dtsb-joiner',
  17460. left: 'dtsb-left',
  17461. notItalic: 'dtsb-notItalic',
  17462. option: 'dtsb-option',
  17463. right: 'dtsb-right',
  17464. select: 'dtsb-select',
  17465. value: 'dtsb-value',
  17466. vertical: 'dtsb-vertical'
  17467. };
  17468. /**
  17469. * Default initialisation function for select conditions
  17470. */
  17471. Criteria.initSelect = function (that, fn, preDefined, array) {
  17472. if (preDefined === void 0) { preDefined = null; }
  17473. if (array === void 0) { array = false; }
  17474. var column = that.dom.data.children('option:selected').val();
  17475. var indexArray = that.s.dt.rows().indexes().toArray();
  17476. var fastData = that.s.dt.settings()[0].fastData;
  17477. that.dom.valueTitle.prop('selected', true);
  17478. // Declare select element to be used with all of the default classes and listeners.
  17479. var el = $$3('<select/>')
  17480. .addClass(Criteria.classes.value)
  17481. .addClass(Criteria.classes.dropDown)
  17482. .addClass(Criteria.classes.italic)
  17483. .addClass(Criteria.classes.select)
  17484. .append(that.dom.valueTitle)
  17485. .on('change.dtsb', function () {
  17486. $$3(this).removeClass(Criteria.classes.italic);
  17487. fn(that, this);
  17488. });
  17489. if (that.c.greyscale) {
  17490. el.addClass(Criteria.classes.greyscale);
  17491. }
  17492. var added = [];
  17493. var options = [];
  17494. // Add all of the options from the table to the select element.
  17495. // Only add one option for each possible value
  17496. for (var _i = 0, indexArray_1 = indexArray; _i < indexArray_1.length; _i++) {
  17497. var index = indexArray_1[_i];
  17498. var filter = fastData(index, column, typeof that.c.orthogonal === 'string' ?
  17499. that.c.orthogonal :
  17500. that.c.orthogonal.search);
  17501. var value = {
  17502. filter: typeof filter === 'string' ?
  17503. filter.replace(/[\r\n\u2028]/g, ' ') : // Need to replace certain characters to match search values
  17504. filter,
  17505. index: index,
  17506. text: fastData(index, column, typeof that.c.orthogonal === 'string' ?
  17507. that.c.orthogonal :
  17508. that.c.orthogonal.display)
  17509. };
  17510. // If we are dealing with an array type, either make sure we are working with arrays, or sort them
  17511. if (that.s.type === 'array') {
  17512. value.filter = !Array.isArray(value.filter) ? [value.filter] : value.filter;
  17513. value.text = !Array.isArray(value.text) ? [value.text] : value.text;
  17514. }
  17515. // Function to add an option to the select element
  17516. var addOption = function (filt, text) {
  17517. if (that.s.type.includes('html') && filt !== null && typeof filt === 'string') {
  17518. filt.replace(/(<([^>]+)>)/ig, '');
  17519. }
  17520. // Add text and value, stripping out any html if that is the column type
  17521. var opt = $$3('<option>', {
  17522. type: Array.isArray(filt) ? 'Array' : 'String',
  17523. value: filt
  17524. })
  17525. .data('sbv', filt)
  17526. .addClass(that.classes.option)
  17527. .addClass(that.classes.notItalic)
  17528. // Have to add the text this way so that special html characters are not escaped - &amp; etc.
  17529. .html(typeof text === 'string' ?
  17530. text.replace(/(<([^>]+)>)/ig, '') :
  17531. text);
  17532. var val = opt.val();
  17533. // Check that this value has not already been added
  17534. if (added.indexOf(val) === -1) {
  17535. added.push(val);
  17536. options.push(opt);
  17537. if (preDefined !== null && Array.isArray(preDefined[0])) {
  17538. preDefined[0] = preDefined[0].sort().join(',');
  17539. }
  17540. // If this value was previously selected as indicated by preDefined, then select it again
  17541. if (preDefined !== null && opt.val() === preDefined[0]) {
  17542. opt.prop('selected', true);
  17543. el.removeClass(Criteria.classes.italic);
  17544. that.dom.valueTitle.removeProp('selected');
  17545. }
  17546. }
  17547. };
  17548. // If this is to add the individual values within the array we need to loop over the array
  17549. if (array) {
  17550. for (var i = 0; i < value.filter.length; i++) {
  17551. addOption(value.filter[i], value.text[i]);
  17552. }
  17553. }
  17554. // Otherwise the value that is in the cell is to be added
  17555. else {
  17556. addOption(value.filter, Array.isArray(value.text) ? value.text.join(', ') : value.text);
  17557. }
  17558. }
  17559. options.sort(function (a, b) {
  17560. if (that.s.type === 'array' ||
  17561. that.s.type === 'string' ||
  17562. that.s.type === 'html') {
  17563. if (a.val() < b.val()) {
  17564. return -1;
  17565. }
  17566. else if (a.val() > b.val()) {
  17567. return 1;
  17568. }
  17569. else {
  17570. return 0;
  17571. }
  17572. }
  17573. else if (that.s.type === 'num' ||
  17574. that.s.type === 'html-num') {
  17575. if (+a.val().replace(/(<([^>]+)>)/ig, '') < +b.val().replace(/(<([^>]+)>)/ig, '')) {
  17576. return -1;
  17577. }
  17578. else if (+a.val().replace(/(<([^>]+)>)/ig, '') > +b.val().replace(/(<([^>]+)>)/ig, '')) {
  17579. return 1;
  17580. }
  17581. else {
  17582. return 0;
  17583. }
  17584. }
  17585. else if (that.s.type === 'num-fmt' || that.s.type === 'html-num-fmt') {
  17586. if (+a.val().replace(/[^0-9.]/g, '') < +b.val().replace(/[^0-9.]/g, '')) {
  17587. return -1;
  17588. }
  17589. else if (+a.val().replace(/[^0-9.]/g, '') > +b.val().replace(/[^0-9.]/g, '')) {
  17590. return 1;
  17591. }
  17592. else {
  17593. return 0;
  17594. }
  17595. }
  17596. });
  17597. for (var _a = 0, options_1 = options; _a < options_1.length; _a++) {
  17598. var opt = options_1[_a];
  17599. el.append(opt);
  17600. }
  17601. return el;
  17602. };
  17603. /**
  17604. * Default initialisation function for select conditions
  17605. */
  17606. Criteria.initSelectSSP = function (that, fn, preDefined) {
  17607. if (preDefined === void 0) { preDefined = null; }
  17608. that.dom.valueTitle.prop('selected', true);
  17609. // Declare select element to be used with all of the default classes and listeners.
  17610. var el = $$3('<select/>')
  17611. .addClass(Criteria.classes.value)
  17612. .addClass(Criteria.classes.dropDown)
  17613. .addClass(Criteria.classes.italic)
  17614. .addClass(Criteria.classes.select)
  17615. .append(that.dom.valueTitle)
  17616. .on('change.dtsb', function () {
  17617. $$3(this).removeClass(Criteria.classes.italic);
  17618. fn(that, this);
  17619. });
  17620. if (that.c.greyscale) {
  17621. el.addClass(Criteria.classes.greyscale);
  17622. }
  17623. var options = [];
  17624. for (var _i = 0, _a = that.s.serverData[that.s.origData]; _i < _a.length; _i++) {
  17625. var option = _a[_i];
  17626. var value = option.value;
  17627. var label = option.label;
  17628. // Function to add an option to the select element
  17629. var addOption = function (filt, text) {
  17630. if (that.s.type.includes('html') && filt !== null && typeof filt === 'string') {
  17631. filt.replace(/(<([^>]+)>)/ig, '');
  17632. }
  17633. // Add text and value, stripping out any html if that is the column type
  17634. var opt = $$3('<option>', {
  17635. type: Array.isArray(filt) ? 'Array' : 'String',
  17636. value: filt
  17637. })
  17638. .data('sbv', filt)
  17639. .addClass(that.classes.option)
  17640. .addClass(that.classes.notItalic)
  17641. // Have to add the text this way so that special html characters are not escaped - &amp; etc.
  17642. .html(typeof text === 'string' ?
  17643. text.replace(/(<([^>]+)>)/ig, '') :
  17644. text);
  17645. options.push(opt);
  17646. // If this value was previously selected as indicated by preDefined, then select it again
  17647. if (preDefined !== null && opt.val() === preDefined[0]) {
  17648. opt.prop('selected', true);
  17649. el.removeClass(Criteria.classes.italic);
  17650. that.dom.valueTitle.removeProp('selected');
  17651. }
  17652. };
  17653. addOption(value, label);
  17654. }
  17655. for (var _b = 0, options_2 = options; _b < options_2.length; _b++) {
  17656. var opt = options_2[_b];
  17657. el.append(opt);
  17658. }
  17659. return el;
  17660. };
  17661. /**
  17662. * Default initialisation function for select array conditions
  17663. *
  17664. * This exists because there needs to be different select functionality for contains/without and equals/not
  17665. */
  17666. Criteria.initSelectArray = function (that, fn, preDefined) {
  17667. if (preDefined === void 0) { preDefined = null; }
  17668. return Criteria.initSelect(that, fn, preDefined, true);
  17669. };
  17670. /**
  17671. * Default initialisation function for input conditions
  17672. */
  17673. Criteria.initInput = function (that, fn, preDefined) {
  17674. if (preDefined === void 0) { preDefined = null; }
  17675. // Declare the input element
  17676. var searchDelay = that.s.dt.settings()[0].searchDelay;
  17677. var el = $$3('<input/>')
  17678. .addClass(Criteria.classes.value)
  17679. .addClass(Criteria.classes.input)
  17680. .on('input.dtsb keypress.dtsb', that._throttle(function (e) {
  17681. var code = e.keyCode || e.which;
  17682. return fn(that, this, code);
  17683. }, searchDelay === null ? 100 : searchDelay));
  17684. if (that.c.greyscale) {
  17685. el.addClass(Criteria.classes.greyscale);
  17686. }
  17687. // If there is a preDefined value then add it
  17688. if (preDefined !== null) {
  17689. el.val(preDefined[0]);
  17690. }
  17691. // This is add responsive functionality to the logic button without redrawing everything else
  17692. that.s.dt.one('draw.dtsb', function () {
  17693. that.s.topGroup.trigger('dtsb-redrawLogic');
  17694. });
  17695. return el;
  17696. };
  17697. /**
  17698. * Default initialisation function for conditions requiring 2 inputs
  17699. */
  17700. Criteria.init2Input = function (that, fn, preDefined) {
  17701. if (preDefined === void 0) { preDefined = null; }
  17702. // Declare all of the necessary jQuery elements
  17703. var searchDelay = that.s.dt.settings()[0].searchDelay;
  17704. var els = [
  17705. $$3('<input/>')
  17706. .addClass(Criteria.classes.value)
  17707. .addClass(Criteria.classes.input)
  17708. .on('input.dtsb keypress.dtsb', that._throttle(function (e) {
  17709. var code = e.keyCode || e.which;
  17710. return fn(that, this, code);
  17711. }, searchDelay === null ? 100 : searchDelay)),
  17712. $$3('<span>')
  17713. .addClass(that.classes.joiner)
  17714. .html(that.s.dt.i18n('searchBuilder.valueJoiner', that.c.i18n.valueJoiner)),
  17715. $$3('<input/>')
  17716. .addClass(Criteria.classes.value)
  17717. .addClass(Criteria.classes.input)
  17718. .on('input.dtsb keypress.dtsb', that._throttle(function (e) {
  17719. var code = e.keyCode || e.which;
  17720. return fn(that, this, code);
  17721. }, searchDelay === null ? 100 : searchDelay))
  17722. ];
  17723. if (that.c.greyscale) {
  17724. els[0].addClass(Criteria.classes.greyscale);
  17725. els[2].addClass(Criteria.classes.greyscale);
  17726. }
  17727. // If there is a preDefined value then add it
  17728. if (preDefined !== null) {
  17729. els[0].val(preDefined[0]);
  17730. els[2].val(preDefined[1]);
  17731. }
  17732. // This is add responsive functionality to the logic button without redrawing everything else
  17733. that.s.dt.one('draw.dtsb', function () {
  17734. that.s.topGroup.trigger('dtsb-redrawLogic');
  17735. });
  17736. return els;
  17737. };
  17738. /**
  17739. * Default initialisation function for date conditions
  17740. */
  17741. Criteria.initDate = function (that, fn, preDefined) {
  17742. if (preDefined === void 0) { preDefined = null; }
  17743. var searchDelay = that.s.dt.settings()[0].searchDelay;
  17744. var i18n = that.s.dt.i18n('datetime', {});
  17745. // Declare date element using DataTables dateTime plugin
  17746. var el = $$3('<input/>')
  17747. .addClass(Criteria.classes.value)
  17748. .addClass(Criteria.classes.input)
  17749. .dtDateTime({
  17750. attachTo: 'input',
  17751. format: that.s.dateFormat ? that.s.dateFormat : undefined,
  17752. i18n: i18n
  17753. })
  17754. .on('change.dtsb', that._throttle(function () {
  17755. return fn(that, this);
  17756. }, searchDelay === null ? 100 : searchDelay))
  17757. .on('input.dtsb keypress.dtsb', function (e) {
  17758. that._throttle(function () {
  17759. var code = e.keyCode || e.which;
  17760. return fn(that, this, code);
  17761. }, searchDelay === null ? 100 : searchDelay);
  17762. });
  17763. if (that.c.greyscale) {
  17764. el.addClass(Criteria.classes.greyscale);
  17765. }
  17766. // If there is a preDefined value then add it
  17767. if (preDefined !== null) {
  17768. el.val(preDefined[0]);
  17769. }
  17770. // This is add responsive functionality to the logic button without redrawing everything else
  17771. that.s.dt.one('draw.dtsb', function () {
  17772. that.s.topGroup.trigger('dtsb-redrawLogic');
  17773. });
  17774. return el;
  17775. };
  17776. Criteria.initNoValue = function (that) {
  17777. // This is add responsive functionality to the logic button without redrawing everything else
  17778. that.s.dt.one('draw.dtsb', function () {
  17779. that.s.topGroup.trigger('dtsb-redrawLogic');
  17780. });
  17781. return [];
  17782. };
  17783. Criteria.init2Date = function (that, fn, preDefined) {
  17784. var _this = this;
  17785. if (preDefined === void 0) { preDefined = null; }
  17786. var searchDelay = that.s.dt.settings()[0].searchDelay;
  17787. var i18n = that.s.dt.i18n('datetime', {});
  17788. // Declare all of the date elements that are required using DataTables dateTime plugin
  17789. var els = [
  17790. $$3('<input/>')
  17791. .addClass(Criteria.classes.value)
  17792. .addClass(Criteria.classes.input)
  17793. .dtDateTime({
  17794. attachTo: 'input',
  17795. format: that.s.dateFormat ? that.s.dateFormat : undefined,
  17796. i18n: i18n
  17797. })
  17798. .on('change.dtsb', searchDelay !== null ?
  17799. DataTable.util.throttle(function () {
  17800. return fn(that, this);
  17801. }, searchDelay) :
  17802. function () {
  17803. fn(that, _this);
  17804. })
  17805. .on('input.dtsb keypress.dtsb', function (e) {
  17806. DataTable.util.throttle(function () {
  17807. var code = e.keyCode || e.which;
  17808. return fn(that, this, code);
  17809. }, searchDelay === null ? 0 : searchDelay);
  17810. }),
  17811. $$3('<span>')
  17812. .addClass(that.classes.joiner)
  17813. .html(that.s.dt.i18n('searchBuilder.valueJoiner', that.c.i18n.valueJoiner)),
  17814. $$3('<input/>')
  17815. .addClass(Criteria.classes.value)
  17816. .addClass(Criteria.classes.input)
  17817. .dtDateTime({
  17818. attachTo: 'input',
  17819. format: that.s.dateFormat ? that.s.dateFormat : undefined,
  17820. i18n: i18n
  17821. })
  17822. .on('change.dtsb', searchDelay !== null ?
  17823. DataTable.util.throttle(function () {
  17824. return fn(that, this);
  17825. }, searchDelay) :
  17826. function () {
  17827. fn(that, _this);
  17828. })
  17829. .on('input.dtsb keypress.dtsb', !that.c.enterSearch &&
  17830. !(that.s.dt.settings()[0].oInit.search !== undefined &&
  17831. that.s.dt.settings()[0].oInit.search["return"]) &&
  17832. searchDelay !== null ?
  17833. DataTable.util.throttle(function () {
  17834. return fn(that, this);
  17835. }, searchDelay) :
  17836. function (e) {
  17837. var code = e.keyCode || e.which;
  17838. fn(that, _this, code);
  17839. })
  17840. ];
  17841. if (that.c.greyscale) {
  17842. els[0].addClass(Criteria.classes.greyscale);
  17843. els[2].addClass(Criteria.classes.greyscale);
  17844. }
  17845. // If there are and preDefined values then add them
  17846. if (preDefined !== null && preDefined.length > 0) {
  17847. els[0].val(preDefined[0]);
  17848. els[2].val(preDefined[1]);
  17849. }
  17850. // This is add responsive functionality to the logic button without redrawing everything else
  17851. that.s.dt.one('draw.dtsb', function () {
  17852. that.s.topGroup.trigger('dtsb-redrawLogic');
  17853. });
  17854. return els;
  17855. };
  17856. /**
  17857. * Default function for select elements to validate condition
  17858. */
  17859. Criteria.isInputValidSelect = function (el) {
  17860. var allFilled = true;
  17861. // Check each element to make sure that the selections are valid
  17862. for (var _i = 0, el_1 = el; _i < el_1.length; _i++) {
  17863. var element = el_1[_i];
  17864. if (element.children('option:selected').length ===
  17865. element.children('option').length -
  17866. element.children('option.' + Criteria.classes.notItalic).length &&
  17867. element.children('option:selected').length === 1 &&
  17868. element.children('option:selected')[0] === element.children('option')[0]) {
  17869. allFilled = false;
  17870. }
  17871. }
  17872. return allFilled;
  17873. };
  17874. /**
  17875. * Default function for input and date elements to validate condition
  17876. */
  17877. Criteria.isInputValidInput = function (el) {
  17878. var allFilled = true;
  17879. // Check each element to make sure that the inputs are valid
  17880. for (var _i = 0, el_2 = el; _i < el_2.length; _i++) {
  17881. var element = el_2[_i];
  17882. if (element.is('input') && element.val().length === 0) {
  17883. allFilled = false;
  17884. }
  17885. }
  17886. return allFilled;
  17887. };
  17888. /**
  17889. * Default function for getting select conditions
  17890. */
  17891. Criteria.inputValueSelect = function (el) {
  17892. var values = [];
  17893. // Go through the select elements and push each selected option to the return array
  17894. for (var _i = 0, el_3 = el; _i < el_3.length; _i++) {
  17895. var element = el_3[_i];
  17896. if (element.is('select')) {
  17897. values.push(Criteria._escapeHTML(element.children('option:selected').data('sbv')));
  17898. }
  17899. }
  17900. return values;
  17901. };
  17902. /**
  17903. * Default function for getting input conditions
  17904. */
  17905. Criteria.inputValueInput = function (el) {
  17906. var values = [];
  17907. // Go through the input elements and push each value to the return array
  17908. for (var _i = 0, el_4 = el; _i < el_4.length; _i++) {
  17909. var element = el_4[_i];
  17910. if (element.is('input')) {
  17911. values.push(Criteria._escapeHTML(element.val()));
  17912. }
  17913. }
  17914. return values;
  17915. };
  17916. /**
  17917. * Function that is run on each element as a call back when a search should be triggered
  17918. */
  17919. Criteria.updateListener = function (that, el, code) {
  17920. // When the value is changed the criteria is now complete so can be included in searches
  17921. // Get the condition from the map based on the key that has been selected for the condition
  17922. var condition = that.s.conditions[that.s.condition];
  17923. var i;
  17924. that.s.filled = condition.isInputValid(that.dom.value, that);
  17925. that.s.value = condition.inputValue(that.dom.value, that);
  17926. if (!that.s.filled) {
  17927. if (!that.c.enterSearch &&
  17928. !(that.s.dt.settings()[0].oInit.search !== undefined &&
  17929. that.s.dt.settings()[0].oInit.search["return"]) ||
  17930. code === 13) {
  17931. that.doSearch();
  17932. }
  17933. return;
  17934. }
  17935. if (!Array.isArray(that.s.value)) {
  17936. that.s.value = [that.s.value];
  17937. }
  17938. for (i = 0; i < that.s.value.length; i++) {
  17939. // If the value is an array we need to sort it
  17940. if (Array.isArray(that.s.value[i])) {
  17941. that.s.value[i].sort();
  17942. }
  17943. }
  17944. // Take note of the cursor position so that we can refocus there later
  17945. var idx = null;
  17946. var cursorPos = null;
  17947. for (i = 0; i < that.dom.value.length; i++) {
  17948. if (el === that.dom.value[i][0]) {
  17949. idx = i;
  17950. if (el.selectionStart !== undefined) {
  17951. cursorPos = el.selectionStart;
  17952. }
  17953. }
  17954. }
  17955. if (!that.c.enterSearch &&
  17956. !(that.s.dt.settings()[0].oInit.search !== undefined &&
  17957. that.s.dt.settings()[0].oInit.search["return"]) ||
  17958. code === 13) {
  17959. // Trigger a search
  17960. that.doSearch();
  17961. }
  17962. // Refocus the element and set the correct cursor position
  17963. if (idx !== null) {
  17964. that.dom.value[idx].removeClass(that.classes.italic);
  17965. that.dom.value[idx].focus();
  17966. if (cursorPos !== null) {
  17967. that.dom.value[idx][0].setSelectionRange(cursorPos, cursorPos);
  17968. }
  17969. }
  17970. };
  17971. // The order of the conditions will make eslint sad :(
  17972. // Has to be in this order so that they are displayed correctly in select elements
  17973. // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
  17974. Criteria.dateConditions = {
  17975. '=': {
  17976. conditionName: function (dt, i18n) {
  17977. return dt.i18n('searchBuilder.conditions.date.equals', i18n.conditions.date.equals);
  17978. },
  17979. init: Criteria.initDate,
  17980. inputValue: Criteria.inputValueInput,
  17981. isInputValid: Criteria.isInputValidInput,
  17982. search: function (value, comparison) {
  17983. value = value.replace(/(\/|-|,)/g, '-');
  17984. return value === comparison[0];
  17985. }
  17986. },
  17987. '!=': {
  17988. conditionName: function (dt, i18n) {
  17989. return dt.i18n('searchBuilder.conditions.date.not', i18n.conditions.date.not);
  17990. },
  17991. init: Criteria.initDate,
  17992. inputValue: Criteria.inputValueInput,
  17993. isInputValid: Criteria.isInputValidInput,
  17994. search: function (value, comparison) {
  17995. value = value.replace(/(\/|-|,)/g, '-');
  17996. return value !== comparison[0];
  17997. }
  17998. },
  17999. '<': {
  18000. conditionName: function (dt, i18n) {
  18001. return dt.i18n('searchBuilder.conditions.date.before', i18n.conditions.date.before);
  18002. },
  18003. init: Criteria.initDate,
  18004. inputValue: Criteria.inputValueInput,
  18005. isInputValid: Criteria.isInputValidInput,
  18006. search: function (value, comparison) {
  18007. value = value.replace(/(\/|-|,)/g, '-');
  18008. return value < comparison[0];
  18009. }
  18010. },
  18011. '>': {
  18012. conditionName: function (dt, i18n) {
  18013. return dt.i18n('searchBuilder.conditions.date.after', i18n.conditions.date.after);
  18014. },
  18015. init: Criteria.initDate,
  18016. inputValue: Criteria.inputValueInput,
  18017. isInputValid: Criteria.isInputValidInput,
  18018. search: function (value, comparison) {
  18019. value = value.replace(/(\/|-|,)/g, '-');
  18020. return value > comparison[0];
  18021. }
  18022. },
  18023. 'between': {
  18024. conditionName: function (dt, i18n) {
  18025. return dt.i18n('searchBuilder.conditions.date.between', i18n.conditions.date.between);
  18026. },
  18027. init: Criteria.init2Date,
  18028. inputValue: Criteria.inputValueInput,
  18029. isInputValid: Criteria.isInputValidInput,
  18030. search: function (value, comparison) {
  18031. value = value.replace(/(\/|-|,)/g, '-');
  18032. if (comparison[0] < comparison[1]) {
  18033. return comparison[0] <= value && value <= comparison[1];
  18034. }
  18035. else {
  18036. return comparison[1] <= value && value <= comparison[0];
  18037. }
  18038. }
  18039. },
  18040. '!between': {
  18041. conditionName: function (dt, i18n) {
  18042. return dt.i18n('searchBuilder.conditions.date.notBetween', i18n.conditions.date.notBetween);
  18043. },
  18044. init: Criteria.init2Date,
  18045. inputValue: Criteria.inputValueInput,
  18046. isInputValid: Criteria.isInputValidInput,
  18047. search: function (value, comparison) {
  18048. value = value.replace(/(\/|-|,)/g, '-');
  18049. if (comparison[0] < comparison[1]) {
  18050. return !(comparison[0] <= value && value <= comparison[1]);
  18051. }
  18052. else {
  18053. return !(comparison[1] <= value && value <= comparison[0]);
  18054. }
  18055. }
  18056. },
  18057. 'null': {
  18058. conditionName: function (dt, i18n) {
  18059. return dt.i18n('searchBuilder.conditions.date.empty', i18n.conditions.date.empty);
  18060. },
  18061. init: Criteria.initNoValue,
  18062. inputValue: function () {
  18063. return;
  18064. },
  18065. isInputValid: function () {
  18066. return true;
  18067. },
  18068. search: function (value) {
  18069. return value === null || value === undefined || value.length === 0;
  18070. }
  18071. },
  18072. '!null': {
  18073. conditionName: function (dt, i18n) {
  18074. return dt.i18n('searchBuilder.conditions.date.notEmpty', i18n.conditions.date.notEmpty);
  18075. },
  18076. init: Criteria.initNoValue,
  18077. inputValue: function () {
  18078. return;
  18079. },
  18080. isInputValid: function () {
  18081. return true;
  18082. },
  18083. search: function (value) {
  18084. return !(value === null || value === undefined || value.length === 0);
  18085. }
  18086. }
  18087. };
  18088. // The order of the conditions will make eslint sad :(
  18089. // Has to be in this order so that they are displayed correctly in select elements
  18090. // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
  18091. Criteria.momentDateConditions = {
  18092. '=': {
  18093. conditionName: function (dt, i18n) {
  18094. return dt.i18n('searchBuilder.conditions.date.equals', i18n.conditions.date.equals);
  18095. },
  18096. init: Criteria.initDate,
  18097. inputValue: Criteria.inputValueInput,
  18098. isInputValid: Criteria.isInputValidInput,
  18099. search: function (value, comparison, that) {
  18100. return moment()(value, that.s.dateFormat).valueOf() ===
  18101. moment()(comparison[0], that.s.dateFormat).valueOf();
  18102. }
  18103. },
  18104. '!=': {
  18105. conditionName: function (dt, i18n) {
  18106. return dt.i18n('searchBuilder.conditions.date.not', i18n.conditions.date.not);
  18107. },
  18108. init: Criteria.initDate,
  18109. inputValue: Criteria.inputValueInput,
  18110. isInputValid: Criteria.isInputValidInput,
  18111. search: function (value, comparison, that) {
  18112. return moment()(value, that.s.dateFormat).valueOf() !==
  18113. moment()(comparison[0], that.s.dateFormat).valueOf();
  18114. }
  18115. },
  18116. '<': {
  18117. conditionName: function (dt, i18n) {
  18118. return dt.i18n('searchBuilder.conditions.date.before', i18n.conditions.date.before);
  18119. },
  18120. init: Criteria.initDate,
  18121. inputValue: Criteria.inputValueInput,
  18122. isInputValid: Criteria.isInputValidInput,
  18123. search: function (value, comparison, that) {
  18124. return moment()(value, that.s.dateFormat).valueOf() < moment()(comparison[0], that.s.dateFormat).valueOf();
  18125. }
  18126. },
  18127. '>': {
  18128. conditionName: function (dt, i18n) {
  18129. return dt.i18n('searchBuilder.conditions.date.after', i18n.conditions.date.after);
  18130. },
  18131. init: Criteria.initDate,
  18132. inputValue: Criteria.inputValueInput,
  18133. isInputValid: Criteria.isInputValidInput,
  18134. search: function (value, comparison, that) {
  18135. return moment()(value, that.s.dateFormat).valueOf() > moment()(comparison[0], that.s.dateFormat).valueOf();
  18136. }
  18137. },
  18138. 'between': {
  18139. conditionName: function (dt, i18n) {
  18140. return dt.i18n('searchBuilder.conditions.date.between', i18n.conditions.date.between);
  18141. },
  18142. init: Criteria.init2Date,
  18143. inputValue: Criteria.inputValueInput,
  18144. isInputValid: Criteria.isInputValidInput,
  18145. search: function (value, comparison, that) {
  18146. var val = moment()(value, that.s.dateFormat).valueOf();
  18147. var comp0 = moment()(comparison[0], that.s.dateFormat).valueOf();
  18148. var comp1 = moment()(comparison[1], that.s.dateFormat).valueOf();
  18149. if (comp0 < comp1) {
  18150. return comp0 <= val && val <= comp1;
  18151. }
  18152. else {
  18153. return comp1 <= val && val <= comp0;
  18154. }
  18155. }
  18156. },
  18157. '!between': {
  18158. conditionName: function (dt, i18n) {
  18159. return dt.i18n('searchBuilder.conditions.date.notBetween', i18n.conditions.date.notBetween);
  18160. },
  18161. init: Criteria.init2Date,
  18162. inputValue: Criteria.inputValueInput,
  18163. isInputValid: Criteria.isInputValidInput,
  18164. search: function (value, comparison, that) {
  18165. var val = moment()(value, that.s.dateFormat).valueOf();
  18166. var comp0 = moment()(comparison[0], that.s.dateFormat).valueOf();
  18167. var comp1 = moment()(comparison[1], that.s.dateFormat).valueOf();
  18168. if (comp0 < comp1) {
  18169. return !(+comp0 <= +val && +val <= +comp1);
  18170. }
  18171. else {
  18172. return !(+comp1 <= +val && +val <= +comp0);
  18173. }
  18174. }
  18175. },
  18176. 'null': {
  18177. conditionName: function (dt, i18n) {
  18178. return dt.i18n('searchBuilder.conditions.date.empty', i18n.conditions.date.empty);
  18179. },
  18180. init: Criteria.initNoValue,
  18181. inputValue: function () {
  18182. return;
  18183. },
  18184. isInputValid: function () {
  18185. return true;
  18186. },
  18187. search: function (value) {
  18188. return value === null || value === undefined || value.length === 0;
  18189. }
  18190. },
  18191. '!null': {
  18192. conditionName: function (dt, i18n) {
  18193. return dt.i18n('searchBuilder.conditions.date.notEmpty', i18n.conditions.date.notEmpty);
  18194. },
  18195. init: Criteria.initNoValue,
  18196. inputValue: function () {
  18197. return;
  18198. },
  18199. isInputValid: function () {
  18200. return true;
  18201. },
  18202. search: function (value) {
  18203. return !(value === null || value === undefined || value.length === 0);
  18204. }
  18205. }
  18206. };
  18207. // The order of the conditions will make eslint sad :(
  18208. // Has to be in this order so that they are displayed correctly in select elements
  18209. // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
  18210. Criteria.luxonDateConditions = {
  18211. '=': {
  18212. conditionName: function (dt, i18n) {
  18213. return dt.i18n('searchBuilder.conditions.date.equals', i18n.conditions.date.equals);
  18214. },
  18215. init: Criteria.initDate,
  18216. inputValue: Criteria.inputValueInput,
  18217. isInputValid: Criteria.isInputValidInput,
  18218. search: function (value, comparison, that) {
  18219. return luxon().DateTime.fromFormat(value, that.s.dateFormat).ts
  18220. === luxon().DateTime.fromFormat(comparison[0], that.s.dateFormat).ts;
  18221. }
  18222. },
  18223. '!=': {
  18224. conditionName: function (dt, i18n) {
  18225. return dt.i18n('searchBuilder.conditions.date.not', i18n.conditions.date.not);
  18226. },
  18227. init: Criteria.initDate,
  18228. inputValue: Criteria.inputValueInput,
  18229. isInputValid: Criteria.isInputValidInput,
  18230. search: function (value, comparison, that) {
  18231. return luxon().DateTime.fromFormat(value, that.s.dateFormat).ts
  18232. !== luxon().DateTime.fromFormat(comparison[0], that.s.dateFormat).ts;
  18233. }
  18234. },
  18235. '<': {
  18236. conditionName: function (dt, i18n) {
  18237. return dt.i18n('searchBuilder.conditions.date.before', i18n.conditions.date.before);
  18238. },
  18239. init: Criteria.initDate,
  18240. inputValue: Criteria.inputValueInput,
  18241. isInputValid: Criteria.isInputValidInput,
  18242. search: function (value, comparison, that) {
  18243. return luxon().DateTime.fromFormat(value, that.s.dateFormat).ts
  18244. < luxon().DateTime.fromFormat(comparison[0], that.s.dateFormat).ts;
  18245. }
  18246. },
  18247. '>': {
  18248. conditionName: function (dt, i18n) {
  18249. return dt.i18n('searchBuilder.conditions.date.after', i18n.conditions.date.after);
  18250. },
  18251. init: Criteria.initDate,
  18252. inputValue: Criteria.inputValueInput,
  18253. isInputValid: Criteria.isInputValidInput,
  18254. search: function (value, comparison, that) {
  18255. return luxon().DateTime.fromFormat(value, that.s.dateFormat).ts
  18256. > luxon().DateTime.fromFormat(comparison[0], that.s.dateFormat).ts;
  18257. }
  18258. },
  18259. 'between': {
  18260. conditionName: function (dt, i18n) {
  18261. return dt.i18n('searchBuilder.conditions.date.between', i18n.conditions.date.between);
  18262. },
  18263. init: Criteria.init2Date,
  18264. inputValue: Criteria.inputValueInput,
  18265. isInputValid: Criteria.isInputValidInput,
  18266. search: function (value, comparison, that) {
  18267. var val = luxon().DateTime.fromFormat(value, that.s.dateFormat).ts;
  18268. var comp0 = luxon().DateTime.fromFormat(comparison[0], that.s.dateFormat).ts;
  18269. var comp1 = luxon().DateTime.fromFormat(comparison[1], that.s.dateFormat).ts;
  18270. if (comp0 < comp1) {
  18271. return comp0 <= val && val <= comp1;
  18272. }
  18273. else {
  18274. return comp1 <= val && val <= comp0;
  18275. }
  18276. }
  18277. },
  18278. '!between': {
  18279. conditionName: function (dt, i18n) {
  18280. return dt.i18n('searchBuilder.conditions.date.notBetween', i18n.conditions.date.notBetween);
  18281. },
  18282. init: Criteria.init2Date,
  18283. inputValue: Criteria.inputValueInput,
  18284. isInputValid: Criteria.isInputValidInput,
  18285. search: function (value, comparison, that) {
  18286. var val = luxon().DateTime.fromFormat(value, that.s.dateFormat).ts;
  18287. var comp0 = luxon().DateTime.fromFormat(comparison[0], that.s.dateFormat).ts;
  18288. var comp1 = luxon().DateTime.fromFormat(comparison[1], that.s.dateFormat).ts;
  18289. if (comp0 < comp1) {
  18290. return !(+comp0 <= +val && +val <= +comp1);
  18291. }
  18292. else {
  18293. return !(+comp1 <= +val && +val <= +comp0);
  18294. }
  18295. }
  18296. },
  18297. 'null': {
  18298. conditionName: function (dt, i18n) {
  18299. return dt.i18n('searchBuilder.conditions.date.empty', i18n.conditions.date.empty);
  18300. },
  18301. init: Criteria.initNoValue,
  18302. inputValue: function () {
  18303. return;
  18304. },
  18305. isInputValid: function () {
  18306. return true;
  18307. },
  18308. search: function (value) {
  18309. return value === null || value === undefined || value.length === 0;
  18310. }
  18311. },
  18312. '!null': {
  18313. conditionName: function (dt, i18n) {
  18314. return dt.i18n('searchBuilder.conditions.date.notEmpty', i18n.conditions.date.notEmpty);
  18315. },
  18316. init: Criteria.initNoValue,
  18317. inputValue: function () {
  18318. return;
  18319. },
  18320. isInputValid: function () {
  18321. return true;
  18322. },
  18323. search: function (value) {
  18324. return !(value === null || value === undefined || value.length === 0);
  18325. }
  18326. }
  18327. };
  18328. // The order of the conditions will make eslint sad :(
  18329. // Has to be in this order so that they are displayed correctly in select elements
  18330. // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
  18331. Criteria.numConditions = {
  18332. '=': {
  18333. conditionName: function (dt, i18n) {
  18334. return dt.i18n('searchBuilder.conditions.number.equals', i18n.conditions.number.equals);
  18335. },
  18336. init: Criteria.initSelect,
  18337. inputValue: Criteria.inputValueSelect,
  18338. isInputValid: Criteria.isInputValidSelect,
  18339. search: function (value, comparison) {
  18340. return +value === +comparison[0];
  18341. }
  18342. },
  18343. '!=': {
  18344. conditionName: function (dt, i18n) {
  18345. return dt.i18n('searchBuilder.conditions.number.not', i18n.conditions.number.not);
  18346. },
  18347. init: Criteria.initSelect,
  18348. inputValue: Criteria.inputValueSelect,
  18349. isInputValid: Criteria.isInputValidSelect,
  18350. search: function (value, comparison) {
  18351. return +value !== +comparison[0];
  18352. }
  18353. },
  18354. '<': {
  18355. conditionName: function (dt, i18n) {
  18356. return dt.i18n('searchBuilder.conditions.number.lt', i18n.conditions.number.lt);
  18357. },
  18358. init: Criteria.initInput,
  18359. inputValue: Criteria.inputValueInput,
  18360. isInputValid: Criteria.isInputValidInput,
  18361. search: function (value, comparison) {
  18362. return +value < +comparison[0];
  18363. }
  18364. },
  18365. '<=': {
  18366. conditionName: function (dt, i18n) {
  18367. return dt.i18n('searchBuilder.conditions.number.lte', i18n.conditions.number.lte);
  18368. },
  18369. init: Criteria.initInput,
  18370. inputValue: Criteria.inputValueInput,
  18371. isInputValid: Criteria.isInputValidInput,
  18372. search: function (value, comparison) {
  18373. return +value <= +comparison[0];
  18374. }
  18375. },
  18376. '>=': {
  18377. conditionName: function (dt, i18n) {
  18378. return dt.i18n('searchBuilder.conditions.number.gte', i18n.conditions.number.gte);
  18379. },
  18380. init: Criteria.initInput,
  18381. inputValue: Criteria.inputValueInput,
  18382. isInputValid: Criteria.isInputValidInput,
  18383. search: function (value, comparison) {
  18384. return +value >= +comparison[0];
  18385. }
  18386. },
  18387. '>': {
  18388. conditionName: function (dt, i18n) {
  18389. return dt.i18n('searchBuilder.conditions.number.gt', i18n.conditions.number.gt);
  18390. },
  18391. init: Criteria.initInput,
  18392. inputValue: Criteria.inputValueInput,
  18393. isInputValid: Criteria.isInputValidInput,
  18394. search: function (value, comparison) {
  18395. return +value > +comparison[0];
  18396. }
  18397. },
  18398. 'between': {
  18399. conditionName: function (dt, i18n) {
  18400. return dt.i18n('searchBuilder.conditions.number.between', i18n.conditions.number.between);
  18401. },
  18402. init: Criteria.init2Input,
  18403. inputValue: Criteria.inputValueInput,
  18404. isInputValid: Criteria.isInputValidInput,
  18405. search: function (value, comparison) {
  18406. if (+comparison[0] < +comparison[1]) {
  18407. return +comparison[0] <= +value && +value <= +comparison[1];
  18408. }
  18409. else {
  18410. return +comparison[1] <= +value && +value <= +comparison[0];
  18411. }
  18412. }
  18413. },
  18414. '!between': {
  18415. conditionName: function (dt, i18n) {
  18416. return dt.i18n('searchBuilder.conditions.number.notBetween', i18n.conditions.number.notBetween);
  18417. },
  18418. init: Criteria.init2Input,
  18419. inputValue: Criteria.inputValueInput,
  18420. isInputValid: Criteria.isInputValidInput,
  18421. search: function (value, comparison) {
  18422. if (+comparison[0] < +comparison[1]) {
  18423. return !(+comparison[0] <= +value && +value <= +comparison[1]);
  18424. }
  18425. else {
  18426. return !(+comparison[1] <= +value && +value <= +comparison[0]);
  18427. }
  18428. }
  18429. },
  18430. 'null': {
  18431. conditionName: function (dt, i18n) {
  18432. return dt.i18n('searchBuilder.conditions.number.empty', i18n.conditions.number.empty);
  18433. },
  18434. init: Criteria.initNoValue,
  18435. inputValue: function () {
  18436. return;
  18437. },
  18438. isInputValid: function () {
  18439. return true;
  18440. },
  18441. search: function (value) {
  18442. return value === null || value === undefined || value.length === 0;
  18443. }
  18444. },
  18445. '!null': {
  18446. conditionName: function (dt, i18n) {
  18447. return dt.i18n('searchBuilder.conditions.number.notEmpty', i18n.conditions.number.notEmpty);
  18448. },
  18449. init: Criteria.initNoValue,
  18450. inputValue: function () {
  18451. return;
  18452. },
  18453. isInputValid: function () {
  18454. return true;
  18455. },
  18456. search: function (value) {
  18457. return !(value === null || value === undefined || value.length === 0);
  18458. }
  18459. }
  18460. };
  18461. // The order of the conditions will make eslint sad :(
  18462. // Has to be in this order so that they are displayed correctly in select elements
  18463. // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
  18464. Criteria.numFmtConditions = {
  18465. '=': {
  18466. conditionName: function (dt, i18n) {
  18467. return dt.i18n('searchBuilder.conditions.number.equals', i18n.conditions.number.equals);
  18468. },
  18469. init: Criteria.initSelect,
  18470. inputValue: Criteria.inputValueSelect,
  18471. isInputValid: Criteria.isInputValidSelect,
  18472. search: function (value, comparison, criteria) {
  18473. return criteria.parseNumber(value) === criteria.parseNumber(comparison[0]);
  18474. }
  18475. },
  18476. '!=': {
  18477. conditionName: function (dt, i18n) {
  18478. return dt.i18n('searchBuilder.conditions.number.not', i18n.conditions.number.not);
  18479. },
  18480. init: Criteria.initSelect,
  18481. inputValue: Criteria.inputValueSelect,
  18482. isInputValid: Criteria.isInputValidSelect,
  18483. search: function (value, comparison, criteria) {
  18484. return criteria.parseNumber(value) !== criteria.parseNumber(comparison[0]);
  18485. }
  18486. },
  18487. '<': {
  18488. conditionName: function (dt, i18n) {
  18489. return dt.i18n('searchBuilder.conditions.number.lt', i18n.conditions.number.lt);
  18490. },
  18491. init: Criteria.initInput,
  18492. inputValue: Criteria.inputValueInput,
  18493. isInputValid: Criteria.isInputValidInput,
  18494. search: function (value, comparison, criteria) {
  18495. return criteria.parseNumber(value) < criteria.parseNumber(comparison[0]);
  18496. }
  18497. },
  18498. '<=': {
  18499. conditionName: function (dt, i18n) {
  18500. return dt.i18n('searchBuilder.conditions.number.lte', i18n.conditions.number.lte);
  18501. },
  18502. init: Criteria.initInput,
  18503. inputValue: Criteria.inputValueInput,
  18504. isInputValid: Criteria.isInputValidInput,
  18505. search: function (value, comparison, criteria) {
  18506. return criteria.parseNumber(value) <= criteria.parseNumber(comparison[0]);
  18507. }
  18508. },
  18509. '>=': {
  18510. conditionName: function (dt, i18n) {
  18511. return dt.i18n('searchBuilder.conditions.number.gte', i18n.conditions.number.gte);
  18512. },
  18513. init: Criteria.initInput,
  18514. inputValue: Criteria.inputValueInput,
  18515. isInputValid: Criteria.isInputValidInput,
  18516. search: function (value, comparison, criteria) {
  18517. return criteria.parseNumber(value) >= criteria.parseNumber(comparison[0]);
  18518. }
  18519. },
  18520. '>': {
  18521. conditionName: function (dt, i18n) {
  18522. return dt.i18n('searchBuilder.conditions.number.gt', i18n.conditions.number.gt);
  18523. },
  18524. init: Criteria.initInput,
  18525. inputValue: Criteria.inputValueInput,
  18526. isInputValid: Criteria.isInputValidInput,
  18527. search: function (value, comparison, criteria) {
  18528. return criteria.parseNumber(value) > criteria.parseNumber(comparison[0]);
  18529. }
  18530. },
  18531. 'between': {
  18532. conditionName: function (dt, i18n) {
  18533. return dt.i18n('searchBuilder.conditions.number.between', i18n.conditions.number.between);
  18534. },
  18535. init: Criteria.init2Input,
  18536. inputValue: Criteria.inputValueInput,
  18537. isInputValid: Criteria.isInputValidInput,
  18538. search: function (value, comparison, criteria) {
  18539. var val = criteria.parseNumber(value);
  18540. var comp0 = criteria.parseNumber(comparison[0]);
  18541. var comp1 = criteria.parseNumber(comparison[1]);
  18542. if (+comp0 < +comp1) {
  18543. return +comp0 <= +val && +val <= +comp1;
  18544. }
  18545. else {
  18546. return +comp1 <= +val && +val <= +comp0;
  18547. }
  18548. }
  18549. },
  18550. '!between': {
  18551. conditionName: function (dt, i18n) {
  18552. return dt.i18n('searchBuilder.conditions.number.notBetween', i18n.conditions.number.notBetween);
  18553. },
  18554. init: Criteria.init2Input,
  18555. inputValue: Criteria.inputValueInput,
  18556. isInputValid: Criteria.isInputValidInput,
  18557. search: function (value, comparison, criteria) {
  18558. var val = criteria.parseNumber(value);
  18559. var comp0 = criteria.parseNumber(comparison[0]);
  18560. var comp1 = criteria.parseNumber(comparison[1]);
  18561. if (+comp0 < +comp1) {
  18562. return !(+comp0 <= +val && +val <= +comp1);
  18563. }
  18564. else {
  18565. return !(+comp1 <= +val && +val <= +comp0);
  18566. }
  18567. }
  18568. },
  18569. 'null': {
  18570. conditionName: function (dt, i18n) {
  18571. return dt.i18n('searchBuilder.conditions.number.empty', i18n.conditions.number.empty);
  18572. },
  18573. init: Criteria.initNoValue,
  18574. inputValue: function () {
  18575. return;
  18576. },
  18577. isInputValid: function () {
  18578. return true;
  18579. },
  18580. search: function (value) {
  18581. return value === null || value === undefined || value.length === 0;
  18582. }
  18583. },
  18584. '!null': {
  18585. conditionName: function (dt, i18n) {
  18586. return dt.i18n('searchBuilder.conditions.number.notEmpty', i18n.conditions.number.notEmpty);
  18587. },
  18588. init: Criteria.initNoValue,
  18589. inputValue: function () {
  18590. return;
  18591. },
  18592. isInputValid: function () {
  18593. return true;
  18594. },
  18595. search: function (value) {
  18596. return !(value === null || value === undefined || value.length === 0);
  18597. }
  18598. }
  18599. };
  18600. // The order of the conditions will make eslint sad :(
  18601. // Has to be in this order so that they are displayed correctly in select elements
  18602. // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
  18603. Criteria.stringConditions = {
  18604. '=': {
  18605. conditionName: function (dt, i18n) {
  18606. return dt.i18n('searchBuilder.conditions.string.equals', i18n.conditions.string.equals);
  18607. },
  18608. init: Criteria.initSelect,
  18609. inputValue: Criteria.inputValueSelect,
  18610. isInputValid: Criteria.isInputValidSelect,
  18611. search: function (value, comparison) {
  18612. return value === comparison[0];
  18613. }
  18614. },
  18615. '!=': {
  18616. conditionName: function (dt, i18n) {
  18617. return dt.i18n('searchBuilder.conditions.string.not', i18n.conditions.string.not);
  18618. },
  18619. init: Criteria.initSelect,
  18620. inputValue: Criteria.inputValueSelect,
  18621. isInputValid: Criteria.isInputValidInput,
  18622. search: function (value, comparison) {
  18623. return value !== comparison[0];
  18624. }
  18625. },
  18626. 'starts': {
  18627. conditionName: function (dt, i18n) {
  18628. return dt.i18n('searchBuilder.conditions.string.startsWith', i18n.conditions.string.startsWith);
  18629. },
  18630. init: Criteria.initInput,
  18631. inputValue: Criteria.inputValueInput,
  18632. isInputValid: Criteria.isInputValidInput,
  18633. search: function (value, comparison) {
  18634. return value.toLowerCase().indexOf(comparison[0].toLowerCase()) === 0;
  18635. }
  18636. },
  18637. '!starts': {
  18638. conditionName: function (dt, i18n) {
  18639. return dt.i18n('searchBuilder.conditions.string.notStartsWith', i18n.conditions.string.notStartsWith);
  18640. },
  18641. init: Criteria.initInput,
  18642. inputValue: Criteria.inputValueInput,
  18643. isInputValid: Criteria.isInputValidInput,
  18644. search: function (value, comparison) {
  18645. return value.toLowerCase().indexOf(comparison[0].toLowerCase()) !== 0;
  18646. }
  18647. },
  18648. 'contains': {
  18649. conditionName: function (dt, i18n) {
  18650. return dt.i18n('searchBuilder.conditions.string.contains', i18n.conditions.string.contains);
  18651. },
  18652. init: Criteria.initInput,
  18653. inputValue: Criteria.inputValueInput,
  18654. isInputValid: Criteria.isInputValidInput,
  18655. search: function (value, comparison) {
  18656. return value.toLowerCase().includes(comparison[0].toLowerCase());
  18657. }
  18658. },
  18659. '!contains': {
  18660. conditionName: function (dt, i18n) {
  18661. return dt.i18n('searchBuilder.conditions.string.notContains', i18n.conditions.string.notContains);
  18662. },
  18663. init: Criteria.initInput,
  18664. inputValue: Criteria.inputValueInput,
  18665. isInputValid: Criteria.isInputValidInput,
  18666. search: function (value, comparison) {
  18667. return !value.toLowerCase().includes(comparison[0].toLowerCase());
  18668. }
  18669. },
  18670. 'ends': {
  18671. conditionName: function (dt, i18n) {
  18672. return dt.i18n('searchBuilder.conditions.string.endsWith', i18n.conditions.string.endsWith);
  18673. },
  18674. init: Criteria.initInput,
  18675. inputValue: Criteria.inputValueInput,
  18676. isInputValid: Criteria.isInputValidInput,
  18677. search: function (value, comparison) {
  18678. return value.toLowerCase().endsWith(comparison[0].toLowerCase());
  18679. }
  18680. },
  18681. '!ends': {
  18682. conditionName: function (dt, i18n) {
  18683. return dt.i18n('searchBuilder.conditions.string.notEndsWith', i18n.conditions.string.notEndsWith);
  18684. },
  18685. init: Criteria.initInput,
  18686. inputValue: Criteria.inputValueInput,
  18687. isInputValid: Criteria.isInputValidInput,
  18688. search: function (value, comparison) {
  18689. return !value.toLowerCase().endsWith(comparison[0].toLowerCase());
  18690. }
  18691. },
  18692. 'null': {
  18693. conditionName: function (dt, i18n) {
  18694. return dt.i18n('searchBuilder.conditions.string.empty', i18n.conditions.string.empty);
  18695. },
  18696. init: Criteria.initNoValue,
  18697. inputValue: function () {
  18698. return;
  18699. },
  18700. isInputValid: function () {
  18701. return true;
  18702. },
  18703. search: function (value) {
  18704. return value === null || value === undefined || value.length === 0;
  18705. }
  18706. },
  18707. '!null': {
  18708. conditionName: function (dt, i18n) {
  18709. return dt.i18n('searchBuilder.conditions.string.notEmpty', i18n.conditions.string.notEmpty);
  18710. },
  18711. init: Criteria.initNoValue,
  18712. inputValue: function () {
  18713. return;
  18714. },
  18715. isInputValid: function () {
  18716. return true;
  18717. },
  18718. search: function (value) {
  18719. return !(value === null || value === undefined || value.length === 0);
  18720. }
  18721. }
  18722. };
  18723. // The order of the conditions will make eslint sad :(
  18724. // Also have to disable member ordering for this as the private methods used are not yet declared otherwise
  18725. Criteria.arrayConditions = {
  18726. 'contains': {
  18727. conditionName: function (dt, i18n) {
  18728. return dt.i18n('searchBuilder.conditions.array.contains', i18n.conditions.array.contains);
  18729. },
  18730. init: Criteria.initSelectArray,
  18731. inputValue: Criteria.inputValueSelect,
  18732. isInputValid: Criteria.isInputValidSelect,
  18733. search: function (value, comparison) {
  18734. return value.includes(comparison[0]);
  18735. }
  18736. },
  18737. 'without': {
  18738. conditionName: function (dt, i18n) {
  18739. return dt.i18n('searchBuilder.conditions.array.without', i18n.conditions.array.without);
  18740. },
  18741. init: Criteria.initSelectArray,
  18742. inputValue: Criteria.inputValueSelect,
  18743. isInputValid: Criteria.isInputValidSelect,
  18744. search: function (value, comparison) {
  18745. return value.indexOf(comparison[0]) === -1;
  18746. }
  18747. },
  18748. '=': {
  18749. conditionName: function (dt, i18n) {
  18750. return dt.i18n('searchBuilder.conditions.array.equals', i18n.conditions.array.equals);
  18751. },
  18752. init: Criteria.initSelect,
  18753. inputValue: Criteria.inputValueSelect,
  18754. isInputValid: Criteria.isInputValidSelect,
  18755. search: function (value, comparison) {
  18756. if (value.length === comparison[0].length) {
  18757. for (var i = 0; i < value.length; i++) {
  18758. if (value[i] !== comparison[0][i]) {
  18759. return false;
  18760. }
  18761. }
  18762. return true;
  18763. }
  18764. return false;
  18765. }
  18766. },
  18767. '!=': {
  18768. conditionName: function (dt, i18n) {
  18769. return dt.i18n('searchBuilder.conditions.array.not', i18n.conditions.array.not);
  18770. },
  18771. init: Criteria.initSelect,
  18772. inputValue: Criteria.inputValueSelect,
  18773. isInputValid: Criteria.isInputValidSelect,
  18774. search: function (value, comparison) {
  18775. if (value.length === comparison[0].length) {
  18776. for (var i = 0; i < value.length; i++) {
  18777. if (value[i] !== comparison[0][i]) {
  18778. return true;
  18779. }
  18780. }
  18781. return false;
  18782. }
  18783. return true;
  18784. }
  18785. },
  18786. 'null': {
  18787. conditionName: function (dt, i18n) {
  18788. return dt.i18n('searchBuilder.conditions.array.empty', i18n.conditions.array.empty);
  18789. },
  18790. init: Criteria.initNoValue,
  18791. inputValue: function () {
  18792. return;
  18793. },
  18794. isInputValid: function () {
  18795. return true;
  18796. },
  18797. search: function (value) {
  18798. return value === null || value === undefined || value.length === 0;
  18799. }
  18800. },
  18801. '!null': {
  18802. conditionName: function (dt, i18n) {
  18803. return dt.i18n('searchBuilder.conditions.array.notEmpty', i18n.conditions.array.notEmpty);
  18804. },
  18805. init: Criteria.initNoValue,
  18806. inputValue: function () {
  18807. return;
  18808. },
  18809. isInputValid: function () {
  18810. return true;
  18811. },
  18812. search: function (value) {
  18813. return value !== null && value !== undefined && value.length !== 0;
  18814. }
  18815. }
  18816. };
  18817. // eslint will be sad because we have to disable member ordering for this as the
  18818. // private static properties used are not yet declared otherwise
  18819. Criteria.defaults = {
  18820. columns: true,
  18821. conditions: {
  18822. 'array': Criteria.arrayConditions,
  18823. 'date': Criteria.dateConditions,
  18824. 'html': Criteria.stringConditions,
  18825. 'html-num': Criteria.numConditions,
  18826. 'html-num-fmt': Criteria.numFmtConditions,
  18827. 'luxon': Criteria.luxonDateConditions,
  18828. 'moment': Criteria.momentDateConditions,
  18829. 'num': Criteria.numConditions,
  18830. 'num-fmt': Criteria.numFmtConditions,
  18831. 'string': Criteria.stringConditions
  18832. },
  18833. depthLimit: false,
  18834. enterSearch: false,
  18835. filterChanged: undefined,
  18836. greyscale: false,
  18837. i18n: {
  18838. add: 'Add Condition',
  18839. button: {
  18840. 0: 'Search Builder',
  18841. _: 'Search Builder (%d)'
  18842. },
  18843. clearAll: 'Clear All',
  18844. condition: 'Condition',
  18845. data: 'Data',
  18846. "delete": '&times',
  18847. deleteTitle: 'Delete filtering rule',
  18848. left: '<',
  18849. leftTitle: 'Outdent criteria',
  18850. logicAnd: 'And',
  18851. logicOr: 'Or',
  18852. right: '>',
  18853. rightTitle: 'Indent criteria',
  18854. search: 'Search',
  18855. title: {
  18856. 0: 'Custom Search Builder',
  18857. _: 'Custom Search Builder (%d)'
  18858. },
  18859. value: 'Value',
  18860. valueJoiner: 'and'
  18861. },
  18862. liveSearch: true,
  18863. logic: 'AND',
  18864. orthogonal: {
  18865. display: 'display',
  18866. search: 'filter'
  18867. },
  18868. preDefined: false
  18869. };
  18870. return Criteria;
  18871. }());
  18872. var $$2;
  18873. var dataTable$2;
  18874. /**
  18875. * Sets the value of jQuery for use in the file
  18876. *
  18877. * @param jq the instance of jQuery to be set
  18878. */
  18879. function setJQuery$1(jq) {
  18880. $$2 = jq;
  18881. dataTable$2 = jq.fn.dataTable;
  18882. }
  18883. /**
  18884. * The Group class is used within SearchBuilder to represent a group of criteria
  18885. */
  18886. var Group = /** @class */ (function () {
  18887. function Group(table, opts, topGroup, index, isChild, depth, serverData) {
  18888. if (index === void 0) { index = 0; }
  18889. if (isChild === void 0) { isChild = false; }
  18890. if (depth === void 0) { depth = 1; }
  18891. if (serverData === void 0) { serverData = undefined; }
  18892. // Check that the required version of DataTables is included
  18893. if (!dataTable$2 || !dataTable$2.versionCheck || !dataTable$2.versionCheck('1.10.0')) {
  18894. throw new Error('SearchBuilder requires DataTables 1.10 or newer');
  18895. }
  18896. this.classes = $$2.extend(true, {}, Group.classes);
  18897. // Get options from user
  18898. this.c = $$2.extend(true, {}, Group.defaults, opts);
  18899. this.s = {
  18900. criteria: [],
  18901. depth: depth,
  18902. dt: table,
  18903. index: index,
  18904. isChild: isChild,
  18905. logic: undefined,
  18906. opts: opts,
  18907. preventRedraw: false,
  18908. serverData: serverData,
  18909. toDrop: undefined,
  18910. topGroup: topGroup
  18911. };
  18912. this.dom = {
  18913. add: $$2('<button/>')
  18914. .addClass(this.classes.add)
  18915. .addClass(this.classes.button)
  18916. .attr('type', 'button'),
  18917. clear: $$2('<button>&times</button>')
  18918. .addClass(this.classes.button)
  18919. .addClass(this.classes.clearGroup)
  18920. .attr('type', 'button'),
  18921. container: $$2('<div/>')
  18922. .addClass(this.classes.group),
  18923. logic: $$2('<button><div/></button>')
  18924. .addClass(this.classes.logic)
  18925. .addClass(this.classes.button)
  18926. .attr('type', 'button'),
  18927. logicContainer: $$2('<div/>')
  18928. .addClass(this.classes.logicContainer),
  18929. search: $$2('<button/>')
  18930. .addClass(this.classes.search)
  18931. .addClass(this.classes.button)
  18932. .attr('type', 'button')
  18933. .css('display', 'none')
  18934. };
  18935. // A reference to the top level group is maintained throughout any subgroups and criteria that may be created
  18936. if (this.s.topGroup === undefined) {
  18937. this.s.topGroup = this.dom.container;
  18938. }
  18939. this._setup();
  18940. return this;
  18941. }
  18942. /**
  18943. * Destroys the groups buttons, clears the internal criteria and removes it from the dom
  18944. */
  18945. Group.prototype.destroy = function () {
  18946. // Turn off listeners
  18947. this.dom.add.off('.dtsb');
  18948. this.dom.logic.off('.dtsb');
  18949. this.dom.search.off('.dtsb');
  18950. // Trigger event for groups at a higher level to pick up on
  18951. this.dom.container
  18952. .trigger('dtsb-destroy')
  18953. .remove();
  18954. this.s.criteria = [];
  18955. };
  18956. /**
  18957. * Gets the details required to rebuild the group
  18958. */
  18959. // Eslint upset at empty object but needs to be done
  18960. Group.prototype.getDetails = function (deFormatDates) {
  18961. if (deFormatDates === void 0) { deFormatDates = false; }
  18962. if (this.s.criteria.length === 0) {
  18963. return {};
  18964. }
  18965. var details = {
  18966. criteria: [],
  18967. logic: this.s.logic
  18968. };
  18969. // NOTE here crit could be either a subgroup or a criteria
  18970. for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
  18971. var crit = _a[_i];
  18972. details.criteria.push(crit.criteria.getDetails(deFormatDates));
  18973. }
  18974. return details;
  18975. };
  18976. /**
  18977. * Getter for the node for the container of the group
  18978. *
  18979. * @returns Node for the container of the group
  18980. */
  18981. Group.prototype.getNode = function () {
  18982. return this.dom.container;
  18983. };
  18984. /**
  18985. * Rebuilds the group based upon the details passed in
  18986. *
  18987. * @param loadedDetails the details required to rebuild the group
  18988. */
  18989. Group.prototype.rebuild = function (loadedDetails) {
  18990. var crit;
  18991. // If no criteria are stored then just return
  18992. if (loadedDetails.criteria === undefined ||
  18993. loadedDetails.criteria === null ||
  18994. Array.isArray(loadedDetails.criteria) && loadedDetails.criteria.length === 0) {
  18995. return;
  18996. }
  18997. this.s.logic = loadedDetails.logic;
  18998. this.dom.logic.children().first().html(this.s.logic === 'OR'
  18999. ? this.s.dt.i18n('searchBuilder.logicOr', this.c.i18n.logicOr)
  19000. : this.s.dt.i18n('searchBuilder.logicAnd', this.c.i18n.logicAnd));
  19001. // Add all of the criteria, be it a sub group or a criteria
  19002. if (Array.isArray(loadedDetails.criteria)) {
  19003. for (var _i = 0, _a = loadedDetails.criteria; _i < _a.length; _i++) {
  19004. crit = _a[_i];
  19005. if (crit.logic !== undefined) {
  19006. this._addPrevGroup(crit);
  19007. }
  19008. else if (crit.logic === undefined) {
  19009. this._addPrevCriteria(crit);
  19010. }
  19011. }
  19012. }
  19013. // For all of the criteria children, update the arrows incase they require changing and set the listeners
  19014. for (var _b = 0, _c = this.s.criteria; _b < _c.length; _b++) {
  19015. crit = _c[_b];
  19016. if (crit.criteria instanceof Criteria) {
  19017. crit.criteria.updateArrows(this.s.criteria.length > 1);
  19018. this._setCriteriaListeners(crit.criteria);
  19019. }
  19020. }
  19021. };
  19022. /**
  19023. * Redraws the Contents of the searchBuilder Groups and Criteria
  19024. */
  19025. Group.prototype.redrawContents = function () {
  19026. if (this.s.preventRedraw) {
  19027. return;
  19028. }
  19029. // Clear the container out and add the basic elements
  19030. this.dom.container.children().detach();
  19031. this.dom.container
  19032. .append(this.dom.logicContainer)
  19033. .append(this.dom.add);
  19034. if (!this.c.liveSearch) {
  19035. this.dom.container.append(this.dom.search);
  19036. }
  19037. // Sort the criteria by index so that they appear in the correct order
  19038. this.s.criteria.sort(function (a, b) {
  19039. if (a.criteria.s.index < b.criteria.s.index) {
  19040. return -1;
  19041. }
  19042. else if (a.criteria.s.index > b.criteria.s.index) {
  19043. return 1;
  19044. }
  19045. return 0;
  19046. });
  19047. this.setListeners();
  19048. for (var i = 0; i < this.s.criteria.length; i++) {
  19049. var crit = this.s.criteria[i].criteria;
  19050. if (crit instanceof Criteria) {
  19051. // Reset the index to the new value
  19052. this.s.criteria[i].index = i;
  19053. this.s.criteria[i].criteria.s.index = i;
  19054. // Add to the group
  19055. this.s.criteria[i].criteria.dom.container.insertBefore(this.dom.add);
  19056. // Set listeners for various points
  19057. this._setCriteriaListeners(crit);
  19058. this.s.criteria[i].criteria.s.preventRedraw = this.s.preventRedraw;
  19059. this.s.criteria[i].criteria.rebuild(this.s.criteria[i].criteria.getDetails());
  19060. this.s.criteria[i].criteria.s.preventRedraw = false;
  19061. }
  19062. else if (crit instanceof Group && crit.s.criteria.length > 0) {
  19063. // Reset the index to the new value
  19064. this.s.criteria[i].index = i;
  19065. this.s.criteria[i].criteria.s.index = i;
  19066. // Add the sub group to the group
  19067. this.s.criteria[i].criteria.dom.container.insertBefore(this.dom.add);
  19068. // Redraw the contents of the group
  19069. crit.s.preventRedraw = this.s.preventRedraw;
  19070. crit.redrawContents();
  19071. crit.s.preventRedraw = false;
  19072. this._setGroupListeners(crit);
  19073. }
  19074. else {
  19075. // The group is empty so remove it
  19076. this.s.criteria.splice(i, 1);
  19077. i--;
  19078. }
  19079. }
  19080. this.setupLogic();
  19081. };
  19082. /**
  19083. * Resizes the logic button only rather than the entire dom.
  19084. */
  19085. Group.prototype.redrawLogic = function () {
  19086. for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
  19087. var crit = _a[_i];
  19088. if (crit.criteria instanceof Group) {
  19089. crit.criteria.redrawLogic();
  19090. }
  19091. }
  19092. this.setupLogic();
  19093. };
  19094. /**
  19095. * Search method, checking the row data against the criteria in the group
  19096. *
  19097. * @param rowData The row data to be compared
  19098. * @returns boolean The result of the search
  19099. */
  19100. Group.prototype.search = function (rowData, rowIdx) {
  19101. if (this.s.logic === 'AND') {
  19102. return this._andSearch(rowData, rowIdx);
  19103. }
  19104. else if (this.s.logic === 'OR') {
  19105. return this._orSearch(rowData, rowIdx);
  19106. }
  19107. return true;
  19108. };
  19109. /**
  19110. * Locates the groups logic button to the correct location on the page
  19111. */
  19112. Group.prototype.setupLogic = function () {
  19113. // Remove logic button
  19114. this.dom.logicContainer.remove();
  19115. this.dom.clear.remove();
  19116. // If there are no criteria in the group then keep the logic removed and return
  19117. if (this.s.criteria.length < 1) {
  19118. if (!this.s.isChild) {
  19119. this.dom.container.trigger('dtsb-destroy');
  19120. // Set criteria left margin
  19121. this.dom.container.css('margin-left', 0);
  19122. }
  19123. this.dom.search.css('display', 'none');
  19124. return;
  19125. }
  19126. this.dom.clear.height('0px');
  19127. this.dom.logicContainer.append(this.dom.clear);
  19128. if (!this.s.isChild) {
  19129. this.dom.search.css('display', 'inline-block');
  19130. }
  19131. // Prepend logic button
  19132. this.dom.container.prepend(this.dom.logicContainer);
  19133. for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
  19134. var crit = _a[_i];
  19135. if (crit.criteria instanceof Criteria) {
  19136. crit.criteria.setupButtons();
  19137. }
  19138. }
  19139. // Set width, take 2 for the border
  19140. var height = this.dom.container.outerHeight() - 1;
  19141. this.dom.logicContainer.width(height);
  19142. this._setLogicListener();
  19143. // Set criteria left margin
  19144. this.dom.container.css('margin-left', this.dom.logicContainer.outerHeight(true));
  19145. var logicOffset = this.dom.logicContainer.offset();
  19146. // Set horizontal alignment
  19147. var currentLeft = logicOffset.left;
  19148. var groupLeft = this.dom.container.offset().left;
  19149. var shuffleLeft = currentLeft - groupLeft;
  19150. var newPos = currentLeft - shuffleLeft - this.dom.logicContainer.outerHeight(true);
  19151. this.dom.logicContainer.offset({ left: newPos });
  19152. // Set vertical alignment
  19153. var firstCrit = this.dom.logicContainer.next();
  19154. var currentTop = logicOffset.top;
  19155. var firstTop = $$2(firstCrit).offset().top;
  19156. var shuffleTop = currentTop - firstTop;
  19157. var newTop = currentTop - shuffleTop;
  19158. this.dom.logicContainer.offset({ top: newTop });
  19159. this.dom.clear.outerHeight(this.dom.logicContainer.height());
  19160. this._setClearListener();
  19161. };
  19162. /**
  19163. * Sets listeners on the groups elements
  19164. */
  19165. Group.prototype.setListeners = function () {
  19166. var _this = this;
  19167. this.dom.add.unbind('click');
  19168. this.dom.add.on('click.dtsb', function () {
  19169. // If this is the parent group then the logic button has not been added yet
  19170. if (!_this.s.isChild) {
  19171. _this.dom.container.prepend(_this.dom.logicContainer);
  19172. }
  19173. _this.addCriteria();
  19174. _this.dom.container.trigger('dtsb-add');
  19175. _this.s.dt.state.save();
  19176. return false;
  19177. });
  19178. this.dom.search
  19179. .off('click.dtsb')
  19180. .on('click.dtsb', function () {
  19181. _this.s.dt.draw();
  19182. });
  19183. for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
  19184. var crit = _a[_i];
  19185. crit.criteria.setListeners();
  19186. }
  19187. this._setClearListener();
  19188. this._setLogicListener();
  19189. };
  19190. /**
  19191. * Adds a criteria to the group
  19192. *
  19193. * @param crit Instance of Criteria to be added to the group
  19194. */
  19195. Group.prototype.addCriteria = function (crit) {
  19196. if (crit === void 0) { crit = null; }
  19197. var index = crit === null ? this.s.criteria.length : crit.s.index;
  19198. var criteria = new Criteria(this.s.dt, this.s.opts, this.s.topGroup, index, this.s.depth, this.s.serverData, this.c.liveSearch);
  19199. // If a Criteria has been passed in then set the values to continue that
  19200. if (crit !== null) {
  19201. criteria.c = crit.c;
  19202. criteria.s = crit.s;
  19203. criteria.s.depth = this.s.depth;
  19204. criteria.classes = crit.classes;
  19205. }
  19206. criteria.populate();
  19207. var inserted = false;
  19208. for (var i = 0; i < this.s.criteria.length; i++) {
  19209. if (i === 0 && this.s.criteria[i].criteria.s.index > criteria.s.index) {
  19210. // Add the node for the criteria at the start of the group
  19211. criteria.getNode().insertBefore(this.s.criteria[i].criteria.dom.container);
  19212. inserted = true;
  19213. }
  19214. else if (i < this.s.criteria.length - 1 &&
  19215. this.s.criteria[i].criteria.s.index < criteria.s.index &&
  19216. this.s.criteria[i + 1].criteria.s.index > criteria.s.index) {
  19217. // Add the node for the criteria in the correct location
  19218. criteria.getNode().insertAfter(this.s.criteria[i].criteria.dom.container);
  19219. inserted = true;
  19220. }
  19221. }
  19222. if (!inserted) {
  19223. criteria.getNode().insertBefore(this.dom.add);
  19224. }
  19225. // Add the details for this criteria to the array
  19226. this.s.criteria.push({
  19227. criteria: criteria,
  19228. index: index
  19229. });
  19230. this.s.criteria = this.s.criteria.sort(function (a, b) { return a.criteria.s.index - b.criteria.s.index; });
  19231. for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
  19232. var opt = _a[_i];
  19233. if (opt.criteria instanceof Criteria) {
  19234. opt.criteria.updateArrows(this.s.criteria.length > 1);
  19235. }
  19236. }
  19237. this._setCriteriaListeners(criteria);
  19238. criteria.setListeners();
  19239. this.setupLogic();
  19240. };
  19241. /**
  19242. * Checks the group to see if it has any filled criteria
  19243. */
  19244. Group.prototype.checkFilled = function () {
  19245. for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
  19246. var crit = _a[_i];
  19247. if (crit.criteria instanceof Criteria && crit.criteria.s.filled ||
  19248. crit.criteria instanceof Group && crit.criteria.checkFilled()) {
  19249. return true;
  19250. }
  19251. }
  19252. return false;
  19253. };
  19254. /**
  19255. * Gets the count for the number of criteria in this group and any sub groups
  19256. */
  19257. Group.prototype.count = function () {
  19258. var count = 0;
  19259. for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
  19260. var crit = _a[_i];
  19261. if (crit.criteria instanceof Group) {
  19262. count += crit.criteria.count();
  19263. }
  19264. else {
  19265. count++;
  19266. }
  19267. }
  19268. return count;
  19269. };
  19270. /**
  19271. * Rebuilds a sub group that previously existed
  19272. *
  19273. * @param loadedGroup The details of a group within this group
  19274. */
  19275. Group.prototype._addPrevGroup = function (loadedGroup) {
  19276. var idx = this.s.criteria.length;
  19277. var group = new Group(this.s.dt, this.c, this.s.topGroup, idx, true, this.s.depth + 1, this.s.serverData);
  19278. // Add the new group to the criteria array
  19279. this.s.criteria.push({
  19280. criteria: group,
  19281. index: idx,
  19282. logic: group.s.logic
  19283. });
  19284. // Rebuild it with the previous conditions for that group
  19285. group.rebuild(loadedGroup);
  19286. this.s.criteria[idx].criteria = group;
  19287. this.s.topGroup.trigger('dtsb-redrawContents');
  19288. this._setGroupListeners(group);
  19289. };
  19290. /**
  19291. * Rebuilds a criteria of this group that previously existed
  19292. *
  19293. * @param loadedCriteria The details of a criteria within the group
  19294. */
  19295. Group.prototype._addPrevCriteria = function (loadedCriteria) {
  19296. var idx = this.s.criteria.length;
  19297. var criteria = new Criteria(this.s.dt, this.s.opts, this.s.topGroup, idx, this.s.depth, this.s.serverData);
  19298. criteria.populate();
  19299. // Add the new criteria to the criteria array
  19300. this.s.criteria.push({
  19301. criteria: criteria,
  19302. index: idx
  19303. });
  19304. // Rebuild it with the previous conditions for that criteria
  19305. criteria.s.preventRedraw = this.s.preventRedraw;
  19306. criteria.rebuild(loadedCriteria);
  19307. criteria.s.preventRedraw = false;
  19308. this.s.criteria[idx].criteria = criteria;
  19309. if (!this.s.preventRedraw) {
  19310. this.s.topGroup.trigger('dtsb-redrawContents');
  19311. }
  19312. };
  19313. /**
  19314. * Checks And the criteria using AND logic
  19315. *
  19316. * @param rowData The row data to be checked against the search criteria
  19317. * @returns boolean The result of the AND search
  19318. */
  19319. Group.prototype._andSearch = function (rowData, rowIdx) {
  19320. // If there are no criteria then return true for this group
  19321. if (this.s.criteria.length === 0) {
  19322. return true;
  19323. }
  19324. for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
  19325. var crit = _a[_i];
  19326. // If the criteria is not complete then skip it
  19327. if (crit.criteria instanceof Criteria && !crit.criteria.s.filled) {
  19328. continue;
  19329. }
  19330. // Otherwise if a single one fails return false
  19331. else if (!crit.criteria.search(rowData, rowIdx)) {
  19332. return false;
  19333. }
  19334. }
  19335. // If we get to here then everything has passed, so return true for the group
  19336. return true;
  19337. };
  19338. /**
  19339. * Checks And the criteria using OR logic
  19340. *
  19341. * @param rowData The row data to be checked against the search criteria
  19342. * @returns boolean The result of the OR search
  19343. */
  19344. Group.prototype._orSearch = function (rowData, rowIdx) {
  19345. // If there are no criteria in the group then return true
  19346. if (this.s.criteria.length === 0) {
  19347. return true;
  19348. }
  19349. // This will check to make sure that at least one criteria in the group is complete
  19350. var filledfound = false;
  19351. for (var _i = 0, _a = this.s.criteria; _i < _a.length; _i++) {
  19352. var crit = _a[_i];
  19353. if (crit.criteria instanceof Criteria && crit.criteria.s.filled) {
  19354. // A completed criteria has been found so set the flag
  19355. filledfound = true;
  19356. // If the search passes then return true
  19357. if (crit.criteria.search(rowData, rowIdx)) {
  19358. return true;
  19359. }
  19360. }
  19361. else if (crit.criteria instanceof Group && crit.criteria.checkFilled()) {
  19362. filledfound = true;
  19363. if (crit.criteria.search(rowData, rowIdx)) {
  19364. return true;
  19365. }
  19366. }
  19367. }
  19368. // If we get here we need to return the inverse of filledfound,
  19369. // as if any have been found and we are here then none have passed
  19370. return !filledfound;
  19371. };
  19372. /**
  19373. * Removes a criteria from the group
  19374. *
  19375. * @param criteria The criteria instance to be removed
  19376. */
  19377. Group.prototype._removeCriteria = function (criteria, group) {
  19378. if (group === void 0) { group = false; }
  19379. var i;
  19380. // If removing a criteria and there is only then then just destroy the group
  19381. if (this.s.criteria.length <= 1 && this.s.isChild) {
  19382. this.destroy();
  19383. }
  19384. else {
  19385. // Otherwise splice the given criteria out and redo the indexes
  19386. var last = void 0;
  19387. for (i = 0; i < this.s.criteria.length; i++) {
  19388. if (this.s.criteria[i].index === criteria.s.index &&
  19389. (!group || this.s.criteria[i].criteria instanceof Group)) {
  19390. last = i;
  19391. }
  19392. }
  19393. // We want to remove the last element with the desired index, as its replacement will be inserted before it
  19394. if (last !== undefined) {
  19395. this.s.criteria.splice(last, 1);
  19396. }
  19397. for (i = 0; i < this.s.criteria.length; i++) {
  19398. this.s.criteria[i].index = i;
  19399. this.s.criteria[i].criteria.s.index = i;
  19400. }
  19401. }
  19402. };
  19403. /**
  19404. * Sets the listeners in group for a criteria
  19405. *
  19406. * @param criteria The criteria for the listeners to be set on
  19407. */
  19408. Group.prototype._setCriteriaListeners = function (criteria) {
  19409. var _this = this;
  19410. criteria.dom["delete"]
  19411. .unbind('click')
  19412. .on('click.dtsb', function () {
  19413. _this._removeCriteria(criteria);
  19414. criteria.dom.container.remove();
  19415. for (var _i = 0, _a = _this.s.criteria; _i < _a.length; _i++) {
  19416. var crit = _a[_i];
  19417. if (crit.criteria instanceof Criteria) {
  19418. crit.criteria.updateArrows(_this.s.criteria.length > 1);
  19419. }
  19420. }
  19421. criteria.destroy();
  19422. _this.s.dt.draw();
  19423. _this.s.topGroup.trigger('dtsb-redrawContents');
  19424. return false;
  19425. });
  19426. criteria.dom.right
  19427. .unbind('click')
  19428. .on('click.dtsb', function () {
  19429. var idx = criteria.s.index;
  19430. var group = new Group(_this.s.dt, _this.s.opts, _this.s.topGroup, criteria.s.index, true, _this.s.depth + 1, _this.s.serverData);
  19431. // Add the criteria that is to be moved to the new group
  19432. group.addCriteria(criteria);
  19433. // Update the details in the current groups criteria array
  19434. _this.s.criteria[idx].criteria = group;
  19435. _this.s.criteria[idx].logic = 'AND';
  19436. _this.s.topGroup.trigger('dtsb-redrawContents');
  19437. _this._setGroupListeners(group);
  19438. return false;
  19439. });
  19440. criteria.dom.left
  19441. .unbind('click')
  19442. .on('click.dtsb', function () {
  19443. _this.s.toDrop = new Criteria(_this.s.dt, _this.s.opts, _this.s.topGroup, criteria.s.index, undefined, _this.s.serverData);
  19444. _this.s.toDrop.s = criteria.s;
  19445. _this.s.toDrop.c = criteria.c;
  19446. _this.s.toDrop.classes = criteria.classes;
  19447. _this.s.toDrop.populate();
  19448. // The dropCriteria event mutates the reference to the index so need to store it
  19449. var index = _this.s.toDrop.s.index;
  19450. _this.dom.container.trigger('dtsb-dropCriteria');
  19451. criteria.s.index = index;
  19452. _this._removeCriteria(criteria);
  19453. // By tracking the top level group we can directly trigger a redraw on it,
  19454. // bubbling is also possible, but that is slow with deep levelled groups
  19455. _this.s.topGroup.trigger('dtsb-redrawContents');
  19456. _this.s.dt.draw();
  19457. return false;
  19458. });
  19459. };
  19460. /**
  19461. * Set's the listeners for the group clear button
  19462. */
  19463. Group.prototype._setClearListener = function () {
  19464. var _this = this;
  19465. this.dom.clear
  19466. .unbind('click')
  19467. .on('click.dtsb', function () {
  19468. if (!_this.s.isChild) {
  19469. _this.dom.container.trigger('dtsb-clearContents');
  19470. return false;
  19471. }
  19472. _this.destroy();
  19473. _this.s.topGroup.trigger('dtsb-redrawContents');
  19474. return false;
  19475. });
  19476. };
  19477. /**
  19478. * Sets listeners for sub groups of this group
  19479. *
  19480. * @param group The sub group that the listeners are to be set on
  19481. */
  19482. Group.prototype._setGroupListeners = function (group) {
  19483. var _this = this;
  19484. // Set listeners for the new group
  19485. group.dom.add
  19486. .unbind('click')
  19487. .on('click.dtsb', function () {
  19488. _this.setupLogic();
  19489. _this.dom.container.trigger('dtsb-add');
  19490. return false;
  19491. });
  19492. group.dom.container
  19493. .unbind('dtsb-add')
  19494. .on('dtsb-add.dtsb', function () {
  19495. _this.setupLogic();
  19496. _this.dom.container.trigger('dtsb-add');
  19497. return false;
  19498. });
  19499. group.dom.container
  19500. .unbind('dtsb-destroy')
  19501. .on('dtsb-destroy.dtsb', function () {
  19502. _this._removeCriteria(group, true);
  19503. group.dom.container.remove();
  19504. _this.setupLogic();
  19505. return false;
  19506. });
  19507. group.dom.container
  19508. .unbind('dtsb-dropCriteria')
  19509. .on('dtsb-dropCriteria.dtsb', function () {
  19510. var toDrop = group.s.toDrop;
  19511. toDrop.s.index = group.s.index;
  19512. toDrop.updateArrows(_this.s.criteria.length > 1);
  19513. _this.addCriteria(toDrop);
  19514. return false;
  19515. });
  19516. group.setListeners();
  19517. };
  19518. /**
  19519. * Sets up the Group instance, setting listeners and appending elements
  19520. */
  19521. Group.prototype._setup = function () {
  19522. this.setListeners();
  19523. this.dom.add.html(this.s.dt.i18n('searchBuilder.add', this.c.i18n.add));
  19524. this.dom.search.html(this.s.dt.i18n('searchBuilder.search', this.c.i18n.search));
  19525. this.dom.logic.children().first().html(this.c.logic === 'OR'
  19526. ? this.s.dt.i18n('searchBuilder.logicOr', this.c.i18n.logicOr)
  19527. : this.s.dt.i18n('searchBuilder.logicAnd', this.c.i18n.logicAnd));
  19528. this.s.logic = this.c.logic === 'OR' ? 'OR' : 'AND';
  19529. if (this.c.greyscale) {
  19530. this.dom.logic.addClass(this.classes.greyscale);
  19531. }
  19532. this.dom.logicContainer.append(this.dom.logic).append(this.dom.clear);
  19533. // Only append the logic button immediately if this is a sub group,
  19534. // otherwise it will be prepended later when adding a criteria
  19535. if (this.s.isChild) {
  19536. this.dom.container.append(this.dom.logicContainer);
  19537. }
  19538. this.dom.container.append(this.dom.add);
  19539. if (!this.c.liveSearch) {
  19540. this.dom.container.append(this.dom.search);
  19541. }
  19542. };
  19543. /**
  19544. * Sets the listener for the logic button
  19545. */
  19546. Group.prototype._setLogicListener = function () {
  19547. var _this = this;
  19548. this.dom.logic
  19549. .unbind('click')
  19550. .on('click.dtsb', function () {
  19551. _this._toggleLogic();
  19552. _this.s.dt.draw();
  19553. for (var _i = 0, _a = _this.s.criteria; _i < _a.length; _i++) {
  19554. var crit = _a[_i];
  19555. crit.criteria.setListeners();
  19556. }
  19557. });
  19558. };
  19559. /**
  19560. * Toggles the logic for the group
  19561. */
  19562. Group.prototype._toggleLogic = function () {
  19563. if (this.s.logic === 'OR') {
  19564. this.s.logic = 'AND';
  19565. this.dom.logic.children().first().html(this.s.dt.i18n('searchBuilder.logicAnd', this.c.i18n.logicAnd));
  19566. }
  19567. else if (this.s.logic === 'AND') {
  19568. this.s.logic = 'OR';
  19569. this.dom.logic.children().first().html(this.s.dt.i18n('searchBuilder.logicOr', this.c.i18n.logicOr));
  19570. }
  19571. };
  19572. Group.version = '1.1.0';
  19573. Group.classes = {
  19574. add: 'dtsb-add',
  19575. button: 'dtsb-button',
  19576. clearGroup: 'dtsb-clearGroup',
  19577. greyscale: 'dtsb-greyscale',
  19578. group: 'dtsb-group',
  19579. inputButton: 'dtsb-iptbtn',
  19580. logic: 'dtsb-logic',
  19581. logicContainer: 'dtsb-logicContainer',
  19582. search: 'dtsb-search'
  19583. };
  19584. Group.defaults = {
  19585. columns: true,
  19586. conditions: {
  19587. 'date': Criteria.dateConditions,
  19588. 'html': Criteria.stringConditions,
  19589. 'html-num': Criteria.numConditions,
  19590. 'html-num-fmt': Criteria.numFmtConditions,
  19591. 'luxon': Criteria.luxonDateConditions,
  19592. 'moment': Criteria.momentDateConditions,
  19593. 'num': Criteria.numConditions,
  19594. 'num-fmt': Criteria.numFmtConditions,
  19595. 'string': Criteria.stringConditions
  19596. },
  19597. depthLimit: false,
  19598. enterSearch: false,
  19599. filterChanged: undefined,
  19600. greyscale: false,
  19601. liveSearch: true,
  19602. i18n: {
  19603. add: 'Add Condition',
  19604. button: {
  19605. 0: 'Search Builder',
  19606. _: 'Search Builder (%d)'
  19607. },
  19608. clearAll: 'Clear All',
  19609. condition: 'Condition',
  19610. data: 'Data',
  19611. "delete": '&times',
  19612. deleteTitle: 'Delete filtering rule',
  19613. left: '<',
  19614. leftTitle: 'Outdent criteria',
  19615. logicAnd: 'And',
  19616. logicOr: 'Or',
  19617. right: '>',
  19618. rightTitle: 'Indent criteria',
  19619. search: 'Search',
  19620. title: {
  19621. 0: 'Custom Search Builder',
  19622. _: 'Custom Search Builder (%d)'
  19623. },
  19624. value: 'Value',
  19625. valueJoiner: 'and'
  19626. },
  19627. logic: 'AND',
  19628. orthogonal: {
  19629. display: 'display',
  19630. search: 'filter'
  19631. },
  19632. preDefined: false
  19633. };
  19634. return Group;
  19635. }());
  19636. var $$1;
  19637. var dataTable$1;
  19638. /**
  19639. * Sets the value of jQuery for use in the file
  19640. *
  19641. * @param jq the instance of jQuery to be set
  19642. */
  19643. function setJQuery(jq) {
  19644. $$1 = jq;
  19645. dataTable$1 = jq.fn.DataTable;
  19646. }
  19647. /**
  19648. * SearchBuilder class for DataTables.
  19649. * Allows for complex search queries to be constructed and implemented on a DataTable
  19650. */
  19651. var SearchBuilder = /** @class */ (function () {
  19652. function SearchBuilder(builderSettings, opts) {
  19653. var _this = this;
  19654. // Check that the required version of DataTables is included
  19655. if (!dataTable$1 || !dataTable$1.versionCheck || !dataTable$1.versionCheck('1.10.0')) {
  19656. throw new Error('SearchBuilder requires DataTables 1.10 or newer');
  19657. }
  19658. var table = new dataTable$1.Api(builderSettings);
  19659. this.classes = $$1.extend(true, {}, SearchBuilder.classes);
  19660. // Get options from user
  19661. this.c = $$1.extend(true, {}, SearchBuilder.defaults, opts);
  19662. this.dom = {
  19663. clearAll: $$1('<button type="button">' + table.i18n('searchBuilder.clearAll', this.c.i18n.clearAll) + '</button>')
  19664. .addClass(this.classes.clearAll)
  19665. .addClass(this.classes.button)
  19666. .attr('type', 'button'),
  19667. container: $$1('<div/>')
  19668. .addClass(this.classes.container),
  19669. title: $$1('<div/>')
  19670. .addClass(this.classes.title),
  19671. titleRow: $$1('<div/>')
  19672. .addClass(this.classes.titleRow),
  19673. topGroup: undefined
  19674. };
  19675. this.s = {
  19676. dt: table,
  19677. opts: opts,
  19678. search: undefined,
  19679. serverData: undefined,
  19680. topGroup: undefined
  19681. };
  19682. // If searchbuilder is already defined for this table then return
  19683. if (table.settings()[0]._searchBuilder !== undefined) {
  19684. return;
  19685. }
  19686. table.settings()[0]._searchBuilder = this;
  19687. // If using SSP we want to include the previous state in the very first server call
  19688. if (this.s.dt.page.info().serverSide) {
  19689. this.s.dt.on('preXhr.dtsb', function (e, settings, data) {
  19690. var loadedState = _this.s.dt.state.loaded();
  19691. if (loadedState && loadedState.searchBuilder) {
  19692. data.searchBuilder = _this._collapseArray(loadedState.searchBuilder);
  19693. }
  19694. });
  19695. this.s.dt.on('xhr.dtsb', function (e, settings, json) {
  19696. if (json && json.searchBuilder && json.searchBuilder.options) {
  19697. _this.s.serverData = json.searchBuilder.options;
  19698. }
  19699. });
  19700. }
  19701. // Run the remaining setup when the table is initialised
  19702. if (this.s.dt.settings()[0]._bInitComplete) {
  19703. this._setUp();
  19704. }
  19705. else {
  19706. table.one('init.dt', function () {
  19707. _this._setUp();
  19708. });
  19709. }
  19710. return this;
  19711. }
  19712. /**
  19713. * Gets the details required to rebuild the SearchBuilder as it currently is
  19714. */
  19715. // eslint upset at empty object but that is what it is
  19716. SearchBuilder.prototype.getDetails = function (deFormatDates) {
  19717. if (deFormatDates === void 0) { deFormatDates = false; }
  19718. return this.s.topGroup.getDetails(deFormatDates);
  19719. };
  19720. /**
  19721. * Getter for the node of the container for the searchBuilder
  19722. *
  19723. * @returns JQuery<HTMLElement> the node of the container
  19724. */
  19725. SearchBuilder.prototype.getNode = function () {
  19726. return this.dom.container;
  19727. };
  19728. /**
  19729. * Rebuilds the SearchBuilder to a state that is provided
  19730. *
  19731. * @param details The details required to perform a rebuild
  19732. */
  19733. SearchBuilder.prototype.rebuild = function (details) {
  19734. this.dom.clearAll.click();
  19735. // If there are no details to rebuild then return
  19736. if (details === undefined || details === null) {
  19737. return this;
  19738. }
  19739. this.s.topGroup.s.preventRedraw = true;
  19740. this.s.topGroup.rebuild(details);
  19741. this.s.topGroup.s.preventRedraw = false;
  19742. this._checkClear();
  19743. this._updateTitle(this.s.topGroup.count());
  19744. this.s.topGroup.redrawContents();
  19745. this.s.dt.draw(false);
  19746. this.s.topGroup.setListeners();
  19747. return this;
  19748. };
  19749. /**
  19750. * Applies the defaults to preDefined criteria
  19751. *
  19752. * @param preDef the array of criteria to be processed.
  19753. */
  19754. SearchBuilder.prototype._applyPreDefDefaults = function (preDef) {
  19755. var _this = this;
  19756. if (preDef.criteria !== undefined && preDef.logic === undefined) {
  19757. preDef.logic = 'AND';
  19758. }
  19759. var _loop_1 = function (crit) {
  19760. // Apply the defaults to any further criteria
  19761. if (crit.criteria !== undefined) {
  19762. crit = this_1._applyPreDefDefaults(crit);
  19763. }
  19764. else {
  19765. this_1.s.dt.columns().every(function (index) {
  19766. if (_this.s.dt.settings()[0].aoColumns[index].sTitle === crit.data) {
  19767. crit.dataIdx = index;
  19768. }
  19769. });
  19770. }
  19771. };
  19772. var this_1 = this;
  19773. for (var _i = 0, _a = preDef.criteria; _i < _a.length; _i++) {
  19774. var crit = _a[_i];
  19775. _loop_1(crit);
  19776. }
  19777. return preDef;
  19778. };
  19779. /**
  19780. * Set's up the SearchBuilder
  19781. */
  19782. SearchBuilder.prototype._setUp = function (loadState) {
  19783. var _this = this;
  19784. if (loadState === void 0) { loadState = true; }
  19785. // Register an Api method for getting the column type. DataTables 2 has
  19786. // this built in
  19787. if (typeof this.s.dt.column().type !== 'function') {
  19788. DataTable.Api.registerPlural('columns().types()', 'column().type()', function () {
  19789. return this.iterator('column', function (settings, column) {
  19790. return settings.aoColumns[column].sType;
  19791. }, 1);
  19792. });
  19793. }
  19794. // Check that DateTime is included, If not need to check if it could be used
  19795. // eslint-disable-next-line no-extra-parens
  19796. if (!dataTable$1.DateTime) {
  19797. var types = this.s.dt.columns().types().toArray();
  19798. if (types === undefined || types.includes(undefined) || types.includes(null)) {
  19799. types = [];
  19800. for (var _i = 0, _a = this.s.dt.settings()[0].aoColumns; _i < _a.length; _i++) {
  19801. var colInit = _a[_i];
  19802. types.push(colInit.searchBuilderType !== undefined ? colInit.searchBuilderType : colInit.sType);
  19803. }
  19804. }
  19805. var columnIdxs = this.s.dt.columns().toArray();
  19806. // If the column type is still unknown use the internal API to detect type
  19807. if (types === undefined || types.includes(undefined) || types.includes(null)) {
  19808. // This can only happen in DT1 - DT2 will do the invalidation of the type itself
  19809. if ($$1.fn.dataTable.ext.oApi) {
  19810. $$1.fn.dataTable.ext.oApi._fnColumnTypes(this.s.dt.settings()[0]);
  19811. }
  19812. types = this.s.dt.columns().types().toArray();
  19813. }
  19814. for (var i = 0; i < columnIdxs[0].length; i++) {
  19815. var column = columnIdxs[0][i];
  19816. var type = types[column];
  19817. if (
  19818. // Check if this column can be filtered
  19819. (this.c.columns === true ||
  19820. Array.isArray(this.c.columns) &&
  19821. this.c.columns.includes(i)) &&
  19822. // Check if the type is one of the restricted types
  19823. (type.includes('date') ||
  19824. type.includes('moment') ||
  19825. type.includes('luxon'))) {
  19826. alert('SearchBuilder Requires DateTime when used with dates.');
  19827. throw new Error('SearchBuilder requires DateTime');
  19828. }
  19829. }
  19830. }
  19831. this.s.topGroup = new Group(this.s.dt, this.c, undefined, undefined, undefined, undefined, this.s.serverData);
  19832. this._setClearListener();
  19833. this.s.dt.on('stateSaveParams.dtsb', function (e, settings, data) {
  19834. data.searchBuilder = _this.getDetails();
  19835. if (!data.scroller) {
  19836. data.page = _this.s.dt.page();
  19837. }
  19838. else {
  19839. data.start = _this.s.dt.state().start;
  19840. }
  19841. });
  19842. this.s.dt.on('stateLoadParams.dtsb', function (e, settings, data) {
  19843. _this.rebuild(data.searchBuilder);
  19844. });
  19845. this._build();
  19846. this.s.dt.on('preXhr.dtsb', function (e, settings, data) {
  19847. if (_this.s.dt.page.info().serverSide) {
  19848. data.searchBuilder = _this._collapseArray(_this.getDetails(true));
  19849. }
  19850. });
  19851. this.s.dt.on(dataTable$1.versionCheck('2')
  19852. ? 'columns-reordered'
  19853. : 'column-reorder', function () {
  19854. _this.rebuild(_this.getDetails());
  19855. });
  19856. if (loadState) {
  19857. var loadedState = this.s.dt.state.loaded();
  19858. // If the loaded State is not null rebuild based on it for statesave
  19859. if (loadedState !== null && loadedState.searchBuilder !== undefined) {
  19860. this.s.topGroup.rebuild(loadedState.searchBuilder);
  19861. this.s.topGroup.dom.container.trigger('dtsb-redrawContents');
  19862. // If using SSP we want to restrict the amount of server calls that take place
  19863. // and this information will already have been processed
  19864. if (!this.s.dt.page.info().serverSide) {
  19865. if (loadedState.page) {
  19866. this.s.dt.page(loadedState.page).draw('page');
  19867. }
  19868. else if (this.s.dt.scroller && loadedState.scroller) {
  19869. this.s.dt.scroller().scrollToRow(loadedState.scroller.topRow);
  19870. }
  19871. }
  19872. this.s.topGroup.setListeners();
  19873. }
  19874. // Otherwise load any predefined options
  19875. else if (this.c.preDefined !== false) {
  19876. this.c.preDefined = this._applyPreDefDefaults(this.c.preDefined);
  19877. this.rebuild(this.c.preDefined);
  19878. }
  19879. }
  19880. this._setEmptyListener();
  19881. this.s.dt.state.save();
  19882. };
  19883. SearchBuilder.prototype._collapseArray = function (criteria) {
  19884. if (criteria.logic === undefined) {
  19885. if (criteria.value !== undefined) {
  19886. criteria.value.sort(function (a, b) {
  19887. if (!isNaN(+a)) {
  19888. a = +a;
  19889. b = +b;
  19890. }
  19891. if (a < b) {
  19892. return -1;
  19893. }
  19894. else if (b < a) {
  19895. return 1;
  19896. }
  19897. else {
  19898. return 0;
  19899. }
  19900. });
  19901. criteria.value1 = criteria.value[0];
  19902. criteria.value2 = criteria.value[1];
  19903. }
  19904. }
  19905. else {
  19906. for (var i = 0; i < criteria.criteria.length; i++) {
  19907. criteria.criteria[i] = this._collapseArray(criteria.criteria[i]);
  19908. }
  19909. }
  19910. return criteria;
  19911. };
  19912. /**
  19913. * Updates the title of the SearchBuilder
  19914. *
  19915. * @param count the number of filters in the SearchBuilder
  19916. */
  19917. SearchBuilder.prototype._updateTitle = function (count) {
  19918. this.dom.title.html(this.s.dt.i18n('searchBuilder.title', this.c.i18n.title, count));
  19919. };
  19920. /**
  19921. * Builds all of the dom elements together
  19922. */
  19923. SearchBuilder.prototype._build = function () {
  19924. var _this = this;
  19925. // Empty and setup the container
  19926. this.dom.clearAll.remove();
  19927. this.dom.container.empty();
  19928. var count = this.s.topGroup.count();
  19929. this._updateTitle(count);
  19930. this.dom.titleRow.append(this.dom.title);
  19931. this.dom.container.append(this.dom.titleRow);
  19932. this.dom.topGroup = this.s.topGroup.getNode();
  19933. this.dom.container.append(this.dom.topGroup);
  19934. this._setRedrawListener();
  19935. var tableNode = this.s.dt.table(0).node();
  19936. if (!$$1.fn.dataTable.ext.search.includes(this.s.search)) {
  19937. // Custom search function for SearchBuilder
  19938. this.s.search = function (settings, searchData, dataIndex) {
  19939. if (settings.nTable !== tableNode) {
  19940. return true;
  19941. }
  19942. return _this.s.topGroup.search(searchData, dataIndex);
  19943. };
  19944. // Add SearchBuilder search function to the dataTables search array
  19945. $$1.fn.dataTable.ext.search.push(this.s.search);
  19946. }
  19947. this.s.dt.on('destroy.dtsb', function () {
  19948. _this.dom.container.remove();
  19949. _this.dom.clearAll.remove();
  19950. var searchIdx = $$1.fn.dataTable.ext.search.indexOf(_this.s.search);
  19951. while (searchIdx !== -1) {
  19952. $$1.fn.dataTable.ext.search.splice(searchIdx, 1);
  19953. searchIdx = $$1.fn.dataTable.ext.search.indexOf(_this.s.search);
  19954. }
  19955. _this.s.dt.off('.dtsb');
  19956. $$1(_this.s.dt.table().node()).off('.dtsb');
  19957. });
  19958. };
  19959. /**
  19960. * Checks if the clearAll button should be added or not
  19961. */
  19962. SearchBuilder.prototype._checkClear = function () {
  19963. if (this.s.topGroup.s.criteria.length > 0) {
  19964. this.dom.clearAll.insertAfter(this.dom.title);
  19965. this._setClearListener();
  19966. }
  19967. else {
  19968. this.dom.clearAll.remove();
  19969. }
  19970. };
  19971. /**
  19972. * Update the count in the title/button
  19973. *
  19974. * @param count Number of filters applied
  19975. */
  19976. SearchBuilder.prototype._filterChanged = function (count) {
  19977. var fn = this.c.filterChanged;
  19978. if (typeof fn === 'function') {
  19979. fn(count, this.s.dt.i18n('searchBuilder.button', this.c.i18n.button, count));
  19980. }
  19981. };
  19982. /**
  19983. * Set the listener for the clear button
  19984. */
  19985. SearchBuilder.prototype._setClearListener = function () {
  19986. var _this = this;
  19987. this.dom.clearAll.unbind('click');
  19988. this.dom.clearAll.on('click.dtsb', function () {
  19989. _this.s.topGroup = new Group(_this.s.dt, _this.c, undefined, undefined, undefined, undefined, _this.s.serverData);
  19990. _this._build();
  19991. _this.s.dt.draw();
  19992. _this.s.topGroup.setListeners();
  19993. _this.dom.clearAll.remove();
  19994. _this._setEmptyListener();
  19995. _this._filterChanged(0);
  19996. return false;
  19997. });
  19998. };
  19999. /**
  20000. * Set the listener for the Redraw event
  20001. */
  20002. SearchBuilder.prototype._setRedrawListener = function () {
  20003. var _this = this;
  20004. this.s.topGroup.dom.container.unbind('dtsb-redrawContents');
  20005. this.s.topGroup.dom.container.on('dtsb-redrawContents.dtsb', function () {
  20006. _this._checkClear();
  20007. _this.s.topGroup.redrawContents();
  20008. _this.s.topGroup.setupLogic();
  20009. _this._setEmptyListener();
  20010. var count = _this.s.topGroup.count();
  20011. _this._updateTitle(count);
  20012. _this._filterChanged(count);
  20013. // If using SSP we want to restrict the amount of server calls that take place
  20014. // and this information will already have been processed
  20015. if (!_this.s.dt.page.info().serverSide) {
  20016. _this.s.dt.draw();
  20017. }
  20018. _this.s.dt.state.save();
  20019. });
  20020. this.s.topGroup.dom.container.unbind('dtsb-redrawContents-noDraw');
  20021. this.s.topGroup.dom.container.on('dtsb-redrawContents-noDraw.dtsb', function () {
  20022. _this._checkClear();
  20023. _this.s.topGroup.s.preventRedraw = true;
  20024. _this.s.topGroup.redrawContents();
  20025. _this.s.topGroup.s.preventRedraw = false;
  20026. _this.s.topGroup.setupLogic();
  20027. _this._setEmptyListener();
  20028. var count = _this.s.topGroup.count();
  20029. _this._updateTitle(count);
  20030. _this._filterChanged(count);
  20031. });
  20032. this.s.topGroup.dom.container.unbind('dtsb-redrawLogic');
  20033. this.s.topGroup.dom.container.on('dtsb-redrawLogic.dtsb', function () {
  20034. _this.s.topGroup.redrawLogic();
  20035. var count = _this.s.topGroup.count();
  20036. _this._updateTitle(count);
  20037. _this._filterChanged(count);
  20038. });
  20039. this.s.topGroup.dom.container.unbind('dtsb-add');
  20040. this.s.topGroup.dom.container.on('dtsb-add.dtsb', function () {
  20041. var count = _this.s.topGroup.count();
  20042. _this._updateTitle(count);
  20043. _this._filterChanged(count);
  20044. _this._checkClear();
  20045. });
  20046. this.s.dt.on('postEdit.dtsb postCreate.dtsb postRemove.dtsb', function () {
  20047. _this.s.topGroup.redrawContents();
  20048. });
  20049. this.s.topGroup.dom.container.unbind('dtsb-clearContents');
  20050. this.s.topGroup.dom.container.on('dtsb-clearContents.dtsb', function () {
  20051. _this._setUp(false);
  20052. _this._filterChanged(0);
  20053. _this.s.dt.draw();
  20054. });
  20055. };
  20056. /**
  20057. * Sets listeners to check whether clearAll should be added or removed
  20058. */
  20059. SearchBuilder.prototype._setEmptyListener = function () {
  20060. var _this = this;
  20061. this.s.topGroup.dom.add.on('click.dtsb', function () {
  20062. _this._checkClear();
  20063. });
  20064. this.s.topGroup.dom.container.on('dtsb-destroy.dtsb', function () {
  20065. _this.dom.clearAll.remove();
  20066. });
  20067. };
  20068. SearchBuilder.version = '1.7.1';
  20069. SearchBuilder.classes = {
  20070. button: 'dtsb-button',
  20071. clearAll: 'dtsb-clearAll',
  20072. container: 'dtsb-searchBuilder',
  20073. inputButton: 'dtsb-iptbtn',
  20074. title: 'dtsb-title',
  20075. titleRow: 'dtsb-titleRow'
  20076. };
  20077. SearchBuilder.defaults = {
  20078. columns: true,
  20079. conditions: {
  20080. 'date': Criteria.dateConditions,
  20081. 'html': Criteria.stringConditions,
  20082. 'html-num': Criteria.numConditions,
  20083. 'html-num-fmt': Criteria.numFmtConditions,
  20084. 'luxon': Criteria.luxonDateConditions,
  20085. 'moment': Criteria.momentDateConditions,
  20086. 'num': Criteria.numConditions,
  20087. 'num-fmt': Criteria.numFmtConditions,
  20088. 'string': Criteria.stringConditions
  20089. },
  20090. depthLimit: false,
  20091. enterSearch: false,
  20092. filterChanged: undefined,
  20093. greyscale: false,
  20094. liveSearch: true,
  20095. i18n: {
  20096. add: 'Add Condition',
  20097. button: {
  20098. 0: 'Search Builder',
  20099. _: 'Search Builder (%d)'
  20100. },
  20101. clearAll: 'Clear All',
  20102. condition: 'Condition',
  20103. conditions: {
  20104. array: {
  20105. contains: 'Contains',
  20106. empty: 'Empty',
  20107. equals: 'Equals',
  20108. not: 'Not',
  20109. notEmpty: 'Not Empty',
  20110. without: 'Without'
  20111. },
  20112. date: {
  20113. after: 'After',
  20114. before: 'Before',
  20115. between: 'Between',
  20116. empty: 'Empty',
  20117. equals: 'Equals',
  20118. not: 'Not',
  20119. notBetween: 'Not Between',
  20120. notEmpty: 'Not Empty'
  20121. },
  20122. // eslint-disable-next-line id-blacklist
  20123. number: {
  20124. between: 'Between',
  20125. empty: 'Empty',
  20126. equals: 'Equals',
  20127. gt: 'Greater Than',
  20128. gte: 'Greater Than Equal To',
  20129. lt: 'Less Than',
  20130. lte: 'Less Than Equal To',
  20131. not: 'Not',
  20132. notBetween: 'Not Between',
  20133. notEmpty: 'Not Empty'
  20134. },
  20135. // eslint-disable-next-line id-blacklist
  20136. string: {
  20137. contains: 'Contains',
  20138. empty: 'Empty',
  20139. endsWith: 'Ends With',
  20140. equals: 'Equals',
  20141. not: 'Not',
  20142. notContains: 'Does Not Contain',
  20143. notEmpty: 'Not Empty',
  20144. notEndsWith: 'Does Not End With',
  20145. notStartsWith: 'Does Not Start With',
  20146. startsWith: 'Starts With'
  20147. }
  20148. },
  20149. data: 'Data',
  20150. "delete": '&times',
  20151. deleteTitle: 'Delete filtering rule',
  20152. left: '<',
  20153. leftTitle: 'Outdent criteria',
  20154. logicAnd: 'And',
  20155. logicOr: 'Or',
  20156. right: '>',
  20157. rightTitle: 'Indent criteria',
  20158. search: 'Search',
  20159. title: {
  20160. 0: 'Custom Search Builder',
  20161. _: 'Custom Search Builder (%d)'
  20162. },
  20163. value: 'Value',
  20164. valueJoiner: 'and'
  20165. },
  20166. logic: 'AND',
  20167. orthogonal: {
  20168. display: 'display',
  20169. search: 'filter'
  20170. },
  20171. preDefined: false
  20172. };
  20173. return SearchBuilder;
  20174. }());
  20175. /*! SearchBuilder 1.7.1
  20176. * ©SpryMedia Ltd - datatables.net/license/mit
  20177. */
  20178. setJQuery($);
  20179. setJQuery$1($);
  20180. setJQuery$2($);
  20181. var dataTable = $.fn.dataTable;
  20182. // eslint-disable-next-line no-extra-parens
  20183. DataTable.SearchBuilder = SearchBuilder;
  20184. // eslint-disable-next-line no-extra-parens
  20185. dataTable.SearchBuilder = SearchBuilder;
  20186. // eslint-disable-next-line no-extra-parens
  20187. DataTable.Group = Group;
  20188. // eslint-disable-next-line no-extra-parens
  20189. dataTable.Group = Group;
  20190. // eslint-disable-next-line no-extra-parens
  20191. DataTable.Criteria = Criteria;
  20192. // eslint-disable-next-line no-extra-parens
  20193. dataTable.Criteria = Criteria;
  20194. // eslint-disable-next-line no-extra-parens
  20195. var apiRegister = DataTable.Api.register;
  20196. // Set up object for plugins
  20197. DataTable.ext.searchBuilder = {
  20198. conditions: {}
  20199. };
  20200. DataTable.ext.buttons.searchBuilder = {
  20201. action: function (e, dt, node, config) {
  20202. this.popover(config._searchBuilder.getNode(), {
  20203. align: 'container',
  20204. span: 'container'
  20205. });
  20206. var topGroup = config._searchBuilder.s.topGroup;
  20207. // Need to redraw the contents to calculate the correct positions for the elements
  20208. if (topGroup !== undefined) {
  20209. topGroup.dom.container.trigger('dtsb-redrawContents-noDraw');
  20210. }
  20211. if (topGroup.s.criteria.length === 0) {
  20212. $('.' + $.fn.dataTable.Group.classes.add.replace(/ /g, '.')).click();
  20213. }
  20214. },
  20215. config: {},
  20216. init: function (dt, node, config) {
  20217. var sb = new DataTable.SearchBuilder(dt, $.extend({
  20218. filterChanged: function (count, text) {
  20219. dt.button(node).text(text);
  20220. }
  20221. }, config.config));
  20222. dt.button(node).text(config.text || dt.i18n('searchBuilder.button', sb.c.i18n.button, 0));
  20223. config._searchBuilder = sb;
  20224. },
  20225. text: null
  20226. };
  20227. apiRegister('searchBuilder.getDetails()', function (deFormatDates) {
  20228. if (deFormatDates === void 0) { deFormatDates = false; }
  20229. var ctx = this.context[0];
  20230. // If SearchBuilder has not been initialised on this instance then return
  20231. return ctx._searchBuilder ?
  20232. ctx._searchBuilder.getDetails(deFormatDates) :
  20233. null;
  20234. });
  20235. apiRegister('searchBuilder.rebuild()', function (details) {
  20236. var ctx = this.context[0];
  20237. // If SearchBuilder has not been initialised on this instance then return
  20238. if (ctx._searchBuilder === undefined) {
  20239. return null;
  20240. }
  20241. ctx._searchBuilder.rebuild(details);
  20242. return this;
  20243. });
  20244. apiRegister('searchBuilder.container()', function () {
  20245. var ctx = this.context[0];
  20246. // If SearchBuilder has not been initialised on this instance then return
  20247. return ctx._searchBuilder ?
  20248. ctx._searchBuilder.getNode() :
  20249. null;
  20250. });
  20251. /**
  20252. * Init function for SearchBuilder
  20253. *
  20254. * @param settings the settings to be applied
  20255. * @param options the options for SearchBuilder
  20256. * @returns JQUERY<HTMLElement> Returns the node of the SearchBuilder
  20257. */
  20258. function _init(settings, options) {
  20259. var api = new DataTable.Api(settings);
  20260. var opts = options
  20261. ? options
  20262. : api.init().searchBuilder || DataTable.defaults.searchBuilder;
  20263. var searchBuilder = new SearchBuilder(api, opts);
  20264. var node = searchBuilder.getNode();
  20265. return node;
  20266. }
  20267. // Attach a listener to the document which listens for DataTables initialisation
  20268. // events so we can automatically initialise
  20269. $(document).on('preInit.dt.dtsp', function (e, settings) {
  20270. if (e.namespace !== 'dt') {
  20271. return;
  20272. }
  20273. if (settings.oInit.searchBuilder ||
  20274. DataTable.defaults.searchBuilder) {
  20275. if (!settings._searchBuilder) {
  20276. _init(settings);
  20277. }
  20278. }
  20279. });
  20280. // DataTables `dom` feature option
  20281. DataTable.ext.feature.push({
  20282. cFeature: 'Q',
  20283. fnInit: _init
  20284. });
  20285. // DataTables 2 layout feature
  20286. if (DataTable.feature) {
  20287. DataTable.feature.register('searchBuilder', _init);
  20288. }
  20289. })();
  20290. return DataTable;
  20291. }));
  20292. /*! Bootstrap 5 ui integration for DataTables' SearchBuilder
  20293. * © SpryMedia Ltd - datatables.net/license
  20294. */
  20295. (function( factory ){
  20296. if ( typeof define === 'function' && define.amd ) {
  20297. // AMD
  20298. define( ['jquery', 'datatables.net-bs5', 'datatables.net-searchbuilder'], function ( $ ) {
  20299. return factory( $, window, document );
  20300. } );
  20301. }
  20302. else if ( typeof exports === 'object' ) {
  20303. // CommonJS
  20304. var jq = require('jquery');
  20305. var cjsRequires = function (root, $) {
  20306. if ( ! $.fn.dataTable ) {
  20307. require('datatables.net-bs5')(root, $);
  20308. }
  20309. if ( ! $.fn.dataTable.SearchBuilder ) {
  20310. require('datatables.net-searchbuilder')(root, $);
  20311. }
  20312. };
  20313. if (typeof window === 'undefined') {
  20314. module.exports = function (root, $) {
  20315. if ( ! root ) {
  20316. // CommonJS environments without a window global must pass a
  20317. // root. This will give an error otherwise
  20318. root = window;
  20319. }
  20320. if ( ! $ ) {
  20321. $ = jq( root );
  20322. }
  20323. cjsRequires( root, $ );
  20324. return factory( $, root, root.document );
  20325. };
  20326. }
  20327. else {
  20328. cjsRequires( window, jq );
  20329. module.exports = factory( jq, window, window.document );
  20330. }
  20331. }
  20332. else {
  20333. // Browser
  20334. factory( jQuery, window, document );
  20335. }
  20336. }(function( $, window, document ) {
  20337. 'use strict';
  20338. var DataTable = $.fn.dataTable;
  20339. $.extend(true, DataTable.SearchBuilder.classes, {
  20340. clearAll: 'btn btn-secondary dtsb-clearAll'
  20341. });
  20342. $.extend(true, DataTable.Group.classes, {
  20343. add: 'btn btn-secondary dtsb-add',
  20344. clearGroup: 'btn btn-secondary dtsb-clearGroup',
  20345. logic: 'btn btn-secondary dtsb-logic',
  20346. search: 'btn btn-secondary dtsb-search'
  20347. });
  20348. $.extend(true, DataTable.Criteria.classes, {
  20349. condition: 'form-select dtsb-condition',
  20350. data: 'dtsb-data form-select',
  20351. "delete": 'btn btn-secondary dtsb-delete',
  20352. input: 'form-control dtsb-input',
  20353. left: 'btn btn-secondary dtsb-left',
  20354. right: 'btn btn-secondary dtsb-right',
  20355. select: 'form-select',
  20356. value: 'dtsb-value'
  20357. });
  20358. return DataTable;
  20359. }));
  20360. /*! SearchPanes 2.3.1
  20361. * © SpryMedia Ltd - datatables.net/license
  20362. */
  20363. (function( factory ){
  20364. if ( typeof define === 'function' && define.amd ) {
  20365. // AMD
  20366. define( ['jquery', 'datatables.net'], function ( $ ) {
  20367. return factory( $, window, document );
  20368. } );
  20369. }
  20370. else if ( typeof exports === 'object' ) {
  20371. // CommonJS
  20372. var jq = require('jquery');
  20373. var cjsRequires = function (root, $) {
  20374. if ( ! $.fn.dataTable ) {
  20375. require('datatables.net')(root, $);
  20376. }
  20377. };
  20378. if (typeof window === 'undefined') {
  20379. module.exports = function (root, $) {
  20380. if ( ! root ) {
  20381. // CommonJS environments without a window global must pass a
  20382. // root. This will give an error otherwise
  20383. root = window;
  20384. }
  20385. if ( ! $ ) {
  20386. $ = jq( root );
  20387. }
  20388. cjsRequires( root, $ );
  20389. return factory( $, root, root.document );
  20390. };
  20391. }
  20392. else {
  20393. cjsRequires( window, jq );
  20394. module.exports = factory( jq, window, window.document );
  20395. }
  20396. }
  20397. else {
  20398. // Browser
  20399. factory( jQuery, window, document );
  20400. }
  20401. }(function( $, window, document ) {
  20402. 'use strict';
  20403. var DataTable = $.fn.dataTable;
  20404. (function () {
  20405. 'use strict';
  20406. var $$5;
  20407. var dataTable$2;
  20408. function setJQuery$4(jq) {
  20409. $$5 = jq;
  20410. dataTable$2 = jq.fn.dataTable;
  20411. }
  20412. var SearchPane = /** @class */ (function () {
  20413. /**
  20414. * Creates the panes, sets up the search function
  20415. *
  20416. * @param paneSettings The settings for the searchPanes
  20417. * @param opts The options for the default features
  20418. * @param index the index of the column for this pane
  20419. * @param panesContainer The overall container for SearchPanes that this pane will be attached to
  20420. * @param panes The custom pane settings if this is a custom pane
  20421. * @returns {object} the pane that has been created, including the table and the index of the pane
  20422. */
  20423. function SearchPane(paneSettings, opts, index, panesContainer, panes) {
  20424. var _this = this;
  20425. if (panes === void 0) { panes = null; }
  20426. // Check that the required version of DataTables is included
  20427. if (!dataTable$2 || !dataTable$2.versionCheck || !dataTable$2.versionCheck('1.10.0')) {
  20428. throw new Error('SearchPane requires DataTables 1.10 or newer');
  20429. }
  20430. // Check that Select is included
  20431. // eslint-disable-next-line no-extra-parens
  20432. if (!dataTable$2.select) {
  20433. throw new Error('SearchPane requires Select');
  20434. }
  20435. var table = new dataTable$2.Api(paneSettings);
  20436. this.classes = $$5.extend(true, {}, SearchPane.classes);
  20437. // Get options from user
  20438. this.c = $$5.extend(true, {}, SearchPane.defaults, opts, panes);
  20439. if (opts && opts.hideCount && opts.viewCount === undefined) {
  20440. this.c.viewCount = !this.c.hideCount;
  20441. }
  20442. var rowLength = table.columns().eq(0).toArray().length;
  20443. this.s = {
  20444. colExists: index < rowLength,
  20445. colOpts: undefined,
  20446. customPaneSettings: panes,
  20447. displayed: false,
  20448. dt: table,
  20449. dtPane: undefined,
  20450. firstSet: true,
  20451. index: index,
  20452. indexes: [],
  20453. listSet: false,
  20454. name: undefined,
  20455. rowData: {
  20456. arrayFilter: [],
  20457. arrayOriginal: [],
  20458. bins: {},
  20459. binsOriginal: {},
  20460. filterMap: new Map(),
  20461. totalOptions: 0
  20462. },
  20463. scrollTop: 0,
  20464. searchFunction: undefined,
  20465. selections: [],
  20466. serverSelect: [],
  20467. serverSelecting: false,
  20468. tableLength: null,
  20469. updating: false
  20470. };
  20471. this.s.colOpts = this.s.colExists ? this._getOptions() : this._getBonusOptions();
  20472. this.dom = {
  20473. buttonGroup: $$5('<div/>').addClass(this.classes.buttonGroup),
  20474. clear: $$5('<button type="button">&#215;</button>')
  20475. .attr('disabled', 'true')
  20476. .addClass(this.classes.disabledButton)
  20477. .addClass(this.classes.paneButton)
  20478. .addClass(this.classes.clearButton)
  20479. .html(this.s.dt.i18n('searchPanes.clearPane', this.c.i18n.clearPane)),
  20480. collapseButton: $$5('<button type="button"><span class="' + this.classes.caret + '">&#x5e;</span></button>')
  20481. .addClass(this.classes.paneButton)
  20482. .addClass(this.classes.collapseButton),
  20483. container: $$5('<div/>')
  20484. .addClass(this.classes.container)
  20485. .addClass(this.s.colOpts.className)
  20486. .addClass(this.classes.layout +
  20487. (parseInt(this.c.layout.split('-')[1], 10) < 10 ?
  20488. this.c.layout :
  20489. this.c.layout.split('-')[0] + '-9'))
  20490. .addClass(this.s.customPaneSettings && this.s.customPaneSettings.className
  20491. ? this.s.customPaneSettings.className
  20492. : ''),
  20493. countButton: $$5('<button type="button"><span></span></button>')
  20494. .addClass(this.classes.paneButton)
  20495. .addClass(this.classes.countButton),
  20496. dtP: $$5('<table width="100%"><thead><tr><th></th><th></th></tr></thead></table>'),
  20497. lower: $$5('<div/>').addClass(this.classes.subRow2).addClass(this.classes.narrowButton),
  20498. nameButton: $$5('<button type="button"><span></span></button>')
  20499. .addClass(this.classes.paneButton)
  20500. .addClass(this.classes.nameButton),
  20501. panesContainer: $$5(panesContainer),
  20502. searchBox: $$5('<input/>').addClass(this.classes.paneInputButton).addClass(this.classes.search),
  20503. searchButton: $$5('<button type="button"><span></span></button>')
  20504. .addClass(this.classes.searchIcon)
  20505. .addClass(this.classes.paneButton),
  20506. searchCont: $$5('<div/>').addClass(this.classes.searchCont),
  20507. searchLabelCont: $$5('<div/>').addClass(this.classes.searchLabelCont),
  20508. topRow: $$5('<div/>').addClass(this.classes.topRow),
  20509. upper: $$5('<div/>').addClass(this.classes.subRow1).addClass(this.classes.narrowSearch)
  20510. };
  20511. var title = '';
  20512. if (this.s.colExists) {
  20513. title = $$5(this.s.dt.column(this.s.index).header()).text();
  20514. this.dom.dtP.find('th').eq(0).text(title);
  20515. }
  20516. else {
  20517. title = this.s.customPaneSettings.header || 'Custom Pane';
  20518. this.dom.dtP.find('th').eq(0).html(title);
  20519. }
  20520. // Set the value of name incase ordering is desired
  20521. if (this.s.colOpts.name) {
  20522. this.s.name = this.s.colOpts.name;
  20523. }
  20524. else if (this.s.customPaneSettings && this.s.customPaneSettings.name) {
  20525. this.s.name = this.s.customPaneSettings.name;
  20526. }
  20527. else {
  20528. this.s.name = title;
  20529. }
  20530. var tableNode = this.s.dt.table(0).node();
  20531. // Custom search function for table
  20532. this.s.searchFunction = function (settings, searchData, dataIndex) {
  20533. // If no data has been selected then show all
  20534. if (_this.s.selections.length === 0) {
  20535. return true;
  20536. }
  20537. if (settings.nTable !== tableNode) {
  20538. return true;
  20539. }
  20540. var filter = null;
  20541. if (_this.s.colExists) {
  20542. // Get the current filtered data
  20543. filter = searchData[_this.s.index];
  20544. if (_this.s.colOpts.orthogonal.filter !== 'filter') {
  20545. // get the filter value from the map
  20546. filter = _this.s.rowData.filterMap.get(dataIndex);
  20547. if (filter instanceof $$5.fn.dataTable.Api) {
  20548. // eslint-disable-next-line no-extra-parens
  20549. filter = filter.toArray();
  20550. }
  20551. }
  20552. }
  20553. return _this._search(filter, dataIndex);
  20554. };
  20555. $$5.fn.dataTable.ext.search.push(this.s.searchFunction);
  20556. // If the clear button for this pane is clicked clear the selections
  20557. if (this.c.clear) {
  20558. this.dom.clear.on('click.dtsp', function () {
  20559. var searches = _this.dom.container.find('.' + _this.classes.search.replace(/\s+/g, '.'));
  20560. searches.each(function () {
  20561. $$5(this).val('').trigger('input');
  20562. });
  20563. _this.clearPane();
  20564. });
  20565. }
  20566. // Sometimes the top row of the panes containing the search box and ordering buttons appears
  20567. // weird if the width of the panes is lower than expected, this fixes the design.
  20568. // Equally this may occur when the table is resized.
  20569. this.s.dt.on('draw.dtsp', function () { return _this.adjustTopRow(); });
  20570. this.s.dt.on('buttons-action.dtsp', function () { return _this.adjustTopRow(); });
  20571. // When column-reorder is present and the columns are moved, it is necessary to
  20572. // reassign all of the panes indexes to the new index of the column.
  20573. this.s.dt.on('column-reorder.dtsp', function (e, settings, details) {
  20574. _this.s.index = details.mapping[_this.s.index];
  20575. });
  20576. return this;
  20577. }
  20578. /**
  20579. * Adds a row to the panes table
  20580. *
  20581. * @param display the value to be displayed to the user
  20582. * @param filter the value to be filtered on when searchpanes is implemented
  20583. * @param shown the number of rows in the table that are currently visible matching this criteria
  20584. * @param total the total number of rows in the table that match this criteria
  20585. * @param sort the value to be sorted in the pane table
  20586. * @param type the value of which the type is to be derived from
  20587. */
  20588. SearchPane.prototype.addRow = function (display, filter, sort, type, className, total, shown) {
  20589. if (!total) {
  20590. total = this.s.rowData.bins[filter] ?
  20591. this.s.rowData.bins[filter] :
  20592. 0;
  20593. }
  20594. if (!shown) {
  20595. shown = this._getShown(filter);
  20596. }
  20597. var index;
  20598. for (var _i = 0, _a = this.s.indexes; _i < _a.length; _i++) {
  20599. var entry = _a[_i];
  20600. if (entry.filter === filter) {
  20601. index = entry.index;
  20602. }
  20603. }
  20604. if (index === undefined) {
  20605. index = this.s.indexes.length;
  20606. this.s.indexes.push({ filter: filter, index: index });
  20607. }
  20608. return this.s.dtPane.row.add({
  20609. className: className,
  20610. display: display !== '' ?
  20611. display :
  20612. this.emptyMessage(),
  20613. filter: filter,
  20614. index: index,
  20615. shown: shown,
  20616. sort: sort,
  20617. total: total,
  20618. type: type
  20619. });
  20620. };
  20621. /**
  20622. * Adjusts the layout of the top row when the screen is resized
  20623. */
  20624. SearchPane.prototype.adjustTopRow = function () {
  20625. var subContainers = this.dom.container.find('.' + this.classes.subRowsContainer.replace(/\s+/g, '.'));
  20626. var subRow1 = this.dom.container.find('.' + this.classes.subRow1.replace(/\s+/g, '.'));
  20627. var subRow2 = this.dom.container.find('.' + this.classes.subRow2.replace(/\s+/g, '.'));
  20628. var topRow = this.dom.container.find('.' + this.classes.topRow.replace(/\s+/g, '.'));
  20629. // If the width is 0 then it is safe to assume that the pane has not yet been displayed.
  20630. // Even if it has, if the width is 0 it won't make a difference if it has the narrow class or not
  20631. if (($$5(subContainers[0]).width() < 252 || $$5(topRow[0]).width() < 252) && $$5(subContainers[0]).width() !== 0) {
  20632. $$5(subContainers[0]).addClass(this.classes.narrow);
  20633. $$5(subRow1[0]).addClass(this.classes.narrowSub).removeClass(this.classes.narrowSearch);
  20634. $$5(subRow2[0]).addClass(this.classes.narrowSub).removeClass(this.classes.narrowButton);
  20635. }
  20636. else {
  20637. $$5(subContainers[0]).removeClass(this.classes.narrow);
  20638. $$5(subRow1[0]).removeClass(this.classes.narrowSub).addClass(this.classes.narrowSearch);
  20639. $$5(subRow2[0]).removeClass(this.classes.narrowSub).addClass(this.classes.narrowButton);
  20640. }
  20641. };
  20642. /**
  20643. * In the case of a rebuild there is potential for new data to have been included or removed
  20644. * so all of the rowData must be reset as a precaution.
  20645. */
  20646. SearchPane.prototype.clearData = function () {
  20647. this.s.rowData = {
  20648. arrayFilter: [],
  20649. arrayOriginal: [],
  20650. bins: {},
  20651. binsOriginal: {},
  20652. filterMap: new Map(),
  20653. totalOptions: 0
  20654. };
  20655. };
  20656. /**
  20657. * Clear the selections in the pane
  20658. */
  20659. SearchPane.prototype.clearPane = function () {
  20660. // Deselect all rows which are selected and update the table and filter count.
  20661. this.s.dtPane.rows({ selected: true }).deselect();
  20662. this.updateTable();
  20663. return this;
  20664. };
  20665. /**
  20666. * Collapses the pane so that only the header is displayed
  20667. */
  20668. SearchPane.prototype.collapse = function () {
  20669. var _this = this;
  20670. if (!this.s.displayed ||
  20671. (
  20672. // If collapsing is disabled globally, and not enabled specifically for this column
  20673. !this.c.collapse && this.s.colOpts.collapse !== true ||
  20674. // OR, collapsing could be enabled globally and this column specifically
  20675. // is not to be collapsed.
  20676. // We can't just take !this.s.colOpts.collapse here as if it is undefined
  20677. // then the global should be used
  20678. this.s.colOpts.collapse === false)) {
  20679. return;
  20680. }
  20681. $$5(this.s.dtPane.table().container()).addClass(this.classes.hidden);
  20682. this.dom.topRow.addClass(this.classes.bordered);
  20683. this.dom.nameButton.addClass(this.classes.disabledButton);
  20684. this.dom.countButton.addClass(this.classes.disabledButton);
  20685. this.dom.searchButton.addClass(this.classes.disabledButton);
  20686. this.dom.collapseButton.addClass(this.classes.rotated);
  20687. this.dom.topRow.one('click.dtsp', function () { return _this.show(); });
  20688. this.dom.topRow.trigger('collapse.dtsps');
  20689. };
  20690. /**
  20691. * Strips all of the SearchPanes elements from the document and turns all of the listeners for the buttons off
  20692. */
  20693. SearchPane.prototype.destroy = function () {
  20694. if (this.s.dtPane) {
  20695. this.s.dtPane.off('.dtsp');
  20696. }
  20697. this.s.dt.off('.dtsp');
  20698. this.dom.clear.off('.dtsp');
  20699. this.dom.nameButton.off('.dtsp');
  20700. this.dom.countButton.off('.dtsp');
  20701. this.dom.searchButton.off('.dtsp');
  20702. this.dom.collapseButton.off('.dtsp');
  20703. $$5(this.s.dt.table().node()).off('.dtsp');
  20704. this.dom.container.detach();
  20705. var searchIdx = $$5.fn.dataTable.ext.search.indexOf(this.s.searchFunction);
  20706. while (searchIdx !== -1) {
  20707. $$5.fn.dataTable.ext.search.splice(searchIdx, 1);
  20708. searchIdx = $$5.fn.dataTable.ext.search.indexOf(this.s.searchFunction);
  20709. }
  20710. // If the datatables have been defined for the panes then also destroy these
  20711. if (this.s.dtPane) {
  20712. this.s.dtPane.destroy();
  20713. }
  20714. this.s.listSet = false;
  20715. };
  20716. /**
  20717. * Getting the legacy message is a little complex due a legacy parameter
  20718. */
  20719. SearchPane.prototype.emptyMessage = function () {
  20720. var def = this.c.i18n.emptyMessage;
  20721. // Legacy parameter support
  20722. if (this.c.emptyMessage) {
  20723. def = this.c.emptyMessage;
  20724. }
  20725. // Override per column
  20726. if (this.s.colOpts.emptyMessage !== false && this.s.colOpts.emptyMessage !== null) {
  20727. def = this.s.colOpts.emptyMessage;
  20728. }
  20729. return this.s.dt.i18n('searchPanes.emptyMessage', def);
  20730. };
  20731. /**
  20732. * Updates the number of filters that have been applied in the title
  20733. */
  20734. SearchPane.prototype.getPaneCount = function () {
  20735. return this.s.dtPane ?
  20736. this.s.dtPane.rows({ selected: true }).data().toArray().length :
  20737. 0;
  20738. };
  20739. /**
  20740. * Rebuilds the panes from the start having deleted the old ones
  20741. *
  20742. * @param? dataIn data to be used in buildPane
  20743. * @param? maintainSelection Whether the current selections are to be maintained over rebuild
  20744. */
  20745. SearchPane.prototype.rebuildPane = function (dataIn, maintainSelection) {
  20746. if (dataIn === void 0) { dataIn = null; }
  20747. if (maintainSelection === void 0) { maintainSelection = false; }
  20748. this.clearData();
  20749. var selectedRows = [];
  20750. this.s.serverSelect = [];
  20751. var prevEl = null;
  20752. // When rebuilding strip all of the HTML Elements out of the container and start from scratch
  20753. if (this.s.dtPane) {
  20754. if (maintainSelection) {
  20755. if (!this.s.dt.page.info().serverSide) {
  20756. selectedRows = this.s.dtPane.rows({ selected: true }).data().toArray();
  20757. }
  20758. else {
  20759. this.s.serverSelect = this.s.dtPane.rows({ selected: true }).data().toArray();
  20760. }
  20761. }
  20762. this.s.dtPane.clear().destroy();
  20763. prevEl = this.dom.container.prev();
  20764. this.destroy();
  20765. this.s.dtPane = undefined;
  20766. $$5.fn.dataTable.ext.search.push(this.s.searchFunction);
  20767. }
  20768. this.dom.container.removeClass(this.classes.hidden);
  20769. this.s.displayed = false;
  20770. this._buildPane(!this.s.dt.page.info().serverSide ?
  20771. selectedRows :
  20772. this.s.serverSelect, dataIn, prevEl);
  20773. return this;
  20774. };
  20775. /**
  20776. * Resizes the pane based on the layout that is passed in
  20777. *
  20778. * @param layout the layout to be applied to this pane
  20779. */
  20780. SearchPane.prototype.resize = function (layout) {
  20781. this.c.layout = layout;
  20782. this.dom.container
  20783. .removeClass()
  20784. .addClass(this.classes.show)
  20785. .addClass(this.classes.container)
  20786. .addClass(this.s.colOpts.className)
  20787. .addClass(this.classes.layout +
  20788. (parseInt(layout.split('-')[1], 10) < 10 ?
  20789. layout :
  20790. layout.split('-')[0] + '-9'))
  20791. .addClass(this.s.customPaneSettings !== null && this.s.customPaneSettings.className
  20792. ? this.s.customPaneSettings.className
  20793. : '');
  20794. this.adjustTopRow();
  20795. };
  20796. /**
  20797. * Sets the listeners for the pane.
  20798. *
  20799. * Having it in it's own function makes it easier to only set them once
  20800. */
  20801. SearchPane.prototype.setListeners = function () {
  20802. var _this = this;
  20803. if (!this.s.dtPane) {
  20804. return;
  20805. }
  20806. // When an item is selected on the pane, add these to the array which holds selected items.
  20807. // Custom search will perform.
  20808. this.s.dtPane.off('select.dtsp').on('select.dtsp', function () {
  20809. clearTimeout(_this.s.deselectTimeout);
  20810. _this._updateSelection(!_this.s.updating);
  20811. _this.dom.clear.removeClass(_this.classes.disabledButton).removeAttr('disabled');
  20812. });
  20813. // When an item is deselected on the pane, re add the currently selected items to the array
  20814. // which holds selected items. Custom search will be performed.
  20815. this.s.dtPane.off('deselect.dtsp').on('deselect.dtsp', function () {
  20816. _this.s.deselectTimeout = setTimeout(function () {
  20817. _this._updateSelection(true);
  20818. if (_this.s.dtPane.rows({ selected: true }).data().toArray().length === 0) {
  20819. _this.dom.clear.addClass(_this.classes.disabledButton).attr('disabled', 'true');
  20820. }
  20821. }, 50);
  20822. });
  20823. // If we attempty to turn off this event then it will ruin behaviour in other panes
  20824. // so need to make sure that it is only done once
  20825. if (this.s.firstSet) {
  20826. this.s.firstSet = false;
  20827. // When saving the state store all of the selected rows for preselection next time around
  20828. this.s.dt.on('stateSaveParams.dtsp', function (e, settings, data) {
  20829. // If the data being passed in is empty then state clear must have occured
  20830. // so clear the panes state as well
  20831. if ($$5.isEmptyObject(data)) {
  20832. _this.s.dtPane.state.clear();
  20833. return;
  20834. }
  20835. var bins;
  20836. var order;
  20837. var selected = [];
  20838. var collapsed;
  20839. var searchTerm;
  20840. var arrayFilter;
  20841. // Get all of the data needed for the state save from the pane
  20842. if (_this.s.dtPane) {
  20843. selected = _this.s.dtPane
  20844. .rows({ selected: true })
  20845. .data()
  20846. .map(function (item) { return item.filter !== null ? item.filter.toString() : null; })
  20847. .toArray();
  20848. searchTerm = _this.dom.searchBox.val();
  20849. order = _this.s.dtPane.order();
  20850. bins = _this.s.rowData.binsOriginal;
  20851. arrayFilter = _this.s.rowData.arrayOriginal;
  20852. collapsed = _this.dom.collapseButton.hasClass(_this.classes.rotated);
  20853. }
  20854. if (data.searchPanes === undefined) {
  20855. data.searchPanes = {};
  20856. }
  20857. if (data.searchPanes.panes === undefined) {
  20858. data.searchPanes.panes = [];
  20859. }
  20860. for (var i = 0; i < data.searchPanes.panes.length; i++) {
  20861. if (data.searchPanes.panes[i].id === _this.s.index) {
  20862. data.searchPanes.panes.splice(i, 1);
  20863. i--;
  20864. }
  20865. }
  20866. // Add the panes data to the state object
  20867. data.searchPanes.panes.push({
  20868. arrayFilter: arrayFilter,
  20869. bins: bins,
  20870. collapsed: collapsed,
  20871. id: _this.s.index,
  20872. order: order,
  20873. searchTerm: searchTerm,
  20874. selected: selected
  20875. });
  20876. });
  20877. }
  20878. this.s.dtPane.off('user-select.dtsp').on('user-select.dtsp', function (e, _dt, type, cell, originalEvent) {
  20879. originalEvent.stopPropagation();
  20880. });
  20881. this.s.dtPane.off('draw.dtsp').on('draw.dtsp', function () { return _this.adjustTopRow(); });
  20882. // When the button to order by the name of the options is clicked then
  20883. // change the ordering to whatever it isn't currently
  20884. this.dom.nameButton.off('click.dtsp').on('click.dtsp', function () {
  20885. var currentOrder = _this.s.dtPane.order()[0][1];
  20886. _this.s.dtPane.order([0, currentOrder === 'asc' ? 'desc' : 'asc']).draw();
  20887. // This state save is required so that the ordering of the panes is maintained
  20888. _this.s.dt.state.save();
  20889. });
  20890. // When the button to order by the number of entries in the column is clicked then
  20891. // change the ordering to whatever it isn't currently
  20892. this.dom.countButton.off('click.dtsp').on('click.dtsp', function () {
  20893. var currentOrder = _this.s.dtPane.order()[0][1];
  20894. _this.s.dtPane.order([1, currentOrder === 'asc' ? 'desc' : 'asc']).draw();
  20895. // This state save is required so that the ordering of the panes is maintained
  20896. _this.s.dt.state.save();
  20897. });
  20898. // When the button to order by the number of entries in the column is clicked then
  20899. // change the ordering to whatever it isn't currently
  20900. this.dom.collapseButton.off('click.dtsp').on('click.dtsp', function (e) {
  20901. e.stopPropagation();
  20902. var container = $$5(_this.s.dtPane.table().container());
  20903. // Toggle the classes
  20904. container.toggleClass(_this.classes.hidden);
  20905. _this.dom.topRow.toggleClass(_this.classes.bordered);
  20906. _this.dom.nameButton.toggleClass(_this.classes.disabledButton);
  20907. _this.dom.countButton.toggleClass(_this.classes.disabledButton);
  20908. _this.dom.searchButton.toggleClass(_this.classes.disabledButton);
  20909. _this.dom.collapseButton.toggleClass(_this.classes.rotated);
  20910. if (container.hasClass(_this.classes.hidden)) {
  20911. _this.dom.topRow.on('click.dtsp', function () { return _this.dom.collapseButton.click(); });
  20912. }
  20913. else {
  20914. _this.dom.topRow.off('click.dtsp');
  20915. }
  20916. _this.s.dt.state.save();
  20917. _this.dom.topRow.trigger('collapse.dtsps');
  20918. });
  20919. // When the clear button is clicked reset the pane
  20920. this.dom.clear.off('click.dtsp').on('click.dtsp', function () {
  20921. var searches = _this.dom.container.find('.' + _this.classes.search.replace(/ /g, '.'));
  20922. searches.each(function () {
  20923. // set the value of the search box to be an empty string and then search on that, effectively reseting
  20924. $$5(this).val('').trigger('input');
  20925. });
  20926. _this.clearPane();
  20927. });
  20928. // When the search button is clicked then draw focus to the search box
  20929. this.dom.searchButton.off('click.dtsp').on('click.dtsp', function () { return _this.dom.searchBox.focus(); });
  20930. // When a character is inputted into the searchbox search the pane for matching values.
  20931. // Doing it this way means that no button has to be clicked to trigger a search, it is done asynchronously
  20932. this.dom.searchBox.off('click.dtsp').on('input.dtsp', function () {
  20933. var searchval = _this.dom.searchBox.val();
  20934. _this.s.dtPane.search(searchval).draw();
  20935. if (typeof searchval === 'string' &&
  20936. (searchval.length > 0 ||
  20937. searchval.length === 0 && _this.s.dtPane.rows({ selected: true }).data().toArray().length > 0)) {
  20938. _this.dom.clear.removeClass(_this.classes.disabledButton).removeAttr('disabled');
  20939. }
  20940. else {
  20941. _this.dom.clear.addClass(_this.classes.disabledButton).attr('disabled', 'true');
  20942. }
  20943. // This state save is required so that the searching on the panes is maintained
  20944. _this.s.dt.state.save();
  20945. });
  20946. this.s.dtPane.select.style(this.s.colOpts.dtOpts && this.s.colOpts.dtOpts.select && this.s.colOpts.dtOpts.select.style
  20947. ? this.s.colOpts.dtOpts.select.style
  20948. : this.c.dtOpts && this.c.dtOpts.select && this.c.dtOpts.select.style
  20949. ? this.c.dtOpts.select.style
  20950. : 'os');
  20951. };
  20952. /**
  20953. * Populates the SearchPane based off of the data that has been recieved from the server
  20954. *
  20955. * This method is overriden by SearchPaneST
  20956. *
  20957. * @param dataIn The data that has been sent from the server
  20958. */
  20959. SearchPane.prototype._serverPopulate = function (dataIn) {
  20960. if (dataIn.tableLength) {
  20961. this.s.tableLength = dataIn.tableLength;
  20962. this.s.rowData.totalOptions = this.s.tableLength;
  20963. }
  20964. else if (this.s.tableLength === null || this.s.dt.rows()[0].length > this.s.tableLength) {
  20965. this.s.tableLength = this.s.dt.rows()[0].length;
  20966. this.s.rowData.totalOptions = this.s.tableLength;
  20967. }
  20968. var colTitle = this.s.dt.column(this.s.index).dataSrc();
  20969. // If there is SP data for this column add it to the data array and bin
  20970. if (dataIn.searchPanes.options[colTitle]) {
  20971. for (var _i = 0, _a = dataIn.searchPanes.options[colTitle]; _i < _a.length; _i++) {
  20972. var dataPoint = _a[_i];
  20973. this.s.rowData.arrayFilter.push({
  20974. display: dataPoint.label,
  20975. filter: dataPoint.value,
  20976. sort: dataPoint.label,
  20977. type: dataPoint.label
  20978. });
  20979. this.s.rowData.bins[dataPoint.value] = dataPoint.total;
  20980. }
  20981. }
  20982. var binLength = Object.keys(this.s.rowData.bins).length;
  20983. var uniqueRatio = this._uniqueRatio(binLength, this.s.tableLength);
  20984. // Don't show the pane if there isnt enough variance in the data, or there is only 1 entry for that pane
  20985. if (this.s.displayed === false &&
  20986. ((this.s.colOpts.show === undefined && this.s.colOpts.threshold === null ?
  20987. uniqueRatio > this.c.threshold :
  20988. uniqueRatio > this.s.colOpts.threshold) ||
  20989. this.s.colOpts.show !== true && binLength <= 1)) {
  20990. this.dom.container.addClass(this.classes.hidden);
  20991. this.s.displayed = false;
  20992. return;
  20993. }
  20994. // Store the original data
  20995. this.s.rowData.arrayOriginal = this.s.rowData.arrayFilter;
  20996. this.s.rowData.binsOriginal = this.s.rowData.bins;
  20997. // Flag this pane as being displayed
  20998. this.s.displayed = true;
  20999. };
  21000. /**
  21001. * Expands the pane from the collapsed state
  21002. */
  21003. SearchPane.prototype.show = function () {
  21004. if (!this.s.displayed) {
  21005. return;
  21006. }
  21007. this.dom.topRow.removeClass(this.classes.bordered);
  21008. this.dom.nameButton.removeClass(this.classes.disabledButton);
  21009. this.dom.countButton.removeClass(this.classes.disabledButton);
  21010. this.dom.searchButton.removeClass(this.classes.disabledButton);
  21011. this.dom.collapseButton.removeClass(this.classes.rotated);
  21012. $$5(this.s.dtPane.table().container()).removeClass(this.classes.hidden);
  21013. this.dom.topRow.trigger('collapse.dtsps');
  21014. };
  21015. /**
  21016. * Finds the ratio of the number of different options in the table to the number of rows
  21017. *
  21018. * @param bins the number of different options in the table
  21019. * @param rowCount the total number of rows in the table
  21020. * @returns {number} returns the ratio
  21021. */
  21022. SearchPane.prototype._uniqueRatio = function (bins, rowCount) {
  21023. if (rowCount > 0 &&
  21024. (this.s.rowData.totalOptions > 0 && !this.s.dt.page.info().serverSide ||
  21025. this.s.dt.page.info().serverSide && this.s.tableLength > 0)) {
  21026. return bins / this.s.rowData.totalOptions;
  21027. }
  21028. return 1;
  21029. };
  21030. /**
  21031. * Updates the panes if one of the options to do so has been set to true
  21032. * rather than the filtered message when using viewTotal.
  21033. */
  21034. SearchPane.prototype.updateTable = function () {
  21035. var selectedRows = this.s.dtPane.rows({ selected: true }).data().toArray().map(function (el) { return el.filter; });
  21036. this.s.selections = selectedRows;
  21037. this._searchExtras();
  21038. };
  21039. /**
  21040. * Adds the custom options to the pane
  21041. *
  21042. * @returns {Array} Returns the array of rows which have been added to the pane
  21043. */
  21044. SearchPane.prototype._getComparisonRows = function () {
  21045. // Find the appropriate options depending on whether this is a pane for a specific column or a custom pane
  21046. var options = this.s.colOpts.options
  21047. ? this.s.colOpts.options
  21048. : this.s.customPaneSettings && this.s.customPaneSettings.options
  21049. ? this.s.customPaneSettings.options
  21050. : undefined;
  21051. if (options === undefined) {
  21052. return;
  21053. }
  21054. var allRows = this.s.dt.rows();
  21055. var tableValsTotal = allRows.data().toArray();
  21056. var rows = [];
  21057. // Clear all of the other rows from the pane, only custom options are to be displayed when they are defined
  21058. this.s.dtPane.clear();
  21059. this.s.indexes = [];
  21060. for (var _i = 0, options_1 = options; _i < options_1.length; _i++) {
  21061. var comp = options_1[_i];
  21062. // Initialise the object which is to be placed in the row
  21063. var insert = comp.label !== '' ?
  21064. comp.label :
  21065. this.emptyMessage();
  21066. var comparisonObj = {
  21067. className: comp.className,
  21068. display: insert,
  21069. filter: typeof comp.value === 'function' ? comp.value : [],
  21070. sort: comp.order !== undefined
  21071. ? comp.order
  21072. : insert,
  21073. total: 0,
  21074. type: insert
  21075. };
  21076. // If a custom function is in place
  21077. if (typeof comp.value === 'function') {
  21078. // Count the number of times the function evaluates to true for the original data in the Table
  21079. for (var i = 0; i < tableValsTotal.length; i++) {
  21080. if (comp.value.call(this.s.dt, tableValsTotal[i], allRows[0][i])) {
  21081. comparisonObj.total++;
  21082. }
  21083. }
  21084. // Update the comparisonObj
  21085. if (typeof comparisonObj.filter !== 'function') {
  21086. comparisonObj.filter.push(comp.filter);
  21087. }
  21088. }
  21089. rows.push(this.addRow(comparisonObj.display, comparisonObj.filter, comparisonObj.sort, comparisonObj.type, comparisonObj.className, comparisonObj.total));
  21090. }
  21091. return rows;
  21092. };
  21093. SearchPane.prototype._getMessage = function (row) {
  21094. return this.s.dt.i18n('searchPanes.count', this.c.i18n.count).replace(/{total}/g, row.total);
  21095. };
  21096. /**
  21097. * Overridden in SearchPaneViewTotal and SearchPaneCascade to get the number of times a specific value is shown
  21098. *
  21099. * Here it is blanked so that it takes no action
  21100. *
  21101. * @param filter The filter value
  21102. * @returns undefined
  21103. */
  21104. SearchPane.prototype._getShown = function (filter) {
  21105. return undefined;
  21106. };
  21107. /**
  21108. * Get's the pane config appropriate to this class
  21109. *
  21110. * @returns The config needed to create a pane of this type
  21111. */
  21112. SearchPane.prototype._getPaneConfig = function () {
  21113. var _this = this;
  21114. // eslint-disable-next-line no-extra-parens
  21115. var haveScroller = dataTable$2.Scroller;
  21116. var langOpts = this.s.dt.settings()[0].oLanguage;
  21117. langOpts.url = undefined;
  21118. langOpts.sUrl = undefined;
  21119. return {
  21120. columnDefs: [
  21121. {
  21122. className: 'dtsp-nameColumn',
  21123. data: 'display',
  21124. render: function (data, type, row) {
  21125. if (type === 'sort') {
  21126. return row.sort;
  21127. }
  21128. else if (type === 'type') {
  21129. return row.type;
  21130. }
  21131. var message = _this._getMessage(row);
  21132. // We are displaying the count in the same columne as the name of the search option.
  21133. // This is so that there is not need to call columns.adjust()
  21134. // which in turn speeds up the code
  21135. var pill = '<span class="' + _this.classes.pill + '">' + message + '</span>';
  21136. if (!_this.c.viewCount || !_this.s.colOpts.viewCount) {
  21137. pill = '';
  21138. }
  21139. if (type === 'filter') {
  21140. return typeof data === 'string' && data.match(/<[^>]*>/) !== null ?
  21141. data.replace(/<[^>]*>/g, '') :
  21142. data;
  21143. }
  21144. return '<div class="' + _this.classes.nameCont + '"><span title="' +
  21145. (typeof data === 'string' && data.match(/<[^>]*>/) !== null ?
  21146. data.replace(/<[^>]*>/g, '') :
  21147. data) +
  21148. '" class="' + _this.classes.name + '">' +
  21149. data + '</span>' +
  21150. pill + '</div>';
  21151. },
  21152. targets: 0,
  21153. // Accessing the private datatables property to set type based on the original table.
  21154. // This is null if not defined by the user, meaning that automatic type detection
  21155. // would take place
  21156. type: this.s.dt.settings()[0].aoColumns[this.s.index] ?
  21157. this.s.dt.settings()[0].aoColumns[this.s.index]._sManualType :
  21158. null
  21159. },
  21160. {
  21161. className: 'dtsp-countColumn ' + this.classes.badgePill,
  21162. data: 'total',
  21163. searchable: false,
  21164. targets: 1,
  21165. visible: false
  21166. }
  21167. ],
  21168. deferRender: true,
  21169. info: false,
  21170. language: langOpts,
  21171. paging: haveScroller ? true : false,
  21172. scrollX: false,
  21173. scrollY: '200px',
  21174. scroller: haveScroller ? true : false,
  21175. select: true,
  21176. stateSave: this.s.dt.settings()[0].oFeatures.bStateSave ? true : false
  21177. };
  21178. };
  21179. /**
  21180. * This method allows for changes to the panes and table to be made when a selection or a deselection occurs
  21181. */
  21182. SearchPane.prototype._makeSelection = function () {
  21183. this.updateTable();
  21184. this.s.updating = true;
  21185. this.s.dt.draw();
  21186. this.s.updating = false;
  21187. };
  21188. /**
  21189. * Populates an array with all of the data for the table
  21190. *
  21191. * @param rowIdx The current row index to be compared
  21192. * @param arrayFilter The array that is to be populated with row Details
  21193. * @param settings The DataTable settings object
  21194. * @param bins The bins object that is to be populated with the row counts
  21195. */
  21196. SearchPane.prototype._populatePaneArray = function (rowIdx, arrayFilter, settings, bins) {
  21197. if (bins === void 0) { bins = this.s.rowData.bins; }
  21198. // Retrieve the rendered data from the cell using the fastData function
  21199. // rather than the cell().render API method for optimisation
  21200. var fastData = settings.fastData
  21201. ? settings.fastData
  21202. : function (row, col, orth) {
  21203. // Legacy DT1
  21204. return settings.oApi._fnGetCellData(settings, row, col, orth);
  21205. };
  21206. if (typeof this.s.colOpts.orthogonal === 'string') {
  21207. var rendered = fastData(rowIdx, this.s.index, this.s.colOpts.orthogonal);
  21208. this.s.rowData.filterMap.set(rowIdx, rendered);
  21209. this._addOption(rendered, rendered, rendered, rendered, arrayFilter, bins);
  21210. this.s.rowData.totalOptions++;
  21211. }
  21212. else {
  21213. var filter = fastData(rowIdx, this.s.index, this.s.colOpts.orthogonal.search);
  21214. // Null and empty string are to be considered the same value
  21215. if (filter === null) {
  21216. filter = '';
  21217. }
  21218. if (typeof filter === 'string') {
  21219. filter = filter.replace(/<[^>]*>/g, '');
  21220. }
  21221. this.s.rowData.filterMap.set(rowIdx, filter);
  21222. if (!bins[filter]) {
  21223. bins[filter] = 1;
  21224. 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);
  21225. this.s.rowData.totalOptions++;
  21226. }
  21227. else {
  21228. bins[filter]++;
  21229. this.s.rowData.totalOptions++;
  21230. }
  21231. }
  21232. };
  21233. /**
  21234. * Reloads all of the previous selects into the panes
  21235. *
  21236. * @param loadedFilter The loaded filters from a previous state
  21237. */
  21238. SearchPane.prototype._reloadSelect = function (loadedFilter) {
  21239. // If the state was not saved don't selected any
  21240. if (loadedFilter === undefined) {
  21241. return;
  21242. }
  21243. var idx;
  21244. // For each pane, check that the loadedFilter list exists and is not null,
  21245. // find the id of each search item and set it to be selected.
  21246. for (var i = 0; i < loadedFilter.searchPanes.panes.length; i++) {
  21247. if (loadedFilter.searchPanes.panes[i].id === this.s.index) {
  21248. idx = i;
  21249. break;
  21250. }
  21251. }
  21252. if (idx) {
  21253. var table = this.s.dtPane;
  21254. var rows = table.rows({ order: 'index' }).data().map(function (item) { return item.filter !== null ?
  21255. item.filter.toString() :
  21256. null; }).toArray();
  21257. for (var _i = 0, _a = loadedFilter.searchPanes.panes[idx].selected; _i < _a.length; _i++) {
  21258. var filter = _a[_i];
  21259. var id = -1;
  21260. if (filter !== null) {
  21261. id = rows.indexOf(filter.toString());
  21262. }
  21263. if (id > -1) {
  21264. this.s.serverSelecting = true;
  21265. table.row(id).select();
  21266. this.s.serverSelecting = false;
  21267. }
  21268. }
  21269. }
  21270. };
  21271. /**
  21272. * Notes the rows that have been selected within this pane and stores them internally
  21273. *
  21274. * @param notUpdating Whether the panes are updating themselves or not
  21275. */
  21276. SearchPane.prototype._updateSelection = function (notUpdating) {
  21277. var _this = this;
  21278. var processing = function (state) {
  21279. if (DataTable.versionCheck('2')) {
  21280. _this.s.dt.processing(state);
  21281. }
  21282. else {
  21283. // Legacy v1
  21284. var settings = _this.s.dt.settings()[0];
  21285. var oApi = settings.oApi;
  21286. oApi._fnProcessingDisplay(settings, false);
  21287. }
  21288. };
  21289. var run = function () {
  21290. _this.s.scrollTop = $$5(_this.s.dtPane.table().node()).parent()[0].scrollTop;
  21291. if (_this.s.dt.page.info().serverSide && !_this.s.updating) {
  21292. if (!_this.s.serverSelecting) {
  21293. _this.s.serverSelect = _this.s.dtPane.rows({ selected: true }).data().toArray();
  21294. _this.s.dt.draw(false);
  21295. }
  21296. }
  21297. else if (notUpdating) {
  21298. _this._makeSelection();
  21299. }
  21300. processing(false);
  21301. };
  21302. processing(true);
  21303. setTimeout(run, 1);
  21304. };
  21305. /**
  21306. * Takes in potentially undetected rows and adds them to the array if they are not yet featured
  21307. *
  21308. * @param filter the filter value of the potential row
  21309. * @param display the display value of the potential row
  21310. * @param sort the sort value of the potential row
  21311. * @param type the type value of the potential row
  21312. * @param arrayFilter the array to be populated
  21313. * @param bins the bins to be populated
  21314. */
  21315. SearchPane.prototype._addOption = function (filter, display, sort, type, arrayFilter, bins) {
  21316. // If the filter is an array then take a note of this, and add the elements to the arrayFilter array
  21317. if (Array.isArray(filter) || filter instanceof dataTable$2.Api) {
  21318. // Convert to an array so that we can work with it
  21319. if (filter instanceof dataTable$2.Api) {
  21320. filter = filter.toArray();
  21321. display = display.toArray();
  21322. }
  21323. if (filter.length === display.length) {
  21324. for (var i = 0; i < filter.length; i++) {
  21325. // If we haven't seen this row before add it
  21326. if (!bins[filter[i]]) {
  21327. bins[filter[i]] = 1;
  21328. arrayFilter.push({
  21329. display: display[i],
  21330. filter: filter[i],
  21331. sort: sort[i],
  21332. type: type[i]
  21333. });
  21334. }
  21335. // Otherwise just increment the count
  21336. else {
  21337. bins[filter[i]]++;
  21338. }
  21339. this.s.rowData.totalOptions++;
  21340. }
  21341. return;
  21342. }
  21343. throw new Error('display and filter not the same length');
  21344. }
  21345. // If the values were affected by othogonal data and are not an array then check if it is already present
  21346. else if (typeof this.s.colOpts.orthogonal === 'string') {
  21347. if (!bins[filter]) {
  21348. bins[filter] = 1;
  21349. arrayFilter.push({
  21350. display: display,
  21351. filter: filter,
  21352. sort: sort,
  21353. type: type
  21354. });
  21355. this.s.rowData.totalOptions++;
  21356. }
  21357. else {
  21358. bins[filter]++;
  21359. this.s.rowData.totalOptions++;
  21360. }
  21361. }
  21362. // Otherwise we must just be adding an option
  21363. else {
  21364. arrayFilter.push({
  21365. display: display,
  21366. filter: filter,
  21367. sort: sort,
  21368. type: type
  21369. });
  21370. }
  21371. };
  21372. /**
  21373. * Method to construct the actual pane.
  21374. *
  21375. * @param selectedRows previously selected Rows to be reselected
  21376. * @param dataIn Data that should be used to populate this pane
  21377. * @param prevEl Reference to the previous element, used to ensure insert is in the correct location
  21378. * @returns boolean to indicate whether this pane was the last one to have a selection made
  21379. */
  21380. SearchPane.prototype._buildPane = function (selectedRows, dataIn, prevEl) {
  21381. var _this = this;
  21382. if (selectedRows === void 0) { selectedRows = []; }
  21383. if (dataIn === void 0) { dataIn = null; }
  21384. if (prevEl === void 0) { prevEl = null; }
  21385. // Aliases
  21386. this.s.selections = [];
  21387. // Other Variables
  21388. var loadedFilter = this.s.dt.state.loaded();
  21389. var row;
  21390. // If the listeners have not been set yet then using the latest state may result in funny errors
  21391. if (this.s.listSet) {
  21392. loadedFilter = this.s.dt.state();
  21393. }
  21394. // If it is not a custom pane in place
  21395. if (this.s.colExists) {
  21396. var idx = -1;
  21397. if (loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.panes) {
  21398. for (var i = 0; i < loadedFilter.searchPanes.panes.length; i++) {
  21399. if (loadedFilter.searchPanes.panes[i].id === this.s.index) {
  21400. idx = i;
  21401. break;
  21402. }
  21403. }
  21404. }
  21405. // Perform checks that do not require populate pane to run
  21406. if ((this.s.colOpts.show === false ||
  21407. this.s.colOpts.show !== undefined && this.s.colOpts.show !== true) &&
  21408. idx === -1) {
  21409. this.dom.container.addClass(this.classes.hidden);
  21410. this.s.displayed = false;
  21411. return false;
  21412. }
  21413. else if (this.s.colOpts.show === true || idx !== -1) {
  21414. this.s.displayed = true;
  21415. }
  21416. if (!this.s.dt.page.info().serverSide &&
  21417. (!dataIn ||
  21418. !dataIn.searchPanes ||
  21419. !dataIn.searchPanes.options)) {
  21420. // Only run populatePane if the data has not been collected yet
  21421. if (this.s.rowData.arrayFilter.length === 0) {
  21422. this.s.rowData.totalOptions = 0;
  21423. this._populatePane();
  21424. this.s.rowData.arrayOriginal = this.s.rowData.arrayFilter;
  21425. this.s.rowData.binsOriginal = this.s.rowData.bins;
  21426. }
  21427. var binLength = Object.keys(this.s.rowData.binsOriginal).length;
  21428. var uniqueRatio = this._uniqueRatio(binLength, this.s.dt.rows()[0].length);
  21429. // Don't show the pane if there isn't enough variance in the data, or there is only 1 entry
  21430. // for that pane
  21431. if (this.s.displayed === false &&
  21432. ((this.s.colOpts.show === undefined && this.s.colOpts.threshold === null ?
  21433. uniqueRatio > this.c.threshold :
  21434. uniqueRatio > this.s.colOpts.threshold) ||
  21435. this.s.colOpts.show !== true && binLength <= 1)) {
  21436. this.dom.container.addClass(this.classes.hidden);
  21437. this.s.displayed = false;
  21438. return;
  21439. }
  21440. this.dom.container.addClass(this.classes.show);
  21441. this.s.displayed = true;
  21442. }
  21443. else if (dataIn && dataIn.searchPanes && dataIn.searchPanes.options) {
  21444. this._serverPopulate(dataIn);
  21445. }
  21446. }
  21447. else {
  21448. this.s.displayed = true;
  21449. }
  21450. // If the variance is accceptable then display the search pane
  21451. this._displayPane();
  21452. if (!this.s.listSet) {
  21453. // Here, when the state is loaded if the data object on the original table is empty,
  21454. // then a state.clear() must have occurred, so delete all of the panes tables state objects too.
  21455. this.dom.dtP.on('stateLoadParams.dtsp', function (e, settings, data) {
  21456. if ($$5.isEmptyObject(_this.s.dt.state.loaded())) {
  21457. $$5.each(data, function (index) {
  21458. delete data[index];
  21459. });
  21460. }
  21461. });
  21462. }
  21463. // Add the container to the document in its original location
  21464. if (prevEl !== null && this.dom.panesContainer.has(prevEl).length > 0) {
  21465. this.dom.container.insertAfter(prevEl);
  21466. }
  21467. else {
  21468. this.dom.panesContainer.prepend(this.dom.container);
  21469. }
  21470. // Declare the datatable for the pane
  21471. var errMode = $$5.fn.dataTable.ext.errMode;
  21472. $$5.fn.dataTable.ext.errMode = 'none';
  21473. // eslint-disable-next-line no-extra-parens
  21474. // For async loading of a DataTable (e.g. language file)
  21475. // we need to set the select style to make sure the event
  21476. // handlers are added.
  21477. this.dom.dtP.on('init.dt', function (e, s) {
  21478. var dt = _this.dom.dtP.DataTable();
  21479. var style = dt.select.style();
  21480. dt.select.style(style);
  21481. });
  21482. 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 ?
  21483. {
  21484. createdRow: function (row, data) {
  21485. $$5(row).addClass(data.className);
  21486. }
  21487. } :
  21488. undefined, this.s.customPaneSettings !== null && this.s.customPaneSettings.dtOpts ?
  21489. this.s.customPaneSettings.dtOpts :
  21490. {}, $$5.fn.dataTable.versionCheck('2')
  21491. ? {
  21492. layout: {
  21493. bottomStart: null,
  21494. bottomEnd: null,
  21495. topStart: null,
  21496. topEnd: null
  21497. }
  21498. }
  21499. : { dom: 't' }));
  21500. this.dom.dtP.addClass(this.classes.table);
  21501. // Getting column titles is a little messy
  21502. var headerText = 'Custom Pane';
  21503. if (this.s.customPaneSettings && this.s.customPaneSettings.header) {
  21504. headerText = this.s.customPaneSettings.header;
  21505. }
  21506. else if (this.s.colOpts.header) {
  21507. headerText = this.s.colOpts.header;
  21508. }
  21509. else if (this.s.colExists) {
  21510. headerText = $$5.fn.dataTable.versionCheck('2')
  21511. ? this.s.dt.column(this.s.index).title()
  21512. : this.s.dt.settings()[0].aoColumns[this.s.index].sTitle;
  21513. }
  21514. headerText = this._escapeHTML(headerText);
  21515. this.dom.searchBox.attr('placeholder', headerText);
  21516. $$5.fn.dataTable.ext.errMode = errMode;
  21517. // If it is not a custom pane
  21518. if (this.s.colExists) {
  21519. // Add all of the search options to the pane
  21520. for (var j = 0, jen = this.s.rowData.arrayFilter.length; j < jen; j++) {
  21521. if (this.s.dt.page.info().serverSide) {
  21522. 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);
  21523. for (var _i = 0, _a = this.s.serverSelect; _i < _a.length; _i++) {
  21524. var option = _a[_i];
  21525. if (option.filter === this.s.rowData.arrayFilter[j].filter) {
  21526. this.s.serverSelecting = true;
  21527. row.select();
  21528. this.s.serverSelecting = false;
  21529. }
  21530. }
  21531. }
  21532. else if (!this.s.dt.page.info().serverSide && this.s.rowData.arrayFilter[j]) {
  21533. 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);
  21534. }
  21535. else if (!this.s.dt.page.info().serverSide) {
  21536. // Just pass an empty string as the message will be calculated based on that in addRow()
  21537. this.addRow('', '', '', '');
  21538. }
  21539. }
  21540. }
  21541. // If there are custom options set or it is a custom pane then get them
  21542. if (this.s.colOpts.options ||
  21543. this.s.customPaneSettings && this.s.customPaneSettings.options) {
  21544. this._getComparisonRows();
  21545. }
  21546. // Display the pane
  21547. this.s.dtPane.draw();
  21548. this.s.dtPane.table().node().parentNode.scrollTop = this.s.scrollTop;
  21549. this.adjustTopRow();
  21550. this.setListeners();
  21551. this.s.listSet = true;
  21552. for (var _b = 0, selectedRows_1 = selectedRows; _b < selectedRows_1.length; _b++) {
  21553. var selection = selectedRows_1[_b];
  21554. if (selection) {
  21555. for (var _c = 0, _d = this.s.dtPane.rows().indexes().toArray(); _c < _d.length; _c++) {
  21556. row = _d[_c];
  21557. if (this.s.dtPane.row(row).data() &&
  21558. selection.filter === this.s.dtPane.row(row).data().filter) {
  21559. // If this is happening when serverSide processing is happening then
  21560. // different behaviour is needed
  21561. if (this.s.dt.page.info().serverSide) {
  21562. this.s.serverSelecting = true;
  21563. this.s.dtPane.row(row).select();
  21564. this.s.serverSelecting = false;
  21565. }
  21566. else {
  21567. this.s.dtPane.row(row).select();
  21568. }
  21569. }
  21570. }
  21571. }
  21572. }
  21573. // If SSP and the table is ready, apply the search for the pane
  21574. if (this.s.dt.page.info().serverSide) {
  21575. this.s.dtPane.search(this.dom.searchBox.val()).draw();
  21576. }
  21577. if ((this.c.initCollapsed && this.s.colOpts.initCollapsed !== false ||
  21578. this.s.colOpts.initCollapsed) &&
  21579. (this.c.collapse && this.s.colOpts.collapse !== false ||
  21580. this.s.colOpts.collapse)) {
  21581. // If the pane has not initialised yet then we need to wait for it to do so before collapsing
  21582. // Otherwise the container that the class is added to does not exist
  21583. if (this.s.dtPane.settings()[0]._bInitComplete) {
  21584. this.collapse();
  21585. }
  21586. else {
  21587. this.s.dtPane.one('init', function () { return _this.collapse(); });
  21588. }
  21589. }
  21590. // Reload the selection, searchbox entry and ordering from the previous state
  21591. // Need to check here if SSP that this is the first draw, otherwise it will infinite loop
  21592. if (loadedFilter &&
  21593. loadedFilter.searchPanes &&
  21594. loadedFilter.searchPanes.panes &&
  21595. (!dataIn ||
  21596. dataIn.draw === 1)) {
  21597. this._reloadSelect(loadedFilter);
  21598. for (var _e = 0, _f = loadedFilter.searchPanes.panes; _e < _f.length; _e++) {
  21599. var pane = _f[_e];
  21600. if (pane.id === this.s.index) {
  21601. // Save some time by only triggering an input if there is a value
  21602. if (pane.searchTerm && pane.searchTerm.length > 0) {
  21603. this.dom.searchBox.val(pane.searchTerm).trigger('input');
  21604. }
  21605. if (pane.order) {
  21606. this.s.dtPane.order(pane.order).draw();
  21607. }
  21608. // Is the pane to be hidden or shown?
  21609. if (pane.collapsed) {
  21610. this.collapse();
  21611. }
  21612. else {
  21613. this.show();
  21614. }
  21615. }
  21616. }
  21617. }
  21618. return true;
  21619. };
  21620. /**
  21621. * Appends all of the HTML elements to their relevant parent Elements
  21622. */
  21623. SearchPane.prototype._displayPane = function () {
  21624. // Empty everything to start again
  21625. this.dom.dtP.empty();
  21626. this.dom.topRow.empty().addClass(this.classes.topRow);
  21627. // If there are more than 3 columns defined then make there be a smaller gap between the panes
  21628. if (parseInt(this.c.layout.split('-')[1], 10) > 3) {
  21629. this.dom.container.addClass(this.classes.smallGap);
  21630. }
  21631. this.dom.topRow
  21632. .addClass(this.classes.subRowsContainer)
  21633. .append(this.dom.upper.append(this.dom.searchCont))
  21634. .append(this.dom.lower.append(this.dom.buttonGroup));
  21635. // If no selections have been made in the pane then disable the clear button
  21636. if (this.c.dtOpts.searching === false ||
  21637. this.s.colOpts.dtOpts && this.s.colOpts.dtOpts.searching === false ||
  21638. (!this.c.controls || !this.s.colOpts.controls) ||
  21639. this.s.customPaneSettings &&
  21640. this.s.customPaneSettings.dtOpts &&
  21641. this.s.customPaneSettings.dtOpts.searching !== undefined &&
  21642. !this.s.customPaneSettings.dtOpts.searching) {
  21643. this.dom.searchBox
  21644. .removeClass(this.classes.paneInputButton)
  21645. .addClass(this.classes.disabledButton)
  21646. .attr('disabled', 'true');
  21647. }
  21648. this.dom.searchBox.appendTo(this.dom.searchCont);
  21649. // Create the contents of the searchCont div. Worth noting that this function will change when using semantic ui
  21650. this._searchContSetup();
  21651. // If the clear button is allowed to show then display it
  21652. if (this.c.clear && this.c.controls && this.s.colOpts.controls) {
  21653. this.dom.clear.appendTo(this.dom.buttonGroup);
  21654. }
  21655. if (this.c.orderable && this.s.colOpts.orderable && this.c.controls && this.s.colOpts.controls) {
  21656. this.dom.nameButton.appendTo(this.dom.buttonGroup);
  21657. }
  21658. // If the count column is hidden then don't display the ordering button for it
  21659. if (this.c.viewCount &&
  21660. this.s.colOpts.viewCount &&
  21661. this.c.orderable &&
  21662. this.s.colOpts.orderable &&
  21663. this.c.controls &&
  21664. this.s.colOpts.controls) {
  21665. this.dom.countButton.appendTo(this.dom.buttonGroup);
  21666. }
  21667. if ((this.c.collapse && this.s.colOpts.collapse !== false ||
  21668. this.s.colOpts.collapse) &&
  21669. this.c.controls && this.s.colOpts.controls) {
  21670. this.dom.collapseButton.appendTo(this.dom.buttonGroup);
  21671. }
  21672. this.dom.container.prepend(this.dom.topRow).append(this.dom.dtP).show();
  21673. };
  21674. /**
  21675. * Escape html characters within a string
  21676. *
  21677. * @param txt the string to be escaped
  21678. * @returns the escaped string
  21679. */
  21680. SearchPane.prototype._escapeHTML = function (txt) {
  21681. return txt
  21682. .toString()
  21683. .replace(/&lt;/g, '<')
  21684. .replace(/&gt;/g, '>')
  21685. .replace(/&quot;/g, '"')
  21686. .replace(/&amp;/g, '&');
  21687. };
  21688. /**
  21689. * Gets the options for the row for the customPanes
  21690. *
  21691. * @returns {object} The options for the row extended to include the options from the user.
  21692. */
  21693. SearchPane.prototype._getBonusOptions = function () {
  21694. // We need to reset the thresholds as if they have a value in colOpts then that value will be used
  21695. var defaultMutator = {
  21696. threshold: null
  21697. };
  21698. return $$5.extend(true, {}, SearchPane.defaults, defaultMutator, this.c ? this.c : {});
  21699. };
  21700. /**
  21701. * Gets the options for the row for the customPanes
  21702. *
  21703. * @returns {object} The options for the row extended to include the options from the user.
  21704. */
  21705. SearchPane.prototype._getOptions = function () {
  21706. var table = this.s.dt;
  21707. // We need to reset the thresholds as if they have a value in colOpts then that value will be used
  21708. var defaultMutator = {
  21709. collapse: null,
  21710. emptyMessage: false,
  21711. initCollapsed: null,
  21712. threshold: null
  21713. };
  21714. var columnOptions = table.settings()[0].aoColumns[this.s.index].searchPanes;
  21715. var colOpts = $$5.extend(true, {}, SearchPane.defaults, defaultMutator, columnOptions);
  21716. if (columnOptions && columnOptions.hideCount && columnOptions.viewCount === undefined) {
  21717. colOpts.viewCount = !columnOptions.hideCount;
  21718. }
  21719. return colOpts;
  21720. };
  21721. /**
  21722. * Fill the array with the values that are currently being displayed in the table
  21723. */
  21724. SearchPane.prototype._populatePane = function () {
  21725. this.s.rowData.arrayFilter = [];
  21726. this.s.rowData.bins = {};
  21727. var settings = this.s.dt.context[0];
  21728. if (!this.s.dt.page.info().serverSide) {
  21729. for (var _i = 0, _a = this.s.dt.rows().indexes().toArray(); _i < _a.length; _i++) {
  21730. var index = _a[_i];
  21731. this._populatePaneArray(index, this.s.rowData.arrayFilter, settings);
  21732. }
  21733. }
  21734. };
  21735. /**
  21736. * This method decides whether a row should contribute to the pane or not
  21737. *
  21738. * @param filter the value that the row is to be filtered on
  21739. * @param dataIndex the row index
  21740. */
  21741. SearchPane.prototype._search = function (filter, dataIndex) {
  21742. var colOpts = this.s.colOpts;
  21743. var table = this.s.dt;
  21744. // For each item selected in the pane, check if it is available in the cell
  21745. for (var _i = 0, _a = this.s.selections; _i < _a.length; _i++) {
  21746. var colSelect = _a[_i];
  21747. if (typeof colSelect === 'string' && typeof filter === 'string') {
  21748. // The filter value will not have the &amp; in place but a &,
  21749. // so we need to do a replace to make sure that they will match
  21750. colSelect = this._escapeHTML(colSelect);
  21751. }
  21752. // if the filter is an array then is the column present in it
  21753. if (Array.isArray(filter)) {
  21754. if (colOpts.combiner === 'and') {
  21755. if (!filter.includes(colSelect)) {
  21756. return false;
  21757. }
  21758. }
  21759. else if (filter.includes(colSelect)) {
  21760. return true;
  21761. }
  21762. }
  21763. // if the filter is a function then does it meet the criteria of that function or not
  21764. else if (typeof colSelect === 'function') {
  21765. if (colSelect.call(table, table.row(dataIndex).data(), dataIndex)) {
  21766. if (colOpts.combiner === 'or') {
  21767. return true;
  21768. }
  21769. }
  21770. // If the combiner is an "and" then we need to check against all possible selections
  21771. // so if it fails here then the and is not met and return false
  21772. else if (colOpts.combiner === 'and') {
  21773. return false;
  21774. }
  21775. }
  21776. // otherwise if the two filter values are equal then return true
  21777. else if (filter === colSelect ||
  21778. // Loose type checking incase number type in column comparing to a string
  21779. // eslint-disable-next-line eqeqeq
  21780. !(typeof filter === 'string' && filter.length === 0) && filter == colSelect ||
  21781. colSelect === null && typeof filter === 'string' && filter === '') {
  21782. return true;
  21783. }
  21784. }
  21785. // If the combiner is an and then we need to check against all possible selections
  21786. // so return true here if so because it would have returned false earlier if it had failed
  21787. if (colOpts.combiner === 'and') {
  21788. return true;
  21789. }
  21790. // Otherwise it hasn't matched with anything by this point so it must be false
  21791. return false;
  21792. };
  21793. /**
  21794. * Creates the contents of the searchCont div
  21795. *
  21796. * NOTE This is overridden when semantic ui styling in order to integrate the search button into the text box.
  21797. */
  21798. SearchPane.prototype._searchContSetup = function () {
  21799. if (this.c.controls && this.s.colOpts.controls) {
  21800. this.dom.searchButton.appendTo(this.dom.searchLabelCont);
  21801. }
  21802. if (!(this.c.dtOpts.searching === false ||
  21803. this.s.colOpts.dtOpts.searching === false ||
  21804. this.s.customPaneSettings &&
  21805. this.s.customPaneSettings.dtOpts &&
  21806. this.s.customPaneSettings.dtOpts.searching !== undefined &&
  21807. !this.s.customPaneSettings.dtOpts.searching)) {
  21808. this.dom.searchLabelCont.appendTo(this.dom.searchCont);
  21809. }
  21810. };
  21811. /**
  21812. * Adds outline to the pane when a selection has been made
  21813. */
  21814. SearchPane.prototype._searchExtras = function () {
  21815. var updating = this.s.updating;
  21816. this.s.updating = true;
  21817. var filters = this.s.dtPane.rows({ selected: true }).data().pluck('filter').toArray();
  21818. var nullIndex = filters.indexOf(this.emptyMessage());
  21819. var container = $$5(this.s.dtPane.table().container());
  21820. // If null index is found then search for empty cells as a filter.
  21821. if (nullIndex > -1) {
  21822. filters[nullIndex] = '';
  21823. }
  21824. // If a filter has been applied then outline the respective pane, remove it when it no longer is.
  21825. if (filters.length > 0) {
  21826. container.addClass(this.classes.selected);
  21827. }
  21828. else if (filters.length === 0) {
  21829. container.removeClass(this.classes.selected);
  21830. }
  21831. this.s.updating = updating;
  21832. };
  21833. SearchPane.version = '2.1.2';
  21834. SearchPane.classes = {
  21835. bordered: 'dtsp-bordered',
  21836. buttonGroup: 'dtsp-buttonGroup',
  21837. buttonSub: 'dtsp-buttonSub',
  21838. caret: 'dtsp-caret',
  21839. clear: 'dtsp-clear',
  21840. clearAll: 'dtsp-clearAll',
  21841. clearButton: 'clearButton',
  21842. collapseAll: 'dtsp-collapseAll',
  21843. collapseButton: 'dtsp-collapseButton',
  21844. container: 'dtsp-searchPane',
  21845. countButton: 'dtsp-countButton',
  21846. disabledButton: 'dtsp-disabledButton',
  21847. hidden: 'dtsp-hidden',
  21848. hide: 'dtsp-hide',
  21849. layout: 'dtsp-',
  21850. name: 'dtsp-name',
  21851. nameButton: 'dtsp-nameButton',
  21852. nameCont: 'dtsp-nameCont',
  21853. narrow: 'dtsp-narrow',
  21854. paneButton: 'dtsp-paneButton',
  21855. paneInputButton: 'dtsp-paneInputButton',
  21856. pill: 'dtsp-pill',
  21857. rotated: 'dtsp-rotated',
  21858. search: 'dtsp-search',
  21859. searchCont: 'dtsp-searchCont',
  21860. searchIcon: 'dtsp-searchIcon',
  21861. searchLabelCont: 'dtsp-searchButtonCont',
  21862. selected: 'dtsp-selected',
  21863. smallGap: 'dtsp-smallGap',
  21864. subRow1: 'dtsp-subRow1',
  21865. subRow2: 'dtsp-subRow2',
  21866. subRowsContainer: 'dtsp-subRowsContainer',
  21867. title: 'dtsp-title',
  21868. topRow: 'dtsp-topRow'
  21869. };
  21870. // Define SearchPanes default options
  21871. SearchPane.defaults = {
  21872. clear: true,
  21873. collapse: true,
  21874. combiner: 'or',
  21875. container: function (dt) {
  21876. return dt.table().container();
  21877. },
  21878. controls: true,
  21879. dtOpts: {},
  21880. emptyMessage: null,
  21881. hideCount: false,
  21882. i18n: {
  21883. clearPane: '&times;',
  21884. count: '{total}',
  21885. emptyMessage: '<em>No data</em>'
  21886. },
  21887. initCollapsed: false,
  21888. layout: 'auto',
  21889. name: undefined,
  21890. orderable: true,
  21891. orthogonal: {
  21892. display: 'display',
  21893. filter: 'filter',
  21894. hideCount: false,
  21895. search: 'filter',
  21896. show: undefined,
  21897. sort: 'sort',
  21898. threshold: 0.6,
  21899. type: 'type',
  21900. viewCount: true
  21901. },
  21902. preSelect: [],
  21903. threshold: 0.6,
  21904. viewCount: true
  21905. };
  21906. return SearchPane;
  21907. }());
  21908. var __extends$4 = (window && window.__extends) || (function () {
  21909. var extendStatics = function (d, b) {
  21910. extendStatics = Object.setPrototypeOf ||
  21911. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  21912. function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  21913. return extendStatics(d, b);
  21914. };
  21915. return function (d, b) {
  21916. extendStatics(d, b);
  21917. function __() { this.constructor = d; }
  21918. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  21919. };
  21920. })();
  21921. var SearchPaneST = /** @class */ (function (_super) {
  21922. __extends$4(SearchPaneST, _super);
  21923. function SearchPaneST(paneSettings, opts, index, panesContainer, panes) {
  21924. return _super.call(this, paneSettings, opts, index, panesContainer, panes) || this;
  21925. }
  21926. /**
  21927. * When server-side processing is enabled, SP will remove rows and then readd them,
  21928. * resulting in Select's reference to the last selected cell being lost.
  21929. * This function is provided to update that reference.
  21930. *
  21931. * @returns Function
  21932. */
  21933. SearchPaneST.prototype._emptyPane = function () {
  21934. var dt = this.s.dtPane;
  21935. if (DataTable.versionCheck('2')) {
  21936. var last = dt.select.last();
  21937. var selectedIndex_1;
  21938. if (last && dt.row(last.row).any()) {
  21939. selectedIndex_1 = dt.row(last.row).data().index;
  21940. }
  21941. dt.rows().remove();
  21942. return function () {
  21943. if (selectedIndex_1 !== undefined) {
  21944. var idx = dt.row(function (i, data) { return data.index === selectedIndex_1; }).index();
  21945. dt.select.last({ row: idx, column: 0 });
  21946. }
  21947. };
  21948. }
  21949. dt.rows().remove();
  21950. return function () { };
  21951. };
  21952. /**
  21953. * Populates the SearchPane based off of the data that has been recieved from the server
  21954. *
  21955. * This method overrides SearchPane's _serverPopulate() method
  21956. *
  21957. * @param dataIn The data that has been sent from the server
  21958. */
  21959. SearchPaneST.prototype._serverPopulate = function (dataIn) {
  21960. var selection, row, data;
  21961. this.s.rowData.binsShown = {};
  21962. this.s.rowData.arrayFilter = [];
  21963. if (dataIn.tableLength !== undefined) {
  21964. this.s.tableLength = dataIn.tableLength;
  21965. this.s.rowData.totalOptions = this.s.tableLength;
  21966. }
  21967. else if (this.s.tableLength === null || this.s.dt.rows()[0].length > this.s.tableLength) {
  21968. this.s.tableLength = this.s.dt.rows()[0].length;
  21969. this.s.rowData.totalOptions = this.s.tableLength;
  21970. }
  21971. var colTitle = this.s.dt.column(this.s.index).dataSrc();
  21972. // If there is SP data for this column add it to the data array and bin
  21973. if (dataIn.searchPanes.options[colTitle] !== undefined) {
  21974. for (var _i = 0, _a = dataIn.searchPanes.options[colTitle]; _i < _a.length; _i++) {
  21975. var dataPoint = _a[_i];
  21976. this.s.rowData.arrayFilter.push({
  21977. display: dataPoint.label,
  21978. filter: dataPoint.value,
  21979. shown: +dataPoint.count,
  21980. sort: dataPoint.label,
  21981. total: +dataPoint.total,
  21982. type: dataPoint.label
  21983. });
  21984. this.s.rowData.binsShown[dataPoint.value] = +dataPoint.count;
  21985. this.s.rowData.bins[dataPoint.value] = +dataPoint.total;
  21986. }
  21987. }
  21988. var binLength = Object.keys(this.s.rowData.bins).length;
  21989. var uniqueRatio = this._uniqueRatio(binLength, this.s.tableLength);
  21990. // Don't show the pane if there isnt enough variance in the data, or there is only 1 entry for that pane
  21991. if (!this.s.colOpts.show &&
  21992. this.s.displayed === false &&
  21993. ((this.s.colOpts.show === undefined && this.s.colOpts.threshold === null ?
  21994. uniqueRatio > this.c.threshold :
  21995. uniqueRatio > this.s.colOpts.threshold) ||
  21996. this.s.colOpts.show !== true && binLength <= 1)) {
  21997. this.dom.container.addClass(this.classes.hidden);
  21998. this.s.displayed = false;
  21999. return;
  22000. }
  22001. // Store the original data
  22002. this.s.rowData.arrayOriginal = this.s.rowData.arrayFilter;
  22003. this.s.rowData.binsOriginal = this.s.rowData.bins;
  22004. // Flag this pane as being displayed
  22005. this.s.displayed = true;
  22006. // If the pane exists
  22007. if (this.s.dtPane) {
  22008. // Not the selections that have been made and remove all of the rows
  22009. var selected = this.s.serverSelect;
  22010. var reselect = this._emptyPane();
  22011. // Add the rows that are to be shown into the pane
  22012. for (var _b = 0, _c = this.s.rowData.arrayFilter; _b < _c.length; _b++) {
  22013. data = _c[_b];
  22014. if (this._shouldAddRow(data)) {
  22015. row = this.addRow(data.display, data.filter, data.sort, data.type);
  22016. // Select the row if it was selected before
  22017. for (var i = 0; i < selected.length; i++) {
  22018. selection = selected[i];
  22019. if (selection.filter === data.filter) {
  22020. // This flag stops another request being made to the server
  22021. this.s.serverSelecting = true;
  22022. row.select();
  22023. this.s.serverSelecting = false;
  22024. // Remove the selection from the to select list and add it to the selected list
  22025. selected.splice(i, 1);
  22026. this.s.selections.push(data.filter);
  22027. break;
  22028. }
  22029. }
  22030. }
  22031. }
  22032. // Remake any selections that are no longer present
  22033. for (var _d = 0, selected_1 = selected; _d < selected_1.length; _d++) {
  22034. selection = selected_1[_d];
  22035. for (var _e = 0, _f = this.s.rowData.arrayOriginal; _e < _f.length; _e++) {
  22036. data = _f[_e];
  22037. if (data.filter === selection.filter) {
  22038. row = this.addRow(data.display, data.filter, data.sort, data.type);
  22039. this.s.serverSelecting = true;
  22040. row.select();
  22041. this.s.serverSelecting = false;
  22042. this.s.selections.push(data.filter);
  22043. }
  22044. }
  22045. }
  22046. // Store the selected rows
  22047. this.s.serverSelect = this.s.dtPane.rows({ selected: true }).data().toArray();
  22048. // Update the pane
  22049. this.s.dtPane.draw();
  22050. reselect();
  22051. }
  22052. };
  22053. /**
  22054. * This method updates the rows and their data within the SearchPanes
  22055. *
  22056. * SearchPaneCascade overrides this method
  22057. */
  22058. SearchPaneST.prototype.updateRows = function () {
  22059. if (!this.s.dt.page.info().serverSide) {
  22060. // Get the latest count values from the table
  22061. this.s.rowData.binsShown = {};
  22062. for (var _i = 0, _a = this.s.dt.rows({ search: 'applied' }).indexes().toArray(); _i < _a.length; _i++) {
  22063. var index = _a[_i];
  22064. this._updateShown(index, this.s.dt.settings()[0], this.s.rowData.binsShown);
  22065. }
  22066. }
  22067. var _loop_1 = function (row) {
  22068. row.shown = typeof this_1.s.rowData.binsShown[row.filter] === 'number' ?
  22069. this_1.s.rowData.binsShown[row.filter] :
  22070. 0;
  22071. this_1.s.dtPane
  22072. .row(function (idx, data) {
  22073. return data && (data.index === row.index);
  22074. })
  22075. .data(row);
  22076. };
  22077. var this_1 = this;
  22078. // Update the rows data to show the current counts
  22079. for (var _b = 0, _c = this.s.dtPane.rows().data().toArray(); _b < _c.length; _b++) {
  22080. var row = _c[_b];
  22081. _loop_1(row);
  22082. }
  22083. // Show updates in the pane
  22084. this.s.dtPane.draw();
  22085. this.s.dtPane.table().node().parentNode.scrollTop = this.s.scrollTop;
  22086. };
  22087. /**
  22088. * Remove functionality from makeSelection - needs to be more advanced when tracking selections
  22089. */
  22090. SearchPaneST.prototype._makeSelection = function () {
  22091. return;
  22092. };
  22093. /**
  22094. * Blank method to remove reloading of selected rows - needs to be more advanced when tracking selections
  22095. */
  22096. SearchPaneST.prototype._reloadSelect = function () {
  22097. return;
  22098. };
  22099. /**
  22100. * Decides if a row should be added when being added from the server
  22101. *
  22102. * Overridden by SearchPaneCascade
  22103. *
  22104. * @param data the row data
  22105. * @returns boolean indicating if the row should be added or not
  22106. */
  22107. SearchPaneST.prototype._shouldAddRow = function (data) {
  22108. return true;
  22109. };
  22110. /**
  22111. * Updates the server selection list where appropriate
  22112. */
  22113. SearchPaneST.prototype._updateSelection = function () {
  22114. if (this.s.dt.page.info().serverSide && !this.s.updating && !this.s.serverSelecting) {
  22115. this.s.serverSelect = this.s.dtPane.rows({ selected: true }).data().toArray();
  22116. }
  22117. };
  22118. /**
  22119. * Used when binning the data for a column
  22120. *
  22121. * @param rowIdx The current row that is to be added to the bins
  22122. * @param settings The datatables settings object
  22123. * @param bins The bins object that is to be incremented
  22124. */
  22125. SearchPaneST.prototype._updateShown = function (rowIdx, settings, bins) {
  22126. if (bins === void 0) { bins = this.s.rowData.binsShown; }
  22127. var orth = typeof this.s.colOpts.orthogonal === 'string'
  22128. ? this.s.colOpts.orthogonal
  22129. : this.s.colOpts.orthogonal.search;
  22130. var fastData = settings.fastData
  22131. ? settings.fastData
  22132. : function (row, col, orth) {
  22133. // Legacy DT1
  22134. return settings.oApi._fnGetCellData(settings, row, col, orth);
  22135. };
  22136. var filter = fastData(rowIdx, this.s.index, orth);
  22137. var add = function (f) {
  22138. if (!bins[f]) {
  22139. bins[f] = 1;
  22140. }
  22141. else {
  22142. bins[f]++;
  22143. }
  22144. };
  22145. if (Array.isArray(filter)) {
  22146. for (var _i = 0, filter_1 = filter; _i < filter_1.length; _i++) {
  22147. var f = filter_1[_i];
  22148. add(f);
  22149. }
  22150. }
  22151. else {
  22152. add(filter);
  22153. }
  22154. };
  22155. return SearchPaneST;
  22156. }(SearchPane));
  22157. var __extends$3 = (window && window.__extends) || (function () {
  22158. var extendStatics = function (d, b) {
  22159. extendStatics = Object.setPrototypeOf ||
  22160. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  22161. function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  22162. return extendStatics(d, b);
  22163. };
  22164. return function (d, b) {
  22165. extendStatics(d, b);
  22166. function __() { this.constructor = d; }
  22167. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  22168. };
  22169. })();
  22170. var $$4;
  22171. function setJQuery$3(jq) {
  22172. $$4 = jq;
  22173. }
  22174. var SearchPaneViewTotal = /** @class */ (function (_super) {
  22175. __extends$3(SearchPaneViewTotal, _super);
  22176. function SearchPaneViewTotal(paneSettings, opts, index, panesContainer, panes) {
  22177. var _this = this;
  22178. var override = {
  22179. i18n: {
  22180. countFiltered: '{shown} ({total})'
  22181. }
  22182. };
  22183. _this = _super.call(this, paneSettings, $$4.extend(override, opts), index, panesContainer, panes) || this;
  22184. return _this;
  22185. }
  22186. /**
  22187. * Gets the message that is to be used to indicate the count for each SearchPane row
  22188. *
  22189. * This method overrides _getMessage() in SearchPane and is overridden by SearchPaneCascadeViewTotal
  22190. *
  22191. * @param row The row object that is being processed
  22192. * @returns string - the message that is to be shown for the count of each entry
  22193. */
  22194. SearchPaneViewTotal.prototype._getMessage = function (row) {
  22195. var countMessage = this.s.dt.i18n('searchPanes.count', this.c.i18n.count);
  22196. var filteredMessage = this.s.dt.i18n('searchPanes.countFiltered', this.c.i18n.countFiltered);
  22197. return (this.s.filteringActive ? filteredMessage : countMessage)
  22198. .replace(/{total}/g, row.total)
  22199. .replace(/{shown}/g, row.shown);
  22200. };
  22201. /**
  22202. * Overrides the blank method in SearchPane to return the number of times a given value is currently being displayed
  22203. *
  22204. * @param filter The filter value
  22205. * @returns number - The number of times the value is shown
  22206. */
  22207. SearchPaneViewTotal.prototype._getShown = function (filter) {
  22208. return this.s.rowData.binsShown && this.s.rowData.binsShown[filter] ?
  22209. this.s.rowData.binsShown[filter] :
  22210. 0;
  22211. };
  22212. return SearchPaneViewTotal;
  22213. }(SearchPaneST));
  22214. var __extends$2 = (window && window.__extends) || (function () {
  22215. var extendStatics = function (d, b) {
  22216. extendStatics = Object.setPrototypeOf ||
  22217. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  22218. function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  22219. return extendStatics(d, b);
  22220. };
  22221. return function (d, b) {
  22222. extendStatics(d, b);
  22223. function __() { this.constructor = d; }
  22224. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  22225. };
  22226. })();
  22227. var $$3;
  22228. function setJQuery$2(jq) {
  22229. $$3 = jq;
  22230. }
  22231. var SearchPaneCascade = /** @class */ (function (_super) {
  22232. __extends$2(SearchPaneCascade, _super);
  22233. function SearchPaneCascade(paneSettings, opts, index, panesContainer, panes) {
  22234. var _this = this;
  22235. var override = {
  22236. i18n: {
  22237. count: '{shown}'
  22238. }
  22239. };
  22240. _this = _super.call(this, paneSettings, $$3.extend(override, opts), index, panesContainer, panes) || this;
  22241. return _this;
  22242. }
  22243. /**
  22244. * This method updates the rows and their data within the SearchPanes
  22245. *
  22246. * This overrides the method in SearchPane
  22247. */
  22248. SearchPaneCascade.prototype.updateRows = function () {
  22249. // Note the currently selected values in the pane and remove all of the rows
  22250. var selected = this.s.dtPane.rows({ selected: true }).data().toArray();
  22251. var selection;
  22252. if (this.s.colOpts.options ||
  22253. this.s.customPaneSettings && this.s.customPaneSettings.options) {
  22254. // If there are custom options set or it is a custom pane then get them
  22255. this._getComparisonRows();
  22256. var rows = this.s.dtPane.rows().toArray()[0];
  22257. for (var i = 0; i < rows.length; i++) {
  22258. var row = this.s.dtPane.row(rows[i]);
  22259. var rowData = row.data();
  22260. if (rowData === undefined) {
  22261. continue;
  22262. }
  22263. if (rowData.shown === 0) {
  22264. row.remove();
  22265. rows = this.s.dtPane.rows().toArray()[0];
  22266. i--;
  22267. continue;
  22268. }
  22269. for (var _i = 0, selected_1 = selected; _i < selected_1.length; _i++) {
  22270. selection = selected_1[_i];
  22271. if (rowData.filter === selection.filter) {
  22272. row.select();
  22273. selected.splice(i, 1);
  22274. this.s.selections.push(rowData.filter);
  22275. break;
  22276. }
  22277. }
  22278. }
  22279. }
  22280. else {
  22281. if (!this.s.dt.page.info().serverSide) {
  22282. // Get the latest count values from the table
  22283. this._activePopulatePane();
  22284. this.s.rowData.binsShown = {};
  22285. for (var _a = 0, _b = this.s.dt.rows({ search: 'applied' }).indexes().toArray(); _a < _b.length; _a++) {
  22286. var index = _b[_a];
  22287. this._updateShown(index, this.s.dt.settings()[0], this.s.rowData.binsShown);
  22288. }
  22289. }
  22290. this.s.dtPane.rows().remove();
  22291. // Go over all of the rows that could be displayed
  22292. for (var _c = 0, _d = this.s.rowData.arrayFilter; _c < _d.length; _c++) {
  22293. var data = _d[_c];
  22294. // Cascade - If there are no rows present in the table don't show the option
  22295. if (data.shown === 0) {
  22296. continue;
  22297. }
  22298. // Add the row to the pane
  22299. var newRow = this.addRow(data.display, data.filter, data.sort, data.type, undefined);
  22300. // Check if this row was selected
  22301. for (var j = 0; j < selected.length; j++) {
  22302. var selectedRow = selected[j];
  22303. if (selectedRow.filter === data.filter) {
  22304. newRow.select();
  22305. // Remove the row from the to find list and then add it to the selections list
  22306. selected.splice(j, 1);
  22307. this.s.selections.push(data.filter);
  22308. break;
  22309. }
  22310. }
  22311. }
  22312. // Add all of the rows that were previously selected but aren't any more
  22313. for (var _e = 0, selected_2 = selected; _e < selected_2.length; _e++) {
  22314. selection = selected_2[_e];
  22315. for (var _f = 0, _g = this.s.rowData.arrayOriginal; _f < _g.length; _f++) {
  22316. var origData = _g[_f];
  22317. if (origData.filter === selection.filter) {
  22318. var addedRow = this.addRow(origData.display, origData.filter, origData.sort, origData.type, undefined);
  22319. addedRow.select();
  22320. this.s.selections.push(origData.filter);
  22321. }
  22322. }
  22323. }
  22324. }
  22325. // Show updates in the pane
  22326. this.s.dtPane.draw();
  22327. this.s.dtPane.table().node().parentNode.scrollTop = this.s.scrollTop;
  22328. // If client side updated the tables results
  22329. if (!this.s.dt.page.info().serverSide) {
  22330. this.s.dt.draw(false);
  22331. }
  22332. };
  22333. /**
  22334. * Fill the array with the values that are currently being displayed in the table
  22335. */
  22336. SearchPaneCascade.prototype._activePopulatePane = function () {
  22337. this.s.rowData.arrayFilter = [];
  22338. this.s.rowData.bins = {};
  22339. var settings = this.s.dt.settings()[0];
  22340. if (!this.s.dt.page.info().serverSide) {
  22341. for (var _i = 0, _a = this.s.dt.rows({ search: 'applied' }).indexes().toArray(); _i < _a.length; _i++) {
  22342. var index = _a[_i];
  22343. this._populatePaneArray(index, this.s.rowData.arrayFilter, settings);
  22344. }
  22345. }
  22346. };
  22347. SearchPaneCascade.prototype._getComparisonRows = function () {
  22348. // Find the appropriate options depending on whether this is a pane for a specific column or a custom pane
  22349. var options = this.s.colOpts.options
  22350. ? this.s.colOpts.options
  22351. : this.s.customPaneSettings && this.s.customPaneSettings.options
  22352. ? this.s.customPaneSettings.options
  22353. : undefined;
  22354. if (options === undefined) {
  22355. return;
  22356. }
  22357. var allRows = this.s.dt.rows();
  22358. var shownRows = this.s.dt.rows({ search: 'applied' });
  22359. var tableValsTotal = allRows.data().toArray();
  22360. var tableValsShown = shownRows.data().toArray();
  22361. var rows = [];
  22362. // Clear all of the other rows from the pane, only custom options are to be displayed when they are defined
  22363. this.s.dtPane.clear();
  22364. this.s.indexes = [];
  22365. for (var _i = 0, options_1 = options; _i < options_1.length; _i++) {
  22366. var comp = options_1[_i];
  22367. // Initialise the object which is to be placed in the row
  22368. var insert = comp.label !== '' ?
  22369. comp.label :
  22370. this.emptyMessage();
  22371. var comparisonObj = {
  22372. className: comp.className,
  22373. display: insert,
  22374. filter: typeof comp.value === 'function' ? comp.value : [],
  22375. shown: 0,
  22376. sort: insert,
  22377. total: 0,
  22378. type: insert
  22379. };
  22380. // If a custom function is in place
  22381. if (typeof comp.value === 'function') {
  22382. // Count the number of times the function evaluates to true for the original data in the Table
  22383. for (var i = 0; i < tableValsTotal.length; i++) {
  22384. if (comp.value.call(this.s.dt, tableValsTotal[i], allRows[0][i])) {
  22385. comparisonObj.total++;
  22386. }
  22387. }
  22388. for (var j = 0; j < tableValsShown.length; j++) {
  22389. if (comp.value.call(this.s.dt, tableValsShown[j], shownRows[0][j])) {
  22390. comparisonObj.shown++;
  22391. }
  22392. }
  22393. // Update the comparisonObj
  22394. if (typeof comparisonObj.filter !== 'function') {
  22395. comparisonObj.filter.push(comp.filter);
  22396. }
  22397. }
  22398. rows.push(this.addRow(comparisonObj.display, comparisonObj.filter, comparisonObj.sort, comparisonObj.type, comparisonObj.className, comparisonObj.total, comparisonObj.shown));
  22399. }
  22400. return rows;
  22401. };
  22402. /**
  22403. * Gets the message that is to be used to indicate the count for each SearchPane row
  22404. *
  22405. * This method overrides _getMessage() in SearchPane and is overridden by SearchPaneCascadeViewTotal
  22406. *
  22407. * @param row The row object that is being processed
  22408. * @returns string - the message that is to be shown for the count of each entry
  22409. */
  22410. SearchPaneCascade.prototype._getMessage = function (row) {
  22411. return this.s.dt.i18n('searchPanes.count', this.c.i18n.count)
  22412. .replace(/{total}/g, row.total)
  22413. .replace(/{shown}/g, row.shown);
  22414. };
  22415. /**
  22416. * Overrides the blank method in SearchPane to return the number of times a given value is currently being displayed
  22417. *
  22418. * @param filter The filter value
  22419. * @returns number - The number of times the value is shown
  22420. */
  22421. SearchPaneCascade.prototype._getShown = function (filter) {
  22422. return this.s.rowData.binsShown && this.s.rowData.binsShown[filter] ?
  22423. this.s.rowData.binsShown[filter] :
  22424. 0;
  22425. };
  22426. /**
  22427. * Decides if a row should be added when being added from the server
  22428. *
  22429. * Overrides method in by SearchPaneST
  22430. *
  22431. * @param data the row data
  22432. * @returns boolean indicating if the row should be added or not
  22433. */
  22434. SearchPaneCascade.prototype._shouldAddRow = function (data) {
  22435. return data.shown > 0;
  22436. };
  22437. return SearchPaneCascade;
  22438. }(SearchPaneST));
  22439. var __extends$1 = (window && window.__extends) || (function () {
  22440. var extendStatics = function (d, b) {
  22441. extendStatics = Object.setPrototypeOf ||
  22442. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  22443. function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  22444. return extendStatics(d, b);
  22445. };
  22446. return function (d, b) {
  22447. extendStatics(d, b);
  22448. function __() { this.constructor = d; }
  22449. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  22450. };
  22451. })();
  22452. var $$2;
  22453. function setJQuery$1(jq) {
  22454. $$2 = jq;
  22455. }
  22456. var SearchPaneCascadeViewTotal = /** @class */ (function (_super) {
  22457. __extends$1(SearchPaneCascadeViewTotal, _super);
  22458. function SearchPaneCascadeViewTotal(paneSettings, opts, index, panesContainer, panes) {
  22459. var _this = this;
  22460. var override = {
  22461. i18n: {
  22462. count: '{total}',
  22463. countFiltered: '{shown} ({total})'
  22464. }
  22465. };
  22466. _this = _super.call(this, paneSettings, $$2.extend(override, opts), index, panesContainer, panes) || this;
  22467. return _this;
  22468. }
  22469. /**
  22470. * Fill the array with the values that are currently being displayed in the table
  22471. *
  22472. * This method overrides _activePopulatePane() in SearchPaneCascade
  22473. */
  22474. SearchPaneCascadeViewTotal.prototype._activePopulatePane = function () {
  22475. this.s.rowData.arrayFilter = [];
  22476. this.s.rowData.binsShown = {};
  22477. var settings = this.s.dt.settings()[0];
  22478. if (!this.s.dt.page.info().serverSide) {
  22479. for (var _i = 0, _a = this.s.dt.rows({ search: 'applied' }).indexes().toArray(); _i < _a.length; _i++) {
  22480. var index = _a[_i];
  22481. this._populatePaneArray(index, this.s.rowData.arrayFilter, settings, this.s.rowData.binsShown);
  22482. }
  22483. }
  22484. };
  22485. /**
  22486. * Gets the message that is to be used to indicate the count for each SearchPane row
  22487. *
  22488. * This method overrides _getMessage() in SearchPaneCascade
  22489. *
  22490. * @param row The row object that is being processed
  22491. * @returns string - the message that is to be shown for the count of each entry
  22492. */
  22493. SearchPaneCascadeViewTotal.prototype._getMessage = function (row) {
  22494. var countMessage = this.s.dt.i18n('searchPanes.count', this.c.i18n.count);
  22495. var filteredMessage = this.s.dt.i18n('searchPanes.countFiltered', this.c.i18n.countFiltered);
  22496. return (this.s.filteringActive ? filteredMessage : countMessage)
  22497. .replace(/{total}/g, row.total)
  22498. .replace(/{shown}/g, row.shown);
  22499. };
  22500. return SearchPaneCascadeViewTotal;
  22501. }(SearchPaneCascade));
  22502. var $$1;
  22503. var dataTable$1;
  22504. function setJQuery(jq) {
  22505. $$1 = jq;
  22506. dataTable$1 = jq.fn.dataTable;
  22507. }
  22508. var SearchPanes = /** @class */ (function () {
  22509. function SearchPanes(paneSettings, opts, fromPreInit, paneClass) {
  22510. var _this = this;
  22511. if (fromPreInit === void 0) { fromPreInit = false; }
  22512. if (paneClass === void 0) { paneClass = SearchPane; }
  22513. // Check that the required version of DataTables is included
  22514. if (!dataTable$1 || !dataTable$1.versionCheck || !dataTable$1.versionCheck('1.10.0')) {
  22515. throw new Error('SearchPane requires DataTables 1.10 or newer');
  22516. }
  22517. // Check that Select is included
  22518. // eslint-disable-next-line no-extra-parens
  22519. if (!dataTable$1.select) {
  22520. throw new Error('SearchPane requires Select');
  22521. }
  22522. var table = new dataTable$1.Api(paneSettings);
  22523. this.classes = $$1.extend(true, {}, SearchPanes.classes);
  22524. // Get options from user
  22525. this.c = $$1.extend(true, {}, SearchPanes.defaults, opts);
  22526. // Add extra elements to DOM object including clear
  22527. this.dom = {
  22528. clearAll: $$1('<button type="button"/>')
  22529. .addClass(this.classes.clearAll)
  22530. .html(table.i18n('searchPanes.clearMessage', this.c.i18n.clearMessage)),
  22531. collapseAll: $$1('<button type="button"/>')
  22532. .addClass(this.classes.collapseAll)
  22533. .html(table.i18n('searchPanes.collapseMessage', this.c.i18n.collapseMessage)),
  22534. container: $$1('<div/>').addClass(this.classes.panes).html(table.i18n('searchPanes.loadMessage', this.c.i18n.loadMessage)),
  22535. emptyMessage: $$1('<div/>').addClass(this.classes.emptyMessage),
  22536. panes: $$1('<div/>').addClass(this.classes.container),
  22537. showAll: $$1('<button type="button"/>')
  22538. .addClass(this.classes.showAll)
  22539. .addClass(this.classes.disabledButton)
  22540. .attr('disabled', 'true')
  22541. .html(table.i18n('searchPanes.showMessage', this.c.i18n.showMessage)),
  22542. title: $$1('<div/>').addClass(this.classes.title),
  22543. titleRow: $$1('<div/>').addClass(this.classes.titleRow)
  22544. };
  22545. this.s = {
  22546. colOpts: [],
  22547. dt: table,
  22548. filterCount: 0,
  22549. minPaneWidth: 260.0,
  22550. page: 0,
  22551. paging: false,
  22552. pagingST: false,
  22553. paneClass: paneClass,
  22554. panes: [],
  22555. selectionList: [],
  22556. serverData: {},
  22557. stateRead: false,
  22558. updating: false
  22559. };
  22560. // Do not reinitialise if already initialised on table
  22561. if (table.settings()[0]._searchPanes) {
  22562. return;
  22563. }
  22564. // When the panes update, we check it the clear buttons needs to be updated
  22565. $$1(document).on('draw.dt', function (e) {
  22566. if (_this.dom.container.find(e.target).length) {
  22567. _this._updateFilterCount();
  22568. }
  22569. });
  22570. this._getState();
  22571. if (this.s.dt.page.info().serverSide) {
  22572. var hostSettings = this.s.dt.settings()[0];
  22573. // Listener to get the data into the server request before it is made
  22574. this.s.dt.on('preXhr.dtsps', function (e, settings, data) {
  22575. if (hostSettings !== settings) {
  22576. return;
  22577. }
  22578. if (data.searchPanes === undefined) {
  22579. data.searchPanes = {};
  22580. }
  22581. if (data.searchPanes_null === undefined) {
  22582. data.searchPanes_null = {};
  22583. }
  22584. var src;
  22585. for (var _i = 0, _a = _this.s.selectionList; _i < _a.length; _i++) {
  22586. var selection = _a[_i];
  22587. src = _this.s.dt.column(selection.column).dataSrc();
  22588. if (data.searchPanes[src] === undefined) {
  22589. data.searchPanes[src] = {};
  22590. }
  22591. if (data.searchPanes_null[src] === undefined) {
  22592. data.searchPanes_null[src] = {};
  22593. }
  22594. for (var i = 0; i < selection.rows.length; i++) {
  22595. data.searchPanes[src][i] = selection.rows[i];
  22596. if (data.searchPanes[src][i] === null) {
  22597. data.searchPanes_null[src][i] = true;
  22598. }
  22599. else {
  22600. data.searchPanes_null[src][i] = false;
  22601. }
  22602. }
  22603. }
  22604. if (_this.s.selectionList.length > 0) {
  22605. data.searchPanesLast = src;
  22606. }
  22607. // Config options that will change how the querying is done
  22608. data.searchPanes_options = {
  22609. cascade: _this.c.cascadePanes,
  22610. viewCount: _this.c.viewCount,
  22611. viewTotal: _this.c.viewTotal
  22612. };
  22613. });
  22614. }
  22615. this._setXHR();
  22616. table.settings()[0]._searchPanes = this;
  22617. if (this.s.dt.settings()[0]._bInitComplete || fromPreInit) {
  22618. this._paneDeclare(table, paneSettings, opts);
  22619. }
  22620. else {
  22621. table.one('preInit.dtsps', function () {
  22622. _this._paneDeclare(table, paneSettings, opts);
  22623. });
  22624. }
  22625. return this;
  22626. }
  22627. /**
  22628. * Clear the selections of all of the panes
  22629. */
  22630. SearchPanes.prototype.clearSelections = function () {
  22631. var pane;
  22632. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  22633. pane = _a[_i];
  22634. if (pane.s.dtPane) {
  22635. pane.s.scrollTop = pane.s.dtPane.table().node().parentNode.scrollTop;
  22636. }
  22637. }
  22638. // Load in all of the searchBoxes in the documents
  22639. var searches = this.dom.container.find('.' + this.classes.search.replace(/\s+/g, '.'));
  22640. // For each searchBox set the input text to be empty and then trigger
  22641. // an input on them so that they no longer filter the panes
  22642. searches.each(function () {
  22643. $$1(this).val('').trigger('input');
  22644. });
  22645. // Clear the selectionList
  22646. this.s.selectionList = [];
  22647. var returnArray = [];
  22648. for (var _b = 0, _c = this.s.panes; _b < _c.length; _b++) {
  22649. pane = _c[_b];
  22650. if (pane.s.dtPane) {
  22651. returnArray.push(pane.clearPane());
  22652. }
  22653. }
  22654. return returnArray;
  22655. };
  22656. /**
  22657. * returns the container node for the searchPanes
  22658. */
  22659. SearchPanes.prototype.getNode = function () {
  22660. return this.dom.container;
  22661. };
  22662. /**
  22663. * rebuilds all of the panes
  22664. */
  22665. SearchPanes.prototype.rebuild = function (targetIdx, maintainSelection) {
  22666. if (targetIdx === void 0) { targetIdx = false; }
  22667. if (maintainSelection === void 0) { maintainSelection = false; }
  22668. this.dom.emptyMessage.detach();
  22669. // As a rebuild from scratch is required, empty the searchpanes container.
  22670. if (targetIdx === false) {
  22671. this.dom.panes.empty();
  22672. }
  22673. // Rebuild each pane individually, if a specific pane has been selected then only rebuild that one
  22674. var returnArray = [];
  22675. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  22676. var pane = _a[_i];
  22677. if (targetIdx === false || pane.s.index === targetIdx) {
  22678. pane.clearData();
  22679. pane.rebuildPane(this.s.dt.page.info().serverSide ?
  22680. this.s.serverData :
  22681. undefined, maintainSelection);
  22682. this.dom.panes.append(pane.dom.container);
  22683. returnArray.push(pane);
  22684. }
  22685. }
  22686. this._updateSelection();
  22687. // Attach panes, clear buttons, and title bar to the document
  22688. this._updateFilterCount();
  22689. this._attachPaneContainer();
  22690. this._initSelectionListeners(false);
  22691. // If the selections are to be maintained, then it is safe to assume that paging is also to be maintained
  22692. // Otherwise, the paging should be reset
  22693. this.s.dt.draw(!maintainSelection);
  22694. // Resize the panes incase there has been a change
  22695. this.resizePanes();
  22696. // If a single pane has been rebuilt then return only that pane
  22697. return returnArray.length === 1 ? returnArray[0] : returnArray;
  22698. };
  22699. /**
  22700. * Resizes all of the panes
  22701. */
  22702. SearchPanes.prototype.resizePanes = function () {
  22703. var pane;
  22704. if (this.c.layout === 'auto') {
  22705. var contWidth = $$1(this.s.dt.searchPanes.container()).width();
  22706. var target = Math.floor(contWidth / this.s.minPaneWidth); // The neatest number of panes per row
  22707. var highest_1 = 1;
  22708. var highestmod_1 = 0;
  22709. // Get the indexes of all of the displayed panes
  22710. var dispIndex = [];
  22711. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  22712. pane = _a[_i];
  22713. if (pane.s.displayed) {
  22714. dispIndex.push(pane.s.index);
  22715. }
  22716. }
  22717. var displayCount = dispIndex.length;
  22718. // If the neatest number is the number we have then use this.
  22719. if (target === displayCount) {
  22720. highest_1 = target;
  22721. }
  22722. else {
  22723. // Go from the target down and find the value with the most panes left over, this will be the best fit
  22724. for (var ppr = target; ppr > 1; ppr--) {
  22725. var rem = displayCount % ppr;
  22726. if (rem === 0) {
  22727. highest_1 = ppr;
  22728. highestmod_1 = 0;
  22729. break;
  22730. }
  22731. // If there are more left over at this amount of panes per row (ppr)
  22732. // then it fits better so new values
  22733. else if (rem > highestmod_1) {
  22734. highest_1 = ppr;
  22735. highestmod_1 = rem;
  22736. }
  22737. }
  22738. }
  22739. // If there is a perfect fit then none are to be wider
  22740. var widerIndexes_1 = highestmod_1 !== 0 ? dispIndex.slice(dispIndex.length - highestmod_1, dispIndex.length) : [];
  22741. this.s.panes.forEach(function (pane) {
  22742. // Resize the pane with the new layout
  22743. if (pane.s.displayed) {
  22744. pane.resize('columns-' + (!widerIndexes_1.includes(pane.s.index) ? highest_1 : highestmod_1));
  22745. }
  22746. });
  22747. }
  22748. else {
  22749. for (var _b = 0, _c = this.s.panes; _b < _c.length; _b++) {
  22750. pane = _c[_b];
  22751. pane.adjustTopRow();
  22752. }
  22753. }
  22754. return this;
  22755. };
  22756. /**
  22757. * Holder method that is userd in SearchPanesST to set listeners that have an effect on other panes
  22758. *
  22759. * @param isPreselect boolean to indicate if the preselect array is to override the current selection list.
  22760. */
  22761. SearchPanes.prototype._initSelectionListeners = function (isPreselect) {
  22762. return;
  22763. };
  22764. /**
  22765. * Blank method that is overridden in SearchPanesST to retrieve the totals from the server data
  22766. */
  22767. SearchPanes.prototype._serverTotals = function () {
  22768. return;
  22769. };
  22770. /**
  22771. * Set's the xhr listener so that SP can extact appropriate data from the response
  22772. */
  22773. SearchPanes.prototype._setXHR = function () {
  22774. var _this = this;
  22775. var hostSettings = this.s.dt.settings()[0];
  22776. var run = function (json) {
  22777. if (json && json.searchPanes && json.searchPanes.options) {
  22778. _this.s.serverData = json;
  22779. _this.s.serverData.tableLength = json.recordsTotal;
  22780. _this._serverTotals();
  22781. }
  22782. };
  22783. // We are using the xhr event to rebuild the panes if required due to viewTotal being enabled
  22784. // If viewTotal is not enabled then we simply update the data from the server
  22785. this.s.dt.on('xhr.dtsps', function (e, settings, json) {
  22786. if (hostSettings === settings) {
  22787. run(json);
  22788. }
  22789. });
  22790. // Account for the initial JSON fetch having already completed
  22791. run(this.s.dt.ajax.json());
  22792. };
  22793. /**
  22794. * Set's the function that is to be performed when a state is loaded
  22795. *
  22796. * Overridden by the method in SearchPanesST
  22797. */
  22798. SearchPanes.prototype._stateLoadListener = function () {
  22799. var _this = this;
  22800. var hostSettings = this.s.dt.settings()[0];
  22801. this.s.dt.on('stateLoadParams.dtsps', function (e, settings, data) {
  22802. if (data.searchPanes === undefined || settings !== hostSettings) {
  22803. return;
  22804. }
  22805. _this.clearSelections();
  22806. // Set the selection list for the panes so that the correct
  22807. // rows can be reselected and in the right order
  22808. _this.s.selectionList =
  22809. data.searchPanes.selectionList ?
  22810. data.searchPanes.selectionList :
  22811. [];
  22812. // Find the panes that match from the state and the actual instance
  22813. if (data.searchPanes.panes) {
  22814. for (var _i = 0, _a = data.searchPanes.panes; _i < _a.length; _i++) {
  22815. var loadedPane = _a[_i];
  22816. for (var _b = 0, _c = _this.s.panes; _b < _c.length; _b++) {
  22817. var pane = _c[_b];
  22818. if (loadedPane.id === pane.s.index && pane.s.dtPane) {
  22819. // Set the value of the searchbox
  22820. pane.dom.searchBox.val(loadedPane.searchTerm);
  22821. // Set the value of the order
  22822. pane.s.dtPane.order(loadedPane.order);
  22823. }
  22824. }
  22825. }
  22826. }
  22827. _this._makeSelections(_this.s.selectionList);
  22828. });
  22829. };
  22830. /**
  22831. * Updates the selectionList when cascade is not in place
  22832. *
  22833. * Overridden in SearchPanesST
  22834. */
  22835. SearchPanes.prototype._updateSelection = function () {
  22836. this.s.selectionList = [];
  22837. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  22838. var pane = _a[_i];
  22839. if (pane.s.dtPane) {
  22840. var rows = pane.s.dtPane.rows({ selected: true }).data().toArray().map(function (el) { return el.filter; });
  22841. if (rows.length) {
  22842. this.s.selectionList.push({
  22843. column: pane.s.index,
  22844. rows: rows
  22845. });
  22846. }
  22847. }
  22848. }
  22849. };
  22850. /**
  22851. * Attach the panes, buttons and title to the document
  22852. */
  22853. SearchPanes.prototype._attach = function () {
  22854. var _this = this;
  22855. this.dom.titleRow
  22856. .removeClass(this.classes.hide)
  22857. .detach()
  22858. .append(this.dom.title);
  22859. // If the clear button is permitted attach it
  22860. if (this.c.clear) {
  22861. this.dom.clearAll
  22862. .appendTo(this.dom.titleRow)
  22863. .off('click.dtsps')
  22864. .on('click.dtsps', function () { return _this.clearSelections(); });
  22865. }
  22866. if (this.c.collapse) {
  22867. this.dom.showAll.appendTo(this.dom.titleRow);
  22868. this.dom.collapseAll.appendTo(this.dom.titleRow);
  22869. this._setCollapseListener();
  22870. }
  22871. // Attach the container for each individual pane to the overall container
  22872. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  22873. var pane = _a[_i];
  22874. this.dom.panes.append(pane.dom.container);
  22875. }
  22876. // Attach everything to the document
  22877. this.dom.container
  22878. .text('')
  22879. .removeClass(this.classes.hide)
  22880. .append(this.dom.titleRow)
  22881. .append(this.dom.panes);
  22882. // WORKAROUND
  22883. this.s.panes.forEach(function (pane) { return pane.setListeners(); });
  22884. if ($$1('div.' + this.classes.container).length === 0) {
  22885. this.dom.container.prependTo(this.s.dt);
  22886. }
  22887. };
  22888. /**
  22889. * If there are no panes to display then this method is called to either
  22890. * display a message in their place or hide them completely.
  22891. */
  22892. SearchPanes.prototype._attachMessage = function () {
  22893. // Create a message to display on the screen
  22894. var message;
  22895. try {
  22896. message = this.s.dt.i18n('searchPanes.emptyPanes', this.c.i18n.emptyPanes);
  22897. }
  22898. catch (error) {
  22899. message = null;
  22900. }
  22901. // If the message is an empty string then searchPanes.emptyPanes is undefined,
  22902. // therefore the pane container should be removed from the display
  22903. if (message === null) {
  22904. this.dom.container.addClass(this.classes.hide);
  22905. this.dom.titleRow.removeClass(this.classes.hide);
  22906. return;
  22907. }
  22908. // Otherwise display the message
  22909. this.dom.container.removeClass(this.classes.hide);
  22910. this.dom.titleRow.addClass(this.classes.hide);
  22911. this.dom.emptyMessage.html(message).appendTo(this.dom.container);
  22912. };
  22913. /**
  22914. * Attaches the panes to the document and displays a message or hides if there are none
  22915. */
  22916. SearchPanes.prototype._attachPaneContainer = function () {
  22917. // If a pane is to be displayed then attach the normal pane output
  22918. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  22919. var pane = _a[_i];
  22920. if (pane.s.displayed === true) {
  22921. this._attach();
  22922. return;
  22923. }
  22924. }
  22925. // Otherwise attach the custom message or remove the container from the display
  22926. this._attachMessage();
  22927. };
  22928. /**
  22929. * Checks which panes are collapsed and then performs relevant actions to the collapse/show all buttons
  22930. */
  22931. SearchPanes.prototype._checkCollapse = function () {
  22932. var disableClose = true;
  22933. var disableShow = true;
  22934. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  22935. var pane = _a[_i];
  22936. if (pane.s.displayed) {
  22937. // If the pane is not collapsed
  22938. if (!pane.dom.collapseButton.hasClass(pane.classes.rotated)) {
  22939. // Enable the collapse all button
  22940. this.dom.collapseAll.removeClass(this.classes.disabledButton).removeAttr('disabled');
  22941. disableClose = false;
  22942. }
  22943. else {
  22944. // Otherwise enable the show all button
  22945. this.dom.showAll.removeClass(this.classes.disabledButton).removeAttr('disabled');
  22946. disableShow = false;
  22947. }
  22948. }
  22949. }
  22950. // If this flag is still true, no panes are open so the close button should be disabled
  22951. if (disableClose) {
  22952. this.dom.collapseAll.addClass(this.classes.disabledButton).attr('disabled', 'true');
  22953. }
  22954. // If this flag is still true, no panes are closed so the show button should be disabled
  22955. if (disableShow) {
  22956. this.dom.showAll.addClass(this.classes.disabledButton).attr('disabled', 'true');
  22957. }
  22958. };
  22959. /**
  22960. * Attaches the message to the document but does not add any panes
  22961. */
  22962. SearchPanes.prototype._checkMessage = function () {
  22963. // If a pane is to be displayed then attach the normal pane output
  22964. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  22965. var pane = _a[_i];
  22966. if (pane.s.displayed === true) {
  22967. // Ensure that the empty message is removed if a pane is displayed
  22968. this.dom.emptyMessage.detach();
  22969. this.dom.titleRow.removeClass(this.classes.hide);
  22970. return;
  22971. }
  22972. }
  22973. // Otherwise attach the custom message or remove the container from the display
  22974. this._attachMessage();
  22975. };
  22976. /**
  22977. * Collapses all of the panes
  22978. */
  22979. SearchPanes.prototype._collapseAll = function () {
  22980. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  22981. var pane = _a[_i];
  22982. pane.collapse();
  22983. }
  22984. };
  22985. /**
  22986. * Finds a pane based upon the name of that pane
  22987. *
  22988. * @param name string representing the name of the pane
  22989. * @returns SearchPane The pane which has that name
  22990. */
  22991. SearchPanes.prototype._findPane = function (name) {
  22992. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  22993. var pane = _a[_i];
  22994. if (name === pane.s.name) {
  22995. return pane;
  22996. }
  22997. }
  22998. };
  22999. /**
  23000. * Gets the selection list from the previous state and stores it in the selectionList Property
  23001. */
  23002. SearchPanes.prototype._getState = function () {
  23003. var loadedFilter = this.s.dt.state.loaded();
  23004. if (loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.selectionList) {
  23005. this.s.selectionList = loadedFilter.searchPanes.selectionList;
  23006. }
  23007. };
  23008. SearchPanes.prototype._makeSelections = function (selectList) {
  23009. for (var _i = 0, selectList_1 = selectList; _i < selectList_1.length; _i++) {
  23010. var selection = selectList_1[_i];
  23011. var pane = void 0;
  23012. for (var _a = 0, _b = this.s.panes; _a < _b.length; _a++) {
  23013. var p = _b[_a];
  23014. if (p.s.index === selection.column) {
  23015. pane = p;
  23016. break;
  23017. }
  23018. }
  23019. if (pane && pane.s.dtPane) {
  23020. for (var j = 0; j < pane.s.dtPane.rows().data().toArray().length; j++) {
  23021. if (selection.rows.includes(typeof pane.s.dtPane.row(j).data().filter === 'function' ?
  23022. pane.s.dtPane.cell(j, 0).data() :
  23023. pane.s.dtPane.row(j).data().filter)) {
  23024. pane.s.dtPane.row(j).select();
  23025. }
  23026. }
  23027. pane.updateTable();
  23028. }
  23029. }
  23030. };
  23031. /**
  23032. * Declares the instances of individual searchpanes dependant on the number of columns.
  23033. * It is necessary to run this once preInit has completed otherwise no panes will be
  23034. * created as the column count will be 0.
  23035. *
  23036. * @param table the DataTable api for the parent table
  23037. * @param paneSettings the settings passed into the constructor
  23038. * @param opts the options passed into the constructor
  23039. */
  23040. SearchPanes.prototype._paneDeclare = function (table, paneSettings, opts) {
  23041. var _this = this;
  23042. // Create Panes
  23043. table
  23044. .columns(this.c.columns.length > 0 ? this.c.columns : undefined)
  23045. .eq(0)
  23046. .each(function (idx) {
  23047. _this.s.panes.push(new _this.s.paneClass(paneSettings, opts, idx, _this.dom.panes));
  23048. });
  23049. // If there is any extra custom panes defined then create panes for them too
  23050. var colCount = table.columns().eq(0).toArray().length;
  23051. for (var i = 0; i < this.c.panes.length; i++) {
  23052. var id = colCount + i;
  23053. this.s.panes.push(new this.s.paneClass(paneSettings, opts, id, this.dom.panes, this.c.panes[i]));
  23054. }
  23055. // If a custom ordering is being used
  23056. if (this.c.order.length > 0) {
  23057. // Make a new Array of panes based upon the order
  23058. this.s.panes = this.c.order.map(function (name) { return _this._findPane(name); });
  23059. }
  23060. // If this internal property is true then the DataTable has been initialised already
  23061. if (this.s.dt.settings()[0]._bInitComplete) {
  23062. this._startup(table);
  23063. }
  23064. else {
  23065. // Otherwise add the paneStartup function to the list of functions
  23066. // that are to be run when the table is initialised. This will garauntee that the
  23067. // panes are initialised before the init event and init Complete callback is fired
  23068. if (dataTable$1.versionCheck('2')) {
  23069. this.s.dt.settings()[0].aoInitComplete.push(function () { return _this._startup(table); });
  23070. }
  23071. else {
  23072. this.s.dt.settings()[0].aoInitComplete.push({
  23073. fn: function () { return _this._startup(table); }
  23074. });
  23075. }
  23076. }
  23077. };
  23078. /**
  23079. * Sets the listeners for the collapse and show all buttons
  23080. * Also sets and performs checks on current panes to see if they are collapsed
  23081. */
  23082. SearchPanes.prototype._setCollapseListener = function () {
  23083. var _this = this;
  23084. this.dom.collapseAll
  23085. .off('click.dtsps')
  23086. .on('click.dtsps', function () {
  23087. _this._collapseAll();
  23088. _this.dom.collapseAll.addClass(_this.classes.disabledButton).attr('disabled', 'true');
  23089. _this.dom.showAll.removeClass(_this.classes.disabledButton).removeAttr('disabled');
  23090. _this.s.dt.state.save();
  23091. });
  23092. this.dom.showAll
  23093. .off('click.dtsps')
  23094. .on('click.dtsps', function () {
  23095. _this._showAll();
  23096. _this.dom.showAll.addClass(_this.classes.disabledButton).attr('disabled', 'true');
  23097. _this.dom.collapseAll.removeClass(_this.classes.disabledButton).removeAttr('disabled');
  23098. _this.s.dt.state.save();
  23099. });
  23100. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  23101. var pane = _a[_i];
  23102. // We want to make the same check whenever there is a collapse/expand
  23103. pane.dom.topRow.off('collapse.dtsps').on('collapse.dtsps', function () { return _this._checkCollapse(); });
  23104. }
  23105. this._checkCollapse();
  23106. };
  23107. /**
  23108. * Shows all of the panes
  23109. */
  23110. SearchPanes.prototype._showAll = function () {
  23111. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  23112. var pane = _a[_i];
  23113. pane.show();
  23114. }
  23115. };
  23116. /**
  23117. * Initialises the tables previous/preset selections and initialises callbacks for events
  23118. *
  23119. * @param table the parent table for which the searchPanes are being created
  23120. */
  23121. SearchPanes.prototype._startup = function (table) {
  23122. var _this = this;
  23123. // Attach clear button and title bar to the document
  23124. this._attach();
  23125. this.dom.panes.empty();
  23126. var hostSettings = this.s.dt.settings()[0];
  23127. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  23128. var pane = _a[_i];
  23129. pane.rebuildPane(Object.keys(this.s.serverData).length > 0 ? this.s.serverData : undefined);
  23130. this.dom.panes.append(pane.dom.container);
  23131. }
  23132. // If the layout is set to auto then the panes need to be resized to their best fit
  23133. if (this.c.layout === 'auto') {
  23134. this.resizePanes();
  23135. }
  23136. var loadedFilter = this.s.dt.state.loaded();
  23137. // Reset the paging if that has been saved in the state
  23138. if (!this.s.stateRead && loadedFilter) {
  23139. this.s.dt
  23140. .page(loadedFilter.start / this.s.dt.page.len())
  23141. .draw('page');
  23142. }
  23143. this.s.stateRead = true;
  23144. this._checkMessage();
  23145. // When a draw is called on the DataTable, update all of the panes incase the data in the DataTable has changed
  23146. table.on('preDraw.dtsps', function () {
  23147. // Check that the panes are not updating to avoid infinite loops
  23148. // Also check that this draw is not due to paging
  23149. if (!_this.s.updating && !_this.s.paging) {
  23150. _this._updateFilterCount();
  23151. _this._updateSelection();
  23152. }
  23153. // Paging flag reset - we only need to dodge the draw once
  23154. _this.s.paging = false;
  23155. });
  23156. $$1(window).on('resize.dtsps', dataTable$1.util.throttle(function () { return _this.resizePanes(); }));
  23157. // Whenever a state save occurs store the selection list in the state object
  23158. this.s.dt.on('stateSaveParams.dtsps', function (e, settings, data) {
  23159. if (settings !== hostSettings) {
  23160. return;
  23161. }
  23162. if (data.searchPanes === undefined) {
  23163. data.searchPanes = {};
  23164. }
  23165. data.searchPanes.selectionList = _this.s.selectionList;
  23166. });
  23167. this._stateLoadListener();
  23168. // Listener for paging on main table
  23169. table.off('page.dtsps page-nc.dtsps').on('page.dtsps page-nc.dtsps', function (e, s) {
  23170. _this.s.paging = true;
  23171. // This is an indicator to any selection tracking classes that paging has occured
  23172. // It has to happen here so that we don't stack event listeners unnecessarily
  23173. // The value is only ever set back to false in the SearchPanesST class
  23174. // Equally it is never read in this class
  23175. _this.s.pagingST = true;
  23176. _this.s.page = _this.s.dt.page();
  23177. });
  23178. if (this.s.dt.page.info().serverSide) {
  23179. table.off('preXhr.dtsps').on('preXhr.dtsps', function (e, settings, data) {
  23180. if (settings !== hostSettings) {
  23181. return;
  23182. }
  23183. if (!data.searchPanes) {
  23184. data.searchPanes = {};
  23185. }
  23186. if (!data.searchPanes_null) {
  23187. data.searchPanes_null = {};
  23188. }
  23189. // Count how many filters are being applied
  23190. var filterCount = 0;
  23191. for (var _i = 0, _a = _this.s.panes; _i < _a.length; _i++) {
  23192. var pane = _a[_i];
  23193. var src = _this.s.dt.column(pane.s.index).dataSrc();
  23194. if (!data.searchPanes[src]) {
  23195. data.searchPanes[src] = {};
  23196. }
  23197. if (!data.searchPanes_null[src]) {
  23198. data.searchPanes_null[src] = {};
  23199. }
  23200. if (pane.s.dtPane) {
  23201. var rowData = pane.s.dtPane.rows({ selected: true }).data().toArray();
  23202. for (var i = 0; i < rowData.length; i++) {
  23203. data.searchPanes[src][i] = rowData[i].filter;
  23204. if (!data.searchPanes[src][i]) {
  23205. data.searchPanes_null[src][i] = true;
  23206. }
  23207. else {
  23208. data.searchPanes_null[src][i] = false;
  23209. }
  23210. filterCount++;
  23211. }
  23212. }
  23213. }
  23214. // If there is a filter to be applied, then we need to read from the start of the result set
  23215. // and set the paging to 0. This matches the behaviour of client side processing
  23216. if (filterCount > 0) {
  23217. // If the number of filters has changed we need to read from the start of the
  23218. // result set and reset the paging
  23219. if (filterCount !== _this.s.filterCount) {
  23220. data.start = 0;
  23221. _this.s.page = 0;
  23222. }
  23223. // Otherwise it is a paging request and we need to read from whatever the paging has been set to
  23224. else {
  23225. data.start = _this.s.page * _this.s.dt.page.len();
  23226. }
  23227. _this.s.dt.page(_this.s.page);
  23228. _this.s.filterCount = filterCount;
  23229. }
  23230. if (_this.s.selectionList.length > 0) {
  23231. data.searchPanesLast = _this.s.dt
  23232. .column(_this.s.selectionList[_this.s.selectionList.length - 1].column)
  23233. .dataSrc();
  23234. }
  23235. // Config options that will change how the querying is done
  23236. data.searchPanes_options = {
  23237. cascade: _this.c.cascadePanes,
  23238. viewCount: _this.c.viewCount,
  23239. viewTotal: _this.c.viewTotal
  23240. };
  23241. });
  23242. }
  23243. else {
  23244. table.on('preXhr.dtsps', function () { return _this.s.panes.forEach(function (pane) { return pane.clearData(); }); });
  23245. }
  23246. // If the data is reloaded from the server then it is possible that it has changed completely,
  23247. // so we need to rebuild the panes
  23248. this.s.dt.on('xhr.dtsps', function (e, settings) {
  23249. if (settings.nTable !== _this.s.dt.table().node()) {
  23250. return;
  23251. }
  23252. if (!_this.s.dt.page.info().serverSide) {
  23253. var processing_1 = false;
  23254. _this.s.dt.one('preDraw.dtsps', function () {
  23255. if (processing_1) {
  23256. return;
  23257. }
  23258. var page = _this.s.dt.page();
  23259. processing_1 = true;
  23260. _this.s.updating = true;
  23261. _this.dom.panes.empty();
  23262. for (var _i = 0, _a = _this.s.panes; _i < _a.length; _i++) {
  23263. var pane = _a[_i];
  23264. pane.clearData(); // Clears all of the bins and will mean that the data has to be re-read
  23265. // Pass a boolean to say whether this is the last choice made for maintaining selections
  23266. // when rebuilding
  23267. pane.rebuildPane(undefined, true);
  23268. _this.dom.panes.append(pane.dom.container);
  23269. }
  23270. if (!_this.s.dt.page.info().serverSide) {
  23271. _this.s.dt.draw();
  23272. }
  23273. _this.s.updating = false;
  23274. _this._updateSelection();
  23275. _this._checkMessage();
  23276. _this.s.dt.one('draw.dtsps', function () {
  23277. _this.s.updating = true;
  23278. _this.s.dt.page(page).draw(false);
  23279. _this.s.updating = false;
  23280. });
  23281. });
  23282. }
  23283. });
  23284. // PreSelect any selections which have been defined using the preSelect option
  23285. var selectList = this.c.preSelect;
  23286. if (loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.selectionList) {
  23287. selectList = loadedFilter.searchPanes.selectionList;
  23288. }
  23289. this._makeSelections(selectList);
  23290. // Update the title bar to show how many filters have been selected
  23291. this._updateFilterCount();
  23292. // If the table is destroyed and restarted then clear the selections so that they do not persist.
  23293. table.on('destroy.dtsps', function (e, settings) {
  23294. if (settings !== hostSettings) {
  23295. return;
  23296. }
  23297. for (var _i = 0, _a = _this.s.panes; _i < _a.length; _i++) {
  23298. var pane = _a[_i];
  23299. pane.destroy();
  23300. }
  23301. table.off('.dtsps');
  23302. _this.dom.showAll.off('.dtsps');
  23303. _this.dom.clearAll.off('.dtsps');
  23304. _this.dom.collapseAll.off('.dtsps');
  23305. $$1(table.table().node()).off('.dtsps');
  23306. _this.dom.container.detach();
  23307. _this.clearSelections();
  23308. });
  23309. if (this.c.collapse) {
  23310. this._setCollapseListener();
  23311. }
  23312. // When the clear All button has been pressed clear all of the selections in the panes
  23313. if (this.c.clear) {
  23314. this.dom.clearAll
  23315. .off('click.dtsps')
  23316. .on('click.dtsps', function () { return _this.clearSelections(); });
  23317. }
  23318. hostSettings._searchPanes = this;
  23319. // This state save is required so that state is maintained over multiple refreshes if no actions are made
  23320. this.s.dt.state.save();
  23321. };
  23322. /**
  23323. * Updates the number of filters that have been applied in the title
  23324. */
  23325. SearchPanes.prototype._updateFilterCount = function () {
  23326. var filterCount = 0;
  23327. var tableSearch = 0;
  23328. // Add the number of all of the filters throughout the panes
  23329. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  23330. var pane = _a[_i];
  23331. if (pane.s.dtPane) {
  23332. filterCount += pane.getPaneCount();
  23333. if (pane.s.dtPane.search()) {
  23334. tableSearch++;
  23335. }
  23336. }
  23337. }
  23338. // Run the message through the internationalisation method to improve readability
  23339. this.dom.title.html(this.s.dt.i18n('searchPanes.title', this.c.i18n.title, filterCount));
  23340. if (this.c.filterChanged && typeof this.c.filterChanged === 'function') {
  23341. this.c.filterChanged.call(this.s.dt, filterCount);
  23342. }
  23343. if (filterCount === 0 && tableSearch === 0) {
  23344. this.dom.clearAll.addClass(this.classes.disabledButton).attr('disabled', 'true');
  23345. }
  23346. else {
  23347. this.dom.clearAll.removeClass(this.classes.disabledButton).removeAttr('disabled');
  23348. }
  23349. };
  23350. SearchPanes.version = '2.3.1';
  23351. SearchPanes.classes = {
  23352. clear: 'dtsp-clear',
  23353. clearAll: 'dtsp-clearAll',
  23354. collapseAll: 'dtsp-collapseAll',
  23355. container: 'dtsp-searchPanes',
  23356. disabledButton: 'dtsp-disabledButton',
  23357. emptyMessage: 'dtsp-emptyMessage',
  23358. hide: 'dtsp-hidden',
  23359. panes: 'dtsp-panesContainer',
  23360. search: 'dtsp-search',
  23361. showAll: 'dtsp-showAll',
  23362. title: 'dtsp-title',
  23363. titleRow: 'dtsp-titleRow'
  23364. };
  23365. // Define SearchPanes default options
  23366. SearchPanes.defaults = {
  23367. cascadePanes: false,
  23368. clear: true,
  23369. collapse: true,
  23370. columns: [],
  23371. container: function (dt) {
  23372. return dt.table().container();
  23373. },
  23374. filterChanged: undefined,
  23375. i18n: {
  23376. clearMessage: 'Clear All',
  23377. clearPane: '&times;',
  23378. collapse: {
  23379. 0: 'SearchPanes',
  23380. _: 'SearchPanes (%d)'
  23381. },
  23382. collapseMessage: 'Collapse All',
  23383. count: '{total}',
  23384. emptyMessage: '<em>No data</em>',
  23385. emptyPanes: 'No SearchPanes',
  23386. loadMessage: 'Loading Search Panes...',
  23387. showMessage: 'Show All',
  23388. title: 'Filters Active - %d'
  23389. },
  23390. layout: 'auto',
  23391. order: [],
  23392. panes: [],
  23393. preSelect: [],
  23394. viewCount: true,
  23395. viewTotal: false
  23396. };
  23397. return SearchPanes;
  23398. }());
  23399. var __extends = (window && window.__extends) || (function () {
  23400. var extendStatics = function (d, b) {
  23401. extendStatics = Object.setPrototypeOf ||
  23402. ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
  23403. function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
  23404. return extendStatics(d, b);
  23405. };
  23406. return function (d, b) {
  23407. extendStatics(d, b);
  23408. function __() { this.constructor = d; }
  23409. d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
  23410. };
  23411. })();
  23412. var SearchPanesST = /** @class */ (function (_super) {
  23413. __extends(SearchPanesST, _super);
  23414. function SearchPanesST(paneSettings, opts, fromPreInit) {
  23415. if (fromPreInit === void 0) { fromPreInit = false; }
  23416. var _this = this;
  23417. var paneClass;
  23418. if (opts.cascadePanes && opts.viewTotal) {
  23419. paneClass = SearchPaneCascadeViewTotal;
  23420. }
  23421. else if (opts.cascadePanes) {
  23422. paneClass = SearchPaneCascade;
  23423. }
  23424. else if (opts.viewTotal) {
  23425. paneClass = SearchPaneViewTotal;
  23426. }
  23427. _this = _super.call(this, paneSettings, opts, fromPreInit, paneClass) || this;
  23428. var dt = _this.s.dt;
  23429. var loadedFilter = dt.state.loaded();
  23430. var loadFn = function () { return _this._initSelectionListeners(true, loadedFilter && loadedFilter.searchPanes && loadedFilter.searchPanes.selectionList ?
  23431. loadedFilter.searchPanes.selectionList :
  23432. _this.c.preSelect); };
  23433. if (dt.settings()[0]._bInitComplete) {
  23434. loadFn();
  23435. }
  23436. else {
  23437. dt.off('init.dtsps').on('init.dtsps', loadFn);
  23438. }
  23439. return _this;
  23440. }
  23441. /**
  23442. * Ensures that the correct selection listeners are set for selection tracking
  23443. *
  23444. * @param preSelect Any values that are to be preselected
  23445. */
  23446. SearchPanesST.prototype._initSelectionListeners = function (isPreselect, preSelect) {
  23447. if (isPreselect === void 0) { isPreselect = true; }
  23448. if (preSelect === void 0) { preSelect = []; }
  23449. if (isPreselect) {
  23450. this.s.selectionList = preSelect;
  23451. }
  23452. // Set selection listeners for each pane
  23453. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  23454. var pane = _a[_i];
  23455. if (pane.s.displayed) {
  23456. pane.s.dtPane
  23457. .off('select.dtsp')
  23458. .on('select.dtsp', this._update(pane))
  23459. .off('deselect.dtsp')
  23460. .on('deselect.dtsp', this._updateTimeout(pane));
  23461. }
  23462. }
  23463. // Update on every draw
  23464. this.s.dt.off('draw.dtsps').on('draw.dtsps', this._update());
  23465. // Also update right now as table has just initialised
  23466. this._updateSelectionList();
  23467. };
  23468. /**
  23469. * Retrieve the total values from the server data
  23470. */
  23471. SearchPanesST.prototype._serverTotals = function () {
  23472. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  23473. var pane = _a[_i];
  23474. if (pane.s.colOpts.show) {
  23475. var colTitle = this.s.dt.column(pane.s.index).dataSrc();
  23476. var blockVT = true;
  23477. // If any of the counts are not equal to the totals filtering must be active
  23478. if (this.s.serverData.searchPanes.options[colTitle]) {
  23479. for (var _b = 0, _c = this.s.serverData.searchPanes.options[colTitle]; _b < _c.length; _b++) {
  23480. var data = _c[_b];
  23481. if (data.total !== data.count) {
  23482. blockVT = false;
  23483. break;
  23484. }
  23485. }
  23486. }
  23487. // Set if filtering is present on the pane and populate the data arrays
  23488. pane.s.filteringActive = !blockVT;
  23489. pane._serverPopulate(this.s.serverData);
  23490. }
  23491. }
  23492. };
  23493. /**
  23494. * Set's the function that is to be performed when a state is loaded
  23495. *
  23496. * Overrides the method in SearchPanes
  23497. */
  23498. SearchPanesST.prototype._stateLoadListener = function () {
  23499. var _this = this;
  23500. var stateLoadFunction = function (e, settings, data) {
  23501. if (data.searchPanes === undefined) {
  23502. return;
  23503. }
  23504. // Set the selection list for the panes so that the correct
  23505. // rows can be reselected and in the right order
  23506. _this.s.selectionList =
  23507. data.searchPanes.selectionList ?
  23508. data.searchPanes.selectionList :
  23509. [];
  23510. // Find the panes that match from the state and the actual instance
  23511. if (data.searchPanes.panes) {
  23512. for (var _i = 0, _a = data.searchPanes.panes; _i < _a.length; _i++) {
  23513. var loadedPane = _a[_i];
  23514. for (var _b = 0, _c = _this.s.panes; _b < _c.length; _b++) {
  23515. var pane = _c[_b];
  23516. if (loadedPane.id === pane.s.index && pane.s.dtPane) {
  23517. // Set the value of the searchbox
  23518. pane.dom.searchBox.val(loadedPane.searchTerm);
  23519. // Set the value of the order
  23520. pane.s.dtPane.order(loadedPane.order);
  23521. }
  23522. }
  23523. }
  23524. }
  23525. _this._updateSelectionList();
  23526. };
  23527. this.s.dt.off('stateLoadParams.dtsps', stateLoadFunction).on('stateLoadParams.dtsps', stateLoadFunction);
  23528. };
  23529. /**
  23530. * Remove the function's actions when using cascade
  23531. *
  23532. * Overrides the method in SearchPanes
  23533. */
  23534. SearchPanesST.prototype._updateSelection = function () {
  23535. return;
  23536. };
  23537. /**
  23538. * Returns a function that updates the selection list based on a specific pane
  23539. * Also clears the timeout to stop the deselect from running
  23540. *
  23541. * @param pane the pane that is to have it's selections loaded
  23542. */
  23543. SearchPanesST.prototype._update = function (pane) {
  23544. var _this = this;
  23545. if (pane === void 0) { pane = undefined; }
  23546. return function () {
  23547. if (pane) {
  23548. clearTimeout(pane.s.deselectTimeout);
  23549. }
  23550. _this._updateSelectionList(pane);
  23551. };
  23552. };
  23553. /**
  23554. * Returns a function that updates the selection list based on a specific pane
  23555. * Also sets a timeout incase a select is about to be made
  23556. *
  23557. * @param pane the pane that is to have it's selections loaded
  23558. */
  23559. SearchPanesST.prototype._updateTimeout = function (pane) {
  23560. var _this = this;
  23561. if (pane === void 0) { pane = undefined; }
  23562. return function () { return pane ?
  23563. pane.s.deselectTimeout = setTimeout(function () { return _this._updateSelectionList(pane); }, 50) :
  23564. _this._updateSelectionList(); };
  23565. };
  23566. /**
  23567. * Updates the selection list to include the latest selections for a given pane
  23568. *
  23569. * @param index The index of the pane that is to be updated
  23570. * @param selected Which rows are selected within the pane
  23571. */
  23572. SearchPanesST.prototype._updateSelectionList = function (paneIn) {
  23573. if (paneIn === void 0) { paneIn = undefined; }
  23574. // Bail if any of these flags are set
  23575. if (this.s.pagingST) {
  23576. // Reset pagingST flag
  23577. this.s.pagingST = false;
  23578. return;
  23579. }
  23580. else if (this.s.updating || paneIn && paneIn.s.serverSelecting) {
  23581. return;
  23582. }
  23583. if (paneIn !== undefined) {
  23584. if (this.s.dt.page.info().serverSide) {
  23585. paneIn._updateSelection();
  23586. }
  23587. // Get filter values for all of the rows and the selections
  23588. var rows = paneIn.s.dtPane.rows({ selected: true }).data().toArray().map(function (el) { return el.filter; });
  23589. this.s.selectionList = this.s.selectionList.filter(function (selection) { return selection.column !== paneIn.s.index; });
  23590. if (rows.length > 0) {
  23591. this.s.selectionList.push({
  23592. column: paneIn.s.index,
  23593. rows: rows
  23594. });
  23595. paneIn.dom.clear.removeClass(this.classes.disabledButton).removeAttr('disabled');
  23596. }
  23597. else {
  23598. paneIn.dom.clear.addClass(this.classes.disabledButton).attr('disabled', 'true');
  23599. }
  23600. if (this.s.dt.page.info().serverSide) {
  23601. this.s.dt.draw(false);
  23602. }
  23603. }
  23604. this._remakeSelections();
  23605. this._updateFilterCount();
  23606. };
  23607. /**
  23608. * Remake the selections that were present before new data or calculations have occured
  23609. */
  23610. SearchPanesST.prototype._remakeSelections = function () {
  23611. var currPane;
  23612. var pane;
  23613. this.s.updating = true;
  23614. if (!this.s.dt.page.info().serverSide) {
  23615. var tmpSL = this.s.selectionList;
  23616. var anotherFilter = false;
  23617. this.clearSelections();
  23618. this.s.dt.draw(false);
  23619. // When there are no selections present if the length of the data does not match the searched data
  23620. // then another filter is present
  23621. if (this.s.dt.rows().toArray()[0].length > this.s.dt.rows({ search: 'applied' }).toArray()[0].length) {
  23622. anotherFilter = true;
  23623. }
  23624. this.s.selectionList = tmpSL;
  23625. // Update the rows in each pane
  23626. for (var _i = 0, _a = this.s.panes; _i < _a.length; _i++) {
  23627. pane = _a[_i];
  23628. if (pane.s.displayed) {
  23629. pane.s.filteringActive = anotherFilter;
  23630. pane.updateRows();
  23631. }
  23632. }
  23633. for (var _b = 0, _c = this.s.selectionList; _b < _c.length; _b++) {
  23634. var selection = _c[_b];
  23635. pane = null;
  23636. for (var _d = 0, _e = this.s.panes; _d < _e.length; _d++) {
  23637. var paneCheck = _e[_d];
  23638. if (paneCheck.s.index === selection.column) {
  23639. pane = paneCheck;
  23640. break;
  23641. }
  23642. }
  23643. if (!pane.s.dtPane) {
  23644. continue;
  23645. }
  23646. var ids = pane.s.dtPane.rows().indexes().toArray();
  23647. // Select the rows that are present in the selection list
  23648. for (var i = 0; i < selection.rows.length; i++) {
  23649. var rowFound = false;
  23650. for (var _f = 0, ids_1 = ids; _f < ids_1.length; _f++) {
  23651. var id = ids_1[_f];
  23652. var currRow = pane.s.dtPane.row(id);
  23653. var data = currRow.data();
  23654. if (selection.rows[i] === data.filter) {
  23655. currRow.select();
  23656. rowFound = true;
  23657. }
  23658. }
  23659. if (!rowFound) {
  23660. selection.rows.splice(i, 1);
  23661. i--;
  23662. }
  23663. }
  23664. pane.s.selections = selection.rows;
  23665. // If there are no rows selected then don't bother continuing past here
  23666. // Will just increase processing time and skew the rows that are shown in the table
  23667. if (selection.rows.length === 0) {
  23668. continue;
  23669. }
  23670. // Update the table to display the current results
  23671. this.s.dt.draw();
  23672. var filteringActive = false;
  23673. var filterCount = 0;
  23674. var prevSelectedPanes = 0;
  23675. var selectedPanes = 0;
  23676. // Add the number of all of the filters throughout the panes
  23677. for (var _g = 0, _h = this.s.panes; _g < _h.length; _g++) {
  23678. currPane = _h[_g];
  23679. if (currPane.s.dtPane) {
  23680. filterCount += currPane.getPaneCount();
  23681. if (filterCount > prevSelectedPanes) {
  23682. selectedPanes++;
  23683. prevSelectedPanes = filterCount;
  23684. }
  23685. }
  23686. }
  23687. filteringActive = filterCount > 0;
  23688. for (var _j = 0, _k = this.s.panes; _j < _k.length; _j++) {
  23689. currPane = _k[_j];
  23690. if (currPane.s.displayed) {
  23691. // Set the filtering active flag
  23692. if (anotherFilter || pane.s.index !== currPane.s.index || !filteringActive) {
  23693. currPane.s.filteringActive = filteringActive || anotherFilter;
  23694. }
  23695. else if (selectedPanes === 1) {
  23696. currPane.s.filteringActive = false;
  23697. }
  23698. // Update the rows to show correct counts
  23699. if (currPane.s.index !== pane.s.index) {
  23700. currPane.updateRows();
  23701. }
  23702. }
  23703. }
  23704. }
  23705. // Update table to show final search results
  23706. this.s.dt.draw(false);
  23707. }
  23708. else {
  23709. // Identify the last pane to have a change in selection
  23710. if (this.s.selectionList.length > 0) {
  23711. pane = this.s.panes[this.s.selectionList[this.s.selectionList.length - 1].column];
  23712. }
  23713. // Update the rows of all of the other panes
  23714. for (var _l = 0, _m = this.s.panes; _l < _m.length; _l++) {
  23715. currPane = _m[_l];
  23716. if (currPane.s.displayed && (!pane || currPane.s.index !== pane.s.index)) {
  23717. currPane.updateRows();
  23718. }
  23719. }
  23720. }
  23721. this.s.updating = false;
  23722. };
  23723. return SearchPanesST;
  23724. }(SearchPanes));
  23725. /*! SearchPanes 2.3.1
  23726. * © SpryMedia Ltd - datatables.net/license
  23727. */
  23728. setJQuery$4($);
  23729. setJQuery($);
  23730. setJQuery$3($);
  23731. setJQuery$2($);
  23732. setJQuery$1($);
  23733. var dataTable = $.fn.dataTable;
  23734. // eslint-disable-next-line no-extra-parens
  23735. dataTable.SearchPanes = SearchPanes;
  23736. // eslint-disable-next-line no-extra-parens
  23737. DataTable.SearchPanes = SearchPanes;
  23738. // eslint-disable-next-line no-extra-parens
  23739. dataTable.SearchPanesST = SearchPanesST;
  23740. // eslint-disable-next-line no-extra-parens
  23741. DataTable.SearchPanesST = SearchPanesST;
  23742. // eslint-disable-next-line no-extra-parens
  23743. dataTable.SearchPane = SearchPane;
  23744. // eslint-disable-next-line no-extra-parens
  23745. DataTable.SearchPane = SearchPane;
  23746. // eslint-disable-next-line no-extra-parens
  23747. dataTable.SearchPaneViewTotal = SearchPaneViewTotal;
  23748. // eslint-disable-next-line no-extra-parens
  23749. DataTable.SearchPaneViewTotal = SearchPaneViewTotal;
  23750. // eslint-disable-next-line no-extra-parens
  23751. dataTable.SearchPaneCascade = SearchPaneCascade;
  23752. // eslint-disable-next-line no-extra-parens
  23753. DataTable.SearchPaneCascade = SearchPaneCascade;
  23754. // eslint-disable-next-line no-extra-parens
  23755. dataTable.SearchPaneCascadeViewTotal = SearchPaneCascadeViewTotal;
  23756. // eslint-disable-next-line no-extra-parens
  23757. DataTable.SearchPaneCascadeViewTotal = SearchPaneCascadeViewTotal;
  23758. // eslint-disable-next-line no-extra-parens
  23759. var apiRegister = $.fn.dataTable.Api.register;
  23760. apiRegister('searchPanes()', function () {
  23761. return this;
  23762. });
  23763. apiRegister('searchPanes.clearSelections()', function () {
  23764. return this.iterator('table', function (ctx) {
  23765. if (ctx._searchPanes) {
  23766. ctx._searchPanes.clearSelections();
  23767. }
  23768. });
  23769. });
  23770. apiRegister('searchPanes.rebuildPane()', function (targetIdx, maintainSelections) {
  23771. return this.iterator('table', function (ctx) {
  23772. if (ctx._searchPanes) {
  23773. ctx._searchPanes.rebuild(targetIdx, maintainSelections);
  23774. }
  23775. });
  23776. });
  23777. apiRegister('searchPanes.resizePanes()', function () {
  23778. var ctx = this.context[0];
  23779. return ctx._searchPanes ?
  23780. ctx._searchPanes.resizePanes() :
  23781. null;
  23782. });
  23783. apiRegister('searchPanes.container()', function () {
  23784. var ctx = this.context[0];
  23785. return ctx._searchPanes
  23786. ? ctx._searchPanes.getNode()
  23787. : null;
  23788. });
  23789. DataTable.ext.buttons.searchPanesClear = {
  23790. action: function (e, dt) {
  23791. dt.searchPanes.clearSelections();
  23792. },
  23793. text: 'Clear Panes'
  23794. };
  23795. DataTable.ext.buttons.searchPanes = {
  23796. action: function (e, dt, node, config) {
  23797. var _this = this;
  23798. var that = this;
  23799. if (!config._panes) {
  23800. // No SearchPanes on this button yet - initialise and show
  23801. this.processing(true);
  23802. setTimeout(function () {
  23803. _buttonSourced(dt, node, config);
  23804. _this.popover(config._panes.getNode(), {
  23805. align: 'container',
  23806. span: 'container'
  23807. });
  23808. config._panes.rebuild(undefined, true);
  23809. // Tables were hidden in the popover, need to be resized
  23810. $('table.dataTable', config._panes.getNode()).DataTable().columns.adjust();
  23811. that.processing(false);
  23812. }, 10);
  23813. }
  23814. else {
  23815. // Already got SP - show it
  23816. this.popover(config._panes.getNode(), {
  23817. align: 'container',
  23818. span: 'container'
  23819. });
  23820. config._panes.rebuild(undefined, true);
  23821. }
  23822. },
  23823. init: function (dt, node, config) {
  23824. dt.button(node).text(config.text || dt.i18n('searchPanes.collapse', 'SearchPanes', 0));
  23825. // For cases when we need to initialise the SearchPane immediately
  23826. if (dt.init().stateSave || config.delayInit === false) {
  23827. _buttonSourced(dt, node, config);
  23828. }
  23829. },
  23830. config: {},
  23831. text: '',
  23832. delayInit: true
  23833. };
  23834. function _buttonSourced(dt, node, config) {
  23835. var buttonOpts = $.extend({
  23836. filterChanged: function (count) {
  23837. dt.button(node).text(dt.i18n('searchPanes.collapse', dt.context[0].oLanguage.searchPanes !== undefined ?
  23838. dt.context[0].oLanguage.searchPanes.collapse :
  23839. dt.context[0]._searchPanes.c.i18n.collapse, count));
  23840. }
  23841. }, config.config);
  23842. var panes = buttonOpts && (buttonOpts.cascadePanes || buttonOpts.viewTotal) ?
  23843. new DataTable.SearchPanesST(dt, buttonOpts) :
  23844. new DataTable.SearchPanes(dt, buttonOpts);
  23845. dt.button(node).text(config.text || dt.i18n('searchPanes.collapse', panes.c.i18n.collapse, 0));
  23846. config._panes = panes;
  23847. }
  23848. function _init(settings, options, fromPre) {
  23849. if (options === void 0) { options = null; }
  23850. if (fromPre === void 0) { fromPre = false; }
  23851. var api = new dataTable.Api(settings);
  23852. var opts = options
  23853. ? options
  23854. : api.init().searchPanes || dataTable.defaults.searchPanes;
  23855. var searchPanes = opts && (opts.cascadePanes || opts.viewTotal) ?
  23856. new SearchPanesST(api, opts, fromPre) :
  23857. new SearchPanes(api, opts, fromPre);
  23858. var node = searchPanes.getNode();
  23859. return node;
  23860. }
  23861. // Attach a listener to the document which listens for DataTables initialisation
  23862. // events so we can automatically initialise
  23863. $(document).on('preInit.dt.dtsp', function (e, settings) {
  23864. if (e.namespace !== 'dt') {
  23865. return;
  23866. }
  23867. if (settings.oInit.searchPanes ||
  23868. DataTable.defaults.searchPanes) {
  23869. if (!settings._searchPanes) {
  23870. _init(settings, null, true);
  23871. }
  23872. }
  23873. });
  23874. // DataTables `dom` feature option
  23875. DataTable.ext.feature.push({
  23876. cFeature: 'P',
  23877. fnInit: _init
  23878. });
  23879. // DataTables 2 layout feature
  23880. if (DataTable.feature) {
  23881. DataTable.feature.register('searchPanes', _init);
  23882. }
  23883. })();
  23884. return DataTable;
  23885. }));
  23886. /*! Bootstrap 5 integration for DataTables' SearchPanes
  23887. * © SpryMedia Ltd - datatables.net/license
  23888. */
  23889. (function( factory ){
  23890. if ( typeof define === 'function' && define.amd ) {
  23891. // AMD
  23892. define( ['jquery', 'datatables.net-bs5', 'datatables.net-searchpanes'], function ( $ ) {
  23893. return factory( $, window, document );
  23894. } );
  23895. }
  23896. else if ( typeof exports === 'object' ) {
  23897. // CommonJS
  23898. var jq = require('jquery');
  23899. var cjsRequires = function (root, $) {
  23900. if ( ! $.fn.dataTable ) {
  23901. require('datatables.net-bs5')(root, $);
  23902. }
  23903. if ( ! $.fn.dataTable.SearchPanes ) {
  23904. require('datatables.net-searchpanes')(root, $);
  23905. }
  23906. };
  23907. if (typeof window === 'undefined') {
  23908. module.exports = function (root, $) {
  23909. if ( ! root ) {
  23910. // CommonJS environments without a window global must pass a
  23911. // root. This will give an error otherwise
  23912. root = window;
  23913. }
  23914. if ( ! $ ) {
  23915. $ = jq( root );
  23916. }
  23917. cjsRequires( root, $ );
  23918. return factory( $, root, root.document );
  23919. };
  23920. }
  23921. else {
  23922. cjsRequires( window, jq );
  23923. module.exports = factory( jq, window, window.document );
  23924. }
  23925. }
  23926. else {
  23927. // Browser
  23928. factory( jQuery, window, document );
  23929. }
  23930. }(function( $, window, document ) {
  23931. 'use strict';
  23932. var DataTable = $.fn.dataTable;
  23933. $.extend(true, DataTable.SearchPane.classes, {
  23934. buttonGroup: 'btn-group',
  23935. disabledButton: 'disabled',
  23936. narrow: 'col',
  23937. pane: {
  23938. container: 'table'
  23939. },
  23940. paneButton: 'btn btn-subtle',
  23941. pill: 'badge rounded-pill bg-secondary',
  23942. search: 'form-control search',
  23943. table: 'table table-sm table-borderless',
  23944. topRow: 'dtsp-topRow'
  23945. });
  23946. $.extend(true, DataTable.SearchPanes.classes, {
  23947. clearAll: 'dtsp-clearAll btn btn-subtle',
  23948. collapseAll: 'dtsp-collapseAll btn btn-subtle',
  23949. container: 'dtsp-searchPanes',
  23950. disabledButton: 'disabled',
  23951. panes: 'dtsp-panes dtsp-panesContainer',
  23952. search: DataTable.SearchPane.classes.search,
  23953. showAll: 'dtsp-showAll btn btn-subtle',
  23954. title: 'dtsp-title',
  23955. titleRow: 'dtsp-titleRow'
  23956. });
  23957. return DataTable;
  23958. }));
  23959. /*! Select for DataTables 2.0.1
  23960. * © SpryMedia Ltd - datatables.net/license/mit
  23961. */
  23962. (function( factory ){
  23963. if ( typeof define === 'function' && define.amd ) {
  23964. // AMD
  23965. define( ['jquery', 'datatables.net'], function ( $ ) {
  23966. return factory( $, window, document );
  23967. } );
  23968. }
  23969. else if ( typeof exports === 'object' ) {
  23970. // CommonJS
  23971. var jq = require('jquery');
  23972. var cjsRequires = function (root, $) {
  23973. if ( ! $.fn.dataTable ) {
  23974. require('datatables.net')(root, $);
  23975. }
  23976. };
  23977. if (typeof window === 'undefined') {
  23978. module.exports = function (root, $) {
  23979. if ( ! root ) {
  23980. // CommonJS environments without a window global must pass a
  23981. // root. This will give an error otherwise
  23982. root = window;
  23983. }
  23984. if ( ! $ ) {
  23985. $ = jq( root );
  23986. }
  23987. cjsRequires( root, $ );
  23988. return factory( $, root, root.document );
  23989. };
  23990. }
  23991. else {
  23992. cjsRequires( window, jq );
  23993. module.exports = factory( jq, window, window.document );
  23994. }
  23995. }
  23996. else {
  23997. // Browser
  23998. factory( jQuery, window, document );
  23999. }
  24000. }(function( $, window, document ) {
  24001. 'use strict';
  24002. var DataTable = $.fn.dataTable;
  24003. // Version information for debugger
  24004. DataTable.select = {};
  24005. DataTable.select.version = '2.0.1';
  24006. DataTable.select.init = function (dt) {
  24007. var ctx = dt.settings()[0];
  24008. if (!DataTable.versionCheck('2')) {
  24009. throw 'Warning: Select requires DataTables 2 or newer';
  24010. }
  24011. if (ctx._select) {
  24012. return;
  24013. }
  24014. var savedSelected = dt.state.loaded();
  24015. var selectAndSave = function (e, settings, data) {
  24016. if (data === null || data.select === undefined) {
  24017. return;
  24018. }
  24019. // Clear any currently selected rows, before restoring state
  24020. // None will be selected on first initialisation
  24021. if (dt.rows({ selected: true }).any()) {
  24022. dt.rows().deselect();
  24023. }
  24024. if (data.select.rows !== undefined) {
  24025. dt.rows(data.select.rows).select();
  24026. }
  24027. if (dt.columns({ selected: true }).any()) {
  24028. dt.columns().deselect();
  24029. }
  24030. if (data.select.columns !== undefined) {
  24031. dt.columns(data.select.columns).select();
  24032. }
  24033. if (dt.cells({ selected: true }).any()) {
  24034. dt.cells().deselect();
  24035. }
  24036. if (data.select.cells !== undefined) {
  24037. for (var i = 0; i < data.select.cells.length; i++) {
  24038. dt.cell(data.select.cells[i].row, data.select.cells[i].column).select();
  24039. }
  24040. }
  24041. dt.state.save();
  24042. };
  24043. dt.on('stateSaveParams', function (e, settings, data) {
  24044. data.select = {};
  24045. data.select.rows = dt.rows({ selected: true }).ids(true).toArray();
  24046. data.select.columns = dt.columns({ selected: true })[0];
  24047. data.select.cells = dt.cells({ selected: true })[0].map(function (coords) {
  24048. return { row: dt.row(coords.row).id(true), column: coords.column };
  24049. });
  24050. })
  24051. .on('stateLoadParams', selectAndSave)
  24052. .one('init', function () {
  24053. selectAndSave(undefined, undefined, savedSelected);
  24054. });
  24055. var init = ctx.oInit.select;
  24056. var defaults = DataTable.defaults.select;
  24057. var opts = init === undefined ? defaults : init;
  24058. // Set defaults
  24059. var items = 'row';
  24060. var style = 'api';
  24061. var blurable = false;
  24062. var toggleable = true;
  24063. var info = true;
  24064. var selector = 'td, th';
  24065. var className = 'selected';
  24066. var headerCheckbox = true;
  24067. var setStyle = false;
  24068. ctx._select = {
  24069. infoEls: []
  24070. };
  24071. // Initialisation customisations
  24072. if (opts === true) {
  24073. style = 'os';
  24074. setStyle = true;
  24075. }
  24076. else if (typeof opts === 'string') {
  24077. style = opts;
  24078. setStyle = true;
  24079. }
  24080. else if ($.isPlainObject(opts)) {
  24081. if (opts.blurable !== undefined) {
  24082. blurable = opts.blurable;
  24083. }
  24084. if (opts.toggleable !== undefined) {
  24085. toggleable = opts.toggleable;
  24086. }
  24087. if (opts.info !== undefined) {
  24088. info = opts.info;
  24089. }
  24090. if (opts.items !== undefined) {
  24091. items = opts.items;
  24092. }
  24093. if (opts.style !== undefined) {
  24094. style = opts.style;
  24095. setStyle = true;
  24096. }
  24097. else {
  24098. style = 'os';
  24099. setStyle = true;
  24100. }
  24101. if (opts.selector !== undefined) {
  24102. selector = opts.selector;
  24103. }
  24104. if (opts.className !== undefined) {
  24105. className = opts.className;
  24106. }
  24107. if (opts.headerCheckbox !== undefined) {
  24108. headerCheckbox = opts.headerCheckbox;
  24109. }
  24110. }
  24111. dt.select.selector(selector);
  24112. dt.select.items(items);
  24113. dt.select.style(style);
  24114. dt.select.blurable(blurable);
  24115. dt.select.toggleable(toggleable);
  24116. dt.select.info(info);
  24117. ctx._select.className = className;
  24118. // If the init options haven't enabled select, but there is a selectable
  24119. // class name, then enable
  24120. if (!setStyle && $(dt.table().node()).hasClass('selectable')) {
  24121. dt.select.style('os');
  24122. }
  24123. // Insert a checkbox into the header if needed - might need to wait
  24124. // for init complete, or it might already be done
  24125. if (headerCheckbox) {
  24126. initCheckboxHeader(dt);
  24127. dt.on('init', function () {
  24128. initCheckboxHeader(dt);
  24129. });
  24130. }
  24131. };
  24132. /*
  24133. Select is a collection of API methods, event handlers, event emitters and
  24134. buttons (for the `Buttons` extension) for DataTables. It provides the following
  24135. features, with an overview of how they are implemented:
  24136. ## Selection of rows, columns and cells. Whether an item is selected or not is
  24137. stored in:
  24138. * rows: a `_select_selected` property which contains a boolean value of the
  24139. DataTables' `aoData` object for each row
  24140. * columns: a `_select_selected` property which contains a boolean value of the
  24141. DataTables' `aoColumns` object for each column
  24142. * cells: a `_selected_cells` property which contains an array of boolean values
  24143. of the `aoData` object for each row. The array is the same length as the
  24144. columns array, with each element of it representing a cell.
  24145. This method of using boolean flags allows Select to operate when nodes have not
  24146. been created for rows / cells (DataTables' defer rendering feature).
  24147. ## API methods
  24148. A range of API methods are available for triggering selection and de-selection
  24149. of rows. Methods are also available to configure the selection events that can
  24150. be triggered by an end user (such as which items are to be selected). To a large
  24151. extent, these of API methods *is* Select. It is basically a collection of helper
  24152. functions that can be used to select items in a DataTable.
  24153. Configuration of select is held in the object `_select` which is attached to the
  24154. DataTables settings object on initialisation. Select being available on a table
  24155. is not optional when Select is loaded, but its default is for selection only to
  24156. be available via the API - so the end user wouldn't be able to select rows
  24157. without additional configuration.
  24158. The `_select` object contains the following properties:
  24159. ```
  24160. {
  24161. items:string - Can be `rows`, `columns` or `cells`. Defines what item
  24162. will be selected if the user is allowed to activate row
  24163. selection using the mouse.
  24164. style:string - Can be `none`, `single`, `multi` or `os`. Defines the
  24165. interaction style when selecting items
  24166. blurable:boolean - If row selection can be cleared by clicking outside of
  24167. the table
  24168. toggleable:boolean - If row selection can be cancelled by repeated clicking
  24169. on the row
  24170. info:boolean - If the selection summary should be shown in the table
  24171. information elements
  24172. infoEls:element[] - List of HTML elements with info elements for a table
  24173. }
  24174. ```
  24175. In addition to the API methods, Select also extends the DataTables selector
  24176. options for rows, columns and cells adding a `selected` option to the selector
  24177. options object, allowing the developer to select only selected items or
  24178. unselected items.
  24179. ## Mouse selection of items
  24180. Clicking on items can be used to select items. This is done by a simple event
  24181. handler that will select the items using the API methods.
  24182. */
  24183. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  24184. * Local functions
  24185. */
  24186. /**
  24187. * Add one or more cells to the selection when shift clicking in OS selection
  24188. * style cell selection.
  24189. *
  24190. * Cell range is more complicated than row and column as we want to select
  24191. * in the visible grid rather than by index in sequence. For example, if you
  24192. * click first in cell 1-1 and then shift click in 2-2 - cells 1-2 and 2-1
  24193. * should also be selected (and not 1-3, 1-4. etc)
  24194. *
  24195. * @param {DataTable.Api} dt DataTable
  24196. * @param {object} idx Cell index to select to
  24197. * @param {object} last Cell index to select from
  24198. * @private
  24199. */
  24200. function cellRange(dt, idx, last) {
  24201. var indexes;
  24202. var columnIndexes;
  24203. var rowIndexes;
  24204. var selectColumns = function (start, end) {
  24205. if (start > end) {
  24206. var tmp = end;
  24207. end = start;
  24208. start = tmp;
  24209. }
  24210. var record = false;
  24211. return dt
  24212. .columns(':visible')
  24213. .indexes()
  24214. .filter(function (i) {
  24215. if (i === start) {
  24216. record = true;
  24217. }
  24218. if (i === end) {
  24219. // not else if, as start might === end
  24220. record = false;
  24221. return true;
  24222. }
  24223. return record;
  24224. });
  24225. };
  24226. var selectRows = function (start, end) {
  24227. var indexes = dt.rows({ search: 'applied' }).indexes();
  24228. // Which comes first - might need to swap
  24229. if (indexes.indexOf(start) > indexes.indexOf(end)) {
  24230. var tmp = end;
  24231. end = start;
  24232. start = tmp;
  24233. }
  24234. var record = false;
  24235. return indexes.filter(function (i) {
  24236. if (i === start) {
  24237. record = true;
  24238. }
  24239. if (i === end) {
  24240. record = false;
  24241. return true;
  24242. }
  24243. return record;
  24244. });
  24245. };
  24246. if (!dt.cells({ selected: true }).any() && !last) {
  24247. // select from the top left cell to this one
  24248. columnIndexes = selectColumns(0, idx.column);
  24249. rowIndexes = selectRows(0, idx.row);
  24250. }
  24251. else {
  24252. // Get column indexes between old and new
  24253. columnIndexes = selectColumns(last.column, idx.column);
  24254. rowIndexes = selectRows(last.row, idx.row);
  24255. }
  24256. indexes = dt.cells(rowIndexes, columnIndexes).flatten();
  24257. if (!dt.cells(idx, { selected: true }).any()) {
  24258. // Select range
  24259. dt.cells(indexes).select();
  24260. }
  24261. else {
  24262. // Deselect range
  24263. dt.cells(indexes).deselect();
  24264. }
  24265. }
  24266. /**
  24267. * Disable mouse selection by removing the selectors
  24268. *
  24269. * @param {DataTable.Api} dt DataTable to remove events from
  24270. * @private
  24271. */
  24272. function disableMouseSelection(dt) {
  24273. var ctx = dt.settings()[0];
  24274. var selector = ctx._select.selector;
  24275. $(dt.table().container())
  24276. .off('mousedown.dtSelect', selector)
  24277. .off('mouseup.dtSelect', selector)
  24278. .off('click.dtSelect', selector);
  24279. $('body').off('click.dtSelect' + _safeId(dt.table().node()));
  24280. }
  24281. /**
  24282. * Attach mouse listeners to the table to allow mouse selection of items
  24283. *
  24284. * @param {DataTable.Api} dt DataTable to remove events from
  24285. * @private
  24286. */
  24287. function enableMouseSelection(dt) {
  24288. var container = $(dt.table().container());
  24289. var ctx = dt.settings()[0];
  24290. var selector = ctx._select.selector;
  24291. var matchSelection;
  24292. container
  24293. .on('mousedown.dtSelect', selector, function (e) {
  24294. // Disallow text selection for shift clicking on the table so multi
  24295. // element selection doesn't look terrible!
  24296. if (e.shiftKey || e.metaKey || e.ctrlKey) {
  24297. container
  24298. .css('-moz-user-select', 'none')
  24299. .one('selectstart.dtSelect', selector, function () {
  24300. return false;
  24301. });
  24302. }
  24303. if (window.getSelection) {
  24304. matchSelection = window.getSelection();
  24305. }
  24306. })
  24307. .on('mouseup.dtSelect', selector, function () {
  24308. // Allow text selection to occur again, Mozilla style (tested in FF
  24309. // 35.0.1 - still required)
  24310. container.css('-moz-user-select', '');
  24311. })
  24312. .on('click.dtSelect', selector, function (e) {
  24313. var items = dt.select.items();
  24314. var idx;
  24315. // If text was selected (click and drag), then we shouldn't change
  24316. // the row's selected state
  24317. if (matchSelection) {
  24318. var selection = window.getSelection();
  24319. // If the element that contains the selection is not in the table, we can ignore it
  24320. // This can happen if the developer selects text from the click event
  24321. if (
  24322. !selection.anchorNode ||
  24323. $(selection.anchorNode).closest('table')[0] === dt.table().node()
  24324. ) {
  24325. if (selection !== matchSelection) {
  24326. return;
  24327. }
  24328. }
  24329. }
  24330. var ctx = dt.settings()[0];
  24331. var container = dt.table().container();
  24332. // Ignore clicks inside a sub-table
  24333. if ($(e.target).closest('div.dt-container')[0] != container) {
  24334. return;
  24335. }
  24336. var cell = dt.cell($(e.target).closest('td, th'));
  24337. // Check the cell actually belongs to the host DataTable (so child
  24338. // rows, etc, are ignored)
  24339. if (!cell.any()) {
  24340. return;
  24341. }
  24342. var event = $.Event('user-select.dt');
  24343. eventTrigger(dt, event, [items, cell, e]);
  24344. if (event.isDefaultPrevented()) {
  24345. return;
  24346. }
  24347. var cellIndex = cell.index();
  24348. if (items === 'row') {
  24349. idx = cellIndex.row;
  24350. typeSelect(e, dt, ctx, 'row', idx);
  24351. }
  24352. else if (items === 'column') {
  24353. idx = cell.index().column;
  24354. typeSelect(e, dt, ctx, 'column', idx);
  24355. }
  24356. else if (items === 'cell') {
  24357. idx = cell.index();
  24358. typeSelect(e, dt, ctx, 'cell', idx);
  24359. }
  24360. ctx._select_lastCell = cellIndex;
  24361. });
  24362. // Blurable
  24363. $('body').on('click.dtSelect' + _safeId(dt.table().node()), function (e) {
  24364. if (ctx._select.blurable) {
  24365. // If the click was inside the DataTables container, don't blur
  24366. if ($(e.target).parents().filter(dt.table().container()).length) {
  24367. return;
  24368. }
  24369. // Ignore elements which have been removed from the DOM (i.e. paging
  24370. // buttons)
  24371. if ($(e.target).parents('html').length === 0) {
  24372. return;
  24373. }
  24374. // Don't blur in Editor form
  24375. if ($(e.target).parents('div.DTE').length) {
  24376. return;
  24377. }
  24378. var event = $.Event('select-blur.dt');
  24379. eventTrigger(dt, event, [e.target, e]);
  24380. if (event.isDefaultPrevented()) {
  24381. return;
  24382. }
  24383. clear(ctx, true);
  24384. }
  24385. });
  24386. }
  24387. /**
  24388. * Trigger an event on a DataTable
  24389. *
  24390. * @param {DataTable.Api} api DataTable to trigger events on
  24391. * @param {boolean} selected true if selected, false if deselected
  24392. * @param {string} type Item type acting on
  24393. * @param {boolean} any Require that there are values before
  24394. * triggering
  24395. * @private
  24396. */
  24397. function eventTrigger(api, type, args, any) {
  24398. if (any && !api.flatten().length) {
  24399. return;
  24400. }
  24401. if (typeof type === 'string') {
  24402. type = type + '.dt';
  24403. }
  24404. args.unshift(api);
  24405. $(api.table().node()).trigger(type, args);
  24406. }
  24407. /**
  24408. * Update the information element of the DataTable showing information about the
  24409. * items selected. This is done by adding tags to the existing text
  24410. *
  24411. * @param {DataTable.Api} api DataTable to update
  24412. * @private
  24413. */
  24414. function info(api, node) {
  24415. if (api.select.style() === 'api' || api.select.info() === false) {
  24416. return;
  24417. }
  24418. var rows = api.rows({ selected: true }).flatten().length;
  24419. var columns = api.columns({ selected: true }).flatten().length;
  24420. var cells = api.cells({ selected: true }).flatten().length;
  24421. var add = function (el, name, num) {
  24422. el.append(
  24423. $('<span class="select-item"/>').append(
  24424. api.i18n(
  24425. 'select.' + name + 's',
  24426. { _: '%d ' + name + 's selected', 0: '', 1: '1 ' + name + ' selected' },
  24427. num
  24428. )
  24429. )
  24430. );
  24431. };
  24432. var el = $(node);
  24433. var output = $('<span class="select-info"/>');
  24434. add(output, 'row', rows);
  24435. add(output, 'column', columns);
  24436. add(output, 'cell', cells);
  24437. var existing = el.children('span.select-info');
  24438. if (existing.length) {
  24439. existing.remove();
  24440. }
  24441. if (output.text() !== '') {
  24442. el.append(output);
  24443. }
  24444. }
  24445. /**
  24446. * Add a checkbox to the header for checkbox columns, allowing all rows to
  24447. * be selected, deselected or just to show the state.
  24448. *
  24449. * @param {*} dt API
  24450. */
  24451. function initCheckboxHeader( dt ) {
  24452. // Find any checkbox column(s)
  24453. dt.columns('.dt-select').every(function () {
  24454. var header = this.header();
  24455. if (! $('input', header).length) {
  24456. // If no checkbox yet, insert one
  24457. var input = $('<input>')
  24458. .attr({
  24459. class: 'dt-select-checkbox',
  24460. type: 'checkbox',
  24461. 'aria-label': dt.i18n('select.aria.headerCheckbox') || 'Select all rows'
  24462. })
  24463. .appendTo(header)
  24464. .on('change', function () {
  24465. if (this.checked) {
  24466. dt.rows({search: 'applied'}).select();
  24467. }
  24468. else {
  24469. dt.rows({selected: true}).deselect();
  24470. }
  24471. })
  24472. .on('click', function (e) {
  24473. e.stopPropagation();
  24474. });
  24475. // Update the header checkbox's state when the selection in the
  24476. // table changes
  24477. dt.on('draw select deselect', function (e, pass, type) {
  24478. if (type === 'row' || ! type) {
  24479. var count = dt.rows({selected: true}).count();
  24480. var search = dt.rows({search: 'applied', selected: true}).count();
  24481. var available = dt.rows({search: 'applied'}).count();
  24482. if (search && search <= count && search === available) {
  24483. input
  24484. .prop('checked', true)
  24485. .prop('indeterminate', false);
  24486. }
  24487. else if (search === 0 && count === 0) {
  24488. input
  24489. .prop('checked', false)
  24490. .prop('indeterminate', false);
  24491. }
  24492. else {
  24493. input
  24494. .prop('checked', false)
  24495. .prop('indeterminate', true);
  24496. }
  24497. }
  24498. });
  24499. }
  24500. });
  24501. }
  24502. /**
  24503. * Initialisation of a new table. Attach event handlers and callbacks to allow
  24504. * Select to operate correctly.
  24505. *
  24506. * This will occur _after_ the initial DataTables initialisation, although
  24507. * before Ajax data is rendered, if there is ajax data
  24508. *
  24509. * @param {DataTable.settings} ctx Settings object to operate on
  24510. * @private
  24511. */
  24512. function init(ctx) {
  24513. var api = new DataTable.Api(ctx);
  24514. ctx._select_init = true;
  24515. // Row callback so that classes can be added to rows and cells if the item
  24516. // was selected before the element was created. This will happen with the
  24517. // `deferRender` option enabled.
  24518. //
  24519. // This method of attaching to `aoRowCreatedCallback` is a hack until
  24520. // DataTables has proper events for row manipulation If you are reviewing
  24521. // this code to create your own plug-ins, please do not do this!
  24522. ctx.aoRowCreatedCallback.push(function (row, data, index) {
  24523. var i, ien;
  24524. var d = ctx.aoData[index];
  24525. // Row
  24526. if (d._select_selected) {
  24527. $(row)
  24528. .addClass(ctx._select.className)
  24529. .find('input.dt-select-checkbox').prop('checked', true);
  24530. }
  24531. // Cells and columns - if separated out, we would need to do two
  24532. // loops, so it makes sense to combine them into a single one
  24533. for (i = 0, ien = ctx.aoColumns.length; i < ien; i++) {
  24534. if (
  24535. ctx.aoColumns[i]._select_selected ||
  24536. (d._selected_cells && d._selected_cells[i])
  24537. ) {
  24538. $(d.anCells[i]).addClass(ctx._select.className)
  24539. }
  24540. }
  24541. }
  24542. );
  24543. // On Ajax reload we want to reselect all rows which are currently selected,
  24544. // if there is an rowId (i.e. a unique value to identify each row with)
  24545. api.on('preXhr.dt.dtSelect', function (e, settings) {
  24546. if (settings !== api.settings()[0]) {
  24547. // Not triggered by our DataTable!
  24548. return;
  24549. }
  24550. // note that column selection doesn't need to be cached and then
  24551. // reselected, as they are already selected
  24552. var rows = api
  24553. .rows({ selected: true })
  24554. .ids(true)
  24555. .filter(function (d) {
  24556. return d !== undefined;
  24557. });
  24558. var cells = api
  24559. .cells({ selected: true })
  24560. .eq(0)
  24561. .map(function (cellIdx) {
  24562. var id = api.row(cellIdx.row).id(true);
  24563. return id ? { row: id, column: cellIdx.column } : undefined;
  24564. })
  24565. .filter(function (d) {
  24566. return d !== undefined;
  24567. });
  24568. // On the next draw, reselect the currently selected items
  24569. api.one('draw.dt.dtSelect', function () {
  24570. api.rows(rows).select();
  24571. // `cells` is not a cell index selector, so it needs a loop
  24572. if (cells.any()) {
  24573. cells.each(function (id) {
  24574. api.cells(id.row, id.column).select();
  24575. });
  24576. }
  24577. });
  24578. });
  24579. // Update the table information element with selected item summary
  24580. api.on('info.dt', function (e, ctx, node) {
  24581. // Store the info node for updating on select / deselect
  24582. if (!ctx._select.infoEls.includes(node)) {
  24583. ctx._select.infoEls.push(node);
  24584. }
  24585. info(api, node);
  24586. });
  24587. api.on('select.dtSelect.dt deselect.dtSelect.dt', function () {
  24588. ctx._select.infoEls.forEach(function (el) {
  24589. info(api, el);
  24590. });
  24591. api.state.save();
  24592. });
  24593. // Clean up and release
  24594. api.on('destroy.dtSelect', function () {
  24595. // Remove class directly rather than calling deselect - which would trigger events
  24596. $(api.rows({ selected: true }).nodes()).removeClass(api.settings()[0]._select.className);
  24597. disableMouseSelection(api);
  24598. api.off('.dtSelect');
  24599. $('body').off('.dtSelect' + _safeId(api.table().node()));
  24600. });
  24601. }
  24602. /**
  24603. * Add one or more items (rows or columns) to the selection when shift clicking
  24604. * in OS selection style
  24605. *
  24606. * @param {DataTable.Api} dt DataTable
  24607. * @param {string} type Row or column range selector
  24608. * @param {object} idx Item index to select to
  24609. * @param {object} last Item index to select from
  24610. * @private
  24611. */
  24612. function rowColumnRange(dt, type, idx, last) {
  24613. // Add a range of rows from the last selected row to this one
  24614. var indexes = dt[type + 's']({ search: 'applied' }).indexes();
  24615. var idx1 = indexes.indexOf(last);
  24616. var idx2 = indexes.indexOf(idx);
  24617. if (!dt[type + 's']({ selected: true }).any() && idx1 === -1) {
  24618. // select from top to here - slightly odd, but both Windows and Mac OS
  24619. // do this
  24620. indexes.splice(indexes.indexOf(idx) + 1, indexes.length);
  24621. }
  24622. else {
  24623. // reverse so we can shift click 'up' as well as down
  24624. if (idx1 > idx2) {
  24625. var tmp = idx2;
  24626. idx2 = idx1;
  24627. idx1 = tmp;
  24628. }
  24629. indexes.splice(idx2 + 1, indexes.length);
  24630. indexes.splice(0, idx1);
  24631. }
  24632. if (!dt[type](idx, { selected: true }).any()) {
  24633. // Select range
  24634. dt[type + 's'](indexes).select();
  24635. }
  24636. else {
  24637. // Deselect range - need to keep the clicked on row selected
  24638. indexes.splice(indexes.indexOf(idx), 1);
  24639. dt[type + 's'](indexes).deselect();
  24640. }
  24641. }
  24642. /**
  24643. * Clear all selected items
  24644. *
  24645. * @param {DataTable.settings} ctx Settings object of the host DataTable
  24646. * @param {boolean} [force=false] Force the de-selection to happen, regardless
  24647. * of selection style
  24648. * @private
  24649. */
  24650. function clear(ctx, force) {
  24651. if (force || ctx._select.style === 'single') {
  24652. var api = new DataTable.Api(ctx);
  24653. api.rows({ selected: true }).deselect();
  24654. api.columns({ selected: true }).deselect();
  24655. api.cells({ selected: true }).deselect();
  24656. }
  24657. }
  24658. /**
  24659. * Select items based on the current configuration for style and items.
  24660. *
  24661. * @param {object} e Mouse event object
  24662. * @param {DataTables.Api} dt DataTable
  24663. * @param {DataTable.settings} ctx Settings object of the host DataTable
  24664. * @param {string} type Items to select
  24665. * @param {int|object} idx Index of the item to select
  24666. * @private
  24667. */
  24668. function typeSelect(e, dt, ctx, type, idx) {
  24669. var style = dt.select.style();
  24670. var toggleable = dt.select.toggleable();
  24671. var isSelected = dt[type](idx, { selected: true }).any();
  24672. if (isSelected && !toggleable) {
  24673. return;
  24674. }
  24675. if (style === 'os') {
  24676. if (e.ctrlKey || e.metaKey) {
  24677. // Add or remove from the selection
  24678. dt[type](idx).select(!isSelected);
  24679. }
  24680. else if (e.shiftKey) {
  24681. if (type === 'cell') {
  24682. cellRange(dt, idx, ctx._select_lastCell || null);
  24683. }
  24684. else {
  24685. rowColumnRange(
  24686. dt,
  24687. type,
  24688. idx,
  24689. ctx._select_lastCell ? ctx._select_lastCell[type] : null
  24690. );
  24691. }
  24692. }
  24693. else {
  24694. // No cmd or shift click - deselect if selected, or select
  24695. // this row only
  24696. var selected = dt[type + 's']({ selected: true });
  24697. if (isSelected && selected.flatten().length === 1) {
  24698. dt[type](idx).deselect();
  24699. }
  24700. else {
  24701. selected.deselect();
  24702. dt[type](idx).select();
  24703. }
  24704. }
  24705. }
  24706. else if (style == 'multi+shift') {
  24707. if (e.shiftKey) {
  24708. if (type === 'cell') {
  24709. cellRange(dt, idx, ctx._select_lastCell || null);
  24710. }
  24711. else {
  24712. rowColumnRange(
  24713. dt,
  24714. type,
  24715. idx,
  24716. ctx._select_lastCell ? ctx._select_lastCell[type] : null
  24717. );
  24718. }
  24719. }
  24720. else {
  24721. dt[type](idx).select(!isSelected);
  24722. }
  24723. }
  24724. else {
  24725. dt[type](idx).select(!isSelected);
  24726. }
  24727. }
  24728. function _safeId(node) {
  24729. return node.id.replace(/[^a-zA-Z0-9\-\_]/g, '-');
  24730. }
  24731. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  24732. * DataTables selectors
  24733. */
  24734. // row and column are basically identical just assigned to different properties
  24735. // and checking a different array, so we can dynamically create the functions to
  24736. // reduce the code size
  24737. $.each(
  24738. [
  24739. { type: 'row', prop: 'aoData' },
  24740. { type: 'column', prop: 'aoColumns' }
  24741. ],
  24742. function (i, o) {
  24743. DataTable.ext.selector[o.type].push(function (settings, opts, indexes) {
  24744. var selected = opts.selected;
  24745. var data;
  24746. var out = [];
  24747. if (selected !== true && selected !== false) {
  24748. return indexes;
  24749. }
  24750. for (var i = 0, ien = indexes.length; i < ien; i++) {
  24751. data = settings[o.prop][indexes[i]];
  24752. if (
  24753. data && (
  24754. (selected === true && data._select_selected === true) ||
  24755. (selected === false && !data._select_selected)
  24756. )
  24757. ) {
  24758. out.push(indexes[i]);
  24759. }
  24760. }
  24761. return out;
  24762. });
  24763. }
  24764. );
  24765. DataTable.ext.selector.cell.push(function (settings, opts, cells) {
  24766. var selected = opts.selected;
  24767. var rowData;
  24768. var out = [];
  24769. if (selected === undefined) {
  24770. return cells;
  24771. }
  24772. for (var i = 0, ien = cells.length; i < ien; i++) {
  24773. rowData = settings.aoData[cells[i].row];
  24774. if (
  24775. rowData && (
  24776. (selected === true &&
  24777. rowData._selected_cells &&
  24778. rowData._selected_cells[cells[i].column] === true) ||
  24779. (selected === false &&
  24780. (!rowData._selected_cells || !rowData._selected_cells[cells[i].column]))
  24781. )
  24782. ) {
  24783. out.push(cells[i]);
  24784. }
  24785. }
  24786. return out;
  24787. });
  24788. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  24789. * DataTables API
  24790. *
  24791. * For complete documentation, please refer to the docs/api directory or the
  24792. * DataTables site
  24793. */
  24794. // Local variables to improve compression
  24795. var apiRegister = DataTable.Api.register;
  24796. var apiRegisterPlural = DataTable.Api.registerPlural;
  24797. apiRegister('select()', function () {
  24798. return this.iterator('table', function (ctx) {
  24799. DataTable.select.init(new DataTable.Api(ctx));
  24800. });
  24801. });
  24802. apiRegister('select.blurable()', function (flag) {
  24803. if (flag === undefined) {
  24804. return this.context[0]._select.blurable;
  24805. }
  24806. return this.iterator('table', function (ctx) {
  24807. ctx._select.blurable = flag;
  24808. });
  24809. });
  24810. apiRegister('select.toggleable()', function (flag) {
  24811. if (flag === undefined) {
  24812. return this.context[0]._select.toggleable;
  24813. }
  24814. return this.iterator('table', function (ctx) {
  24815. ctx._select.toggleable = flag;
  24816. });
  24817. });
  24818. apiRegister('select.info()', function (flag) {
  24819. if (flag === undefined) {
  24820. return this.context[0]._select.info;
  24821. }
  24822. return this.iterator('table', function (ctx) {
  24823. ctx._select.info = flag;
  24824. });
  24825. });
  24826. apiRegister('select.items()', function (items) {
  24827. if (items === undefined) {
  24828. return this.context[0]._select.items;
  24829. }
  24830. return this.iterator('table', function (ctx) {
  24831. ctx._select.items = items;
  24832. eventTrigger(new DataTable.Api(ctx), 'selectItems', [items]);
  24833. });
  24834. });
  24835. // Takes effect from the _next_ selection. None disables future selection, but
  24836. // does not clear the current selection. Use the `deselect` methods for that
  24837. apiRegister('select.style()', function (style) {
  24838. if (style === undefined) {
  24839. return this.context[0]._select.style;
  24840. }
  24841. return this.iterator('table', function (ctx) {
  24842. if (!ctx._select) {
  24843. DataTable.select.init(new DataTable.Api(ctx));
  24844. }
  24845. if (!ctx._select_init) {
  24846. init(ctx);
  24847. }
  24848. ctx._select.style = style;
  24849. // Add / remove mouse event handlers. They aren't required when only
  24850. // API selection is available
  24851. var dt = new DataTable.Api(ctx);
  24852. disableMouseSelection(dt);
  24853. if (style !== 'api') {
  24854. enableMouseSelection(dt);
  24855. }
  24856. eventTrigger(new DataTable.Api(ctx), 'selectStyle', [style]);
  24857. });
  24858. });
  24859. apiRegister('select.selector()', function (selector) {
  24860. if (selector === undefined) {
  24861. return this.context[0]._select.selector;
  24862. }
  24863. return this.iterator('table', function (ctx) {
  24864. disableMouseSelection(new DataTable.Api(ctx));
  24865. ctx._select.selector = selector;
  24866. if (ctx._select.style !== 'api') {
  24867. enableMouseSelection(new DataTable.Api(ctx));
  24868. }
  24869. });
  24870. });
  24871. apiRegister('select.last()', function (set) {
  24872. let ctx = this.context[0];
  24873. if (set) {
  24874. ctx._select_lastCell = set;
  24875. return this;
  24876. }
  24877. return ctx._select_lastCell;
  24878. });
  24879. apiRegisterPlural('rows().select()', 'row().select()', function (select) {
  24880. var api = this;
  24881. if (select === false) {
  24882. return this.deselect();
  24883. }
  24884. this.iterator('row', function (ctx, idx) {
  24885. clear(ctx);
  24886. // There is a good amount of knowledge of DataTables internals in
  24887. // this function. It _could_ be done without that, but it would hurt
  24888. // performance (or DT would need new APIs for this work)
  24889. var dtData = ctx.aoData[idx];
  24890. var dtColumns = ctx.aoColumns;
  24891. $(dtData.nTr).addClass(ctx._select.className);
  24892. dtData._select_selected = true;
  24893. for (var i=0 ; i<dtColumns.length ; i++) {
  24894. var col = dtColumns[i];
  24895. // Regenerate the column type if not present
  24896. if (col.sType === null) {
  24897. api.columns().types()
  24898. }
  24899. if (col.sType === 'select-checkbox') {
  24900. var cells = dtData.anCells;
  24901. // Make sure the checkbox shows the right state
  24902. if (cells && cells[i]) {
  24903. $('input.dt-select-checkbox', cells[i]).prop('checked', true);
  24904. }
  24905. // Invalidate the sort data for this column, if not already done
  24906. if (dtData._aSortData !== null) {
  24907. dtData._aSortData[i] = null;
  24908. }
  24909. }
  24910. }
  24911. });
  24912. this.iterator('table', function (ctx, i) {
  24913. eventTrigger(api, 'select', ['row', api[i]], true);
  24914. });
  24915. return this;
  24916. });
  24917. apiRegister('row().selected()', function () {
  24918. var ctx = this.context[0];
  24919. if (ctx && this.length && ctx.aoData[this[0]] && ctx.aoData[this[0]]._select_selected) {
  24920. return true;
  24921. }
  24922. return false;
  24923. });
  24924. apiRegisterPlural('columns().select()', 'column().select()', function (select) {
  24925. var api = this;
  24926. if (select === false) {
  24927. return this.deselect();
  24928. }
  24929. this.iterator('column', function (ctx, idx) {
  24930. clear(ctx);
  24931. ctx.aoColumns[idx]._select_selected = true;
  24932. var column = new DataTable.Api(ctx).column(idx);
  24933. $(column.header()).addClass(ctx._select.className);
  24934. $(column.footer()).addClass(ctx._select.className);
  24935. column.nodes().to$().addClass(ctx._select.className);
  24936. });
  24937. this.iterator('table', function (ctx, i) {
  24938. eventTrigger(api, 'select', ['column', api[i]], true);
  24939. });
  24940. return this;
  24941. });
  24942. apiRegister('column().selected()', function () {
  24943. var ctx = this.context[0];
  24944. if (ctx && this.length && ctx.aoColumns[this[0]] && ctx.aoColumns[this[0]]._select_selected) {
  24945. return true;
  24946. }
  24947. return false;
  24948. });
  24949. apiRegisterPlural('cells().select()', 'cell().select()', function (select) {
  24950. var api = this;
  24951. if (select === false) {
  24952. return this.deselect();
  24953. }
  24954. this.iterator('cell', function (ctx, rowIdx, colIdx) {
  24955. clear(ctx);
  24956. var data = ctx.aoData[rowIdx];
  24957. if (data._selected_cells === undefined) {
  24958. data._selected_cells = [];
  24959. }
  24960. data._selected_cells[colIdx] = true;
  24961. if (data.anCells) {
  24962. $(data.anCells[colIdx]).addClass(ctx._select.className);
  24963. }
  24964. });
  24965. this.iterator('table', function (ctx, i) {
  24966. eventTrigger(api, 'select', ['cell', api.cells(api[i]).indexes().toArray()], true);
  24967. });
  24968. return this;
  24969. });
  24970. apiRegister('cell().selected()', function () {
  24971. var ctx = this.context[0];
  24972. if (ctx && this.length) {
  24973. var row = ctx.aoData[this[0][0].row];
  24974. if (row && row._selected_cells && row._selected_cells[this[0][0].column]) {
  24975. return true;
  24976. }
  24977. }
  24978. return false;
  24979. });
  24980. apiRegisterPlural('rows().deselect()', 'row().deselect()', function () {
  24981. var api = this;
  24982. this.iterator('row', function (ctx, idx) {
  24983. // Like the select action, this has a lot of knowledge about DT internally
  24984. var dtData = ctx.aoData[idx];
  24985. var dtColumns = ctx.aoColumns;
  24986. $(dtData.nTr).removeClass(ctx._select.className);
  24987. dtData._select_selected = false;
  24988. ctx._select_lastCell = null;
  24989. for (var i=0 ; i<dtColumns.length ; i++) {
  24990. var col = dtColumns[i];
  24991. // Regenerate the column type if not present
  24992. if (col.sType === null) {
  24993. api.columns().types()
  24994. }
  24995. if (col.sType === 'select-checkbox') {
  24996. var cells = dtData.anCells;
  24997. // Make sure the checkbox shows the right state
  24998. if (cells && cells[i]) {
  24999. $('input.dt-select-checkbox', dtData.anCells[i]).prop('checked', false);
  25000. }
  25001. // Invalidate the sort data for this column, if not already done
  25002. if (dtData._aSortData !== null) {
  25003. dtData._aSortData[i] = null;
  25004. }
  25005. }
  25006. }
  25007. });
  25008. this.iterator('table', function (ctx, i) {
  25009. eventTrigger(api, 'deselect', ['row', api[i]], true);
  25010. });
  25011. return this;
  25012. });
  25013. apiRegisterPlural('columns().deselect()', 'column().deselect()', function () {
  25014. var api = this;
  25015. this.iterator('column', function (ctx, idx) {
  25016. ctx.aoColumns[idx]._select_selected = false;
  25017. var api = new DataTable.Api(ctx);
  25018. var column = api.column(idx);
  25019. $(column.header()).removeClass(ctx._select.className);
  25020. $(column.footer()).removeClass(ctx._select.className);
  25021. // Need to loop over each cell, rather than just using
  25022. // `column().nodes()` as cells which are individually selected should
  25023. // not have the `selected` class removed from them
  25024. api.cells(null, idx)
  25025. .indexes()
  25026. .each(function (cellIdx) {
  25027. var data = ctx.aoData[cellIdx.row];
  25028. var cellSelected = data._selected_cells;
  25029. if (data.anCells && (!cellSelected || !cellSelected[cellIdx.column])) {
  25030. $(data.anCells[cellIdx.column]).removeClass(ctx._select.className);
  25031. }
  25032. });
  25033. });
  25034. this.iterator('table', function (ctx, i) {
  25035. eventTrigger(api, 'deselect', ['column', api[i]], true);
  25036. });
  25037. return this;
  25038. });
  25039. apiRegisterPlural('cells().deselect()', 'cell().deselect()', function () {
  25040. var api = this;
  25041. this.iterator('cell', function (ctx, rowIdx, colIdx) {
  25042. var data = ctx.aoData[rowIdx];
  25043. if (data._selected_cells !== undefined) {
  25044. data._selected_cells[colIdx] = false;
  25045. }
  25046. // Remove class only if the cells exist, and the cell is not column
  25047. // selected, in which case the class should remain (since it is selected
  25048. // in the column)
  25049. if (data.anCells && !ctx.aoColumns[colIdx]._select_selected) {
  25050. $(data.anCells[colIdx]).removeClass(ctx._select.className);
  25051. }
  25052. });
  25053. this.iterator('table', function (ctx, i) {
  25054. eventTrigger(api, 'deselect', ['cell', api[i]], true);
  25055. });
  25056. return this;
  25057. });
  25058. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  25059. * Buttons
  25060. */
  25061. function i18n(label, def) {
  25062. return function (dt) {
  25063. return dt.i18n('buttons.' + label, def);
  25064. };
  25065. }
  25066. // Common events with suitable namespaces
  25067. function namespacedEvents(config) {
  25068. var unique = config._eventNamespace;
  25069. return 'draw.dt.DT' + unique + ' select.dt.DT' + unique + ' deselect.dt.DT' + unique;
  25070. }
  25071. function enabled(dt, config) {
  25072. if (config.limitTo.indexOf('rows') !== -1 && dt.rows({ selected: true }).any()) {
  25073. return true;
  25074. }
  25075. if (config.limitTo.indexOf('columns') !== -1 && dt.columns({ selected: true }).any()) {
  25076. return true;
  25077. }
  25078. if (config.limitTo.indexOf('cells') !== -1 && dt.cells({ selected: true }).any()) {
  25079. return true;
  25080. }
  25081. return false;
  25082. }
  25083. var _buttonNamespace = 0;
  25084. $.extend(DataTable.ext.buttons, {
  25085. selected: {
  25086. text: i18n('selected', 'Selected'),
  25087. className: 'buttons-selected',
  25088. limitTo: ['rows', 'columns', 'cells'],
  25089. init: function (dt, node, config) {
  25090. var that = this;
  25091. config._eventNamespace = '.select' + _buttonNamespace++;
  25092. // .DT namespace listeners are removed by DataTables automatically
  25093. // on table destroy
  25094. dt.on(namespacedEvents(config), function () {
  25095. that.enable(enabled(dt, config));
  25096. });
  25097. this.disable();
  25098. },
  25099. destroy: function (dt, node, config) {
  25100. dt.off(config._eventNamespace);
  25101. }
  25102. },
  25103. selectedSingle: {
  25104. text: i18n('selectedSingle', 'Selected single'),
  25105. className: 'buttons-selected-single',
  25106. init: function (dt, node, config) {
  25107. var that = this;
  25108. config._eventNamespace = '.select' + _buttonNamespace++;
  25109. dt.on(namespacedEvents(config), function () {
  25110. var count =
  25111. dt.rows({ selected: true }).flatten().length +
  25112. dt.columns({ selected: true }).flatten().length +
  25113. dt.cells({ selected: true }).flatten().length;
  25114. that.enable(count === 1);
  25115. });
  25116. this.disable();
  25117. },
  25118. destroy: function (dt, node, config) {
  25119. dt.off(config._eventNamespace);
  25120. }
  25121. },
  25122. selectAll: {
  25123. text: i18n('selectAll', 'Select all'),
  25124. className: 'buttons-select-all',
  25125. action: function (e, dt, node, config) {
  25126. var items = this.select.items();
  25127. var mod = config.selectorModifier;
  25128. if (mod) {
  25129. if (typeof mod === 'function') {
  25130. mod = mod.call(dt, e, dt, node, config);
  25131. }
  25132. this[items + 's'](mod).select();
  25133. }
  25134. else {
  25135. this[items + 's']().select();
  25136. }
  25137. }
  25138. // selectorModifier can be specified
  25139. },
  25140. selectNone: {
  25141. text: i18n('selectNone', 'Deselect all'),
  25142. className: 'buttons-select-none',
  25143. action: function () {
  25144. clear(this.settings()[0], true);
  25145. },
  25146. init: function (dt, node, config) {
  25147. var that = this;
  25148. config._eventNamespace = '.select' + _buttonNamespace++;
  25149. dt.on(namespacedEvents(config), function () {
  25150. var count =
  25151. dt.rows({ selected: true }).flatten().length +
  25152. dt.columns({ selected: true }).flatten().length +
  25153. dt.cells({ selected: true }).flatten().length;
  25154. that.enable(count > 0);
  25155. });
  25156. this.disable();
  25157. },
  25158. destroy: function (dt, node, config) {
  25159. dt.off(config._eventNamespace);
  25160. }
  25161. },
  25162. showSelected: {
  25163. text: i18n('showSelected', 'Show only selected'),
  25164. className: 'buttons-show-selected',
  25165. action: function (e, dt) {
  25166. if (dt.search.fixed('dt-select')) {
  25167. // Remove existing function
  25168. dt.search.fixed('dt-select', null);
  25169. this.active(false);
  25170. }
  25171. else {
  25172. // Use a fixed filtering function to match on selected rows
  25173. // This needs to reference the internal aoData since that is
  25174. // where Select stores its reference for the selected state
  25175. var dataSrc = dt.settings()[0].aoData;
  25176. dt.search.fixed('dt-select', function (text, data, idx) {
  25177. // _select_selected is set by Select on the data object for the row
  25178. return dataSrc[idx]._select_selected;
  25179. });
  25180. this.active(true);
  25181. }
  25182. dt.draw();
  25183. }
  25184. }
  25185. });
  25186. $.each(['Row', 'Column', 'Cell'], function (i, item) {
  25187. var lc = item.toLowerCase();
  25188. DataTable.ext.buttons['select' + item + 's'] = {
  25189. text: i18n('select' + item + 's', 'Select ' + lc + 's'),
  25190. className: 'buttons-select-' + lc + 's',
  25191. action: function () {
  25192. this.select.items(lc);
  25193. },
  25194. init: function (dt) {
  25195. var that = this;
  25196. dt.on('selectItems.dt.DT', function (e, ctx, items) {
  25197. that.active(items === lc);
  25198. });
  25199. }
  25200. };
  25201. });
  25202. DataTable.type('select-checkbox', {
  25203. className: 'dt-select',
  25204. detect: function (data) {
  25205. // Rendering function will tell us if it is a checkbox type
  25206. return data === 'select-checkbox' ? data : false;
  25207. },
  25208. order: {
  25209. pre: function (d) {
  25210. return d === 'X' ? -1 : 0;
  25211. }
  25212. }
  25213. });
  25214. $.extend(true, DataTable.defaults.oLanguage, {
  25215. select: {
  25216. aria: {
  25217. rowCheckbox: 'Select row'
  25218. }
  25219. }
  25220. });
  25221. DataTable.render.select = function (valueProp, nameProp) {
  25222. var valueFn = valueProp ? DataTable.util.get(valueProp) : null;
  25223. var nameFn = nameProp ? DataTable.util.get(nameProp) : null;
  25224. return function (data, type, row, meta) {
  25225. var dtRow = meta.settings.aoData[meta.row];
  25226. var selected = dtRow._select_selected;
  25227. var ariaLabel = meta.settings.oLanguage.select.aria.rowCheckbox;
  25228. if (type === 'display') {
  25229. return $('<input>')
  25230. .attr({
  25231. 'aria-label': ariaLabel,
  25232. class: 'dt-select-checkbox',
  25233. name: nameFn ? nameFn(row) : null,
  25234. type: 'checkbox',
  25235. value: valueFn ? valueFn(row) : null,
  25236. checked: selected
  25237. })[0];
  25238. }
  25239. else if (type === 'type') {
  25240. return 'select-checkbox';
  25241. }
  25242. else if (type === 'filter') {
  25243. return '';
  25244. }
  25245. return selected ? 'X' : '';
  25246. }
  25247. }
  25248. // Legacy checkbox ordering
  25249. DataTable.ext.order['select-checkbox'] = function (settings, col) {
  25250. return this.api()
  25251. .column(col, { order: 'index' })
  25252. .nodes()
  25253. .map(function (td) {
  25254. if (settings._select.items === 'row') {
  25255. return $(td).parent().hasClass(settings._select.className);
  25256. }
  25257. else if (settings._select.items === 'cell') {
  25258. return $(td).hasClass(settings._select.className);
  25259. }
  25260. return false;
  25261. });
  25262. };
  25263. $.fn.DataTable.select = DataTable.select;
  25264. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  25265. * Initialisation
  25266. */
  25267. // DataTables creation - check if select has been defined in the options. Note
  25268. // this required that the table be in the document! If it isn't then something
  25269. // needs to trigger this method unfortunately. The next major release of
  25270. // DataTables will rework the events and address this.
  25271. $(document).on('preInit.dt.dtSelect', function (e, ctx) {
  25272. if (e.namespace !== 'dt') {
  25273. return;
  25274. }
  25275. DataTable.select.init(new DataTable.Api(ctx));
  25276. });
  25277. return DataTable;
  25278. }));