123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956109571095810959109601096110962109631096410965109661096710968109691097010971109721097310974109751097610977109781097910980109811098210983109841098510986109871098810989109901099110992109931099410995109961099710998109991100011001110021100311004110051100611007110081100911010110111101211013110141101511016110171101811019110201102111022110231102411025110261102711028110291103011031110321103311034110351103611037110381103911040110411104211043110441104511046110471104811049110501105111052110531105411055110561105711058110591106011061110621106311064110651106611067110681106911070110711107211073110741107511076110771107811079110801108111082110831108411085110861108711088110891109011091110921109311094110951109611097110981109911100111011110211103111041110511106111071110811109111101111111112111131111411115111161111711118111191112011121111221112311124111251112611127111281112911130111311113211133111341113511136111371113811139111401114111142111431114411145111461114711148111491115011151111521115311154111551115611157111581115911160111611116211163111641116511166111671116811169111701117111172111731117411175111761117711178111791118011181111821118311184111851118611187111881118911190111911119211193111941119511196111971119811199112001120111202112031120411205112061120711208112091121011211112121121311214112151121611217112181121911220112211122211223112241122511226112271122811229112301123111232112331123411235112361123711238112391124011241112421124311244112451124611247112481124911250112511125211253112541125511256112571125811259112601126111262112631126411265112661126711268112691127011271112721127311274112751127611277112781127911280112811128211283112841128511286112871128811289112901129111292112931129411295112961129711298112991130011301113021130311304113051130611307113081130911310113111131211313113141131511316113171131811319113201132111322113231132411325113261132711328113291133011331113321133311334113351133611337113381133911340113411134211343113441134511346113471134811349113501135111352113531135411355113561135711358113591136011361113621136311364113651136611367113681136911370113711137211373113741137511376113771137811379113801138111382113831138411385113861138711388113891139011391113921139311394113951139611397113981139911400114011140211403114041140511406114071140811409114101141111412114131141411415114161141711418114191142011421114221142311424114251142611427114281142911430114311143211433114341143511436114371143811439114401144111442114431144411445114461144711448114491145011451114521145311454114551145611457114581145911460114611146211463114641146511466114671146811469114701147111472114731147411475114761147711478114791148011481114821148311484114851148611487114881148911490114911149211493114941149511496114971149811499115001150111502115031150411505115061150711508115091151011511115121151311514115151151611517115181151911520115211152211523115241152511526115271152811529115301153111532115331153411535115361153711538115391154011541115421154311544115451154611547115481154911550115511155211553115541155511556115571155811559115601156111562115631156411565115661156711568115691157011571115721157311574115751157611577115781157911580115811158211583115841158511586115871158811589115901159111592115931159411595115961159711598115991160011601116021160311604116051160611607116081160911610116111161211613116141161511616116171161811619116201162111622116231162411625116261162711628116291163011631116321163311634116351163611637116381163911640116411164211643116441164511646116471164811649116501165111652116531165411655116561165711658116591166011661116621166311664116651166611667116681166911670116711167211673116741167511676116771167811679116801168111682116831168411685116861168711688116891169011691116921169311694116951169611697116981169911700117011170211703117041170511706117071170811709117101171111712117131171411715117161171711718117191172011721117221172311724117251172611727117281172911730117311173211733117341173511736117371173811739117401174111742117431174411745117461174711748117491175011751117521175311754117551175611757117581175911760117611176211763117641176511766117671176811769117701177111772117731177411775117761177711778117791178011781117821178311784117851178611787117881178911790117911179211793117941179511796117971179811799118001180111802118031180411805118061180711808118091181011811118121181311814118151181611817118181181911820118211182211823118241182511826118271182811829118301183111832118331183411835118361183711838118391184011841118421184311844118451184611847118481184911850118511185211853118541185511856118571185811859118601186111862118631186411865118661186711868118691187011871118721187311874118751187611877118781187911880118811188211883118841188511886118871188811889118901189111892118931189411895118961189711898118991190011901119021190311904119051190611907119081190911910119111191211913119141191511916119171191811919119201192111922119231192411925119261192711928119291193011931119321193311934119351193611937119381193911940119411194211943119441194511946119471194811949119501195111952119531195411955119561195711958119591196011961119621196311964119651196611967119681196911970119711197211973119741197511976119771197811979119801198111982119831198411985119861198711988119891199011991119921199311994119951199611997119981199912000120011200212003120041200512006120071200812009120101201112012120131201412015120161201712018120191202012021120221202312024120251202612027120281202912030120311203212033120341203512036120371203812039120401204112042120431204412045120461204712048120491205012051120521205312054120551205612057120581205912060120611206212063120641206512066120671206812069120701207112072120731207412075120761207712078120791208012081120821208312084120851208612087120881208912090120911209212093120941209512096120971209812099121001210112102121031210412105121061210712108121091211012111121121211312114121151211612117121181211912120121211212212123121241212512126121271212812129121301213112132121331213412135121361213712138121391214012141121421214312144121451214612147121481214912150121511215212153121541215512156121571215812159121601216112162121631216412165121661216712168121691217012171121721217312174121751217612177121781217912180121811218212183121841218512186121871218812189121901219112192121931219412195121961219712198121991220012201122021220312204122051220612207122081220912210122111221212213122141221512216122171221812219122201222112222122231222412225122261222712228122291223012231122321223312234122351223612237122381223912240122411224212243122441224512246122471224812249122501225112252122531225412255122561225712258122591226012261122621226312264122651226612267122681226912270122711227212273122741227512276122771227812279122801228112282122831228412285122861228712288122891229012291122921229312294122951229612297122981229912300123011230212303123041230512306123071230812309123101231112312123131231412315123161231712318123191232012321123221232312324123251232612327123281232912330123311233212333123341233512336123371233812339123401234112342123431234412345123461234712348123491235012351123521235312354123551235612357123581235912360123611236212363123641236512366123671236812369123701237112372123731237412375123761237712378123791238012381123821238312384123851238612387123881238912390123911239212393123941239512396123971239812399124001240112402124031240412405124061240712408124091241012411124121241312414124151241612417124181241912420124211242212423124241242512426124271242812429124301243112432124331243412435124361243712438124391244012441124421244312444124451244612447124481244912450124511245212453124541245512456124571245812459124601246112462124631246412465124661246712468124691247012471124721247312474124751247612477124781247912480124811248212483124841248512486124871248812489124901249112492124931249412495124961249712498124991250012501125021250312504125051250612507125081250912510125111251212513125141251512516125171251812519125201252112522125231252412525125261252712528125291253012531125321253312534125351253612537125381253912540125411254212543125441254512546125471254812549125501255112552125531255412555125561255712558125591256012561125621256312564125651256612567125681256912570125711257212573125741257512576125771257812579125801258112582125831258412585125861258712588125891259012591125921259312594125951259612597125981259912600126011260212603126041260512606126071260812609126101261112612126131261412615126161261712618126191262012621126221262312624126251262612627126281262912630126311263212633126341263512636126371263812639126401264112642126431264412645126461264712648126491265012651126521265312654126551265612657126581265912660126611266212663126641266512666126671266812669126701267112672126731267412675126761267712678126791268012681126821268312684126851268612687126881268912690126911269212693126941269512696126971269812699127001270112702127031270412705127061270712708127091271012711127121271312714127151271612717127181271912720127211272212723127241272512726127271272812729127301273112732127331273412735127361273712738127391274012741127421274312744127451274612747127481274912750127511275212753127541275512756127571275812759127601276112762127631276412765127661276712768127691277012771127721277312774127751277612777127781277912780127811278212783127841278512786127871278812789127901279112792127931279412795127961279712798127991280012801128021280312804128051280612807128081280912810128111281212813128141281512816128171281812819128201282112822128231282412825128261282712828128291283012831128321283312834128351283612837128381283912840128411284212843128441284512846128471284812849128501285112852128531285412855128561285712858128591286012861128621286312864128651286612867128681286912870128711287212873128741287512876128771287812879128801288112882128831288412885128861288712888128891289012891128921289312894128951289612897128981289912900129011290212903129041290512906129071290812909129101291112912129131291412915129161291712918129191292012921129221292312924129251292612927129281292912930129311293212933129341293512936129371293812939129401294112942129431294412945129461294712948129491295012951129521295312954129551295612957129581295912960129611296212963129641296512966129671296812969129701297112972129731297412975129761297712978129791298012981129821298312984129851298612987129881298912990129911299212993129941299512996129971299812999130001300113002130031300413005130061300713008130091301013011130121301313014130151301613017130181301913020130211302213023130241302513026130271302813029130301303113032130331303413035130361303713038130391304013041130421304313044130451304613047130481304913050130511305213053130541305513056130571305813059130601306113062130631306413065130661306713068130691307013071130721307313074130751307613077130781307913080130811308213083130841308513086130871308813089130901309113092130931309413095130961309713098130991310013101131021310313104131051310613107131081310913110131111311213113131141311513116131171311813119131201312113122131231312413125131261312713128131291313013131131321313313134131351313613137131381313913140131411314213143131441314513146131471314813149131501315113152131531315413155131561315713158131591316013161131621316313164131651316613167131681316913170131711317213173131741317513176131771317813179131801318113182131831318413185131861318713188131891319013191131921319313194131951319613197131981319913200132011320213203132041320513206132071320813209132101321113212132131321413215132161321713218132191322013221132221322313224132251322613227132281322913230132311323213233132341323513236132371323813239132401324113242132431324413245132461324713248132491325013251132521325313254132551325613257132581325913260132611326213263132641326513266132671326813269132701327113272132731327413275132761327713278132791328013281132821328313284132851328613287132881328913290132911329213293132941329513296132971329813299133001330113302133031330413305133061330713308133091331013311133121331313314133151331613317133181331913320133211332213323133241332513326133271332813329133301333113332133331333413335133361333713338133391334013341133421334313344133451334613347133481334913350133511335213353133541335513356133571335813359133601336113362133631336413365133661336713368133691337013371133721337313374133751337613377133781337913380133811338213383133841338513386133871338813389133901339113392133931339413395133961339713398133991340013401134021340313404134051340613407134081340913410134111341213413134141341513416134171341813419134201342113422134231342413425134261342713428134291343013431134321343313434134351343613437134381343913440134411344213443134441344513446134471344813449134501345113452134531345413455134561345713458134591346013461134621346313464134651346613467134681346913470134711347213473134741347513476134771347813479134801348113482134831348413485134861348713488134891349013491134921349313494134951349613497134981349913500135011350213503135041350513506135071350813509135101351113512135131351413515135161351713518135191352013521135221352313524135251352613527135281352913530135311353213533135341353513536135371353813539135401354113542135431354413545135461354713548135491355013551135521355313554135551355613557135581355913560135611356213563135641356513566135671356813569135701357113572135731357413575135761357713578135791358013581135821358313584135851358613587135881358913590135911359213593135941359513596135971359813599136001360113602136031360413605136061360713608136091361013611136121361313614136151361613617136181361913620136211362213623136241362513626136271362813629136301363113632136331363413635136361363713638136391364013641136421364313644136451364613647136481364913650136511365213653136541365513656136571365813659136601366113662136631366413665136661366713668136691367013671136721367313674136751367613677136781367913680136811368213683136841368513686136871368813689136901369113692136931369413695136961369713698136991370013701137021370313704137051370613707137081370913710137111371213713137141371513716137171371813719137201372113722137231372413725137261372713728137291373013731137321373313734137351373613737137381373913740137411374213743137441374513746137471374813749137501375113752137531375413755137561375713758137591376013761137621376313764137651376613767137681376913770137711377213773137741377513776137771377813779137801378113782137831378413785137861378713788137891379013791137921379313794137951379613797137981379913800138011380213803138041380513806138071380813809138101381113812138131381413815138161381713818138191382013821138221382313824138251382613827138281382913830138311383213833138341383513836138371383813839138401384113842138431384413845138461384713848138491385013851138521385313854138551385613857138581385913860138611386213863138641386513866138671386813869138701387113872138731387413875138761387713878138791388013881138821388313884138851388613887138881388913890138911389213893138941389513896138971389813899139001390113902139031390413905139061390713908139091391013911139121391313914139151391613917139181391913920139211392213923139241392513926139271392813929139301393113932139331393413935139361393713938139391394013941139421394313944139451394613947139481394913950139511395213953139541395513956139571395813959139601396113962139631396413965139661396713968139691397013971139721397313974139751397613977139781397913980139811398213983139841398513986139871398813989139901399113992139931399413995139961399713998139991400014001140021400314004140051400614007140081400914010140111401214013140141401514016140171401814019140201402114022140231402414025140261402714028140291403014031140321403314034140351403614037140381403914040140411404214043140441404514046140471404814049140501405114052140531405414055140561405714058140591406014061140621406314064140651406614067140681406914070140711407214073140741407514076140771407814079140801408114082140831408414085140861408714088140891409014091140921409314094140951409614097140981409914100141011410214103141041410514106141071410814109141101411114112141131411414115141161411714118141191412014121141221412314124141251412614127141281412914130141311413214133141341413514136141371413814139141401414114142141431414414145141461414714148141491415014151141521415314154141551415614157141581415914160141611416214163141641416514166141671416814169141701417114172141731417414175141761417714178141791418014181141821418314184141851418614187141881418914190141911419214193141941419514196141971419814199142001420114202142031420414205142061420714208142091421014211142121421314214142151421614217142181421914220142211422214223142241422514226142271422814229142301423114232142331423414235142361423714238142391424014241142421424314244142451424614247142481424914250142511425214253142541425514256142571425814259142601426114262142631426414265142661426714268142691427014271142721427314274142751427614277142781427914280142811428214283142841428514286142871428814289142901429114292142931429414295142961429714298142991430014301143021430314304143051430614307143081430914310143111431214313143141431514316143171431814319143201432114322143231432414325143261432714328143291433014331143321433314334143351433614337143381433914340143411434214343143441434514346143471434814349143501435114352143531435414355143561435714358143591436014361143621436314364143651436614367143681436914370143711437214373143741437514376143771437814379143801438114382143831438414385143861438714388143891439014391143921439314394143951439614397143981439914400144011440214403144041440514406144071440814409144101441114412144131441414415144161441714418144191442014421144221442314424144251442614427144281442914430144311443214433144341443514436144371443814439144401444114442144431444414445144461444714448144491445014451144521445314454144551445614457144581445914460144611446214463144641446514466144671446814469144701447114472144731447414475144761447714478144791448014481144821448314484144851448614487144881448914490144911449214493144941449514496144971449814499145001450114502145031450414505145061450714508145091451014511145121451314514145151451614517145181451914520145211452214523145241452514526145271452814529145301453114532145331453414535145361453714538145391454014541145421454314544145451454614547145481454914550145511455214553145541455514556145571455814559145601456114562145631456414565145661456714568145691457014571145721457314574145751457614577145781457914580145811458214583145841458514586145871458814589145901459114592145931459414595145961459714598145991460014601146021460314604146051460614607146081460914610146111461214613146141461514616146171461814619146201462114622146231462414625146261462714628146291463014631146321463314634146351463614637146381463914640146411464214643146441464514646146471464814649146501465114652146531465414655146561465714658146591466014661146621466314664146651466614667146681466914670146711467214673146741467514676146771467814679146801468114682146831468414685146861468714688146891469014691146921469314694146951469614697146981469914700147011470214703147041470514706147071470814709147101471114712147131471414715147161471714718147191472014721147221472314724147251472614727147281472914730147311473214733147341473514736147371473814739147401474114742147431474414745147461474714748147491475014751147521475314754147551475614757147581475914760147611476214763147641476514766147671476814769147701477114772147731477414775147761477714778147791478014781147821478314784147851478614787147881478914790147911479214793147941479514796147971479814799148001480114802148031480414805148061480714808148091481014811148121481314814148151481614817148181481914820148211482214823148241482514826148271482814829148301483114832148331483414835148361483714838148391484014841148421484314844148451484614847148481484914850148511485214853148541485514856148571485814859148601486114862148631486414865148661486714868148691487014871148721487314874148751487614877148781487914880148811488214883148841488514886148871488814889148901489114892148931489414895148961489714898148991490014901149021490314904149051490614907149081490914910149111491214913149141491514916149171491814919149201492114922149231492414925149261492714928149291493014931149321493314934149351493614937149381493914940149411494214943149441494514946149471494814949149501495114952149531495414955149561495714958149591496014961149621496314964149651496614967149681496914970149711497214973149741497514976149771497814979149801498114982149831498414985149861498714988149891499014991149921499314994149951499614997149981499915000150011500215003150041500515006150071500815009150101501115012150131501415015150161501715018150191502015021150221502315024150251502615027150281502915030150311503215033150341503515036150371503815039150401504115042150431504415045150461504715048150491505015051150521505315054150551505615057150581505915060150611506215063150641506515066150671506815069150701507115072150731507415075150761507715078150791508015081150821508315084150851508615087150881508915090150911509215093150941509515096150971509815099151001510115102151031510415105151061510715108151091511015111151121511315114151151511615117151181511915120151211512215123151241512515126151271512815129151301513115132151331513415135151361513715138151391514015141151421514315144151451514615147151481514915150151511515215153151541515515156151571515815159151601516115162151631516415165151661516715168151691517015171151721517315174151751517615177151781517915180151811518215183151841518515186151871518815189151901519115192151931519415195151961519715198151991520015201152021520315204152051520615207152081520915210152111521215213152141521515216152171521815219152201522115222152231522415225152261522715228152291523015231152321523315234152351523615237152381523915240152411524215243152441524515246152471524815249152501525115252152531525415255152561525715258152591526015261152621526315264152651526615267152681526915270152711527215273152741527515276152771527815279152801528115282152831528415285152861528715288152891529015291152921529315294152951529615297152981529915300153011530215303153041530515306153071530815309153101531115312153131531415315153161531715318153191532015321153221532315324153251532615327153281532915330153311533215333153341533515336153371533815339153401534115342153431534415345153461534715348153491535015351153521535315354153551535615357153581535915360153611536215363153641536515366153671536815369153701537115372153731537415375153761537715378153791538015381153821538315384153851538615387153881538915390153911539215393153941539515396153971539815399154001540115402154031540415405154061540715408154091541015411154121541315414154151541615417154181541915420154211542215423154241542515426154271542815429154301543115432154331543415435154361543715438154391544015441154421544315444154451544615447154481544915450154511545215453154541545515456154571545815459154601546115462154631546415465154661546715468154691547015471154721547315474154751547615477154781547915480154811548215483154841548515486154871548815489154901549115492154931549415495154961549715498154991550015501155021550315504155051550615507155081550915510155111551215513155141551515516155171551815519155201552115522155231552415525155261552715528155291553015531155321553315534155351553615537155381553915540155411554215543155441554515546155471554815549155501555115552155531555415555155561555715558155591556015561155621556315564155651556615567155681556915570155711557215573155741557515576155771557815579155801558115582155831558415585155861558715588155891559015591155921559315594155951559615597155981559915600156011560215603156041560515606156071560815609156101561115612156131561415615156161561715618156191562015621156221562315624156251562615627156281562915630156311563215633156341563515636156371563815639156401564115642156431564415645156461564715648156491565015651156521565315654156551565615657156581565915660156611566215663156641566515666156671566815669156701567115672156731567415675156761567715678156791568015681156821568315684156851568615687156881568915690156911569215693156941569515696156971569815699157001570115702157031570415705157061570715708157091571015711157121571315714157151571615717157181571915720157211572215723157241572515726157271572815729157301573115732157331573415735157361573715738157391574015741157421574315744157451574615747157481574915750157511575215753157541575515756157571575815759157601576115762157631576415765157661576715768157691577015771157721577315774157751577615777157781577915780157811578215783157841578515786157871578815789157901579115792157931579415795157961579715798157991580015801158021580315804158051580615807158081580915810158111581215813158141581515816158171581815819158201582115822158231582415825158261582715828158291583015831158321583315834158351583615837158381583915840158411584215843158441584515846158471584815849158501585115852158531585415855158561585715858158591586015861158621586315864158651586615867158681586915870158711587215873158741587515876158771587815879158801588115882158831588415885158861588715888158891589015891158921589315894158951589615897158981589915900159011590215903159041590515906159071590815909159101591115912159131591415915159161591715918159191592015921159221592315924159251592615927159281592915930159311593215933159341593515936159371593815939159401594115942159431594415945159461594715948159491595015951159521595315954159551595615957159581595915960159611596215963159641596515966159671596815969159701597115972159731597415975159761597715978159791598015981159821598315984159851598615987159881598915990159911599215993159941599515996159971599815999160001600116002160031600416005160061600716008160091601016011160121601316014160151601616017160181601916020160211602216023160241602516026160271602816029160301603116032160331603416035160361603716038160391604016041160421604316044160451604616047160481604916050160511605216053160541605516056160571605816059160601606116062160631606416065160661606716068160691607016071160721607316074160751607616077160781607916080160811608216083160841608516086160871608816089160901609116092160931609416095160961609716098160991610016101161021610316104161051610616107161081610916110161111611216113161141611516116161171611816119161201612116122161231612416125161261612716128161291613016131161321613316134161351613616137161381613916140161411614216143161441614516146161471614816149161501615116152161531615416155161561615716158161591616016161161621616316164161651616616167161681616916170161711617216173161741617516176161771617816179161801618116182161831618416185161861618716188161891619016191161921619316194161951619616197161981619916200162011620216203162041620516206162071620816209162101621116212162131621416215162161621716218162191622016221162221622316224162251622616227162281622916230162311623216233162341623516236162371623816239162401624116242162431624416245162461624716248162491625016251162521625316254162551625616257162581625916260162611626216263162641626516266162671626816269162701627116272162731627416275162761627716278162791628016281162821628316284162851628616287162881628916290162911629216293162941629516296162971629816299163001630116302163031630416305163061630716308163091631016311163121631316314163151631616317163181631916320163211632216323163241632516326163271632816329163301633116332163331633416335163361633716338163391634016341163421634316344163451634616347163481634916350163511635216353163541635516356163571635816359163601636116362163631636416365163661636716368163691637016371163721637316374163751637616377163781637916380163811638216383163841638516386163871638816389163901639116392163931639416395163961639716398163991640016401164021640316404164051640616407164081640916410164111641216413164141641516416164171641816419164201642116422164231642416425164261642716428164291643016431164321643316434164351643616437164381643916440164411644216443164441644516446164471644816449164501645116452164531645416455164561645716458164591646016461164621646316464164651646616467164681646916470164711647216473164741647516476164771647816479164801648116482164831648416485164861648716488164891649016491164921649316494164951649616497164981649916500165011650216503165041650516506165071650816509165101651116512165131651416515165161651716518165191652016521165221652316524165251652616527165281652916530165311653216533165341653516536165371653816539165401654116542165431654416545165461654716548165491655016551165521655316554165551655616557165581655916560165611656216563165641656516566165671656816569165701657116572165731657416575165761657716578165791658016581165821658316584165851658616587165881658916590165911659216593165941659516596165971659816599166001660116602166031660416605166061660716608166091661016611166121661316614166151661616617166181661916620166211662216623166241662516626166271662816629166301663116632166331663416635166361663716638166391664016641166421664316644166451664616647166481664916650166511665216653166541665516656166571665816659166601666116662166631666416665166661666716668166691667016671166721667316674166751667616677166781667916680166811668216683166841668516686166871668816689166901669116692166931669416695166961669716698166991670016701167021670316704167051670616707167081670916710167111671216713167141671516716167171671816719167201672116722167231672416725167261672716728167291673016731167321673316734167351673616737167381673916740167411674216743167441674516746167471674816749167501675116752167531675416755167561675716758167591676016761167621676316764167651676616767167681676916770167711677216773167741677516776167771677816779167801678116782167831678416785167861678716788167891679016791167921679316794167951679616797167981679916800168011680216803168041680516806168071680816809168101681116812168131681416815168161681716818168191682016821168221682316824168251682616827168281682916830168311683216833168341683516836168371683816839168401684116842168431684416845168461684716848168491685016851168521685316854168551685616857168581685916860168611686216863168641686516866168671686816869168701687116872168731687416875168761687716878168791688016881168821688316884168851688616887168881688916890168911689216893168941689516896168971689816899169001690116902169031690416905169061690716908169091691016911169121691316914169151691616917169181691916920169211692216923169241692516926169271692816929169301693116932169331693416935169361693716938169391694016941169421694316944169451694616947169481694916950169511695216953169541695516956169571695816959169601696116962169631696416965169661696716968169691697016971169721697316974169751697616977169781697916980169811698216983169841698516986169871698816989169901699116992169931699416995169961699716998169991700017001170021700317004170051700617007170081700917010170111701217013170141701517016170171701817019170201702117022170231702417025170261702717028170291703017031170321703317034170351703617037170381703917040170411704217043170441704517046170471704817049170501705117052170531705417055170561705717058170591706017061170621706317064170651706617067170681706917070170711707217073170741707517076170771707817079170801708117082170831708417085170861708717088170891709017091170921709317094170951709617097170981709917100171011710217103171041710517106171071710817109171101711117112171131711417115171161711717118171191712017121171221712317124171251712617127171281712917130171311713217133171341713517136171371713817139171401714117142171431714417145171461714717148171491715017151171521715317154171551715617157171581715917160171611716217163171641716517166171671716817169171701717117172171731717417175171761717717178171791718017181171821718317184171851718617187171881718917190171911719217193171941719517196171971719817199172001720117202172031720417205172061720717208172091721017211172121721317214172151721617217172181721917220172211722217223172241722517226172271722817229172301723117232172331723417235172361723717238172391724017241172421724317244172451724617247172481724917250172511725217253172541725517256172571725817259172601726117262172631726417265172661726717268172691727017271172721727317274172751727617277172781727917280172811728217283172841728517286172871728817289172901729117292172931729417295172961729717298172991730017301173021730317304173051730617307173081730917310173111731217313173141731517316173171731817319173201732117322173231732417325173261732717328173291733017331173321733317334173351733617337173381733917340173411734217343173441734517346173471734817349173501735117352173531735417355173561735717358173591736017361173621736317364173651736617367173681736917370173711737217373173741737517376173771737817379173801738117382173831738417385173861738717388173891739017391173921739317394173951739617397173981739917400174011740217403174041740517406174071740817409174101741117412174131741417415174161741717418174191742017421174221742317424174251742617427174281742917430174311743217433174341743517436174371743817439174401744117442174431744417445174461744717448174491745017451174521745317454174551745617457174581745917460174611746217463174641746517466174671746817469174701747117472174731747417475174761747717478174791748017481174821748317484174851748617487174881748917490174911749217493174941749517496174971749817499175001750117502175031750417505175061750717508175091751017511175121751317514175151751617517175181751917520175211752217523175241752517526175271752817529175301753117532175331753417535175361753717538175391754017541175421754317544175451754617547175481754917550175511755217553175541755517556175571755817559175601756117562175631756417565175661756717568175691757017571175721757317574175751757617577175781757917580175811758217583175841758517586175871758817589175901759117592175931759417595175961759717598175991760017601176021760317604176051760617607176081760917610176111761217613176141761517616176171761817619176201762117622176231762417625176261762717628176291763017631176321763317634176351763617637176381763917640176411764217643176441764517646176471764817649176501765117652176531765417655176561765717658176591766017661176621766317664176651766617667176681766917670176711767217673176741767517676176771767817679176801768117682176831768417685176861768717688176891769017691176921769317694176951769617697176981769917700177011770217703177041770517706177071770817709177101771117712177131771417715177161771717718177191772017721177221772317724177251772617727177281772917730177311773217733177341773517736177371773817739177401774117742177431774417745177461774717748177491775017751177521775317754177551775617757177581775917760177611776217763177641776517766177671776817769177701777117772177731777417775177761777717778177791778017781177821778317784177851778617787177881778917790177911779217793177941779517796177971779817799178001780117802178031780417805178061780717808178091781017811178121781317814178151781617817178181781917820178211782217823178241782517826178271782817829178301783117832178331783417835178361783717838178391784017841178421784317844178451784617847178481784917850178511785217853178541785517856178571785817859178601786117862178631786417865178661786717868178691787017871178721787317874178751787617877178781787917880178811788217883178841788517886178871788817889178901789117892178931789417895178961789717898178991790017901179021790317904179051790617907179081790917910179111791217913179141791517916179171791817919179201792117922179231792417925179261792717928179291793017931179321793317934179351793617937179381793917940179411794217943179441794517946179471794817949179501795117952179531795417955179561795717958179591796017961179621796317964179651796617967179681796917970179711797217973179741797517976179771797817979179801798117982179831798417985179861798717988179891799017991179921799317994179951799617997179981799918000180011800218003180041800518006180071800818009180101801118012180131801418015180161801718018180191802018021180221802318024180251802618027180281802918030180311803218033180341803518036180371803818039180401804118042180431804418045180461804718048180491805018051180521805318054180551805618057180581805918060180611806218063180641806518066180671806818069180701807118072180731807418075180761807718078180791808018081180821808318084180851808618087180881808918090180911809218093180941809518096180971809818099181001810118102181031810418105181061810718108181091811018111181121811318114181151811618117181181811918120181211812218123181241812518126181271812818129181301813118132181331813418135181361813718138181391814018141181421814318144181451814618147181481814918150181511815218153181541815518156181571815818159181601816118162181631816418165181661816718168181691817018171181721817318174181751817618177181781817918180181811818218183181841818518186181871818818189181901819118192181931819418195181961819718198181991820018201182021820318204182051820618207182081820918210182111821218213182141821518216182171821818219182201822118222182231822418225182261822718228182291823018231182321823318234182351823618237182381823918240182411824218243182441824518246182471824818249182501825118252182531825418255182561825718258182591826018261182621826318264182651826618267182681826918270182711827218273182741827518276182771827818279182801828118282182831828418285182861828718288182891829018291182921829318294182951829618297182981829918300183011830218303183041830518306183071830818309183101831118312183131831418315183161831718318183191832018321183221832318324183251832618327183281832918330183311833218333183341833518336183371833818339183401834118342183431834418345183461834718348183491835018351183521835318354183551835618357183581835918360183611836218363183641836518366183671836818369183701837118372183731837418375183761837718378183791838018381183821838318384183851838618387183881838918390183911839218393183941839518396183971839818399184001840118402184031840418405184061840718408184091841018411184121841318414184151841618417184181841918420184211842218423184241842518426184271842818429184301843118432184331843418435184361843718438184391844018441184421844318444184451844618447184481844918450184511845218453184541845518456184571845818459184601846118462184631846418465184661846718468184691847018471184721847318474184751847618477184781847918480184811848218483184841848518486184871848818489184901849118492184931849418495184961849718498184991850018501185021850318504185051850618507185081850918510185111851218513185141851518516185171851818519185201852118522185231852418525185261852718528185291853018531185321853318534185351853618537185381853918540185411854218543185441854518546185471854818549185501855118552185531855418555185561855718558185591856018561185621856318564185651856618567185681856918570185711857218573185741857518576185771857818579185801858118582185831858418585185861858718588185891859018591185921859318594185951859618597185981859918600186011860218603186041860518606186071860818609186101861118612186131861418615186161861718618186191862018621186221862318624186251862618627186281862918630186311863218633186341863518636186371863818639186401864118642186431864418645186461864718648186491865018651186521865318654186551865618657186581865918660186611866218663186641866518666186671866818669186701867118672186731867418675186761867718678186791868018681186821868318684186851868618687186881868918690186911869218693186941869518696186971869818699187001870118702187031870418705187061870718708187091871018711187121871318714187151871618717187181871918720187211872218723187241872518726187271872818729187301873118732187331873418735187361873718738187391874018741187421874318744187451874618747187481874918750187511875218753187541875518756187571875818759187601876118762187631876418765187661876718768187691877018771187721877318774187751877618777187781877918780187811878218783187841878518786187871878818789187901879118792187931879418795187961879718798187991880018801188021880318804188051880618807188081880918810188111881218813188141881518816188171881818819188201882118822188231882418825188261882718828188291883018831188321883318834188351883618837188381883918840188411884218843188441884518846188471884818849188501885118852188531885418855188561885718858188591886018861188621886318864188651886618867188681886918870188711887218873188741887518876188771887818879188801888118882188831888418885188861888718888188891889018891188921889318894188951889618897188981889918900189011890218903189041890518906189071890818909189101891118912189131891418915189161891718918189191892018921189221892318924189251892618927189281892918930189311893218933189341893518936189371893818939189401894118942189431894418945189461894718948189491895018951189521895318954189551895618957189581895918960189611896218963189641896518966189671896818969189701897118972189731897418975189761897718978189791898018981189821898318984189851898618987189881898918990189911899218993189941899518996189971899818999190001900119002190031900419005190061900719008190091901019011190121901319014190151901619017190181901919020190211902219023190241902519026190271902819029190301903119032190331903419035190361903719038190391904019041190421904319044190451904619047190481904919050190511905219053190541905519056190571905819059190601906119062190631906419065190661906719068190691907019071190721907319074190751907619077190781907919080190811908219083190841908519086190871908819089190901909119092190931909419095190961909719098190991910019101191021910319104191051910619107191081910919110191111911219113191141911519116191171911819119191201912119122191231912419125191261912719128191291913019131191321913319134191351913619137191381913919140191411914219143191441914519146191471914819149191501915119152191531915419155191561915719158191591916019161191621916319164191651916619167191681916919170191711917219173191741917519176191771917819179191801918119182191831918419185191861918719188191891919019191191921919319194191951919619197191981919919200192011920219203192041920519206192071920819209192101921119212192131921419215192161921719218192191922019221192221922319224192251922619227192281922919230192311923219233192341923519236192371923819239192401924119242192431924419245192461924719248192491925019251192521925319254192551925619257192581925919260192611926219263192641926519266192671926819269192701927119272192731927419275192761927719278192791928019281192821928319284192851928619287192881928919290192911929219293192941929519296192971929819299193001930119302193031930419305193061930719308193091931019311193121931319314193151931619317193181931919320193211932219323193241932519326193271932819329193301933119332193331933419335193361933719338193391934019341193421934319344193451934619347193481934919350193511935219353193541935519356193571935819359193601936119362193631936419365193661936719368193691937019371193721937319374193751937619377193781937919380193811938219383193841938519386193871938819389193901939119392193931939419395193961939719398193991940019401194021940319404194051940619407194081940919410194111941219413194141941519416194171941819419194201942119422194231942419425194261942719428194291943019431194321943319434194351943619437194381943919440194411944219443194441944519446194471944819449194501945119452194531945419455194561945719458194591946019461194621946319464194651946619467194681946919470194711947219473194741947519476194771947819479194801948119482194831948419485194861948719488194891949019491194921949319494194951949619497194981949919500195011950219503195041950519506195071950819509195101951119512195131951419515195161951719518195191952019521195221952319524195251952619527195281952919530195311953219533195341953519536195371953819539195401954119542195431954419545195461954719548195491955019551195521955319554195551955619557195581955919560195611956219563195641956519566195671956819569195701957119572195731957419575195761957719578195791958019581195821958319584195851958619587195881958919590195911959219593195941959519596195971959819599196001960119602196031960419605196061960719608196091961019611196121961319614196151961619617196181961919620196211962219623196241962519626196271962819629196301963119632196331963419635196361963719638196391964019641196421964319644196451964619647196481964919650196511965219653196541965519656196571965819659196601966119662196631966419665196661966719668196691967019671196721967319674196751967619677196781967919680196811968219683196841968519686196871968819689196901969119692196931969419695196961969719698196991970019701197021970319704197051970619707197081970919710197111971219713197141971519716197171971819719197201972119722197231972419725197261972719728197291973019731197321973319734197351973619737197381973919740197411974219743197441974519746197471974819749197501975119752197531975419755197561975719758197591976019761197621976319764197651976619767197681976919770197711977219773197741977519776197771977819779197801978119782197831978419785197861978719788197891979019791197921979319794197951979619797197981979919800198011980219803198041980519806198071980819809198101981119812198131981419815198161981719818198191982019821198221982319824198251982619827198281982919830198311983219833198341983519836198371983819839198401984119842198431984419845198461984719848198491985019851198521985319854198551985619857198581985919860198611986219863198641986519866198671986819869198701987119872198731987419875198761987719878198791988019881198821988319884198851988619887198881988919890198911989219893198941989519896198971989819899199001990119902199031990419905199061990719908199091991019911199121991319914199151991619917199181991919920199211992219923199241992519926199271992819929199301993119932199331993419935199361993719938199391994019941199421994319944199451994619947199481994919950199511995219953199541995519956199571995819959199601996119962199631996419965199661996719968199691997019971199721997319974199751997619977199781997919980199811998219983199841998519986199871998819989199901999119992199931999419995199961999719998199992000020001200022000320004200052000620007200082000920010200112001220013200142001520016200172001820019200202002120022200232002420025200262002720028200292003020031200322003320034200352003620037200382003920040200412004220043200442004520046200472004820049200502005120052200532005420055200562005720058200592006020061200622006320064200652006620067200682006920070200712007220073200742007520076200772007820079200802008120082200832008420085200862008720088200892009020091200922009320094200952009620097200982009920100201012010220103201042010520106201072010820109201102011120112201132011420115201162011720118201192012020121201222012320124201252012620127201282012920130201312013220133201342013520136201372013820139201402014120142201432014420145201462014720148201492015020151201522015320154201552015620157201582015920160201612016220163201642016520166201672016820169201702017120172201732017420175201762017720178201792018020181201822018320184201852018620187201882018920190201912019220193201942019520196201972019820199202002020120202202032020420205202062020720208202092021020211202122021320214202152021620217202182021920220202212022220223202242022520226202272022820229202302023120232202332023420235202362023720238202392024020241202422024320244202452024620247202482024920250202512025220253202542025520256202572025820259202602026120262202632026420265202662026720268202692027020271202722027320274202752027620277202782027920280202812028220283202842028520286202872028820289202902029120292202932029420295202962029720298202992030020301203022030320304203052030620307203082030920310203112031220313203142031520316203172031820319203202032120322203232032420325203262032720328203292033020331203322033320334203352033620337203382033920340203412034220343203442034520346203472034820349203502035120352203532035420355203562035720358203592036020361203622036320364203652036620367203682036920370203712037220373203742037520376203772037820379203802038120382203832038420385203862038720388203892039020391203922039320394203952039620397203982039920400204012040220403204042040520406204072040820409204102041120412204132041420415204162041720418204192042020421204222042320424204252042620427204282042920430204312043220433204342043520436204372043820439204402044120442204432044420445204462044720448204492045020451204522045320454204552045620457204582045920460204612046220463204642046520466204672046820469204702047120472204732047420475204762047720478204792048020481204822048320484204852048620487204882048920490204912049220493204942049520496204972049820499205002050120502205032050420505205062050720508205092051020511205122051320514205152051620517205182051920520205212052220523205242052520526205272052820529205302053120532205332053420535205362053720538205392054020541205422054320544205452054620547205482054920550205512055220553205542055520556205572055820559205602056120562205632056420565205662056720568205692057020571205722057320574205752057620577205782057920580205812058220583205842058520586205872058820589205902059120592205932059420595205962059720598205992060020601206022060320604206052060620607206082060920610206112061220613206142061520616206172061820619206202062120622206232062420625206262062720628206292063020631206322063320634206352063620637206382063920640206412064220643206442064520646206472064820649206502065120652206532065420655206562065720658206592066020661206622066320664206652066620667206682066920670206712067220673206742067520676206772067820679206802068120682206832068420685206862068720688206892069020691206922069320694206952069620697206982069920700207012070220703207042070520706207072070820709207102071120712207132071420715207162071720718207192072020721207222072320724207252072620727207282072920730207312073220733207342073520736207372073820739207402074120742207432074420745207462074720748207492075020751207522075320754207552075620757207582075920760207612076220763207642076520766207672076820769207702077120772207732077420775207762077720778207792078020781207822078320784207852078620787207882078920790207912079220793207942079520796207972079820799208002080120802208032080420805208062080720808208092081020811208122081320814208152081620817208182081920820208212082220823208242082520826208272082820829208302083120832208332083420835208362083720838208392084020841208422084320844208452084620847208482084920850208512085220853208542085520856208572085820859208602086120862208632086420865208662086720868208692087020871208722087320874208752087620877208782087920880208812088220883208842088520886208872088820889208902089120892208932089420895208962089720898208992090020901209022090320904209052090620907209082090920910209112091220913209142091520916209172091820919209202092120922209232092420925209262092720928209292093020931209322093320934209352093620937209382093920940209412094220943209442094520946209472094820949209502095120952209532095420955209562095720958209592096020961209622096320964209652096620967209682096920970209712097220973209742097520976209772097820979209802098120982209832098420985209862098720988209892099020991209922099320994209952099620997209982099921000210012100221003210042100521006210072100821009210102101121012210132101421015210162101721018210192102021021210222102321024210252102621027210282102921030210312103221033210342103521036210372103821039210402104121042210432104421045210462104721048210492105021051210522105321054210552105621057210582105921060210612106221063210642106521066210672106821069210702107121072210732107421075210762107721078210792108021081210822108321084210852108621087210882108921090210912109221093210942109521096210972109821099211002110121102211032110421105211062110721108211092111021111211122111321114211152111621117211182111921120211212112221123211242112521126211272112821129211302113121132211332113421135211362113721138211392114021141211422114321144211452114621147211482114921150211512115221153211542115521156211572115821159211602116121162211632116421165211662116721168211692117021171211722117321174211752117621177211782117921180211812118221183211842118521186211872118821189211902119121192211932119421195211962119721198211992120021201212022120321204212052120621207212082120921210212112121221213212142121521216212172121821219212202122121222212232122421225212262122721228212292123021231212322123321234212352123621237212382123921240212412124221243212442124521246212472124821249212502125121252212532125421255212562125721258212592126021261212622126321264212652126621267212682126921270212712127221273212742127521276212772127821279212802128121282212832128421285212862128721288212892129021291212922129321294212952129621297212982129921300213012130221303213042130521306213072130821309213102131121312213132131421315213162131721318213192132021321213222132321324213252132621327213282132921330213312133221333213342133521336213372133821339213402134121342213432134421345213462134721348213492135021351213522135321354213552135621357213582135921360213612136221363213642136521366213672136821369213702137121372213732137421375213762137721378213792138021381213822138321384213852138621387213882138921390213912139221393213942139521396213972139821399214002140121402214032140421405214062140721408214092141021411214122141321414214152141621417214182141921420214212142221423214242142521426214272142821429214302143121432214332143421435214362143721438214392144021441214422144321444214452144621447214482144921450214512145221453214542145521456214572145821459214602146121462214632146421465214662146721468214692147021471214722147321474214752147621477214782147921480214812148221483214842148521486214872148821489214902149121492214932149421495214962149721498214992150021501215022150321504215052150621507215082150921510215112151221513215142151521516215172151821519215202152121522215232152421525215262152721528215292153021531215322153321534215352153621537215382153921540215412154221543215442154521546215472154821549215502155121552215532155421555215562155721558215592156021561215622156321564215652156621567215682156921570215712157221573215742157521576215772157821579215802158121582215832158421585215862158721588215892159021591215922159321594215952159621597215982159921600216012160221603216042160521606216072160821609216102161121612216132161421615216162161721618216192162021621216222162321624216252162621627216282162921630216312163221633216342163521636216372163821639216402164121642216432164421645216462164721648216492165021651216522165321654216552165621657216582165921660216612166221663216642166521666216672166821669216702167121672216732167421675216762167721678216792168021681216822168321684216852168621687216882168921690216912169221693216942169521696216972169821699217002170121702217032170421705217062170721708217092171021711217122171321714217152171621717217182171921720217212172221723217242172521726217272172821729217302173121732217332173421735217362173721738217392174021741217422174321744217452174621747217482174921750217512175221753217542175521756217572175821759217602176121762217632176421765217662176721768217692177021771217722177321774217752177621777217782177921780217812178221783217842178521786217872178821789217902179121792217932179421795217962179721798217992180021801218022180321804218052180621807218082180921810218112181221813218142181521816218172181821819218202182121822218232182421825218262182721828218292183021831218322183321834218352183621837218382183921840218412184221843218442184521846218472184821849218502185121852218532185421855218562185721858218592186021861218622186321864218652186621867218682186921870218712187221873218742187521876218772187821879218802188121882218832188421885218862188721888218892189021891218922189321894218952189621897218982189921900219012190221903219042190521906219072190821909219102191121912219132191421915219162191721918219192192021921219222192321924219252192621927219282192921930219312193221933219342193521936219372193821939219402194121942219432194421945219462194721948219492195021951219522195321954219552195621957219582195921960219612196221963219642196521966219672196821969219702197121972219732197421975219762197721978219792198021981219822198321984219852198621987219882198921990219912199221993219942199521996219972199821999220002200122002220032200422005220062200722008220092201022011220122201322014220152201622017220182201922020220212202222023220242202522026220272202822029220302203122032220332203422035220362203722038220392204022041220422204322044220452204622047220482204922050220512205222053220542205522056220572205822059220602206122062220632206422065220662206722068220692207022071220722207322074220752207622077220782207922080220812208222083220842208522086220872208822089220902209122092220932209422095220962209722098220992210022101221022210322104221052210622107221082210922110221112211222113221142211522116221172211822119221202212122122221232212422125221262212722128221292213022131221322213322134221352213622137221382213922140221412214222143221442214522146221472214822149221502215122152221532215422155221562215722158221592216022161221622216322164221652216622167221682216922170221712217222173221742217522176221772217822179221802218122182221832218422185221862218722188221892219022191221922219322194221952219622197221982219922200222012220222203222042220522206222072220822209222102221122212222132221422215222162221722218222192222022221222222222322224222252222622227222282222922230222312223222233222342223522236222372223822239222402224122242222432224422245222462224722248222492225022251222522225322254222552225622257222582225922260222612226222263222642226522266222672226822269222702227122272222732227422275222762227722278222792228022281222822228322284222852228622287222882228922290222912229222293222942229522296222972229822299223002230122302223032230422305223062230722308223092231022311223122231322314223152231622317223182231922320223212232222323223242232522326223272232822329223302233122332223332233422335223362233722338223392234022341223422234322344223452234622347223482234922350223512235222353223542235522356223572235822359223602236122362223632236422365223662236722368223692237022371223722237322374223752237622377223782237922380223812238222383223842238522386223872238822389223902239122392223932239422395223962239722398223992240022401224022240322404224052240622407224082240922410224112241222413224142241522416224172241822419224202242122422224232242422425224262242722428224292243022431224322243322434224352243622437224382243922440224412244222443224442244522446224472244822449224502245122452224532245422455224562245722458224592246022461224622246322464224652246622467224682246922470224712247222473224742247522476224772247822479224802248122482224832248422485224862248722488224892249022491224922249322494224952249622497224982249922500225012250222503225042250522506225072250822509225102251122512225132251422515225162251722518225192252022521225222252322524225252252622527225282252922530225312253222533225342253522536225372253822539225402254122542225432254422545225462254722548225492255022551225522255322554225552255622557225582255922560225612256222563225642256522566225672256822569225702257122572225732257422575225762257722578225792258022581225822258322584225852258622587225882258922590225912259222593225942259522596225972259822599226002260122602226032260422605226062260722608226092261022611226122261322614226152261622617226182261922620226212262222623226242262522626226272262822629226302263122632226332263422635226362263722638226392264022641226422264322644226452264622647226482264922650226512265222653226542265522656226572265822659226602266122662226632266422665226662266722668226692267022671226722267322674226752267622677226782267922680226812268222683226842268522686226872268822689226902269122692226932269422695226962269722698226992270022701227022270322704227052270622707227082270922710227112271222713227142271522716227172271822719227202272122722227232272422725227262272722728227292273022731227322273322734227352273622737227382273922740227412274222743227442274522746227472274822749227502275122752227532275422755227562275722758227592276022761227622276322764227652276622767227682276922770227712277222773227742277522776227772277822779227802278122782227832278422785227862278722788227892279022791227922279322794227952279622797227982279922800228012280222803228042280522806228072280822809228102281122812228132281422815228162281722818228192282022821228222282322824228252282622827228282282922830228312283222833228342283522836228372283822839228402284122842228432284422845228462284722848228492285022851228522285322854228552285622857228582285922860228612286222863228642286522866228672286822869228702287122872228732287422875228762287722878228792288022881228822288322884228852288622887228882288922890228912289222893228942289522896228972289822899229002290122902229032290422905229062290722908229092291022911229122291322914229152291622917229182291922920229212292222923229242292522926229272292822929229302293122932229332293422935229362293722938229392294022941229422294322944229452294622947229482294922950229512295222953229542295522956229572295822959229602296122962229632296422965229662296722968229692297022971229722297322974229752297622977229782297922980229812298222983229842298522986229872298822989229902299122992229932299422995229962299722998229992300023001230022300323004230052300623007230082300923010230112301223013230142301523016230172301823019230202302123022230232302423025230262302723028230292303023031230322303323034230352303623037230382303923040230412304223043230442304523046230472304823049230502305123052230532305423055230562305723058230592306023061230622306323064230652306623067230682306923070230712307223073230742307523076230772307823079230802308123082230832308423085230862308723088230892309023091230922309323094230952309623097230982309923100231012310223103231042310523106231072310823109231102311123112231132311423115231162311723118231192312023121231222312323124231252312623127231282312923130231312313223133231342313523136231372313823139231402314123142231432314423145231462314723148231492315023151231522315323154231552315623157231582315923160231612316223163231642316523166231672316823169231702317123172231732317423175231762317723178231792318023181231822318323184231852318623187231882318923190231912319223193231942319523196231972319823199232002320123202232032320423205232062320723208232092321023211232122321323214232152321623217232182321923220232212322223223232242322523226232272322823229232302323123232232332323423235232362323723238232392324023241232422324323244232452324623247232482324923250232512325223253232542325523256232572325823259232602326123262232632326423265232662326723268232692327023271232722327323274232752327623277232782327923280232812328223283232842328523286232872328823289232902329123292232932329423295232962329723298232992330023301233022330323304233052330623307233082330923310233112331223313233142331523316233172331823319233202332123322233232332423325233262332723328233292333023331233322333323334233352333623337233382333923340233412334223343233442334523346233472334823349233502335123352233532335423355233562335723358233592336023361233622336323364233652336623367233682336923370233712337223373233742337523376233772337823379233802338123382233832338423385233862338723388233892339023391233922339323394233952339623397233982339923400234012340223403234042340523406234072340823409234102341123412234132341423415234162341723418234192342023421234222342323424234252342623427234282342923430234312343223433234342343523436234372343823439234402344123442234432344423445234462344723448234492345023451234522345323454234552345623457234582345923460234612346223463234642346523466234672346823469234702347123472234732347423475234762347723478234792348023481234822348323484234852348623487234882348923490234912349223493234942349523496234972349823499235002350123502235032350423505235062350723508235092351023511235122351323514235152351623517235182351923520235212352223523235242352523526235272352823529235302353123532235332353423535235362353723538235392354023541235422354323544235452354623547235482354923550235512355223553235542355523556235572355823559235602356123562235632356423565235662356723568235692357023571235722357323574235752357623577235782357923580235812358223583235842358523586235872358823589235902359123592235932359423595235962359723598235992360023601236022360323604236052360623607236082360923610236112361223613236142361523616236172361823619236202362123622236232362423625236262362723628236292363023631236322363323634236352363623637236382363923640236412364223643236442364523646236472364823649236502365123652236532365423655236562365723658236592366023661236622366323664236652366623667236682366923670236712367223673236742367523676236772367823679236802368123682236832368423685236862368723688236892369023691236922369323694236952369623697236982369923700237012370223703237042370523706237072370823709237102371123712237132371423715237162371723718237192372023721237222372323724237252372623727237282372923730237312373223733237342373523736237372373823739237402374123742237432374423745237462374723748237492375023751237522375323754237552375623757237582375923760237612376223763237642376523766237672376823769237702377123772237732377423775237762377723778237792378023781237822378323784237852378623787237882378923790237912379223793237942379523796237972379823799238002380123802238032380423805238062380723808238092381023811238122381323814238152381623817238182381923820238212382223823238242382523826238272382823829238302383123832238332383423835238362383723838238392384023841238422384323844238452384623847238482384923850238512385223853238542385523856238572385823859238602386123862238632386423865238662386723868238692387023871238722387323874238752387623877238782387923880238812388223883238842388523886238872388823889238902389123892238932389423895238962389723898238992390023901239022390323904239052390623907239082390923910239112391223913239142391523916239172391823919239202392123922239232392423925239262392723928239292393023931239322393323934239352393623937239382393923940239412394223943239442394523946239472394823949239502395123952239532395423955239562395723958239592396023961239622396323964239652396623967239682396923970239712397223973239742397523976239772397823979239802398123982239832398423985239862398723988239892399023991239922399323994239952399623997239982399924000240012400224003240042400524006240072400824009240102401124012240132401424015240162401724018240192402024021240222402324024240252402624027240282402924030240312403224033240342403524036240372403824039240402404124042240432404424045240462404724048240492405024051240522405324054240552405624057240582405924060240612406224063240642406524066240672406824069240702407124072240732407424075240762407724078240792408024081240822408324084240852408624087240882408924090240912409224093240942409524096240972409824099241002410124102241032410424105241062410724108241092411024111241122411324114241152411624117241182411924120241212412224123241242412524126241272412824129241302413124132241332413424135241362413724138241392414024141241422414324144241452414624147241482414924150241512415224153241542415524156241572415824159241602416124162241632416424165241662416724168241692417024171241722417324174241752417624177241782417924180241812418224183241842418524186241872418824189241902419124192241932419424195241962419724198241992420024201242022420324204242052420624207242082420924210242112421224213242142421524216242172421824219242202422124222242232422424225242262422724228242292423024231242322423324234242352423624237242382423924240242412424224243242442424524246242472424824249242502425124252242532425424255242562425724258242592426024261242622426324264242652426624267242682426924270242712427224273242742427524276242772427824279242802428124282242832428424285242862428724288242892429024291242922429324294242952429624297242982429924300243012430224303243042430524306243072430824309243102431124312243132431424315243162431724318243192432024321243222432324324243252432624327243282432924330243312433224333243342433524336243372433824339243402434124342243432434424345243462434724348243492435024351243522435324354243552435624357243582435924360243612436224363243642436524366243672436824369243702437124372243732437424375243762437724378243792438024381243822438324384243852438624387243882438924390243912439224393243942439524396243972439824399244002440124402244032440424405244062440724408244092441024411244122441324414244152441624417244182441924420244212442224423244242442524426244272442824429244302443124432244332443424435244362443724438244392444024441244422444324444244452444624447244482444924450244512445224453244542445524456244572445824459244602446124462244632446424465244662446724468244692447024471244722447324474244752447624477244782447924480244812448224483244842448524486244872448824489244902449124492244932449424495244962449724498244992450024501245022450324504245052450624507245082450924510245112451224513245142451524516245172451824519245202452124522245232452424525245262452724528245292453024531245322453324534245352453624537245382453924540245412454224543245442454524546245472454824549245502455124552245532455424555245562455724558245592456024561245622456324564245652456624567245682456924570245712457224573245742457524576245772457824579245802458124582245832458424585245862458724588245892459024591245922459324594245952459624597245982459924600246012460224603246042460524606246072460824609246102461124612246132461424615246162461724618246192462024621246222462324624246252462624627246282462924630246312463224633246342463524636246372463824639246402464124642246432464424645246462464724648246492465024651246522465324654246552465624657246582465924660246612466224663246642466524666246672466824669246702467124672246732467424675246762467724678246792468024681246822468324684246852468624687246882468924690246912469224693246942469524696246972469824699247002470124702247032470424705247062470724708247092471024711247122471324714247152471624717247182471924720247212472224723247242472524726247272472824729247302473124732247332473424735247362473724738247392474024741247422474324744247452474624747247482474924750247512475224753247542475524756247572475824759247602476124762247632476424765247662476724768247692477024771247722477324774247752477624777247782477924780247812478224783247842478524786247872478824789247902479124792247932479424795247962479724798247992480024801248022480324804248052480624807248082480924810248112481224813248142481524816248172481824819248202482124822248232482424825248262482724828248292483024831248322483324834248352483624837248382483924840248412484224843248442484524846248472484824849248502485124852248532485424855248562485724858248592486024861248622486324864248652486624867248682486924870248712487224873248742487524876248772487824879248802488124882248832488424885248862488724888248892489024891248922489324894248952489624897248982489924900249012490224903249042490524906249072490824909249102491124912249132491424915249162491724918249192492024921249222492324924249252492624927249282492924930249312493224933249342493524936249372493824939249402494124942249432494424945249462494724948249492495024951249522495324954249552495624957249582495924960249612496224963249642496524966249672496824969249702497124972249732497424975249762497724978249792498024981249822498324984249852498624987249882498924990249912499224993249942499524996249972499824999250002500125002250032500425005250062500725008250092501025011250122501325014250152501625017250182501925020250212502225023250242502525026250272502825029250302503125032250332503425035250362503725038250392504025041250422504325044250452504625047250482504925050250512505225053250542505525056250572505825059250602506125062250632506425065250662506725068250692507025071250722507325074250752507625077250782507925080250812508225083250842508525086250872508825089250902509125092250932509425095250962509725098250992510025101251022510325104251052510625107251082510925110251112511225113251142511525116251172511825119251202512125122251232512425125251262512725128251292513025131251322513325134251352513625137251382513925140251412514225143251442514525146251472514825149251502515125152251532515425155251562515725158251592516025161251622516325164251652516625167251682516925170251712517225173251742517525176251772517825179251802518125182251832518425185251862518725188251892519025191251922519325194251952519625197251982519925200252012520225203252042520525206252072520825209252102521125212252132521425215252162521725218252192522025221252222522325224252252522625227252282522925230252312523225233252342523525236252372523825239252402524125242252432524425245252462524725248252492525025251252522525325254252552525625257252582525925260252612526225263252642526525266252672526825269252702527125272252732527425275252762527725278252792528025281252822528325284252852528625287252882528925290252912529225293252942529525296252972529825299253002530125302253032530425305253062530725308253092531025311253122531325314253152531625317253182531925320253212532225323253242532525326253272532825329253302533125332253332533425335253362533725338253392534025341253422534325344253452534625347253482534925350253512535225353253542535525356253572535825359253602536125362253632536425365253662536725368253692537025371253722537325374253752537625377253782537925380253812538225383253842538525386253872538825389253902539125392253932539425395253962539725398253992540025401254022540325404254052540625407254082540925410254112541225413254142541525416254172541825419254202542125422254232542425425254262542725428254292543025431254322543325434254352543625437254382543925440254412544225443254442544525446254472544825449254502545125452254532545425455254562545725458254592546025461254622546325464254652546625467254682546925470254712547225473254742547525476254772547825479254802548125482254832548425485254862548725488254892549025491254922549325494254952549625497254982549925500255012550225503255042550525506255072550825509255102551125512255132551425515255162551725518255192552025521255222552325524255252552625527255282552925530255312553225533255342553525536255372553825539255402554125542255432554425545255462554725548255492555025551255522555325554255552555625557255582555925560255612556225563255642556525566255672556825569255702557125572255732557425575255762557725578255792558025581255822558325584255852558625587255882558925590255912559225593255942559525596255972559825599256002560125602256032560425605256062560725608256092561025611256122561325614256152561625617256182561925620256212562225623256242562525626256272562825629256302563125632256332563425635256362563725638256392564025641256422564325644256452564625647256482564925650256512565225653256542565525656256572565825659256602566125662256632566425665256662566725668256692567025671256722567325674256752567625677256782567925680256812568225683256842568525686256872568825689256902569125692256932569425695256962569725698256992570025701257022570325704257052570625707257082570925710257112571225713257142571525716257172571825719257202572125722257232572425725257262572725728257292573025731257322573325734257352573625737257382573925740257412574225743257442574525746257472574825749257502575125752257532575425755257562575725758257592576025761257622576325764257652576625767257682576925770257712577225773257742577525776257772577825779257802578125782257832578425785257862578725788257892579025791257922579325794257952579625797257982579925800258012580225803258042580525806258072580825809258102581125812258132581425815258162581725818258192582025821258222582325824258252582625827258282582925830258312583225833258342583525836258372583825839258402584125842258432584425845258462584725848258492585025851258522585325854258552585625857258582585925860258612586225863258642586525866258672586825869258702587125872258732587425875258762587725878258792588025881258822588325884258852588625887258882588925890258912589225893258942589525896258972589825899259002590125902259032590425905259062590725908259092591025911259122591325914259152591625917259182591925920259212592225923259242592525926259272592825929259302593125932259332593425935259362593725938259392594025941259422594325944259452594625947259482594925950259512595225953259542595525956259572595825959259602596125962259632596425965259662596725968259692597025971259722597325974259752597625977259782597925980259812598225983259842598525986259872598825989259902599125992259932599425995259962599725998259992600026001260022600326004260052600626007260082600926010260112601226013260142601526016260172601826019260202602126022260232602426025260262602726028260292603026031260322603326034260352603626037260382603926040260412604226043260442604526046260472604826049260502605126052260532605426055260562605726058260592606026061260622606326064260652606626067260682606926070260712607226073260742607526076260772607826079260802608126082260832608426085260862608726088260892609026091260922609326094260952609626097260982609926100261012610226103261042610526106261072610826109261102611126112261132611426115261162611726118261192612026121261222612326124261252612626127261282612926130261312613226133261342613526136261372613826139261402614126142261432614426145261462614726148261492615026151261522615326154261552615626157261582615926160261612616226163261642616526166261672616826169261702617126172261732617426175261762617726178261792618026181261822618326184261852618626187261882618926190261912619226193261942619526196261972619826199262002620126202262032620426205262062620726208262092621026211262122621326214262152621626217262182621926220262212622226223262242622526226262272622826229262302623126232262332623426235262362623726238262392624026241262422624326244262452624626247262482624926250262512625226253262542625526256262572625826259262602626126262262632626426265262662626726268262692627026271262722627326274262752627626277262782627926280262812628226283262842628526286262872628826289262902629126292262932629426295262962629726298262992630026301263022630326304263052630626307263082630926310263112631226313263142631526316263172631826319263202632126322263232632426325263262632726328263292633026331263322633326334263352633626337263382633926340263412634226343263442634526346263472634826349263502635126352263532635426355263562635726358263592636026361263622636326364263652636626367263682636926370263712637226373263742637526376263772637826379263802638126382263832638426385263862638726388263892639026391263922639326394263952639626397263982639926400264012640226403264042640526406264072640826409264102641126412264132641426415264162641726418264192642026421264222642326424264252642626427264282642926430264312643226433264342643526436264372643826439264402644126442264432644426445264462644726448264492645026451264522645326454264552645626457264582645926460264612646226463264642646526466264672646826469264702647126472264732647426475264762647726478264792648026481264822648326484264852648626487264882648926490264912649226493264942649526496264972649826499265002650126502265032650426505265062650726508265092651026511265122651326514265152651626517265182651926520265212652226523265242652526526265272652826529265302653126532265332653426535265362653726538265392654026541265422654326544265452654626547265482654926550265512655226553265542655526556265572655826559265602656126562265632656426565265662656726568265692657026571265722657326574265752657626577265782657926580265812658226583265842658526586265872658826589265902659126592265932659426595265962659726598265992660026601266022660326604266052660626607266082660926610266112661226613266142661526616266172661826619266202662126622266232662426625266262662726628266292663026631266322663326634266352663626637266382663926640266412664226643266442664526646266472664826649266502665126652266532665426655266562665726658266592666026661266622666326664266652666626667266682666926670266712667226673266742667526676266772667826679266802668126682266832668426685266862668726688266892669026691266922669326694266952669626697266982669926700267012670226703267042670526706267072670826709267102671126712267132671426715267162671726718267192672026721267222672326724267252672626727267282672926730267312673226733267342673526736267372673826739267402674126742267432674426745267462674726748267492675026751267522675326754267552675626757267582675926760267612676226763267642676526766267672676826769267702677126772267732677426775267762677726778267792678026781267822678326784267852678626787267882678926790267912679226793267942679526796267972679826799268002680126802268032680426805268062680726808268092681026811268122681326814268152681626817268182681926820268212682226823268242682526826268272682826829268302683126832268332683426835268362683726838268392684026841268422684326844268452684626847268482684926850268512685226853268542685526856268572685826859268602686126862268632686426865268662686726868268692687026871268722687326874268752687626877268782687926880268812688226883268842688526886268872688826889268902689126892268932689426895268962689726898268992690026901269022690326904269052690626907269082690926910269112691226913269142691526916269172691826919269202692126922269232692426925269262692726928269292693026931269322693326934269352693626937269382693926940269412694226943269442694526946269472694826949269502695126952269532695426955269562695726958269592696026961269622696326964269652696626967269682696926970269712697226973269742697526976269772697826979269802698126982269832698426985269862698726988269892699026991269922699326994269952699626997269982699927000270012700227003270042700527006270072700827009270102701127012270132701427015270162701727018270192702027021270222702327024270252702627027270282702927030270312703227033270342703527036270372703827039270402704127042270432704427045270462704727048270492705027051270522705327054270552705627057270582705927060270612706227063270642706527066270672706827069270702707127072270732707427075270762707727078270792708027081270822708327084270852708627087270882708927090270912709227093270942709527096270972709827099271002710127102271032710427105271062710727108271092711027111271122711327114271152711627117271182711927120271212712227123271242712527126271272712827129271302713127132271332713427135271362713727138271392714027141271422714327144271452714627147271482714927150271512715227153271542715527156271572715827159271602716127162271632716427165271662716727168271692717027171271722717327174271752717627177271782717927180271812718227183271842718527186271872718827189271902719127192271932719427195271962719727198271992720027201272022720327204272052720627207272082720927210272112721227213272142721527216272172721827219272202722127222272232722427225272262722727228272292723027231272322723327234272352723627237272382723927240272412724227243272442724527246272472724827249272502725127252272532725427255272562725727258272592726027261272622726327264272652726627267272682726927270272712727227273272742727527276272772727827279272802728127282272832728427285272862728727288272892729027291272922729327294272952729627297272982729927300273012730227303273042730527306273072730827309273102731127312273132731427315273162731727318273192732027321273222732327324273252732627327273282732927330273312733227333273342733527336273372733827339273402734127342273432734427345273462734727348273492735027351273522735327354273552735627357273582735927360273612736227363273642736527366273672736827369273702737127372273732737427375273762737727378273792738027381273822738327384273852738627387273882738927390273912739227393273942739527396273972739827399274002740127402274032740427405274062740727408274092741027411274122741327414274152741627417274182741927420274212742227423274242742527426274272742827429274302743127432274332743427435274362743727438274392744027441274422744327444274452744627447274482744927450274512745227453274542745527456274572745827459274602746127462274632746427465274662746727468274692747027471274722747327474274752747627477274782747927480274812748227483274842748527486274872748827489274902749127492274932749427495274962749727498274992750027501275022750327504275052750627507275082750927510275112751227513275142751527516275172751827519275202752127522275232752427525275262752727528275292753027531275322753327534275352753627537275382753927540275412754227543275442754527546275472754827549275502755127552275532755427555275562755727558275592756027561275622756327564275652756627567275682756927570275712757227573275742757527576275772757827579275802758127582275832758427585275862758727588275892759027591275922759327594275952759627597275982759927600276012760227603276042760527606276072760827609276102761127612276132761427615276162761727618276192762027621276222762327624276252762627627276282762927630276312763227633276342763527636276372763827639276402764127642276432764427645276462764727648276492765027651276522765327654276552765627657276582765927660276612766227663276642766527666276672766827669276702767127672276732767427675276762767727678276792768027681276822768327684276852768627687276882768927690276912769227693276942769527696276972769827699277002770127702277032770427705277062770727708277092771027711277122771327714277152771627717277182771927720277212772227723277242772527726277272772827729277302773127732277332773427735277362773727738277392774027741277422774327744277452774627747277482774927750277512775227753277542775527756277572775827759277602776127762277632776427765277662776727768277692777027771277722777327774277752777627777277782777927780277812778227783277842778527786277872778827789277902779127792277932779427795277962779727798277992780027801278022780327804278052780627807278082780927810278112781227813278142781527816278172781827819278202782127822278232782427825278262782727828278292783027831278322783327834278352783627837278382783927840278412784227843278442784527846278472784827849278502785127852278532785427855278562785727858278592786027861278622786327864278652786627867278682786927870278712787227873278742787527876278772787827879278802788127882278832788427885278862788727888278892789027891278922789327894278952789627897278982789927900279012790227903279042790527906279072790827909279102791127912279132791427915279162791727918279192792027921279222792327924279252792627927279282792927930279312793227933279342793527936279372793827939279402794127942279432794427945279462794727948279492795027951279522795327954279552795627957279582795927960279612796227963279642796527966279672796827969279702797127972279732797427975279762797727978279792798027981279822798327984279852798627987279882798927990279912799227993279942799527996279972799827999280002800128002280032800428005280062800728008280092801028011280122801328014280152801628017280182801928020280212802228023280242802528026280272802828029280302803128032280332803428035280362803728038280392804028041280422804328044280452804628047280482804928050280512805228053280542805528056280572805828059280602806128062280632806428065280662806728068280692807028071280722807328074280752807628077280782807928080280812808228083280842808528086280872808828089280902809128092280932809428095280962809728098280992810028101281022810328104281052810628107281082810928110281112811228113281142811528116281172811828119281202812128122281232812428125281262812728128281292813028131281322813328134281352813628137281382813928140281412814228143281442814528146281472814828149281502815128152281532815428155281562815728158281592816028161281622816328164281652816628167281682816928170281712817228173281742817528176281772817828179281802818128182281832818428185281862818728188281892819028191281922819328194281952819628197281982819928200282012820228203282042820528206282072820828209282102821128212282132821428215282162821728218282192822028221282222822328224282252822628227282282822928230282312823228233282342823528236282372823828239282402824128242282432824428245282462824728248282492825028251282522825328254282552825628257282582825928260282612826228263282642826528266282672826828269282702827128272282732827428275282762827728278282792828028281282822828328284282852828628287282882828928290282912829228293282942829528296282972829828299283002830128302283032830428305283062830728308283092831028311283122831328314283152831628317283182831928320283212832228323283242832528326283272832828329283302833128332283332833428335283362833728338283392834028341283422834328344283452834628347283482834928350283512835228353283542835528356283572835828359283602836128362283632836428365283662836728368283692837028371283722837328374283752837628377283782837928380283812838228383283842838528386283872838828389283902839128392283932839428395283962839728398283992840028401284022840328404284052840628407284082840928410284112841228413284142841528416284172841828419284202842128422284232842428425284262842728428284292843028431284322843328434284352843628437284382843928440284412844228443284442844528446284472844828449284502845128452284532845428455284562845728458284592846028461284622846328464284652846628467284682846928470284712847228473284742847528476284772847828479284802848128482284832848428485284862848728488284892849028491284922849328494284952849628497284982849928500285012850228503285042850528506285072850828509285102851128512285132851428515285162851728518285192852028521285222852328524285252852628527285282852928530285312853228533285342853528536285372853828539285402854128542285432854428545285462854728548285492855028551285522855328554285552855628557285582855928560285612856228563285642856528566285672856828569285702857128572285732857428575285762857728578285792858028581285822858328584285852858628587285882858928590285912859228593285942859528596285972859828599286002860128602286032860428605286062860728608286092861028611286122861328614286152861628617286182861928620286212862228623286242862528626286272862828629286302863128632286332863428635286362863728638286392864028641286422864328644286452864628647286482864928650286512865228653286542865528656286572865828659286602866128662286632866428665286662866728668286692867028671286722867328674286752867628677286782867928680286812868228683286842868528686286872868828689286902869128692286932869428695286962869728698286992870028701287022870328704287052870628707287082870928710287112871228713287142871528716287172871828719287202872128722287232872428725287262872728728287292873028731287322873328734287352873628737287382873928740287412874228743287442874528746287472874828749287502875128752287532875428755287562875728758287592876028761287622876328764287652876628767287682876928770287712877228773287742877528776287772877828779287802878128782287832878428785287862878728788287892879028791287922879328794287952879628797287982879928800288012880228803288042880528806288072880828809288102881128812288132881428815288162881728818288192882028821288222882328824288252882628827288282882928830288312883228833288342883528836288372883828839288402884128842288432884428845288462884728848288492885028851288522885328854288552885628857288582885928860288612886228863288642886528866288672886828869288702887128872288732887428875288762887728878288792888028881288822888328884288852888628887288882888928890288912889228893288942889528896288972889828899289002890128902289032890428905289062890728908289092891028911289122891328914289152891628917289182891928920289212892228923289242892528926289272892828929289302893128932289332893428935289362893728938289392894028941289422894328944289452894628947289482894928950289512895228953289542895528956289572895828959289602896128962289632896428965289662896728968289692897028971289722897328974289752897628977289782897928980289812898228983289842898528986289872898828989289902899128992289932899428995289962899728998289992900029001290022900329004290052900629007290082900929010290112901229013290142901529016290172901829019290202902129022290232902429025290262902729028290292903029031290322903329034290352903629037290382903929040290412904229043290442904529046290472904829049290502905129052290532905429055290562905729058290592906029061290622906329064290652906629067290682906929070290712907229073290742907529076290772907829079290802908129082290832908429085290862908729088290892909029091290922909329094290952909629097290982909929100291012910229103291042910529106291072910829109291102911129112291132911429115291162911729118291192912029121291222912329124291252912629127291282912929130291312913229133291342913529136291372913829139291402914129142291432914429145291462914729148291492915029151291522915329154291552915629157291582915929160291612916229163291642916529166291672916829169291702917129172291732917429175291762917729178291792918029181291822918329184291852918629187291882918929190291912919229193291942919529196291972919829199292002920129202292032920429205292062920729208292092921029211292122921329214292152921629217292182921929220292212922229223292242922529226292272922829229292302923129232292332923429235292362923729238292392924029241292422924329244292452924629247292482924929250292512925229253292542925529256292572925829259292602926129262292632926429265292662926729268292692927029271292722927329274292752927629277292782927929280292812928229283292842928529286292872928829289292902929129292292932929429295292962929729298292992930029301293022930329304293052930629307293082930929310293112931229313293142931529316293172931829319293202932129322293232932429325293262932729328293292933029331293322933329334293352933629337293382933929340293412934229343293442934529346293472934829349293502935129352293532935429355293562935729358293592936029361293622936329364293652936629367293682936929370293712937229373293742937529376293772937829379293802938129382293832938429385293862938729388293892939029391293922939329394293952939629397293982939929400294012940229403294042940529406294072940829409294102941129412294132941429415294162941729418294192942029421294222942329424294252942629427294282942929430294312943229433294342943529436294372943829439294402944129442294432944429445294462944729448294492945029451294522945329454294552945629457294582945929460294612946229463294642946529466294672946829469294702947129472294732947429475294762947729478294792948029481294822948329484294852948629487294882948929490294912949229493294942949529496294972949829499295002950129502295032950429505295062950729508295092951029511295122951329514295152951629517295182951929520295212952229523295242952529526295272952829529295302953129532295332953429535295362953729538295392954029541295422954329544295452954629547295482954929550295512955229553295542955529556295572955829559295602956129562295632956429565295662956729568295692957029571295722957329574295752957629577295782957929580295812958229583295842958529586295872958829589295902959129592295932959429595295962959729598295992960029601296022960329604296052960629607296082960929610296112961229613296142961529616296172961829619296202962129622296232962429625296262962729628296292963029631296322963329634296352963629637296382963929640296412964229643296442964529646296472964829649296502965129652296532965429655296562965729658296592966029661296622966329664296652966629667296682966929670296712967229673296742967529676296772967829679296802968129682296832968429685296862968729688296892969029691296922969329694296952969629697296982969929700297012970229703297042970529706297072970829709297102971129712297132971429715297162971729718297192972029721297222972329724297252972629727297282972929730297312973229733297342973529736297372973829739297402974129742297432974429745297462974729748297492975029751297522975329754297552975629757297582975929760297612976229763297642976529766297672976829769297702977129772297732977429775297762977729778297792978029781297822978329784297852978629787297882978929790297912979229793297942979529796297972979829799298002980129802298032980429805298062980729808298092981029811298122981329814298152981629817298182981929820298212982229823298242982529826298272982829829298302983129832298332983429835298362983729838298392984029841298422984329844298452984629847298482984929850298512985229853298542985529856298572985829859298602986129862298632986429865298662986729868298692987029871298722987329874298752987629877298782987929880298812988229883298842988529886298872988829889298902989129892298932989429895298962989729898298992990029901299022990329904299052990629907299082990929910299112991229913299142991529916299172991829919299202992129922299232992429925299262992729928299292993029931299322993329934299352993629937299382993929940299412994229943299442994529946299472994829949299502995129952299532995429955299562995729958299592996029961299622996329964299652996629967299682996929970299712997229973299742997529976299772997829979299802998129982299832998429985299862998729988299892999029991299922999329994299952999629997299982999930000300013000230003300043000530006300073000830009300103001130012300133001430015300163001730018300193002030021300223002330024300253002630027300283002930030300313003230033300343003530036300373003830039300403004130042300433004430045300463004730048300493005030051300523005330054300553005630057300583005930060300613006230063300643006530066300673006830069300703007130072300733007430075300763007730078300793008030081300823008330084300853008630087300883008930090300913009230093300943009530096300973009830099301003010130102301033010430105301063010730108301093011030111301123011330114301153011630117301183011930120301213012230123301243012530126301273012830129301303013130132301333013430135301363013730138301393014030141301423014330144301453014630147301483014930150301513015230153301543015530156301573015830159301603016130162301633016430165301663016730168301693017030171301723017330174301753017630177301783017930180301813018230183301843018530186301873018830189301903019130192301933019430195301963019730198301993020030201302023020330204302053020630207302083020930210302113021230213302143021530216302173021830219302203022130222302233022430225302263022730228302293023030231302323023330234302353023630237302383023930240302413024230243302443024530246302473024830249302503025130252302533025430255302563025730258302593026030261302623026330264302653026630267302683026930270302713027230273302743027530276302773027830279302803028130282302833028430285302863028730288302893029030291302923029330294302953029630297302983029930300303013030230303303043030530306303073030830309303103031130312303133031430315303163031730318303193032030321303223032330324303253032630327303283032930330303313033230333303343033530336303373033830339303403034130342303433034430345303463034730348303493035030351303523035330354303553035630357303583035930360303613036230363303643036530366303673036830369303703037130372303733037430375303763037730378303793038030381303823038330384303853038630387303883038930390303913039230393303943039530396303973039830399304003040130402304033040430405304063040730408304093041030411304123041330414304153041630417304183041930420304213042230423304243042530426304273042830429304303043130432304333043430435304363043730438304393044030441304423044330444304453044630447304483044930450304513045230453304543045530456304573045830459304603046130462304633046430465304663046730468304693047030471304723047330474304753047630477304783047930480304813048230483304843048530486304873048830489304903049130492304933049430495304963049730498304993050030501305023050330504305053050630507305083050930510305113051230513305143051530516305173051830519305203052130522305233052430525305263052730528305293053030531305323053330534305353053630537305383053930540305413054230543305443054530546305473054830549305503055130552305533055430555305563055730558305593056030561305623056330564305653056630567305683056930570305713057230573305743057530576305773057830579305803058130582305833058430585305863058730588305893059030591305923059330594305953059630597305983059930600306013060230603306043060530606306073060830609306103061130612306133061430615306163061730618306193062030621306223062330624306253062630627306283062930630306313063230633306343063530636306373063830639306403064130642306433064430645306463064730648306493065030651306523065330654306553065630657306583065930660306613066230663306643066530666306673066830669306703067130672306733067430675306763067730678306793068030681306823068330684306853068630687306883068930690306913069230693306943069530696306973069830699307003070130702307033070430705307063070730708307093071030711307123071330714307153071630717307183071930720307213072230723307243072530726307273072830729307303073130732307333073430735307363073730738307393074030741307423074330744307453074630747307483074930750307513075230753307543075530756307573075830759307603076130762307633076430765307663076730768307693077030771307723077330774307753077630777307783077930780307813078230783307843078530786307873078830789307903079130792307933079430795307963079730798307993080030801308023080330804308053080630807308083080930810308113081230813308143081530816308173081830819308203082130822308233082430825308263082730828308293083030831308323083330834308353083630837308383083930840308413084230843308443084530846308473084830849308503085130852308533085430855308563085730858308593086030861308623086330864308653086630867308683086930870308713087230873308743087530876308773087830879308803088130882308833088430885308863088730888308893089030891308923089330894308953089630897308983089930900309013090230903309043090530906309073090830909309103091130912309133091430915309163091730918309193092030921309223092330924309253092630927309283092930930309313093230933309343093530936309373093830939309403094130942309433094430945309463094730948309493095030951309523095330954309553095630957309583095930960309613096230963309643096530966309673096830969309703097130972309733097430975309763097730978309793098030981309823098330984309853098630987309883098930990309913099230993309943099530996309973099830999310003100131002310033100431005310063100731008310093101031011310123101331014310153101631017310183101931020310213102231023310243102531026310273102831029310303103131032310333103431035310363103731038310393104031041310423104331044310453104631047310483104931050310513105231053310543105531056310573105831059310603106131062310633106431065310663106731068310693107031071310723107331074310753107631077310783107931080310813108231083310843108531086310873108831089310903109131092310933109431095310963109731098310993110031101311023110331104311053110631107311083110931110311113111231113311143111531116311173111831119311203112131122311233112431125311263112731128311293113031131311323113331134311353113631137311383113931140311413114231143311443114531146311473114831149311503115131152311533115431155311563115731158311593116031161311623116331164311653116631167311683116931170311713117231173311743117531176311773117831179311803118131182311833118431185311863118731188311893119031191311923119331194311953119631197311983119931200312013120231203312043120531206312073120831209312103121131212312133121431215312163121731218312193122031221312223122331224312253122631227312283122931230312313123231233312343123531236312373123831239312403124131242312433124431245312463124731248312493125031251312523125331254312553125631257312583125931260312613126231263312643126531266312673126831269312703127131272312733127431275312763127731278312793128031281312823128331284312853128631287312883128931290312913129231293312943129531296312973129831299313003130131302313033130431305313063130731308313093131031311313123131331314313153131631317313183131931320313213132231323313243132531326313273132831329313303133131332313333133431335313363133731338313393134031341313423134331344313453134631347313483134931350313513135231353313543135531356313573135831359313603136131362313633136431365313663136731368313693137031371313723137331374313753137631377313783137931380313813138231383313843138531386313873138831389313903139131392313933139431395313963139731398313993140031401314023140331404314053140631407314083140931410314113141231413314143141531416314173141831419314203142131422314233142431425314263142731428314293143031431314323143331434314353143631437314383143931440314413144231443314443144531446314473144831449314503145131452314533145431455314563145731458314593146031461314623146331464314653146631467314683146931470314713147231473314743147531476314773147831479314803148131482314833148431485314863148731488314893149031491314923149331494314953149631497314983149931500315013150231503315043150531506315073150831509315103151131512315133151431515315163151731518315193152031521315223152331524315253152631527315283152931530315313153231533315343153531536315373153831539315403154131542315433154431545315463154731548315493155031551315523155331554315553155631557315583155931560315613156231563315643156531566315673156831569315703157131572315733157431575315763157731578315793158031581315823158331584315853158631587315883158931590315913159231593315943159531596315973159831599316003160131602316033160431605316063160731608316093161031611316123161331614316153161631617316183161931620316213162231623316243162531626316273162831629316303163131632316333163431635316363163731638316393164031641316423164331644316453164631647316483164931650316513165231653316543165531656316573165831659316603166131662316633166431665316663166731668316693167031671316723167331674316753167631677316783167931680316813168231683316843168531686316873168831689316903169131692316933169431695316963169731698316993170031701317023170331704317053170631707317083170931710317113171231713317143171531716317173171831719317203172131722317233172431725317263172731728317293173031731317323173331734317353173631737317383173931740317413174231743317443174531746317473174831749317503175131752317533175431755317563175731758317593176031761317623176331764317653176631767317683176931770317713177231773317743177531776317773177831779317803178131782317833178431785317863178731788317893179031791317923179331794317953179631797317983179931800318013180231803318043180531806318073180831809318103181131812318133181431815318163181731818318193182031821318223182331824318253182631827318283182931830318313183231833318343183531836318373183831839318403184131842318433184431845318463184731848318493185031851318523185331854318553185631857318583185931860318613186231863318643186531866318673186831869318703187131872318733187431875318763187731878318793188031881318823188331884318853188631887318883188931890318913189231893318943189531896318973189831899319003190131902319033190431905319063190731908319093191031911319123191331914319153191631917319183191931920319213192231923319243192531926319273192831929319303193131932319333193431935319363193731938319393194031941319423194331944319453194631947319483194931950319513195231953319543195531956319573195831959319603196131962319633196431965319663196731968319693197031971319723197331974319753197631977319783197931980319813198231983319843198531986319873198831989319903199131992319933199431995319963199731998319993200032001320023200332004320053200632007320083200932010320113201232013320143201532016320173201832019320203202132022320233202432025320263202732028320293203032031320323203332034320353203632037320383203932040320413204232043320443204532046320473204832049320503205132052320533205432055320563205732058320593206032061320623206332064320653206632067320683206932070320713207232073320743207532076320773207832079320803208132082320833208432085320863208732088320893209032091320923209332094320953209632097320983209932100321013210232103321043210532106321073210832109321103211132112321133211432115 |
- /*! DataTables 1.10.19
- * ©2008-2018 SpryMedia Ltd - datatables.net/license
- */
- /**
- * @summary DataTables
- * @description Paginate, search and order HTML tables
- * @version 1.10.19
- * @file jquery.dataTables.js
- * @author SpryMedia Ltd
- * @contact www.datatables.net
- * @copyright Copyright 2008-2018 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net
- */
- /*jslint evil: true, undef: true, browser: true */
- /*globals $,require,jQuery,define,_selector_run,_selector_opts,_selector_first,_selector_row_indexes,_ext,_Api,_api_register,_api_registerPlural,_re_new_lines,_re_html,_re_formatted_numeric,_re_escape_regex,_empty,_intVal,_numToDecimal,_isNumber,_isHtml,_htmlNumeric,_pluck,_pluck_order,_range,_stripHtml,_unique,_fnBuildAjax,_fnAjaxUpdate,_fnAjaxParameters,_fnAjaxUpdateDraw,_fnAjaxDataSrc,_fnAddColumn,_fnColumnOptions,_fnAdjustColumnSizing,_fnVisibleToColumnIndex,_fnColumnIndexToVisible,_fnVisbleColumns,_fnGetColumns,_fnColumnTypes,_fnApplyColumnDefs,_fnHungarianMap,_fnCamelToHungarian,_fnLanguageCompat,_fnBrowserDetect,_fnAddData,_fnAddTr,_fnNodeToDataIndex,_fnNodeToColumnIndex,_fnGetCellData,_fnSetCellData,_fnSplitObjNotation,_fnGetObjectDataFn,_fnSetObjectDataFn,_fnGetDataMaster,_fnClearTable,_fnDeleteIndex,_fnInvalidate,_fnGetRowElements,_fnCreateTr,_fnBuildHead,_fnDrawHead,_fnDraw,_fnReDraw,_fnAddOptionsHtml,_fnDetectHeader,_fnGetUniqueThs,_fnFeatureHtmlFilter,_fnFilterComplete,_fnFilterCustom,_fnFilterColumn,_fnFilter,_fnFilterCreateSearch,_fnEscapeRegex,_fnFilterData,_fnFeatureHtmlInfo,_fnUpdateInfo,_fnInfoMacros,_fnInitialise,_fnInitComplete,_fnLengthChange,_fnFeatureHtmlLength,_fnFeatureHtmlPaginate,_fnPageChange,_fnFeatureHtmlProcessing,_fnProcessingDisplay,_fnFeatureHtmlTable,_fnScrollDraw,_fnApplyToChildren,_fnCalculateColumnWidths,_fnThrottle,_fnConvertToWidth,_fnGetWidestNode,_fnGetMaxLenString,_fnStringToCss,_fnSortFlatten,_fnSort,_fnSortAria,_fnSortListener,_fnSortAttachListener,_fnSortingClasses,_fnSortData,_fnSaveState,_fnLoadState,_fnSettingsFromNode,_fnLog,_fnMap,_fnBindAction,_fnCallbackReg,_fnCallbackFire,_fnLengthOverflow,_fnRenderer,_fnDataSource,_fnRowAttributes*/
- (function( factory ) {
- "use strict";
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- // CommonJS environments without a window global must pass a
- // root. This will give an error otherwise
- root = window;
- }
- if ( ! $ ) {
- $ = typeof window !== 'undefined' ? // jQuery's factory checks for a global window
- require('jquery') :
- require('jquery')( root );
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }
- (function( $, window, document, undefined ) {
- "use strict";
- /**
- * DataTables is a plug-in for the jQuery Javascript library. It is a highly
- * flexible tool, based upon the foundations of progressive enhancement,
- * which will add advanced interaction controls to any HTML table. For a
- * full list of features please refer to
- * [DataTables.net](href="http://datatables.net).
- *
- * Note that the `DataTable` object is not a global variable but is aliased
- * to `jQuery.fn.DataTable` and `jQuery.fn.dataTable` through which it may
- * be accessed.
- *
- * @class
- * @param {object} [init={}] Configuration object for DataTables. Options
- * are defined by {@link DataTable.defaults}
- * @requires jQuery 1.7+
- *
- * @example
- * // Basic initialisation
- * $(document).ready( function {
- * $('#example').dataTable();
- * } );
- *
- * @example
- * // Initialisation with configuration options - in this case, disable
- * // pagination and sorting.
- * $(document).ready( function {
- * $('#example').dataTable( {
- * "paginate": false,
- * "sort": false
- * } );
- * } );
- */
- var DataTable = function ( options )
- {
- /**
- * Perform a jQuery selector action on the table's TR elements (from the tbody) and
- * return the resulting jQuery object.
- * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
- * @param {object} [oOpts] Optional parameters for modifying the rows to be included
- * @param {string} [oOpts.filter=none] Select TR elements that meet the current filter
- * criterion ("applied") or all TR elements (i.e. no filter).
- * @param {string} [oOpts.order=current] Order of the TR elements in the processed array.
- * Can be either 'current', whereby the current sorting of the table is used, or
- * 'original' whereby the original order the data was read into the table is used.
- * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
- * ("current") or not ("all"). If 'current' is given, then order is assumed to be
- * 'current' and filter is 'applied', regardless of what they might be given as.
- * @returns {object} jQuery object, filtered by the given selector.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Highlight every second row
- * oTable.$('tr:odd').css('backgroundColor', 'blue');
- * } );
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Filter to rows with 'Webkit' in them, add a background colour and then
- * // remove the filter, thus highlighting the 'Webkit' rows only.
- * oTable.fnFilter('Webkit');
- * oTable.$('tr', {"search": "applied"}).css('backgroundColor', 'blue');
- * oTable.fnFilter('');
- * } );
- */
- this.$ = function ( sSelector, oOpts )
- {
- return this.api(true).$( sSelector, oOpts );
- };
-
-
- /**
- * Almost identical to $ in operation, but in this case returns the data for the matched
- * rows - as such, the jQuery selector used should match TR row nodes or TD/TH cell nodes
- * rather than any descendants, so the data can be obtained for the row/cell. If matching
- * rows are found, the data returned is the original data array/object that was used to
- * create the row (or a generated array if from a DOM source).
- *
- * This method is often useful in-combination with $ where both functions are given the
- * same parameters and the array indexes will match identically.
- * @param {string|node|jQuery} sSelector jQuery selector or node collection to act on
- * @param {object} [oOpts] Optional parameters for modifying the rows to be included
- * @param {string} [oOpts.filter=none] Select elements that meet the current filter
- * criterion ("applied") or all elements (i.e. no filter).
- * @param {string} [oOpts.order=current] Order of the data in the processed array.
- * Can be either 'current', whereby the current sorting of the table is used, or
- * 'original' whereby the original order the data was read into the table is used.
- * @param {string} [oOpts.page=all] Limit the selection to the currently displayed page
- * ("current") or not ("all"). If 'current' is given, then order is assumed to be
- * 'current' and filter is 'applied', regardless of what they might be given as.
- * @returns {array} Data for the matched elements. If any elements, as a result of the
- * selector, were not TR, TD or TH elements in the DataTable, they will have a null
- * entry in the array.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Get the data from the first row in the table
- * var data = oTable._('tr:first');
- *
- * // Do something useful with the data
- * alert( "First cell is: "+data[0] );
- * } );
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Filter to 'Webkit' and get all data for
- * oTable.fnFilter('Webkit');
- * var data = oTable._('tr', {"search": "applied"});
- *
- * // Do something with the data
- * alert( data.length+" rows matched the search" );
- * } );
- */
- this._ = function ( sSelector, oOpts )
- {
- return this.api(true).rows( sSelector, oOpts ).data();
- };
-
-
- /**
- * Create a DataTables Api instance, with the currently selected tables for
- * the Api's context.
- * @param {boolean} [traditional=false] Set the API instance's context to be
- * only the table referred to by the `DataTable.ext.iApiIndex` option, as was
- * used in the API presented by DataTables 1.9- (i.e. the traditional mode),
- * or if all tables captured in the jQuery object should be used.
- * @return {DataTables.Api}
- */
- this.api = function ( traditional )
- {
- return traditional ?
- new _Api(
- _fnSettingsFromNode( this[ _ext.iApiIndex ] )
- ) :
- new _Api( this );
- };
-
-
- /**
- * Add a single new row or multiple rows of data to the table. Please note
- * that this is suitable for client-side processing only - if you are using
- * server-side processing (i.e. "bServerSide": true), then to add data, you
- * must add it to the data source, i.e. the server-side, through an Ajax call.
- * @param {array|object} data The data to be added to the table. This can be:
- * <ul>
- * <li>1D array of data - add a single row with the data provided</li>
- * <li>2D array of arrays - add multiple rows in a single call</li>
- * <li>object - data object when using <i>mData</i></li>
- * <li>array of objects - multiple data objects when using <i>mData</i></li>
- * </ul>
- * @param {bool} [redraw=true] redraw the table or not
- * @returns {array} An array of integers, representing the list of indexes in
- * <i>aoData</i> ({@link DataTable.models.oSettings}) that have been added to
- * the table.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * // Global var for counter
- * var giCount = 2;
- *
- * $(document).ready(function() {
- * $('#example').dataTable();
- * } );
- *
- * function fnClickAddRow() {
- * $('#example').dataTable().fnAddData( [
- * giCount+".1",
- * giCount+".2",
- * giCount+".3",
- * giCount+".4" ]
- * );
- *
- * giCount++;
- * }
- */
- this.fnAddData = function( data, redraw )
- {
- var api = this.api( true );
-
- /* Check if we want to add multiple rows or not */
- var rows = $.isArray(data) && ( $.isArray(data[0]) || $.isPlainObject(data[0]) ) ?
- api.rows.add( data ) :
- api.row.add( data );
-
- if ( redraw === undefined || redraw ) {
- api.draw();
- }
-
- return rows.flatten().toArray();
- };
-
-
- /**
- * This function will make DataTables recalculate the column sizes, based on the data
- * contained in the table and the sizes applied to the columns (in the DOM, CSS or
- * through the sWidth parameter). This can be useful when the width of the table's
- * parent element changes (for example a window resize).
- * @param {boolean} [bRedraw=true] Redraw the table or not, you will typically want to
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable( {
- * "sScrollY": "200px",
- * "bPaginate": false
- * } );
- *
- * $(window).on('resize', function () {
- * oTable.fnAdjustColumnSizing();
- * } );
- * } );
- */
- this.fnAdjustColumnSizing = function ( bRedraw )
- {
- var api = this.api( true ).columns.adjust();
- var settings = api.settings()[0];
- var scroll = settings.oScroll;
-
- if ( bRedraw === undefined || bRedraw ) {
- api.draw( false );
- }
- else if ( scroll.sX !== "" || scroll.sY !== "" ) {
- /* If not redrawing, but scrolling, we want to apply the new column sizes anyway */
- _fnScrollDraw( settings );
- }
- };
-
-
- /**
- * Quickly and simply clear a table
- * @param {bool} [bRedraw=true] redraw the table or not
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Immediately 'nuke' the current rows (perhaps waiting for an Ajax callback...)
- * oTable.fnClearTable();
- * } );
- */
- this.fnClearTable = function( bRedraw )
- {
- var api = this.api( true ).clear();
-
- if ( bRedraw === undefined || bRedraw ) {
- api.draw();
- }
- };
-
-
- /**
- * The exact opposite of 'opening' a row, this function will close any rows which
- * are currently 'open'.
- * @param {node} nTr the table row to 'close'
- * @returns {int} 0 on success, or 1 if failed (can't find the row)
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable;
- *
- * // 'open' an information row when a row is clicked on
- * $('#example tbody tr').click( function () {
- * if ( oTable.fnIsOpen(this) ) {
- * oTable.fnClose( this );
- * } else {
- * oTable.fnOpen( this, "Temporary row opened", "info_row" );
- * }
- * } );
- *
- * oTable = $('#example').dataTable();
- * } );
- */
- this.fnClose = function( nTr )
- {
- this.api( true ).row( nTr ).child.hide();
- };
-
-
- /**
- * Remove a row for the table
- * @param {mixed} target The index of the row from aoData to be deleted, or
- * the TR element you want to delete
- * @param {function|null} [callBack] Callback function
- * @param {bool} [redraw=true] Redraw the table or not
- * @returns {array} The row that was deleted
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Immediately remove the first row
- * oTable.fnDeleteRow( 0 );
- * } );
- */
- this.fnDeleteRow = function( target, callback, redraw )
- {
- var api = this.api( true );
- var rows = api.rows( target );
- var settings = rows.settings()[0];
- var data = settings.aoData[ rows[0][0] ];
-
- rows.remove();
-
- if ( callback ) {
- callback.call( this, settings, data );
- }
-
- if ( redraw === undefined || redraw ) {
- api.draw();
- }
-
- return data;
- };
-
-
- /**
- * Restore the table to it's original state in the DOM by removing all of DataTables
- * enhancements, alterations to the DOM structure of the table and event listeners.
- * @param {boolean} [remove=false] Completely remove the table from the DOM
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * // This example is fairly pointless in reality, but shows how fnDestroy can be used
- * var oTable = $('#example').dataTable();
- * oTable.fnDestroy();
- * } );
- */
- this.fnDestroy = function ( remove )
- {
- this.api( true ).destroy( remove );
- };
-
-
- /**
- * Redraw the table
- * @param {bool} [complete=true] Re-filter and resort (if enabled) the table before the draw.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Re-draw the table - you wouldn't want to do it here, but it's an example :-)
- * oTable.fnDraw();
- * } );
- */
- this.fnDraw = function( complete )
- {
- // Note that this isn't an exact match to the old call to _fnDraw - it takes
- // into account the new data, but can hold position.
- this.api( true ).draw( complete );
- };
-
-
- /**
- * Filter the input based on data
- * @param {string} sInput String to filter the table on
- * @param {int|null} [iColumn] Column to limit filtering to
- * @param {bool} [bRegex=false] Treat as regular expression or not
- * @param {bool} [bSmart=true] Perform smart filtering or not
- * @param {bool} [bShowGlobal=true] Show the input global filter in it's input box(es)
- * @param {bool} [bCaseInsensitive=true] Do case-insensitive matching (true) or not (false)
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Sometime later - filter...
- * oTable.fnFilter( 'test string' );
- * } );
- */
- this.fnFilter = function( sInput, iColumn, bRegex, bSmart, bShowGlobal, bCaseInsensitive )
- {
- var api = this.api( true );
-
- if ( iColumn === null || iColumn === undefined ) {
- api.search( sInput, bRegex, bSmart, bCaseInsensitive );
- }
- else {
- api.column( iColumn ).search( sInput, bRegex, bSmart, bCaseInsensitive );
- }
-
- api.draw();
- };
-
-
- /**
- * Get the data for the whole table, an individual row or an individual cell based on the
- * provided parameters.
- * @param {int|node} [src] A TR row node, TD/TH cell node or an integer. If given as
- * a TR node then the data source for the whole row will be returned. If given as a
- * TD/TH cell node then iCol will be automatically calculated and the data for the
- * cell returned. If given as an integer, then this is treated as the aoData internal
- * data index for the row (see fnGetPosition) and the data for that row used.
- * @param {int} [col] Optional column index that you want the data of.
- * @returns {array|object|string} If mRow is undefined, then the data for all rows is
- * returned. If mRow is defined, just data for that row, and is iCol is
- * defined, only data for the designated cell is returned.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * // Row data
- * $(document).ready(function() {
- * oTable = $('#example').dataTable();
- *
- * oTable.$('tr').click( function () {
- * var data = oTable.fnGetData( this );
- * // ... do something with the array / object of data for the row
- * } );
- * } );
- *
- * @example
- * // Individual cell data
- * $(document).ready(function() {
- * oTable = $('#example').dataTable();
- *
- * oTable.$('td').click( function () {
- * var sData = oTable.fnGetData( this );
- * alert( 'The cell clicked on had the value of '+sData );
- * } );
- * } );
- */
- this.fnGetData = function( src, col )
- {
- var api = this.api( true );
-
- if ( src !== undefined ) {
- var type = src.nodeName ? src.nodeName.toLowerCase() : '';
-
- return col !== undefined || type == 'td' || type == 'th' ?
- api.cell( src, col ).data() :
- api.row( src ).data() || null;
- }
-
- return api.data().toArray();
- };
-
-
- /**
- * Get an array of the TR nodes that are used in the table's body. Note that you will
- * typically want to use the '$' API method in preference to this as it is more
- * flexible.
- * @param {int} [iRow] Optional row index for the TR element you want
- * @returns {array|node} If iRow is undefined, returns an array of all TR elements
- * in the table's body, or iRow is defined, just the TR element requested.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Get the nodes from the table
- * var nNodes = oTable.fnGetNodes( );
- * } );
- */
- this.fnGetNodes = function( iRow )
- {
- var api = this.api( true );
-
- return iRow !== undefined ?
- api.row( iRow ).node() :
- api.rows().nodes().flatten().toArray();
- };
-
-
- /**
- * Get the array indexes of a particular cell from it's DOM element
- * and column index including hidden columns
- * @param {node} node this can either be a TR, TD or TH in the table's body
- * @returns {int} If nNode is given as a TR, then a single index is returned, or
- * if given as a cell, an array of [row index, column index (visible),
- * column index (all)] is given.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * $('#example tbody td').click( function () {
- * // Get the position of the current data from the node
- * var aPos = oTable.fnGetPosition( this );
- *
- * // Get the data array for this row
- * var aData = oTable.fnGetData( aPos[0] );
- *
- * // Update the data array and return the value
- * aData[ aPos[1] ] = 'clicked';
- * this.innerHTML = 'clicked';
- * } );
- *
- * // Init DataTables
- * oTable = $('#example').dataTable();
- * } );
- */
- this.fnGetPosition = function( node )
- {
- var api = this.api( true );
- var nodeName = node.nodeName.toUpperCase();
-
- if ( nodeName == 'TR' ) {
- return api.row( node ).index();
- }
- else if ( nodeName == 'TD' || nodeName == 'TH' ) {
- var cell = api.cell( node ).index();
-
- return [
- cell.row,
- cell.columnVisible,
- cell.column
- ];
- }
- return null;
- };
-
-
- /**
- * Check to see if a row is 'open' or not.
- * @param {node} nTr the table row to check
- * @returns {boolean} true if the row is currently open, false otherwise
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable;
- *
- * // 'open' an information row when a row is clicked on
- * $('#example tbody tr').click( function () {
- * if ( oTable.fnIsOpen(this) ) {
- * oTable.fnClose( this );
- * } else {
- * oTable.fnOpen( this, "Temporary row opened", "info_row" );
- * }
- * } );
- *
- * oTable = $('#example').dataTable();
- * } );
- */
- this.fnIsOpen = function( nTr )
- {
- return this.api( true ).row( nTr ).child.isShown();
- };
-
-
- /**
- * This function will place a new row directly after a row which is currently
- * on display on the page, with the HTML contents that is passed into the
- * function. This can be used, for example, to ask for confirmation that a
- * particular record should be deleted.
- * @param {node} nTr The table row to 'open'
- * @param {string|node|jQuery} mHtml The HTML to put into the row
- * @param {string} sClass Class to give the new TD cell
- * @returns {node} The row opened. Note that if the table row passed in as the
- * first parameter, is not found in the table, this method will silently
- * return.
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable;
- *
- * // 'open' an information row when a row is clicked on
- * $('#example tbody tr').click( function () {
- * if ( oTable.fnIsOpen(this) ) {
- * oTable.fnClose( this );
- * } else {
- * oTable.fnOpen( this, "Temporary row opened", "info_row" );
- * }
- * } );
- *
- * oTable = $('#example').dataTable();
- * } );
- */
- this.fnOpen = function( nTr, mHtml, sClass )
- {
- return this.api( true )
- .row( nTr )
- .child( mHtml, sClass )
- .show()
- .child()[0];
- };
-
-
- /**
- * Change the pagination - provides the internal logic for pagination in a simple API
- * function. With this function you can have a DataTables table go to the next,
- * previous, first or last pages.
- * @param {string|int} mAction Paging action to take: "first", "previous", "next" or "last"
- * or page number to jump to (integer), note that page 0 is the first page.
- * @param {bool} [bRedraw=true] Redraw the table or not
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- * oTable.fnPageChange( 'next' );
- * } );
- */
- this.fnPageChange = function ( mAction, bRedraw )
- {
- var api = this.api( true ).page( mAction );
-
- if ( bRedraw === undefined || bRedraw ) {
- api.draw(false);
- }
- };
-
-
- /**
- * Show a particular column
- * @param {int} iCol The column whose display should be changed
- * @param {bool} bShow Show (true) or hide (false) the column
- * @param {bool} [bRedraw=true] Redraw the table or not
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Hide the second column after initialisation
- * oTable.fnSetColumnVis( 1, false );
- * } );
- */
- this.fnSetColumnVis = function ( iCol, bShow, bRedraw )
- {
- var api = this.api( true ).column( iCol ).visible( bShow );
-
- if ( bRedraw === undefined || bRedraw ) {
- api.columns.adjust().draw();
- }
- };
-
-
- /**
- * Get the settings for a particular table for external manipulation
- * @returns {object} DataTables settings object. See
- * {@link DataTable.models.oSettings}
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- * var oSettings = oTable.fnSettings();
- *
- * // Show an example parameter from the settings
- * alert( oSettings._iDisplayStart );
- * } );
- */
- this.fnSettings = function()
- {
- return _fnSettingsFromNode( this[_ext.iApiIndex] );
- };
-
-
- /**
- * Sort the table by a particular column
- * @param {int} iCol the data index to sort on. Note that this will not match the
- * 'display index' if you have hidden data entries
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Sort immediately with columns 0 and 1
- * oTable.fnSort( [ [0,'asc'], [1,'asc'] ] );
- * } );
- */
- this.fnSort = function( aaSort )
- {
- this.api( true ).order( aaSort ).draw();
- };
-
-
- /**
- * Attach a sort listener to an element for a given column
- * @param {node} nNode the element to attach the sort listener to
- * @param {int} iColumn the column that a click on this node will sort on
- * @param {function} [fnCallback] callback function when sort is run
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- *
- * // Sort on column 1, when 'sorter' is clicked on
- * oTable.fnSortListener( document.getElementById('sorter'), 1 );
- * } );
- */
- this.fnSortListener = function( nNode, iColumn, fnCallback )
- {
- this.api( true ).order.listener( nNode, iColumn, fnCallback );
- };
-
-
- /**
- * Update a table cell or row - this method will accept either a single value to
- * update the cell with, an array of values with one element for each column or
- * an object in the same format as the original data source. The function is
- * self-referencing in order to make the multi column updates easier.
- * @param {object|array|string} mData Data to update the cell/row with
- * @param {node|int} mRow TR element you want to update or the aoData index
- * @param {int} [iColumn] The column to update, give as null or undefined to
- * update a whole row.
- * @param {bool} [bRedraw=true] Redraw the table or not
- * @param {bool} [bAction=true] Perform pre-draw actions or not
- * @returns {int} 0 on success, 1 on error
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- * oTable.fnUpdate( 'Example update', 0, 0 ); // Single cell
- * oTable.fnUpdate( ['a', 'b', 'c', 'd', 'e'], $('tbody tr')[0] ); // Row
- * } );
- */
- this.fnUpdate = function( mData, mRow, iColumn, bRedraw, bAction )
- {
- var api = this.api( true );
-
- if ( iColumn === undefined || iColumn === null ) {
- api.row( mRow ).data( mData );
- }
- else {
- api.cell( mRow, iColumn ).data( mData );
- }
-
- if ( bAction === undefined || bAction ) {
- api.columns.adjust();
- }
-
- if ( bRedraw === undefined || bRedraw ) {
- api.draw();
- }
- return 0;
- };
-
-
- /**
- * Provide a common method for plug-ins to check the version of DataTables being used, in order
- * to ensure compatibility.
- * @param {string} sVersion Version string to check for, in the format "X.Y.Z". Note that the
- * formats "X" and "X.Y" are also acceptable.
- * @returns {boolean} true if this version of DataTables is greater or equal to the required
- * version, or false if this version of DataTales is not suitable
- * @method
- * @dtopt API
- * @deprecated Since v1.10
- *
- * @example
- * $(document).ready(function() {
- * var oTable = $('#example').dataTable();
- * alert( oTable.fnVersionCheck( '1.9.0' ) );
- * } );
- */
- this.fnVersionCheck = _ext.fnVersionCheck;
-
- var _that = this;
- var emptyInit = options === undefined;
- var len = this.length;
- if ( emptyInit ) {
- options = {};
- }
- this.oApi = this.internal = _ext.internal;
- // Extend with old style plug-in API methods
- for ( var fn in DataTable.ext.internal ) {
- if ( fn ) {
- this[fn] = _fnExternApiFunc(fn);
- }
- }
- this.each(function() {
- // For each initialisation we want to give it a clean initialisation
- // object that can be bashed around
- var o = {};
- var oInit = len > 1 ? // optimisation for single table case
- _fnExtend( o, options, true ) :
- options;
- /*global oInit,_that,emptyInit*/
- var i=0, iLen, j, jLen, k, kLen;
- var sId = this.getAttribute( 'id' );
- var bInitHandedOff = false;
- var defaults = DataTable.defaults;
- var $this = $(this);
-
-
- /* Sanity check */
- if ( this.nodeName.toLowerCase() != 'table' )
- {
- _fnLog( null, 0, 'Non-table node initialisation ('+this.nodeName+')', 2 );
- return;
- }
-
- /* Backwards compatibility for the defaults */
- _fnCompatOpts( defaults );
- _fnCompatCols( defaults.column );
-
- /* Convert the camel-case defaults to Hungarian */
- _fnCamelToHungarian( defaults, defaults, true );
- _fnCamelToHungarian( defaults.column, defaults.column, true );
-
- /* Setting up the initialisation object */
- _fnCamelToHungarian( defaults, $.extend( oInit, $this.data() ) );
-
-
-
- /* Check to see if we are re-initialising a table */
- var allSettings = DataTable.settings;
- for ( i=0, iLen=allSettings.length ; i<iLen ; i++ )
- {
- var s = allSettings[i];
-
- /* Base check on table node */
- if (
- s.nTable == this ||
- (s.nTHead && s.nTHead.parentNode == this) ||
- (s.nTFoot && s.nTFoot.parentNode == this)
- ) {
- var bRetrieve = oInit.bRetrieve !== undefined ? oInit.bRetrieve : defaults.bRetrieve;
- var bDestroy = oInit.bDestroy !== undefined ? oInit.bDestroy : defaults.bDestroy;
-
- if ( emptyInit || bRetrieve )
- {
- return s.oInstance;
- }
- else if ( bDestroy )
- {
- s.oInstance.fnDestroy();
- break;
- }
- else
- {
- _fnLog( s, 0, 'Cannot reinitialise DataTable', 3 );
- return;
- }
- }
-
- /* If the element we are initialising has the same ID as a table which was previously
- * initialised, but the table nodes don't match (from before) then we destroy the old
- * instance by simply deleting it. This is under the assumption that the table has been
- * destroyed by other methods. Anyone using non-id selectors will need to do this manually
- */
- if ( s.sTableId == this.id )
- {
- allSettings.splice( i, 1 );
- break;
- }
- }
-
- /* Ensure the table has an ID - required for accessibility */
- if ( sId === null || sId === "" )
- {
- sId = "DataTables_Table_"+(DataTable.ext._unique++);
- this.id = sId;
- }
-
- /* Create the settings object for this table and set some of the default parameters */
- var oSettings = $.extend( true, {}, DataTable.models.oSettings, {
- "sDestroyWidth": $this[0].style.width,
- "sInstance": sId,
- "sTableId": sId
- } );
- oSettings.nTable = this;
- oSettings.oApi = _that.internal;
- oSettings.oInit = oInit;
-
- allSettings.push( oSettings );
-
- // Need to add the instance after the instance after the settings object has been added
- // to the settings array, so we can self reference the table instance if more than one
- oSettings.oInstance = (_that.length===1) ? _that : $this.dataTable();
-
- // Backwards compatibility, before we apply all the defaults
- _fnCompatOpts( oInit );
- _fnLanguageCompat( oInit.oLanguage );
-
- // If the length menu is given, but the init display length is not, use the length menu
- if ( oInit.aLengthMenu && ! oInit.iDisplayLength )
- {
- oInit.iDisplayLength = $.isArray( oInit.aLengthMenu[0] ) ?
- oInit.aLengthMenu[0][0] : oInit.aLengthMenu[0];
- }
-
- // Apply the defaults and init options to make a single init object will all
- // options defined from defaults and instance options.
- oInit = _fnExtend( $.extend( true, {}, defaults ), oInit );
-
-
- // Map the initialisation options onto the settings object
- _fnMap( oSettings.oFeatures, oInit, [
- "bPaginate",
- "bLengthChange",
- "bFilter",
- "bSort",
- "bSortMulti",
- "bInfo",
- "bProcessing",
- "bAutoWidth",
- "bSortClasses",
- "bServerSide",
- "bDeferRender"
- ] );
- _fnMap( oSettings, oInit, [
- "asStripeClasses",
- "ajax",
- "fnServerData",
- "fnFormatNumber",
- "sServerMethod",
- "aaSorting",
- "aaSortingFixed",
- "aLengthMenu",
- "sPaginationType",
- "sAjaxSource",
- "sAjaxDataProp",
- "iStateDuration",
- "sDom",
- "bSortCellsTop",
- "iTabIndex",
- "fnStateLoadCallback",
- "fnStateSaveCallback",
- "renderer",
- "searchDelay",
- "rowId",
- [ "iCookieDuration", "iStateDuration" ], // backwards compat
- [ "oSearch", "oPreviousSearch" ],
- [ "aoSearchCols", "aoPreSearchCols" ],
- [ "iDisplayLength", "_iDisplayLength" ]
- ] );
- _fnMap( oSettings.oScroll, oInit, [
- [ "sScrollX", "sX" ],
- [ "sScrollXInner", "sXInner" ],
- [ "sScrollY", "sY" ],
- [ "bScrollCollapse", "bCollapse" ]
- ] );
- _fnMap( oSettings.oLanguage, oInit, "fnInfoCallback" );
-
- /* Callback functions which are array driven */
- _fnCallbackReg( oSettings, 'aoDrawCallback', oInit.fnDrawCallback, 'user' );
- _fnCallbackReg( oSettings, 'aoServerParams', oInit.fnServerParams, 'user' );
- _fnCallbackReg( oSettings, 'aoStateSaveParams', oInit.fnStateSaveParams, 'user' );
- _fnCallbackReg( oSettings, 'aoStateLoadParams', oInit.fnStateLoadParams, 'user' );
- _fnCallbackReg( oSettings, 'aoStateLoaded', oInit.fnStateLoaded, 'user' );
- _fnCallbackReg( oSettings, 'aoRowCallback', oInit.fnRowCallback, 'user' );
- _fnCallbackReg( oSettings, 'aoRowCreatedCallback', oInit.fnCreatedRow, 'user' );
- _fnCallbackReg( oSettings, 'aoHeaderCallback', oInit.fnHeaderCallback, 'user' );
- _fnCallbackReg( oSettings, 'aoFooterCallback', oInit.fnFooterCallback, 'user' );
- _fnCallbackReg( oSettings, 'aoInitComplete', oInit.fnInitComplete, 'user' );
- _fnCallbackReg( oSettings, 'aoPreDrawCallback', oInit.fnPreDrawCallback, 'user' );
-
- oSettings.rowIdFn = _fnGetObjectDataFn( oInit.rowId );
-
- /* Browser support detection */
- _fnBrowserDetect( oSettings );
-
- var oClasses = oSettings.oClasses;
-
- $.extend( oClasses, DataTable.ext.classes, oInit.oClasses );
- $this.addClass( oClasses.sTable );
-
-
- if ( oSettings.iInitDisplayStart === undefined )
- {
- /* Display start point, taking into account the save saving */
- oSettings.iInitDisplayStart = oInit.iDisplayStart;
- oSettings._iDisplayStart = oInit.iDisplayStart;
- }
-
- if ( oInit.iDeferLoading !== null )
- {
- oSettings.bDeferLoading = true;
- var tmp = $.isArray( oInit.iDeferLoading );
- oSettings._iRecordsDisplay = tmp ? oInit.iDeferLoading[0] : oInit.iDeferLoading;
- oSettings._iRecordsTotal = tmp ? oInit.iDeferLoading[1] : oInit.iDeferLoading;
- }
-
- /* Language definitions */
- var oLanguage = oSettings.oLanguage;
- $.extend( true, oLanguage, oInit.oLanguage );
-
- if ( oLanguage.sUrl )
- {
- /* Get the language definitions from a file - because this Ajax call makes the language
- * get async to the remainder of this function we use bInitHandedOff to indicate that
- * _fnInitialise will be fired by the returned Ajax handler, rather than the constructor
- */
- $.ajax( {
- dataType: 'json',
- url: oLanguage.sUrl,
- success: function ( json ) {
- _fnLanguageCompat( json );
- _fnCamelToHungarian( defaults.oLanguage, json );
- $.extend( true, oLanguage, json );
- _fnInitialise( oSettings );
- },
- error: function () {
- // Error occurred loading language file, continue on as best we can
- _fnInitialise( oSettings );
- }
- } );
- bInitHandedOff = true;
- }
-
- /*
- * Stripes
- */
- if ( oInit.asStripeClasses === null )
- {
- oSettings.asStripeClasses =[
- oClasses.sStripeOdd,
- oClasses.sStripeEven
- ];
- }
-
- /* Remove row stripe classes if they are already on the table row */
- var stripeClasses = oSettings.asStripeClasses;
- var rowOne = $this.children('tbody').find('tr').eq(0);
- if ( $.inArray( true, $.map( stripeClasses, function(el, i) {
- return rowOne.hasClass(el);
- } ) ) !== -1 ) {
- $('tbody tr', this).removeClass( stripeClasses.join(' ') );
- oSettings.asDestroyStripes = stripeClasses.slice();
- }
-
- /*
- * Columns
- * See if we should load columns automatically or use defined ones
- */
- var anThs = [];
- var aoColumnsInit;
- var nThead = this.getElementsByTagName('thead');
- if ( nThead.length !== 0 )
- {
- _fnDetectHeader( oSettings.aoHeader, nThead[0] );
- anThs = _fnGetUniqueThs( oSettings );
- }
-
- /* If not given a column array, generate one with nulls */
- if ( oInit.aoColumns === null )
- {
- aoColumnsInit = [];
- for ( i=0, iLen=anThs.length ; i<iLen ; i++ )
- {
- aoColumnsInit.push( null );
- }
- }
- else
- {
- aoColumnsInit = oInit.aoColumns;
- }
-
- /* Add the columns */
- for ( i=0, iLen=aoColumnsInit.length ; i<iLen ; i++ )
- {
- _fnAddColumn( oSettings, anThs ? anThs[i] : null );
- }
-
- /* Apply the column definitions */
- _fnApplyColumnDefs( oSettings, oInit.aoColumnDefs, aoColumnsInit, function (iCol, oDef) {
- _fnColumnOptions( oSettings, iCol, oDef );
- } );
-
- /* HTML5 attribute detection - build an mData object automatically if the
- * attributes are found
- */
- if ( rowOne.length ) {
- var a = function ( cell, name ) {
- return cell.getAttribute( 'data-'+name ) !== null ? name : null;
- };
-
- $( rowOne[0] ).children('th, td').each( function (i, cell) {
- var col = oSettings.aoColumns[i];
-
- if ( col.mData === i ) {
- var sort = a( cell, 'sort' ) || a( cell, 'order' );
- var filter = a( cell, 'filter' ) || a( cell, 'search' );
-
- if ( sort !== null || filter !== null ) {
- col.mData = {
- _: i+'.display',
- sort: sort !== null ? i+'.@data-'+sort : undefined,
- type: sort !== null ? i+'.@data-'+sort : undefined,
- filter: filter !== null ? i+'.@data-'+filter : undefined
- };
-
- _fnColumnOptions( oSettings, i );
- }
- }
- } );
- }
-
- var features = oSettings.oFeatures;
- var loadedInit = function () {
- /*
- * Sorting
- * @todo For modularisation (1.11) this needs to do into a sort start up handler
- */
-
- // If aaSorting is not defined, then we use the first indicator in asSorting
- // in case that has been altered, so the default sort reflects that option
- if ( oInit.aaSorting === undefined ) {
- var sorting = oSettings.aaSorting;
- for ( i=0, iLen=sorting.length ; i<iLen ; i++ ) {
- sorting[i][1] = oSettings.aoColumns[ i ].asSorting[0];
- }
- }
-
- /* Do a first pass on the sorting classes (allows any size changes to be taken into
- * account, and also will apply sorting disabled classes if disabled
- */
- _fnSortingClasses( oSettings );
-
- if ( features.bSort ) {
- _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
- if ( oSettings.bSorted ) {
- var aSort = _fnSortFlatten( oSettings );
- var sortedColumns = {};
-
- $.each( aSort, function (i, val) {
- sortedColumns[ val.src ] = val.dir;
- } );
-
- _fnCallbackFire( oSettings, null, 'order', [oSettings, aSort, sortedColumns] );
- _fnSortAria( oSettings );
- }
- } );
- }
-
- _fnCallbackReg( oSettings, 'aoDrawCallback', function () {
- if ( oSettings.bSorted || _fnDataSource( oSettings ) === 'ssp' || features.bDeferRender ) {
- _fnSortingClasses( oSettings );
- }
- }, 'sc' );
-
-
- /*
- * Final init
- * Cache the header, body and footer as required, creating them if needed
- */
-
- // Work around for Webkit bug 83867 - store the caption-side before removing from doc
- var captions = $this.children('caption').each( function () {
- this._captionSide = $(this).css('caption-side');
- } );
-
- var thead = $this.children('thead');
- if ( thead.length === 0 ) {
- thead = $('<thead/>').appendTo($this);
- }
- oSettings.nTHead = thead[0];
-
- var tbody = $this.children('tbody');
- if ( tbody.length === 0 ) {
- tbody = $('<tbody/>').appendTo($this);
- }
- oSettings.nTBody = tbody[0];
-
- var tfoot = $this.children('tfoot');
- if ( tfoot.length === 0 && captions.length > 0 && (oSettings.oScroll.sX !== "" || oSettings.oScroll.sY !== "") ) {
- // If we are a scrolling table, and no footer has been given, then we need to create
- // a tfoot element for the caption element to be appended to
- tfoot = $('<tfoot/>').appendTo($this);
- }
-
- if ( tfoot.length === 0 || tfoot.children().length === 0 ) {
- $this.addClass( oClasses.sNoFooter );
- }
- else if ( tfoot.length > 0 ) {
- oSettings.nTFoot = tfoot[0];
- _fnDetectHeader( oSettings.aoFooter, oSettings.nTFoot );
- }
-
- /* Check if there is data passing into the constructor */
- if ( oInit.aaData ) {
- for ( i=0 ; i<oInit.aaData.length ; i++ ) {
- _fnAddData( oSettings, oInit.aaData[ i ] );
- }
- }
- else if ( oSettings.bDeferLoading || _fnDataSource( oSettings ) == 'dom' ) {
- /* Grab the data from the page - only do this when deferred loading or no Ajax
- * source since there is no point in reading the DOM data if we are then going
- * to replace it with Ajax data
- */
- _fnAddTr( oSettings, $(oSettings.nTBody).children('tr') );
- }
-
- /* Copy the data index array */
- oSettings.aiDisplay = oSettings.aiDisplayMaster.slice();
-
- /* Initialisation complete - table can be drawn */
- oSettings.bInitialised = true;
-
- /* Check if we need to initialise the table (it might not have been handed off to the
- * language processor)
- */
- if ( bInitHandedOff === false ) {
- _fnInitialise( oSettings );
- }
- };
-
- /* Must be done after everything which can be overridden by the state saving! */
- if ( oInit.bStateSave )
- {
- features.bStateSave = true;
- _fnCallbackReg( oSettings, 'aoDrawCallback', _fnSaveState, 'state_save' );
- _fnLoadState( oSettings, oInit, loadedInit );
- }
- else {
- loadedInit();
- }
-
- } );
- _that = null;
- return this;
- };
-
- /*
- * It is useful to have variables which are scoped locally so only the
- * DataTables functions can access them and they don't leak into global space.
- * At the same time these functions are often useful over multiple files in the
- * core and API, so we list, or at least document, all variables which are used
- * by DataTables as private variables here. This also ensures that there is no
- * clashing of variable names and that they can easily referenced for reuse.
- */
-
-
- // Defined else where
- // _selector_run
- // _selector_opts
- // _selector_first
- // _selector_row_indexes
-
- var _ext; // DataTable.ext
- var _Api; // DataTable.Api
- var _api_register; // DataTable.Api.register
- var _api_registerPlural; // DataTable.Api.registerPlural
-
- var _re_dic = {};
- var _re_new_lines = /[\r\n]/g;
- var _re_html = /<.*?>/g;
-
- // This is not strict ISO8601 - Date.parse() is quite lax, although
- // implementations differ between browsers.
- var _re_date = /^\d{2,4}[\.\/\-]\d{1,2}[\.\/\-]\d{1,2}([T ]{1}\d{1,2}[:\.]\d{2}([\.:]\d{2})?)?$/;
-
- // Escape regular expression special characters
- var _re_escape_regex = new RegExp( '(\\' + [ '/', '.', '*', '+', '?', '|', '(', ')', '[', ']', '{', '}', '\\', '$', '^', '-' ].join('|\\') + ')', 'g' );
-
- // http://en.wikipedia.org/wiki/Foreign_exchange_market
- // - \u20BD - Russian ruble.
- // - \u20a9 - South Korean Won
- // - \u20BA - Turkish Lira
- // - \u20B9 - Indian Rupee
- // - R - Brazil (R$) and South Africa
- // - fr - Swiss Franc
- // - kr - Swedish krona, Norwegian krone and Danish krone
- // - \u2009 is thin space and \u202F is narrow no-break space, both used in many
- // - Ƀ - Bitcoin
- // - Ξ - Ethereum
- // standards as thousands separators.
- var _re_formatted_numeric = /[',$£€¥%\u2009\u202F\u20BD\u20a9\u20BArfkɃΞ]/gi;
-
-
- var _empty = function ( d ) {
- return !d || d === true || d === '-' ? true : false;
- };
-
-
- var _intVal = function ( s ) {
- var integer = parseInt( s, 10 );
- return !isNaN(integer) && isFinite(s) ? integer : null;
- };
-
- // Convert from a formatted number with characters other than `.` as the
- // decimal place, to a Javascript number
- var _numToDecimal = function ( num, decimalPoint ) {
- // Cache created regular expressions for speed as this function is called often
- if ( ! _re_dic[ decimalPoint ] ) {
- _re_dic[ decimalPoint ] = new RegExp( _fnEscapeRegex( decimalPoint ), 'g' );
- }
- return typeof num === 'string' && decimalPoint !== '.' ?
- num.replace( /\./g, '' ).replace( _re_dic[ decimalPoint ], '.' ) :
- num;
- };
-
-
- var _isNumber = function ( d, decimalPoint, formatted ) {
- var strType = typeof d === 'string';
-
- // If empty return immediately so there must be a number if it is a
- // formatted string (this stops the string "k", or "kr", etc being detected
- // as a formatted number for currency
- if ( _empty( d ) ) {
- return true;
- }
-
- if ( decimalPoint && strType ) {
- d = _numToDecimal( d, decimalPoint );
- }
-
- if ( formatted && strType ) {
- d = d.replace( _re_formatted_numeric, '' );
- }
-
- return !isNaN( parseFloat(d) ) && isFinite( d );
- };
-
-
- // A string without HTML in it can be considered to be HTML still
- var _isHtml = function ( d ) {
- return _empty( d ) || typeof d === 'string';
- };
-
-
- var _htmlNumeric = function ( d, decimalPoint, formatted ) {
- if ( _empty( d ) ) {
- return true;
- }
-
- var html = _isHtml( d );
- return ! html ?
- null :
- _isNumber( _stripHtml( d ), decimalPoint, formatted ) ?
- true :
- null;
- };
-
-
- var _pluck = function ( a, prop, prop2 ) {
- var out = [];
- var i=0, ien=a.length;
-
- // Could have the test in the loop for slightly smaller code, but speed
- // is essential here
- if ( prop2 !== undefined ) {
- for ( ; i<ien ; i++ ) {
- if ( a[i] && a[i][ prop ] ) {
- out.push( a[i][ prop ][ prop2 ] );
- }
- }
- }
- else {
- for ( ; i<ien ; i++ ) {
- if ( a[i] ) {
- out.push( a[i][ prop ] );
- }
- }
- }
-
- return out;
- };
-
-
- // Basically the same as _pluck, but rather than looping over `a` we use `order`
- // as the indexes to pick from `a`
- var _pluck_order = function ( a, order, prop, prop2 )
- {
- var out = [];
- var i=0, ien=order.length;
-
- // Could have the test in the loop for slightly smaller code, but speed
- // is essential here
- if ( prop2 !== undefined ) {
- for ( ; i<ien ; i++ ) {
- if ( a[ order[i] ][ prop ] ) {
- out.push( a[ order[i] ][ prop ][ prop2 ] );
- }
- }
- }
- else {
- for ( ; i<ien ; i++ ) {
- out.push( a[ order[i] ][ prop ] );
- }
- }
-
- return out;
- };
-
-
- var _range = function ( len, start )
- {
- var out = [];
- var end;
-
- if ( start === undefined ) {
- start = 0;
- end = len;
- }
- else {
- end = start;
- start = len;
- }
-
- for ( var i=start ; i<end ; i++ ) {
- out.push( i );
- }
-
- return out;
- };
-
-
- var _removeEmpty = function ( a )
- {
- var out = [];
-
- for ( var i=0, ien=a.length ; i<ien ; i++ ) {
- if ( a[i] ) { // careful - will remove all falsy values!
- out.push( a[i] );
- }
- }
-
- return out;
- };
-
-
- var _stripHtml = function ( d ) {
- return d.replace( _re_html, '' );
- };
-
-
- /**
- * Determine if all values in the array are unique. This means we can short
- * cut the _unique method at the cost of a single loop. A sorted array is used
- * to easily check the values.
- *
- * @param {array} src Source array
- * @return {boolean} true if all unique, false otherwise
- * @ignore
- */
- var _areAllUnique = function ( src ) {
- if ( src.length < 2 ) {
- return true;
- }
-
- var sorted = src.slice().sort();
- var last = sorted[0];
-
- for ( var i=1, ien=sorted.length ; i<ien ; i++ ) {
- if ( sorted[i] === last ) {
- return false;
- }
-
- last = sorted[i];
- }
-
- return true;
- };
-
-
- /**
- * Find the unique elements in a source array.
- *
- * @param {array} src Source array
- * @return {array} Array of unique items
- * @ignore
- */
- var _unique = function ( src )
- {
- if ( _areAllUnique( src ) ) {
- return src.slice();
- }
-
- // A faster unique method is to use object keys to identify used values,
- // but this doesn't work with arrays or objects, which we must also
- // consider. See jsperf.com/compare-array-unique-versions/4 for more
- // information.
- var
- out = [],
- val,
- i, ien=src.length,
- j, k=0;
-
- again: for ( i=0 ; i<ien ; i++ ) {
- val = src[i];
-
- for ( j=0 ; j<k ; j++ ) {
- if ( out[j] === val ) {
- continue again;
- }
- }
-
- out.push( val );
- k++;
- }
-
- return out;
- };
-
-
- /**
- * DataTables utility methods
- *
- * This namespace provides helper methods that DataTables uses internally to
- * create a DataTable, but which are not exclusively used only for DataTables.
- * These methods can be used by extension authors to save the duplication of
- * code.
- *
- * @namespace
- */
- DataTable.util = {
- /**
- * Throttle the calls to a function. Arguments and context are maintained
- * for the throttled function.
- *
- * @param {function} fn Function to be called
- * @param {integer} freq Call frequency in mS
- * @return {function} Wrapped function
- */
- throttle: function ( fn, freq ) {
- var
- frequency = freq !== undefined ? freq : 200,
- last,
- timer;
-
- return function () {
- var
- that = this,
- now = +new Date(),
- args = arguments;
-
- if ( last && now < last + frequency ) {
- clearTimeout( timer );
-
- timer = setTimeout( function () {
- last = undefined;
- fn.apply( that, args );
- }, frequency );
- }
- else {
- last = now;
- fn.apply( that, args );
- }
- };
- },
-
-
- /**
- * Escape a string such that it can be used in a regular expression
- *
- * @param {string} val string to escape
- * @returns {string} escaped string
- */
- escapeRegex: function ( val ) {
- return val.replace( _re_escape_regex, '\\$1' );
- }
- };
-
-
-
- /**
- * Create a mapping object that allows camel case parameters to be looked up
- * for their Hungarian counterparts. The mapping is stored in a private
- * parameter called `_hungarianMap` which can be accessed on the source object.
- * @param {object} o
- * @memberof DataTable#oApi
- */
- function _fnHungarianMap ( o )
- {
- var
- hungarian = 'a aa ai ao as b fn i m o s ',
- match,
- newKey,
- map = {};
-
- $.each( o, function (key, val) {
- match = key.match(/^([^A-Z]+?)([A-Z])/);
-
- if ( match && hungarian.indexOf(match[1]+' ') !== -1 )
- {
- newKey = key.replace( match[0], match[2].toLowerCase() );
- map[ newKey ] = key;
-
- if ( match[1] === 'o' )
- {
- _fnHungarianMap( o[key] );
- }
- }
- } );
-
- o._hungarianMap = map;
- }
-
-
- /**
- * Convert from camel case parameters to Hungarian, based on a Hungarian map
- * created by _fnHungarianMap.
- * @param {object} src The model object which holds all parameters that can be
- * mapped.
- * @param {object} user The object to convert from camel case to Hungarian.
- * @param {boolean} force When set to `true`, properties which already have a
- * Hungarian value in the `user` object will be overwritten. Otherwise they
- * won't be.
- * @memberof DataTable#oApi
- */
- function _fnCamelToHungarian ( src, user, force )
- {
- if ( ! src._hungarianMap ) {
- _fnHungarianMap( src );
- }
-
- var hungarianKey;
-
- $.each( user, function (key, val) {
- hungarianKey = src._hungarianMap[ key ];
-
- if ( hungarianKey !== undefined && (force || user[hungarianKey] === undefined) )
- {
- // For objects, we need to buzz down into the object to copy parameters
- if ( hungarianKey.charAt(0) === 'o' )
- {
- // Copy the camelCase options over to the hungarian
- if ( ! user[ hungarianKey ] ) {
- user[ hungarianKey ] = {};
- }
- $.extend( true, user[hungarianKey], user[key] );
-
- _fnCamelToHungarian( src[hungarianKey], user[hungarianKey], force );
- }
- else {
- user[hungarianKey] = user[ key ];
- }
- }
- } );
- }
-
-
- /**
- * Language compatibility - when certain options are given, and others aren't, we
- * need to duplicate the values over, in order to provide backwards compatibility
- * with older language files.
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnLanguageCompat( lang )
- {
- // Note the use of the Hungarian notation for the parameters in this method as
- // this is called after the mapping of camelCase to Hungarian
- var defaults = DataTable.defaults.oLanguage;
-
- // Default mapping
- var defaultDecimal = defaults.sDecimal;
- if ( defaultDecimal ) {
- _addNumericSort( defaultDecimal );
- }
-
- if ( lang ) {
- var zeroRecords = lang.sZeroRecords;
-
- // Backwards compatibility - if there is no sEmptyTable given, then use the same as
- // sZeroRecords - assuming that is given.
- if ( ! lang.sEmptyTable && zeroRecords &&
- defaults.sEmptyTable === "No data available in table" )
- {
- _fnMap( lang, lang, 'sZeroRecords', 'sEmptyTable' );
- }
-
- // Likewise with loading records
- if ( ! lang.sLoadingRecords && zeroRecords &&
- defaults.sLoadingRecords === "Loading..." )
- {
- _fnMap( lang, lang, 'sZeroRecords', 'sLoadingRecords' );
- }
-
- // Old parameter name of the thousands separator mapped onto the new
- if ( lang.sInfoThousands ) {
- lang.sThousands = lang.sInfoThousands;
- }
-
- var decimal = lang.sDecimal;
- if ( decimal && defaultDecimal !== decimal ) {
- _addNumericSort( decimal );
- }
- }
- }
-
-
- /**
- * Map one parameter onto another
- * @param {object} o Object to map
- * @param {*} knew The new parameter name
- * @param {*} old The old parameter name
- */
- var _fnCompatMap = function ( o, knew, old ) {
- if ( o[ knew ] !== undefined ) {
- o[ old ] = o[ knew ];
- }
- };
-
-
- /**
- * Provide backwards compatibility for the main DT options. Note that the new
- * options are mapped onto the old parameters, so this is an external interface
- * change only.
- * @param {object} init Object to map
- */
- function _fnCompatOpts ( init )
- {
- _fnCompatMap( init, 'ordering', 'bSort' );
- _fnCompatMap( init, 'orderMulti', 'bSortMulti' );
- _fnCompatMap( init, 'orderClasses', 'bSortClasses' );
- _fnCompatMap( init, 'orderCellsTop', 'bSortCellsTop' );
- _fnCompatMap( init, 'order', 'aaSorting' );
- _fnCompatMap( init, 'orderFixed', 'aaSortingFixed' );
- _fnCompatMap( init, 'paging', 'bPaginate' );
- _fnCompatMap( init, 'pagingType', 'sPaginationType' );
- _fnCompatMap( init, 'pageLength', 'iDisplayLength' );
- _fnCompatMap( init, 'searching', 'bFilter' );
-
- // Boolean initialisation of x-scrolling
- if ( typeof init.sScrollX === 'boolean' ) {
- init.sScrollX = init.sScrollX ? '100%' : '';
- }
- if ( typeof init.scrollX === 'boolean' ) {
- init.scrollX = init.scrollX ? '100%' : '';
- }
-
- // Column search objects are in an array, so it needs to be converted
- // element by element
- var searchCols = init.aoSearchCols;
-
- if ( searchCols ) {
- for ( var i=0, ien=searchCols.length ; i<ien ; i++ ) {
- if ( searchCols[i] ) {
- _fnCamelToHungarian( DataTable.models.oSearch, searchCols[i] );
- }
- }
- }
- }
-
-
- /**
- * Provide backwards compatibility for column options. Note that the new options
- * are mapped onto the old parameters, so this is an external interface change
- * only.
- * @param {object} init Object to map
- */
- function _fnCompatCols ( init )
- {
- _fnCompatMap( init, 'orderable', 'bSortable' );
- _fnCompatMap( init, 'orderData', 'aDataSort' );
- _fnCompatMap( init, 'orderSequence', 'asSorting' );
- _fnCompatMap( init, 'orderDataType', 'sortDataType' );
-
- // orderData can be given as an integer
- var dataSort = init.aDataSort;
- if ( typeof dataSort === 'number' && ! $.isArray( dataSort ) ) {
- init.aDataSort = [ dataSort ];
- }
- }
-
-
- /**
- * Browser feature detection for capabilities, quirks
- * @param {object} settings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnBrowserDetect( settings )
- {
- // We don't need to do this every time DataTables is constructed, the values
- // calculated are specific to the browser and OS configuration which we
- // don't expect to change between initialisations
- if ( ! DataTable.__browser ) {
- var browser = {};
- DataTable.__browser = browser;
-
- // Scrolling feature / quirks detection
- var n = $('<div/>')
- .css( {
- position: 'fixed',
- top: 0,
- left: $(window).scrollLeft()*-1, // allow for scrolling
- height: 1,
- width: 1,
- overflow: 'hidden'
- } )
- .append(
- $('<div/>')
- .css( {
- position: 'absolute',
- top: 1,
- left: 1,
- width: 100,
- overflow: 'scroll'
- } )
- .append(
- $('<div/>')
- .css( {
- width: '100%',
- height: 10
- } )
- )
- )
- .appendTo( 'body' );
-
- var outer = n.children();
- var inner = outer.children();
-
- // Numbers below, in order, are:
- // inner.offsetWidth, inner.clientWidth, outer.offsetWidth, outer.clientWidth
- //
- // IE6 XP: 100 100 100 83
- // IE7 Vista: 100 100 100 83
- // IE 8+ Windows: 83 83 100 83
- // Evergreen Windows: 83 83 100 83
- // Evergreen Mac with scrollbars: 85 85 100 85
- // Evergreen Mac without scrollbars: 100 100 100 100
-
- // Get scrollbar width
- browser.barWidth = outer[0].offsetWidth - outer[0].clientWidth;
-
- // IE6/7 will oversize a width 100% element inside a scrolling element, to
- // include the width of the scrollbar, while other browsers ensure the inner
- // element is contained without forcing scrolling
- browser.bScrollOversize = inner[0].offsetWidth === 100 && outer[0].clientWidth !== 100;
-
- // In rtl text layout, some browsers (most, but not all) will place the
- // scrollbar on the left, rather than the right.
- browser.bScrollbarLeft = Math.round( inner.offset().left ) !== 1;
-
- // IE8- don't provide height and width for getBoundingClientRect
- browser.bBounding = n[0].getBoundingClientRect().width ? true : false;
-
- n.remove();
- }
-
- $.extend( settings.oBrowser, DataTable.__browser );
- settings.oScroll.iBarWidth = DataTable.__browser.barWidth;
- }
-
-
- /**
- * Array.prototype reduce[Right] method, used for browsers which don't support
- * JS 1.6. Done this way to reduce code size, since we iterate either way
- * @param {object} settings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnReduce ( that, fn, init, start, end, inc )
- {
- var
- i = start,
- value,
- isSet = false;
-
- if ( init !== undefined ) {
- value = init;
- isSet = true;
- }
-
- while ( i !== end ) {
- if ( ! that.hasOwnProperty(i) ) {
- continue;
- }
-
- value = isSet ?
- fn( value, that[i], i, that ) :
- that[i];
-
- isSet = true;
- i += inc;
- }
-
- return value;
- }
-
- /**
- * Add a column to the list used for the table with default values
- * @param {object} oSettings dataTables settings object
- * @param {node} nTh The th element for this column
- * @memberof DataTable#oApi
- */
- function _fnAddColumn( oSettings, nTh )
- {
- // Add column to aoColumns array
- var oDefaults = DataTable.defaults.column;
- var iCol = oSettings.aoColumns.length;
- var oCol = $.extend( {}, DataTable.models.oColumn, oDefaults, {
- "nTh": nTh ? nTh : document.createElement('th'),
- "sTitle": oDefaults.sTitle ? oDefaults.sTitle : nTh ? nTh.innerHTML : '',
- "aDataSort": oDefaults.aDataSort ? oDefaults.aDataSort : [iCol],
- "mData": oDefaults.mData ? oDefaults.mData : iCol,
- idx: iCol
- } );
- oSettings.aoColumns.push( oCol );
-
- // Add search object for column specific search. Note that the `searchCols[ iCol ]`
- // passed into extend can be undefined. This allows the user to give a default
- // with only some of the parameters defined, and also not give a default
- var searchCols = oSettings.aoPreSearchCols;
- searchCols[ iCol ] = $.extend( {}, DataTable.models.oSearch, searchCols[ iCol ] );
-
- // Use the default column options function to initialise classes etc
- _fnColumnOptions( oSettings, iCol, $(nTh).data() );
- }
-
-
- /**
- * Apply options for a column
- * @param {object} oSettings dataTables settings object
- * @param {int} iCol column index to consider
- * @param {object} oOptions object with sType, bVisible and bSearchable etc
- * @memberof DataTable#oApi
- */
- function _fnColumnOptions( oSettings, iCol, oOptions )
- {
- var oCol = oSettings.aoColumns[ iCol ];
- var oClasses = oSettings.oClasses;
- var th = $(oCol.nTh);
-
- // Try to get width information from the DOM. We can't get it from CSS
- // as we'd need to parse the CSS stylesheet. `width` option can override
- if ( ! oCol.sWidthOrig ) {
- // Width attribute
- oCol.sWidthOrig = th.attr('width') || null;
-
- // Style attribute
- var t = (th.attr('style') || '').match(/width:\s*(\d+[pxem%]+)/);
- if ( t ) {
- oCol.sWidthOrig = t[1];
- }
- }
-
- /* User specified column options */
- if ( oOptions !== undefined && oOptions !== null )
- {
- // Backwards compatibility
- _fnCompatCols( oOptions );
-
- // Map camel case parameters to their Hungarian counterparts
- _fnCamelToHungarian( DataTable.defaults.column, oOptions );
-
- /* Backwards compatibility for mDataProp */
- if ( oOptions.mDataProp !== undefined && !oOptions.mData )
- {
- oOptions.mData = oOptions.mDataProp;
- }
-
- if ( oOptions.sType )
- {
- oCol._sManualType = oOptions.sType;
- }
-
- // `class` is a reserved word in Javascript, so we need to provide
- // the ability to use a valid name for the camel case input
- if ( oOptions.className && ! oOptions.sClass )
- {
- oOptions.sClass = oOptions.className;
- }
- if ( oOptions.sClass ) {
- th.addClass( oOptions.sClass );
- }
-
- $.extend( oCol, oOptions );
- _fnMap( oCol, oOptions, "sWidth", "sWidthOrig" );
-
- /* iDataSort to be applied (backwards compatibility), but aDataSort will take
- * priority if defined
- */
- if ( oOptions.iDataSort !== undefined )
- {
- oCol.aDataSort = [ oOptions.iDataSort ];
- }
- _fnMap( oCol, oOptions, "aDataSort" );
- }
-
- /* Cache the data get and set functions for speed */
- var mDataSrc = oCol.mData;
- var mData = _fnGetObjectDataFn( mDataSrc );
- var mRender = oCol.mRender ? _fnGetObjectDataFn( oCol.mRender ) : null;
-
- var attrTest = function( src ) {
- return typeof src === 'string' && src.indexOf('@') !== -1;
- };
- oCol._bAttrSrc = $.isPlainObject( mDataSrc ) && (
- attrTest(mDataSrc.sort) || attrTest(mDataSrc.type) || attrTest(mDataSrc.filter)
- );
- oCol._setter = null;
-
- oCol.fnGetData = function (rowData, type, meta) {
- var innerData = mData( rowData, type, undefined, meta );
-
- return mRender && type ?
- mRender( innerData, type, rowData, meta ) :
- innerData;
- };
- oCol.fnSetData = function ( rowData, val, meta ) {
- return _fnSetObjectDataFn( mDataSrc )( rowData, val, meta );
- };
-
- // Indicate if DataTables should read DOM data as an object or array
- // Used in _fnGetRowElements
- if ( typeof mDataSrc !== 'number' ) {
- oSettings._rowReadObject = true;
- }
-
- /* Feature sorting overrides column specific when off */
- if ( !oSettings.oFeatures.bSort )
- {
- oCol.bSortable = false;
- th.addClass( oClasses.sSortableNone ); // Have to add class here as order event isn't called
- }
-
- /* Check that the class assignment is correct for sorting */
- var bAsc = $.inArray('asc', oCol.asSorting) !== -1;
- var bDesc = $.inArray('desc', oCol.asSorting) !== -1;
- if ( !oCol.bSortable || (!bAsc && !bDesc) )
- {
- oCol.sSortingClass = oClasses.sSortableNone;
- oCol.sSortingClassJUI = "";
- }
- else if ( bAsc && !bDesc )
- {
- oCol.sSortingClass = oClasses.sSortableAsc;
- oCol.sSortingClassJUI = oClasses.sSortJUIAscAllowed;
- }
- else if ( !bAsc && bDesc )
- {
- oCol.sSortingClass = oClasses.sSortableDesc;
- oCol.sSortingClassJUI = oClasses.sSortJUIDescAllowed;
- }
- else
- {
- oCol.sSortingClass = oClasses.sSortable;
- oCol.sSortingClassJUI = oClasses.sSortJUI;
- }
- }
-
-
- /**
- * Adjust the table column widths for new data. Note: you would probably want to
- * do a redraw after calling this function!
- * @param {object} settings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnAdjustColumnSizing ( settings )
- {
- /* Not interested in doing column width calculation if auto-width is disabled */
- if ( settings.oFeatures.bAutoWidth !== false )
- {
- var columns = settings.aoColumns;
-
- _fnCalculateColumnWidths( settings );
- for ( var i=0 , iLen=columns.length ; i<iLen ; i++ )
- {
- columns[i].nTh.style.width = columns[i].sWidth;
- }
- }
-
- var scroll = settings.oScroll;
- if ( scroll.sY !== '' || scroll.sX !== '')
- {
- _fnScrollDraw( settings );
- }
-
- _fnCallbackFire( settings, null, 'column-sizing', [settings] );
- }
-
-
- /**
- * Covert the index of a visible column to the index in the data array (take account
- * of hidden columns)
- * @param {object} oSettings dataTables settings object
- * @param {int} iMatch Visible column index to lookup
- * @returns {int} i the data index
- * @memberof DataTable#oApi
- */
- function _fnVisibleToColumnIndex( oSettings, iMatch )
- {
- var aiVis = _fnGetColumns( oSettings, 'bVisible' );
-
- return typeof aiVis[iMatch] === 'number' ?
- aiVis[iMatch] :
- null;
- }
-
-
- /**
- * Covert the index of an index in the data array and convert it to the visible
- * column index (take account of hidden columns)
- * @param {int} iMatch Column index to lookup
- * @param {object} oSettings dataTables settings object
- * @returns {int} i the data index
- * @memberof DataTable#oApi
- */
- function _fnColumnIndexToVisible( oSettings, iMatch )
- {
- var aiVis = _fnGetColumns( oSettings, 'bVisible' );
- var iPos = $.inArray( iMatch, aiVis );
-
- return iPos !== -1 ? iPos : null;
- }
-
-
- /**
- * Get the number of visible columns
- * @param {object} oSettings dataTables settings object
- * @returns {int} i the number of visible columns
- * @memberof DataTable#oApi
- */
- function _fnVisbleColumns( oSettings )
- {
- var vis = 0;
-
- // No reduce in IE8, use a loop for now
- $.each( oSettings.aoColumns, function ( i, col ) {
- if ( col.bVisible && $(col.nTh).css('display') !== 'none' ) {
- vis++;
- }
- } );
-
- return vis;
- }
-
-
- /**
- * Get an array of column indexes that match a given property
- * @param {object} oSettings dataTables settings object
- * @param {string} sParam Parameter in aoColumns to look for - typically
- * bVisible or bSearchable
- * @returns {array} Array of indexes with matched properties
- * @memberof DataTable#oApi
- */
- function _fnGetColumns( oSettings, sParam )
- {
- var a = [];
-
- $.map( oSettings.aoColumns, function(val, i) {
- if ( val[sParam] ) {
- a.push( i );
- }
- } );
-
- return a;
- }
-
-
- /**
- * Calculate the 'type' of a column
- * @param {object} settings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnColumnTypes ( settings )
- {
- var columns = settings.aoColumns;
- var data = settings.aoData;
- var types = DataTable.ext.type.detect;
- var i, ien, j, jen, k, ken;
- var col, cell, detectedType, cache;
-
- // For each column, spin over the
- for ( i=0, ien=columns.length ; i<ien ; i++ ) {
- col = columns[i];
- cache = [];
-
- if ( ! col.sType && col._sManualType ) {
- col.sType = col._sManualType;
- }
- else if ( ! col.sType ) {
- for ( j=0, jen=types.length ; j<jen ; j++ ) {
- for ( k=0, ken=data.length ; k<ken ; k++ ) {
- // Use a cache array so we only need to get the type data
- // from the formatter once (when using multiple detectors)
- if ( cache[k] === undefined ) {
- cache[k] = _fnGetCellData( settings, k, i, 'type' );
- }
-
- detectedType = types[j]( cache[k], settings );
-
- // If null, then this type can't apply to this column, so
- // rather than testing all cells, break out. There is an
- // exception for the last type which is `html`. We need to
- // scan all rows since it is possible to mix string and HTML
- // types
- if ( ! detectedType && j !== types.length-1 ) {
- break;
- }
-
- // Only a single match is needed for html type since it is
- // bottom of the pile and very similar to string
- if ( detectedType === 'html' ) {
- break;
- }
- }
-
- // Type is valid for all data points in the column - use this
- // type
- if ( detectedType ) {
- col.sType = detectedType;
- break;
- }
- }
-
- // Fall back - if no type was detected, always use string
- if ( ! col.sType ) {
- col.sType = 'string';
- }
- }
- }
- }
-
-
- /**
- * Take the column definitions and static columns arrays and calculate how
- * they relate to column indexes. The callback function will then apply the
- * definition found for a column to a suitable configuration object.
- * @param {object} oSettings dataTables settings object
- * @param {array} aoColDefs The aoColumnDefs array that is to be applied
- * @param {array} aoCols The aoColumns array that defines columns individually
- * @param {function} fn Callback function - takes two parameters, the calculated
- * column index and the definition for that column.
- * @memberof DataTable#oApi
- */
- function _fnApplyColumnDefs( oSettings, aoColDefs, aoCols, fn )
- {
- var i, iLen, j, jLen, k, kLen, def;
- var columns = oSettings.aoColumns;
-
- // Column definitions with aTargets
- if ( aoColDefs )
- {
- /* Loop over the definitions array - loop in reverse so first instance has priority */
- for ( i=aoColDefs.length-1 ; i>=0 ; i-- )
- {
- def = aoColDefs[i];
-
- /* Each definition can target multiple columns, as it is an array */
- var aTargets = def.targets !== undefined ?
- def.targets :
- def.aTargets;
-
- if ( ! $.isArray( aTargets ) )
- {
- aTargets = [ aTargets ];
- }
-
- for ( j=0, jLen=aTargets.length ; j<jLen ; j++ )
- {
- if ( typeof aTargets[j] === 'number' && aTargets[j] >= 0 )
- {
- /* Add columns that we don't yet know about */
- while( columns.length <= aTargets[j] )
- {
- _fnAddColumn( oSettings );
- }
-
- /* Integer, basic index */
- fn( aTargets[j], def );
- }
- else if ( typeof aTargets[j] === 'number' && aTargets[j] < 0 )
- {
- /* Negative integer, right to left column counting */
- fn( columns.length+aTargets[j], def );
- }
- else if ( typeof aTargets[j] === 'string' )
- {
- /* Class name matching on TH element */
- for ( k=0, kLen=columns.length ; k<kLen ; k++ )
- {
- if ( aTargets[j] == "_all" ||
- $(columns[k].nTh).hasClass( aTargets[j] ) )
- {
- fn( k, def );
- }
- }
- }
- }
- }
- }
-
- // Statically defined columns array
- if ( aoCols )
- {
- for ( i=0, iLen=aoCols.length ; i<iLen ; i++ )
- {
- fn( i, aoCols[i] );
- }
- }
- }
-
- /**
- * Add a data array to the table, creating DOM node etc. This is the parallel to
- * _fnGatherData, but for adding rows from a Javascript source, rather than a
- * DOM source.
- * @param {object} oSettings dataTables settings object
- * @param {array} aData data array to be added
- * @param {node} [nTr] TR element to add to the table - optional. If not given,
- * DataTables will create a row automatically
- * @param {array} [anTds] Array of TD|TH elements for the row - must be given
- * if nTr is.
- * @returns {int} >=0 if successful (index of new aoData entry), -1 if failed
- * @memberof DataTable#oApi
- */
- function _fnAddData ( oSettings, aDataIn, nTr, anTds )
- {
- /* Create the object for storing information about this new row */
- var iRow = oSettings.aoData.length;
- var oData = $.extend( true, {}, DataTable.models.oRow, {
- src: nTr ? 'dom' : 'data',
- idx: iRow
- } );
-
- oData._aData = aDataIn;
- oSettings.aoData.push( oData );
-
- /* Create the cells */
- var nTd, sThisType;
- var columns = oSettings.aoColumns;
-
- // Invalidate the column types as the new data needs to be revalidated
- for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
- {
- columns[i].sType = null;
- }
-
- /* Add to the display array */
- oSettings.aiDisplayMaster.push( iRow );
-
- var id = oSettings.rowIdFn( aDataIn );
- if ( id !== undefined ) {
- oSettings.aIds[ id ] = oData;
- }
-
- /* Create the DOM information, or register it if already present */
- if ( nTr || ! oSettings.oFeatures.bDeferRender )
- {
- _fnCreateTr( oSettings, iRow, nTr, anTds );
- }
-
- return iRow;
- }
-
-
- /**
- * Add one or more TR elements to the table. Generally we'd expect to
- * use this for reading data from a DOM sourced table, but it could be
- * used for an TR element. Note that if a TR is given, it is used (i.e.
- * it is not cloned).
- * @param {object} settings dataTables settings object
- * @param {array|node|jQuery} trs The TR element(s) to add to the table
- * @returns {array} Array of indexes for the added rows
- * @memberof DataTable#oApi
- */
- function _fnAddTr( settings, trs )
- {
- var row;
-
- // Allow an individual node to be passed in
- if ( ! (trs instanceof $) ) {
- trs = $(trs);
- }
-
- return trs.map( function (i, el) {
- row = _fnGetRowElements( settings, el );
- return _fnAddData( settings, row.data, el, row.cells );
- } );
- }
-
-
- /**
- * Take a TR element and convert it to an index in aoData
- * @param {object} oSettings dataTables settings object
- * @param {node} n the TR element to find
- * @returns {int} index if the node is found, null if not
- * @memberof DataTable#oApi
- */
- function _fnNodeToDataIndex( oSettings, n )
- {
- return (n._DT_RowIndex!==undefined) ? n._DT_RowIndex : null;
- }
-
-
- /**
- * Take a TD element and convert it into a column data index (not the visible index)
- * @param {object} oSettings dataTables settings object
- * @param {int} iRow The row number the TD/TH can be found in
- * @param {node} n The TD/TH element to find
- * @returns {int} index if the node is found, -1 if not
- * @memberof DataTable#oApi
- */
- function _fnNodeToColumnIndex( oSettings, iRow, n )
- {
- return $.inArray( n, oSettings.aoData[ iRow ].anCells );
- }
-
-
- /**
- * Get the data for a given cell from the internal cache, taking into account data mapping
- * @param {object} settings dataTables settings object
- * @param {int} rowIdx aoData row id
- * @param {int} colIdx Column index
- * @param {string} type data get type ('display', 'type' 'filter' 'sort')
- * @returns {*} Cell data
- * @memberof DataTable#oApi
- */
- function _fnGetCellData( settings, rowIdx, colIdx, type )
- {
- var draw = settings.iDraw;
- var col = settings.aoColumns[colIdx];
- var rowData = settings.aoData[rowIdx]._aData;
- var defaultContent = col.sDefaultContent;
- var cellData = col.fnGetData( rowData, type, {
- settings: settings,
- row: rowIdx,
- col: colIdx
- } );
-
- if ( cellData === undefined ) {
- if ( settings.iDrawError != draw && defaultContent === null ) {
- _fnLog( settings, 0, "Requested unknown parameter "+
- (typeof col.mData=='function' ? '{function}' : "'"+col.mData+"'")+
- " for row "+rowIdx+", column "+colIdx, 4 );
- settings.iDrawError = draw;
- }
- return defaultContent;
- }
-
- // When the data source is null and a specific data type is requested (i.e.
- // not the original data), we can use default column data
- if ( (cellData === rowData || cellData === null) && defaultContent !== null && type !== undefined ) {
- cellData = defaultContent;
- }
- else if ( typeof cellData === 'function' ) {
- // If the data source is a function, then we run it and use the return,
- // executing in the scope of the data object (for instances)
- return cellData.call( rowData );
- }
-
- if ( cellData === null && type == 'display' ) {
- return '';
- }
- return cellData;
- }
-
-
- /**
- * Set the value for a specific cell, into the internal data cache
- * @param {object} settings dataTables settings object
- * @param {int} rowIdx aoData row id
- * @param {int} colIdx Column index
- * @param {*} val Value to set
- * @memberof DataTable#oApi
- */
- function _fnSetCellData( settings, rowIdx, colIdx, val )
- {
- var col = settings.aoColumns[colIdx];
- var rowData = settings.aoData[rowIdx]._aData;
-
- col.fnSetData( rowData, val, {
- settings: settings,
- row: rowIdx,
- col: colIdx
- } );
- }
-
-
- // Private variable that is used to match action syntax in the data property object
- var __reArray = /\[.*?\]$/;
- var __reFn = /\(\)$/;
-
- /**
- * Split string on periods, taking into account escaped periods
- * @param {string} str String to split
- * @return {array} Split string
- */
- function _fnSplitObjNotation( str )
- {
- return $.map( str.match(/(\\.|[^\.])+/g) || [''], function ( s ) {
- return s.replace(/\\\./g, '.');
- } );
- }
-
-
- /**
- * Return a function that can be used to get data from a source object, taking
- * into account the ability to use nested objects as a source
- * @param {string|int|function} mSource The data source for the object
- * @returns {function} Data get function
- * @memberof DataTable#oApi
- */
- function _fnGetObjectDataFn( mSource )
- {
- if ( $.isPlainObject( mSource ) )
- {
- /* Build an object of get functions, and wrap them in a single call */
- var o = {};
- $.each( mSource, function (key, val) {
- if ( val ) {
- o[key] = _fnGetObjectDataFn( val );
- }
- } );
-
- return function (data, type, row, meta) {
- var t = o[type] || o._;
- return t !== undefined ?
- t(data, type, row, meta) :
- data;
- };
- }
- else if ( mSource === null )
- {
- /* Give an empty string for rendering / sorting etc */
- return function (data) { // type, row and meta also passed, but not used
- return data;
- };
- }
- else if ( typeof mSource === 'function' )
- {
- return function (data, type, row, meta) {
- return mSource( data, type, row, meta );
- };
- }
- else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
- mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
- {
- /* If there is a . in the source string then the data source is in a
- * nested object so we loop over the data for each level to get the next
- * level down. On each loop we test for undefined, and if found immediately
- * return. This allows entire objects to be missing and sDefaultContent to
- * be used if defined, rather than throwing an error
- */
- var fetchData = function (data, type, src) {
- var arrayNotation, funcNotation, out, innerSrc;
-
- if ( src !== "" )
- {
- var a = _fnSplitObjNotation( src );
-
- for ( var i=0, iLen=a.length ; i<iLen ; i++ )
- {
- // Check if we are dealing with special notation
- arrayNotation = a[i].match(__reArray);
- funcNotation = a[i].match(__reFn);
-
- if ( arrayNotation )
- {
- // Array notation
- a[i] = a[i].replace(__reArray, '');
-
- // Condition allows simply [] to be passed in
- if ( a[i] !== "" ) {
- data = data[ a[i] ];
- }
- out = [];
-
- // Get the remainder of the nested object to get
- a.splice( 0, i+1 );
- innerSrc = a.join('.');
-
- // Traverse each entry in the array getting the properties requested
- if ( $.isArray( data ) ) {
- for ( var j=0, jLen=data.length ; j<jLen ; j++ ) {
- out.push( fetchData( data[j], type, innerSrc ) );
- }
- }
-
- // If a string is given in between the array notation indicators, that
- // is used to join the strings together, otherwise an array is returned
- var join = arrayNotation[0].substring(1, arrayNotation[0].length-1);
- data = (join==="") ? out : out.join(join);
-
- // The inner call to fetchData has already traversed through the remainder
- // of the source requested, so we exit from the loop
- break;
- }
- else if ( funcNotation )
- {
- // Function call
- a[i] = a[i].replace(__reFn, '');
- data = data[ a[i] ]();
- continue;
- }
-
- if ( data === null || data[ a[i] ] === undefined )
- {
- return undefined;
- }
- data = data[ a[i] ];
- }
- }
-
- return data;
- };
-
- return function (data, type) { // row and meta also passed, but not used
- return fetchData( data, type, mSource );
- };
- }
- else
- {
- /* Array or flat object mapping */
- return function (data, type) { // row and meta also passed, but not used
- return data[mSource];
- };
- }
- }
-
-
- /**
- * Return a function that can be used to set data from a source object, taking
- * into account the ability to use nested objects as a source
- * @param {string|int|function} mSource The data source for the object
- * @returns {function} Data set function
- * @memberof DataTable#oApi
- */
- function _fnSetObjectDataFn( mSource )
- {
- if ( $.isPlainObject( mSource ) )
- {
- /* Unlike get, only the underscore (global) option is used for for
- * setting data since we don't know the type here. This is why an object
- * option is not documented for `mData` (which is read/write), but it is
- * for `mRender` which is read only.
- */
- return _fnSetObjectDataFn( mSource._ );
- }
- else if ( mSource === null )
- {
- /* Nothing to do when the data source is null */
- return function () {};
- }
- else if ( typeof mSource === 'function' )
- {
- return function (data, val, meta) {
- mSource( data, 'set', val, meta );
- };
- }
- else if ( typeof mSource === 'string' && (mSource.indexOf('.') !== -1 ||
- mSource.indexOf('[') !== -1 || mSource.indexOf('(') !== -1) )
- {
- /* Like the get, we need to get data from a nested object */
- var setData = function (data, val, src) {
- var a = _fnSplitObjNotation( src ), b;
- var aLast = a[a.length-1];
- var arrayNotation, funcNotation, o, innerSrc;
-
- for ( var i=0, iLen=a.length-1 ; i<iLen ; i++ )
- {
- // Check if we are dealing with an array notation request
- arrayNotation = a[i].match(__reArray);
- funcNotation = a[i].match(__reFn);
-
- if ( arrayNotation )
- {
- a[i] = a[i].replace(__reArray, '');
- data[ a[i] ] = [];
-
- // Get the remainder of the nested object to set so we can recurse
- b = a.slice();
- b.splice( 0, i+1 );
- innerSrc = b.join('.');
-
- // Traverse each entry in the array setting the properties requested
- if ( $.isArray( val ) )
- {
- for ( var j=0, jLen=val.length ; j<jLen ; j++ )
- {
- o = {};
- setData( o, val[j], innerSrc );
- data[ a[i] ].push( o );
- }
- }
- else
- {
- // We've been asked to save data to an array, but it
- // isn't array data to be saved. Best that can be done
- // is to just save the value.
- data[ a[i] ] = val;
- }
-
- // The inner call to setData has already traversed through the remainder
- // of the source and has set the data, thus we can exit here
- return;
- }
- else if ( funcNotation )
- {
- // Function call
- a[i] = a[i].replace(__reFn, '');
- data = data[ a[i] ]( val );
- }
-
- // If the nested object doesn't currently exist - since we are
- // trying to set the value - create it
- if ( data[ a[i] ] === null || data[ a[i] ] === undefined )
- {
- data[ a[i] ] = {};
- }
- data = data[ a[i] ];
- }
-
- // Last item in the input - i.e, the actual set
- if ( aLast.match(__reFn ) )
- {
- // Function call
- data = data[ aLast.replace(__reFn, '') ]( val );
- }
- else
- {
- // If array notation is used, we just want to strip it and use the property name
- // and assign the value. If it isn't used, then we get the result we want anyway
- data[ aLast.replace(__reArray, '') ] = val;
- }
- };
-
- return function (data, val) { // meta is also passed in, but not used
- return setData( data, val, mSource );
- };
- }
- else
- {
- /* Array or flat object mapping */
- return function (data, val) { // meta is also passed in, but not used
- data[mSource] = val;
- };
- }
- }
-
-
- /**
- * Return an array with the full table data
- * @param {object} oSettings dataTables settings object
- * @returns array {array} aData Master data array
- * @memberof DataTable#oApi
- */
- function _fnGetDataMaster ( settings )
- {
- return _pluck( settings.aoData, '_aData' );
- }
-
-
- /**
- * Nuke the table
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnClearTable( settings )
- {
- settings.aoData.length = 0;
- settings.aiDisplayMaster.length = 0;
- settings.aiDisplay.length = 0;
- settings.aIds = {};
- }
-
-
- /**
- * Take an array of integers (index array) and remove a target integer (value - not
- * the key!)
- * @param {array} a Index array to target
- * @param {int} iTarget value to find
- * @memberof DataTable#oApi
- */
- function _fnDeleteIndex( a, iTarget, splice )
- {
- var iTargetIndex = -1;
-
- for ( var i=0, iLen=a.length ; i<iLen ; i++ )
- {
- if ( a[i] == iTarget )
- {
- iTargetIndex = i;
- }
- else if ( a[i] > iTarget )
- {
- a[i]--;
- }
- }
-
- if ( iTargetIndex != -1 && splice === undefined )
- {
- a.splice( iTargetIndex, 1 );
- }
- }
-
-
- /**
- * Mark cached data as invalid such that a re-read of the data will occur when
- * the cached data is next requested. Also update from the data source object.
- *
- * @param {object} settings DataTables settings object
- * @param {int} rowIdx Row index to invalidate
- * @param {string} [src] Source to invalidate from: undefined, 'auto', 'dom'
- * or 'data'
- * @param {int} [colIdx] Column index to invalidate. If undefined the whole
- * row will be invalidated
- * @memberof DataTable#oApi
- *
- * @todo For the modularisation of v1.11 this will need to become a callback, so
- * the sort and filter methods can subscribe to it. That will required
- * initialisation options for sorting, which is why it is not already baked in
- */
- function _fnInvalidate( settings, rowIdx, src, colIdx )
- {
- var row = settings.aoData[ rowIdx ];
- var i, ien;
- var cellWrite = function ( cell, col ) {
- // This is very frustrating, but in IE if you just write directly
- // to innerHTML, and elements that are overwritten are GC'ed,
- // even if there is a reference to them elsewhere
- while ( cell.childNodes.length ) {
- cell.removeChild( cell.firstChild );
- }
-
- cell.innerHTML = _fnGetCellData( settings, rowIdx, col, 'display' );
- };
-
- // Are we reading last data from DOM or the data object?
- if ( src === 'dom' || ((! src || src === 'auto') && row.src === 'dom') ) {
- // Read the data from the DOM
- row._aData = _fnGetRowElements(
- settings, row, colIdx, colIdx === undefined ? undefined : row._aData
- )
- .data;
- }
- else {
- // Reading from data object, update the DOM
- var cells = row.anCells;
-
- if ( cells ) {
- if ( colIdx !== undefined ) {
- cellWrite( cells[colIdx], colIdx );
- }
- else {
- for ( i=0, ien=cells.length ; i<ien ; i++ ) {
- cellWrite( cells[i], i );
- }
- }
- }
- }
-
- // For both row and cell invalidation, the cached data for sorting and
- // filtering is nulled out
- row._aSortData = null;
- row._aFilterData = null;
-
- // Invalidate the type for a specific column (if given) or all columns since
- // the data might have changed
- var cols = settings.aoColumns;
- if ( colIdx !== undefined ) {
- cols[ colIdx ].sType = null;
- }
- else {
- for ( i=0, ien=cols.length ; i<ien ; i++ ) {
- cols[i].sType = null;
- }
-
- // Update DataTables special `DT_*` attributes for the row
- _fnRowAttributes( settings, row );
- }
- }
-
-
- /**
- * Build a data source object from an HTML row, reading the contents of the
- * cells that are in the row.
- *
- * @param {object} settings DataTables settings object
- * @param {node|object} TR element from which to read data or existing row
- * object from which to re-read the data from the cells
- * @param {int} [colIdx] Optional column index
- * @param {array|object} [d] Data source object. If `colIdx` is given then this
- * parameter should also be given and will be used to write the data into.
- * Only the column in question will be written
- * @returns {object} Object with two parameters: `data` the data read, in
- * document order, and `cells` and array of nodes (they can be useful to the
- * caller, so rather than needing a second traversal to get them, just return
- * them from here).
- * @memberof DataTable#oApi
- */
- function _fnGetRowElements( settings, row, colIdx, d )
- {
- var
- tds = [],
- td = row.firstChild,
- name, col, o, i=0, contents,
- columns = settings.aoColumns,
- objectRead = settings._rowReadObject;
-
- // Allow the data object to be passed in, or construct
- d = d !== undefined ?
- d :
- objectRead ?
- {} :
- [];
-
- var attr = function ( str, td ) {
- if ( typeof str === 'string' ) {
- var idx = str.indexOf('@');
-
- if ( idx !== -1 ) {
- var attr = str.substring( idx+1 );
- var setter = _fnSetObjectDataFn( str );
- setter( d, td.getAttribute( attr ) );
- }
- }
- };
-
- // Read data from a cell and store into the data object
- var cellProcess = function ( cell ) {
- if ( colIdx === undefined || colIdx === i ) {
- col = columns[i];
- contents = $.trim(cell.innerHTML);
-
- if ( col && col._bAttrSrc ) {
- var setter = _fnSetObjectDataFn( col.mData._ );
- setter( d, contents );
-
- attr( col.mData.sort, cell );
- attr( col.mData.type, cell );
- attr( col.mData.filter, cell );
- }
- else {
- // Depending on the `data` option for the columns the data can
- // be read to either an object or an array.
- if ( objectRead ) {
- if ( ! col._setter ) {
- // Cache the setter function
- col._setter = _fnSetObjectDataFn( col.mData );
- }
- col._setter( d, contents );
- }
- else {
- d[i] = contents;
- }
- }
- }
-
- i++;
- };
-
- if ( td ) {
- // `tr` element was passed in
- while ( td ) {
- name = td.nodeName.toUpperCase();
-
- if ( name == "TD" || name == "TH" ) {
- cellProcess( td );
- tds.push( td );
- }
-
- td = td.nextSibling;
- }
- }
- else {
- // Existing row object passed in
- tds = row.anCells;
-
- for ( var j=0, jen=tds.length ; j<jen ; j++ ) {
- cellProcess( tds[j] );
- }
- }
-
- // Read the ID from the DOM if present
- var rowNode = row.firstChild ? row : row.nTr;
-
- if ( rowNode ) {
- var id = rowNode.getAttribute( 'id' );
-
- if ( id ) {
- _fnSetObjectDataFn( settings.rowId )( d, id );
- }
- }
-
- return {
- data: d,
- cells: tds
- };
- }
- /**
- * Create a new TR element (and it's TD children) for a row
- * @param {object} oSettings dataTables settings object
- * @param {int} iRow Row to consider
- * @param {node} [nTrIn] TR element to add to the table - optional. If not given,
- * DataTables will create a row automatically
- * @param {array} [anTds] Array of TD|TH elements for the row - must be given
- * if nTr is.
- * @memberof DataTable#oApi
- */
- function _fnCreateTr ( oSettings, iRow, nTrIn, anTds )
- {
- var
- row = oSettings.aoData[iRow],
- rowData = row._aData,
- cells = [],
- nTr, nTd, oCol,
- i, iLen;
-
- if ( row.nTr === null )
- {
- nTr = nTrIn || document.createElement('tr');
-
- row.nTr = nTr;
- row.anCells = cells;
-
- /* Use a private property on the node to allow reserve mapping from the node
- * to the aoData array for fast look up
- */
- nTr._DT_RowIndex = iRow;
-
- /* Special parameters can be given by the data source to be used on the row */
- _fnRowAttributes( oSettings, row );
-
- /* Process each column */
- for ( i=0, iLen=oSettings.aoColumns.length ; i<iLen ; i++ )
- {
- oCol = oSettings.aoColumns[i];
-
- nTd = nTrIn ? anTds[i] : document.createElement( oCol.sCellType );
- nTd._DT_CellIndex = {
- row: iRow,
- column: i
- };
-
- cells.push( nTd );
-
- // Need to create the HTML if new, or if a rendering function is defined
- if ( (!nTrIn || oCol.mRender || oCol.mData !== i) &&
- (!$.isPlainObject(oCol.mData) || oCol.mData._ !== i+'.display')
- ) {
- nTd.innerHTML = _fnGetCellData( oSettings, iRow, i, 'display' );
- }
-
- /* Add user defined class */
- if ( oCol.sClass )
- {
- nTd.className += ' '+oCol.sClass;
- }
-
- // Visibility - add or remove as required
- if ( oCol.bVisible && ! nTrIn )
- {
- nTr.appendChild( nTd );
- }
- else if ( ! oCol.bVisible && nTrIn )
- {
- nTd.parentNode.removeChild( nTd );
- }
-
- if ( oCol.fnCreatedCell )
- {
- oCol.fnCreatedCell.call( oSettings.oInstance,
- nTd, _fnGetCellData( oSettings, iRow, i ), rowData, iRow, i
- );
- }
- }
-
- _fnCallbackFire( oSettings, 'aoRowCreatedCallback', null, [nTr, rowData, iRow, cells] );
- }
-
- // Remove once webkit bug 131819 and Chromium bug 365619 have been resolved
- // and deployed
- row.nTr.setAttribute( 'role', 'row' );
- }
-
-
- /**
- * Add attributes to a row based on the special `DT_*` parameters in a data
- * source object.
- * @param {object} settings DataTables settings object
- * @param {object} DataTables row object for the row to be modified
- * @memberof DataTable#oApi
- */
- function _fnRowAttributes( settings, row )
- {
- var tr = row.nTr;
- var data = row._aData;
-
- if ( tr ) {
- var id = settings.rowIdFn( data );
-
- if ( id ) {
- tr.id = id;
- }
-
- if ( data.DT_RowClass ) {
- // Remove any classes added by DT_RowClass before
- var a = data.DT_RowClass.split(' ');
- row.__rowc = row.__rowc ?
- _unique( row.__rowc.concat( a ) ) :
- a;
-
- $(tr)
- .removeClass( row.__rowc.join(' ') )
- .addClass( data.DT_RowClass );
- }
-
- if ( data.DT_RowAttr ) {
- $(tr).attr( data.DT_RowAttr );
- }
-
- if ( data.DT_RowData ) {
- $(tr).data( data.DT_RowData );
- }
- }
- }
-
-
- /**
- * Create the HTML header for the table
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnBuildHead( oSettings )
- {
- var i, ien, cell, row, column;
- var thead = oSettings.nTHead;
- var tfoot = oSettings.nTFoot;
- var createHeader = $('th, td', thead).length === 0;
- var classes = oSettings.oClasses;
- var columns = oSettings.aoColumns;
-
- if ( createHeader ) {
- row = $('<tr/>').appendTo( thead );
- }
-
- for ( i=0, ien=columns.length ; i<ien ; i++ ) {
- column = columns[i];
- cell = $( column.nTh ).addClass( column.sClass );
-
- if ( createHeader ) {
- cell.appendTo( row );
- }
-
- // 1.11 move into sorting
- if ( oSettings.oFeatures.bSort ) {
- cell.addClass( column.sSortingClass );
-
- if ( column.bSortable !== false ) {
- cell
- .attr( 'tabindex', oSettings.iTabIndex )
- .attr( 'aria-controls', oSettings.sTableId );
-
- _fnSortAttachListener( oSettings, column.nTh, i );
- }
- }
-
- if ( column.sTitle != cell[0].innerHTML ) {
- cell.html( column.sTitle );
- }
-
- _fnRenderer( oSettings, 'header' )(
- oSettings, cell, column, classes
- );
- }
-
- if ( createHeader ) {
- _fnDetectHeader( oSettings.aoHeader, thead );
- }
-
- /* ARIA role for the rows */
- $(thead).find('>tr').attr('role', 'row');
-
- /* Deal with the footer - add classes if required */
- $(thead).find('>tr>th, >tr>td').addClass( classes.sHeaderTH );
- $(tfoot).find('>tr>th, >tr>td').addClass( classes.sFooterTH );
-
- // Cache the footer cells. Note that we only take the cells from the first
- // row in the footer. If there is more than one row the user wants to
- // interact with, they need to use the table().foot() method. Note also this
- // allows cells to be used for multiple columns using colspan
- if ( tfoot !== null ) {
- var cells = oSettings.aoFooter[0];
-
- for ( i=0, ien=cells.length ; i<ien ; i++ ) {
- column = columns[i];
- column.nTf = cells[i].cell;
-
- if ( column.sClass ) {
- $(column.nTf).addClass( column.sClass );
- }
- }
- }
- }
-
-
- /**
- * Draw the header (or footer) element based on the column visibility states. The
- * methodology here is to use the layout array from _fnDetectHeader, modified for
- * the instantaneous column visibility, to construct the new layout. The grid is
- * traversed over cell at a time in a rows x columns grid fashion, although each
- * cell insert can cover multiple elements in the grid - which is tracks using the
- * aApplied array. Cell inserts in the grid will only occur where there isn't
- * already a cell in that position.
- * @param {object} oSettings dataTables settings object
- * @param array {objects} aoSource Layout array from _fnDetectHeader
- * @param {boolean} [bIncludeHidden=false] If true then include the hidden columns in the calc,
- * @memberof DataTable#oApi
- */
- function _fnDrawHead( oSettings, aoSource, bIncludeHidden )
- {
- var i, iLen, j, jLen, k, kLen, n, nLocalTr;
- var aoLocal = [];
- var aApplied = [];
- var iColumns = oSettings.aoColumns.length;
- var iRowspan, iColspan;
-
- if ( ! aoSource )
- {
- return;
- }
-
- if ( bIncludeHidden === undefined )
- {
- bIncludeHidden = false;
- }
-
- /* Make a copy of the master layout array, but without the visible columns in it */
- for ( i=0, iLen=aoSource.length ; i<iLen ; i++ )
- {
- aoLocal[i] = aoSource[i].slice();
- aoLocal[i].nTr = aoSource[i].nTr;
-
- /* Remove any columns which are currently hidden */
- for ( j=iColumns-1 ; j>=0 ; j-- )
- {
- if ( !oSettings.aoColumns[j].bVisible && !bIncludeHidden )
- {
- aoLocal[i].splice( j, 1 );
- }
- }
-
- /* Prep the applied array - it needs an element for each row */
- aApplied.push( [] );
- }
-
- for ( i=0, iLen=aoLocal.length ; i<iLen ; i++ )
- {
- nLocalTr = aoLocal[i].nTr;
-
- /* All cells are going to be replaced, so empty out the row */
- if ( nLocalTr )
- {
- while( (n = nLocalTr.firstChild) )
- {
- nLocalTr.removeChild( n );
- }
- }
-
- for ( j=0, jLen=aoLocal[i].length ; j<jLen ; j++ )
- {
- iRowspan = 1;
- iColspan = 1;
-
- /* Check to see if there is already a cell (row/colspan) covering our target
- * insert point. If there is, then there is nothing to do.
- */
- if ( aApplied[i][j] === undefined )
- {
- nLocalTr.appendChild( aoLocal[i][j].cell );
- aApplied[i][j] = 1;
-
- /* Expand the cell to cover as many rows as needed */
- while ( aoLocal[i+iRowspan] !== undefined &&
- aoLocal[i][j].cell == aoLocal[i+iRowspan][j].cell )
- {
- aApplied[i+iRowspan][j] = 1;
- iRowspan++;
- }
-
- /* Expand the cell to cover as many columns as needed */
- while ( aoLocal[i][j+iColspan] !== undefined &&
- aoLocal[i][j].cell == aoLocal[i][j+iColspan].cell )
- {
- /* Must update the applied array over the rows for the columns */
- for ( k=0 ; k<iRowspan ; k++ )
- {
- aApplied[i+k][j+iColspan] = 1;
- }
- iColspan++;
- }
-
- /* Do the actual expansion in the DOM */
- $(aoLocal[i][j].cell)
- .attr('rowspan', iRowspan)
- .attr('colspan', iColspan);
- }
- }
- }
- }
-
-
- /**
- * Insert the required TR nodes into the table for display
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnDraw( oSettings )
- {
- /* Provide a pre-callback function which can be used to cancel the draw is false is returned */
- var aPreDraw = _fnCallbackFire( oSettings, 'aoPreDrawCallback', 'preDraw', [oSettings] );
- if ( $.inArray( false, aPreDraw ) !== -1 )
- {
- _fnProcessingDisplay( oSettings, false );
- return;
- }
-
- var i, iLen, n;
- var anRows = [];
- var iRowCount = 0;
- var asStripeClasses = oSettings.asStripeClasses;
- var iStripes = asStripeClasses.length;
- var iOpenRows = oSettings.aoOpenRows.length;
- var oLang = oSettings.oLanguage;
- var iInitDisplayStart = oSettings.iInitDisplayStart;
- var bServerSide = _fnDataSource( oSettings ) == 'ssp';
- var aiDisplay = oSettings.aiDisplay;
-
- oSettings.bDrawing = true;
-
- /* Check and see if we have an initial draw position from state saving */
- if ( iInitDisplayStart !== undefined && iInitDisplayStart !== -1 )
- {
- oSettings._iDisplayStart = bServerSide ?
- iInitDisplayStart :
- iInitDisplayStart >= oSettings.fnRecordsDisplay() ?
- 0 :
- iInitDisplayStart;
-
- oSettings.iInitDisplayStart = -1;
- }
-
- var iDisplayStart = oSettings._iDisplayStart;
- var iDisplayEnd = oSettings.fnDisplayEnd();
-
- /* Server-side processing draw intercept */
- if ( oSettings.bDeferLoading )
- {
- oSettings.bDeferLoading = false;
- oSettings.iDraw++;
- _fnProcessingDisplay( oSettings, false );
- }
- else if ( !bServerSide )
- {
- oSettings.iDraw++;
- }
- else if ( !oSettings.bDestroying && !_fnAjaxUpdate( oSettings ) )
- {
- return;
- }
-
- if ( aiDisplay.length !== 0 )
- {
- var iStart = bServerSide ? 0 : iDisplayStart;
- var iEnd = bServerSide ? oSettings.aoData.length : iDisplayEnd;
-
- for ( var j=iStart ; j<iEnd ; j++ )
- {
- var iDataIndex = aiDisplay[j];
- var aoData = oSettings.aoData[ iDataIndex ];
- if ( aoData.nTr === null )
- {
- _fnCreateTr( oSettings, iDataIndex );
- }
-
- var nRow = aoData.nTr;
-
- /* Remove the old striping classes and then add the new one */
- if ( iStripes !== 0 )
- {
- var sStripe = asStripeClasses[ iRowCount % iStripes ];
- if ( aoData._sRowStripe != sStripe )
- {
- $(nRow).removeClass( aoData._sRowStripe ).addClass( sStripe );
- aoData._sRowStripe = sStripe;
- }
- }
-
- // Row callback functions - might want to manipulate the row
- // iRowCount and j are not currently documented. Are they at all
- // useful?
- _fnCallbackFire( oSettings, 'aoRowCallback', null,
- [nRow, aoData._aData, iRowCount, j, iDataIndex] );
-
- anRows.push( nRow );
- iRowCount++;
- }
- }
- else
- {
- /* Table is empty - create a row with an empty message in it */
- var sZero = oLang.sZeroRecords;
- if ( oSettings.iDraw == 1 && _fnDataSource( oSettings ) == 'ajax' )
- {
- sZero = oLang.sLoadingRecords;
- }
- else if ( oLang.sEmptyTable && oSettings.fnRecordsTotal() === 0 )
- {
- sZero = oLang.sEmptyTable;
- }
-
- anRows[ 0 ] = $( '<tr/>', { 'class': iStripes ? asStripeClasses[0] : '' } )
- .append( $('<td />', {
- 'valign': 'top',
- 'colSpan': _fnVisbleColumns( oSettings ),
- 'class': oSettings.oClasses.sRowEmpty
- } ).html( sZero ) )[0];
- }
-
- /* Header and footer callbacks */
- _fnCallbackFire( oSettings, 'aoHeaderCallback', 'header', [ $(oSettings.nTHead).children('tr')[0],
- _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
-
- _fnCallbackFire( oSettings, 'aoFooterCallback', 'footer', [ $(oSettings.nTFoot).children('tr')[0],
- _fnGetDataMaster( oSettings ), iDisplayStart, iDisplayEnd, aiDisplay ] );
-
- var body = $(oSettings.nTBody);
-
- body.children().detach();
- body.append( $(anRows) );
-
- /* Call all required callback functions for the end of a draw */
- _fnCallbackFire( oSettings, 'aoDrawCallback', 'draw', [oSettings] );
-
- /* Draw is complete, sorting and filtering must be as well */
- oSettings.bSorted = false;
- oSettings.bFiltered = false;
- oSettings.bDrawing = false;
- }
-
-
- /**
- * Redraw the table - taking account of the various features which are enabled
- * @param {object} oSettings dataTables settings object
- * @param {boolean} [holdPosition] Keep the current paging position. By default
- * the paging is reset to the first page
- * @memberof DataTable#oApi
- */
- function _fnReDraw( settings, holdPosition )
- {
- var
- features = settings.oFeatures,
- sort = features.bSort,
- filter = features.bFilter;
-
- if ( sort ) {
- _fnSort( settings );
- }
-
- if ( filter ) {
- _fnFilterComplete( settings, settings.oPreviousSearch );
- }
- else {
- // No filtering, so we want to just use the display master
- settings.aiDisplay = settings.aiDisplayMaster.slice();
- }
-
- if ( holdPosition !== true ) {
- settings._iDisplayStart = 0;
- }
-
- // Let any modules know about the draw hold position state (used by
- // scrolling internally)
- settings._drawHold = holdPosition;
-
- _fnDraw( settings );
-
- settings._drawHold = false;
- }
-
-
- /**
- * Add the options to the page HTML for the table
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnAddOptionsHtml ( oSettings )
- {
- var classes = oSettings.oClasses;
- var table = $(oSettings.nTable);
- var holding = $('<div/>').insertBefore( table ); // Holding element for speed
- var features = oSettings.oFeatures;
-
- // All DataTables are wrapped in a div
- var insert = $('<div/>', {
- id: oSettings.sTableId+'_wrapper',
- 'class': classes.sWrapper + (oSettings.nTFoot ? '' : ' '+classes.sNoFooter)
- } );
-
- oSettings.nHolding = holding[0];
- oSettings.nTableWrapper = insert[0];
- oSettings.nTableReinsertBefore = oSettings.nTable.nextSibling;
-
- /* Loop over the user set positioning and place the elements as needed */
- var aDom = oSettings.sDom.split('');
- var featureNode, cOption, nNewNode, cNext, sAttr, j;
- for ( var i=0 ; i<aDom.length ; i++ )
- {
- featureNode = null;
- cOption = aDom[i];
-
- if ( cOption == '<' )
- {
- /* New container div */
- nNewNode = $('<div/>')[0];
-
- /* Check to see if we should append an id and/or a class name to the container */
- cNext = aDom[i+1];
- if ( cNext == "'" || cNext == '"' )
- {
- sAttr = "";
- j = 2;
- while ( aDom[i+j] != cNext )
- {
- sAttr += aDom[i+j];
- j++;
- }
-
- /* Replace jQuery UI constants @todo depreciated */
- if ( sAttr == "H" )
- {
- sAttr = classes.sJUIHeader;
- }
- else if ( sAttr == "F" )
- {
- sAttr = classes.sJUIFooter;
- }
-
- /* The attribute can be in the format of "#id.class", "#id" or "class" This logic
- * breaks the string into parts and applies them as needed
- */
- if ( sAttr.indexOf('.') != -1 )
- {
- var aSplit = sAttr.split('.');
- nNewNode.id = aSplit[0].substr(1, aSplit[0].length-1);
- nNewNode.className = aSplit[1];
- }
- else if ( sAttr.charAt(0) == "#" )
- {
- nNewNode.id = sAttr.substr(1, sAttr.length-1);
- }
- else
- {
- nNewNode.className = sAttr;
- }
-
- i += j; /* Move along the position array */
- }
-
- insert.append( nNewNode );
- insert = $(nNewNode);
- }
- else if ( cOption == '>' )
- {
- /* End container div */
- insert = insert.parent();
- }
- // @todo Move options into their own plugins?
- else if ( cOption == 'l' && features.bPaginate && features.bLengthChange )
- {
- /* Length */
- featureNode = _fnFeatureHtmlLength( oSettings );
- }
- else if ( cOption == 'f' && features.bFilter )
- {
- /* Filter */
- featureNode = _fnFeatureHtmlFilter( oSettings );
- }
- else if ( cOption == 'r' && features.bProcessing )
- {
- /* pRocessing */
- featureNode = _fnFeatureHtmlProcessing( oSettings );
- }
- else if ( cOption == 't' )
- {
- /* Table */
- featureNode = _fnFeatureHtmlTable( oSettings );
- }
- else if ( cOption == 'i' && features.bInfo )
- {
- /* Info */
- featureNode = _fnFeatureHtmlInfo( oSettings );
- }
- else if ( cOption == 'p' && features.bPaginate )
- {
- /* Pagination */
- featureNode = _fnFeatureHtmlPaginate( oSettings );
- }
- else if ( DataTable.ext.feature.length !== 0 )
- {
- /* Plug-in features */
- var aoFeatures = DataTable.ext.feature;
- for ( var k=0, kLen=aoFeatures.length ; k<kLen ; k++ )
- {
- if ( cOption == aoFeatures[k].cFeature )
- {
- featureNode = aoFeatures[k].fnInit( oSettings );
- break;
- }
- }
- }
-
- /* Add to the 2D features array */
- if ( featureNode )
- {
- var aanFeatures = oSettings.aanFeatures;
-
- if ( ! aanFeatures[cOption] )
- {
- aanFeatures[cOption] = [];
- }
-
- aanFeatures[cOption].push( featureNode );
- insert.append( featureNode );
- }
- }
-
- /* Built our DOM structure - replace the holding div with what we want */
- holding.replaceWith( insert );
- oSettings.nHolding = null;
- }
-
-
- /**
- * Use the DOM source to create up an array of header cells. The idea here is to
- * create a layout grid (array) of rows x columns, which contains a reference
- * to the cell that that point in the grid (regardless of col/rowspan), such that
- * any column / row could be removed and the new grid constructed
- * @param array {object} aLayout Array to store the calculated layout in
- * @param {node} nThead The header/footer element for the table
- * @memberof DataTable#oApi
- */
- function _fnDetectHeader ( aLayout, nThead )
- {
- var nTrs = $(nThead).children('tr');
- var nTr, nCell;
- var i, k, l, iLen, jLen, iColShifted, iColumn, iColspan, iRowspan;
- var bUnique;
- var fnShiftCol = function ( a, i, j ) {
- var k = a[i];
- while ( k[j] ) {
- j++;
- }
- return j;
- };
-
- aLayout.splice( 0, aLayout.length );
-
- /* We know how many rows there are in the layout - so prep it */
- for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
- {
- aLayout.push( [] );
- }
-
- /* Calculate a layout array */
- for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
- {
- nTr = nTrs[i];
- iColumn = 0;
-
- /* For every cell in the row... */
- nCell = nTr.firstChild;
- while ( nCell ) {
- if ( nCell.nodeName.toUpperCase() == "TD" ||
- nCell.nodeName.toUpperCase() == "TH" )
- {
- /* Get the col and rowspan attributes from the DOM and sanitise them */
- iColspan = nCell.getAttribute('colspan') * 1;
- iRowspan = nCell.getAttribute('rowspan') * 1;
- iColspan = (!iColspan || iColspan===0 || iColspan===1) ? 1 : iColspan;
- iRowspan = (!iRowspan || iRowspan===0 || iRowspan===1) ? 1 : iRowspan;
-
- /* There might be colspan cells already in this row, so shift our target
- * accordingly
- */
- iColShifted = fnShiftCol( aLayout, i, iColumn );
-
- /* Cache calculation for unique columns */
- bUnique = iColspan === 1 ? true : false;
-
- /* If there is col / rowspan, copy the information into the layout grid */
- for ( l=0 ; l<iColspan ; l++ )
- {
- for ( k=0 ; k<iRowspan ; k++ )
- {
- aLayout[i+k][iColShifted+l] = {
- "cell": nCell,
- "unique": bUnique
- };
- aLayout[i+k].nTr = nTr;
- }
- }
- }
- nCell = nCell.nextSibling;
- }
- }
- }
-
-
- /**
- * Get an array of unique th elements, one for each column
- * @param {object} oSettings dataTables settings object
- * @param {node} nHeader automatically detect the layout from this node - optional
- * @param {array} aLayout thead/tfoot layout from _fnDetectHeader - optional
- * @returns array {node} aReturn list of unique th's
- * @memberof DataTable#oApi
- */
- function _fnGetUniqueThs ( oSettings, nHeader, aLayout )
- {
- var aReturn = [];
- if ( !aLayout )
- {
- aLayout = oSettings.aoHeader;
- if ( nHeader )
- {
- aLayout = [];
- _fnDetectHeader( aLayout, nHeader );
- }
- }
-
- for ( var i=0, iLen=aLayout.length ; i<iLen ; i++ )
- {
- for ( var j=0, jLen=aLayout[i].length ; j<jLen ; j++ )
- {
- if ( aLayout[i][j].unique &&
- (!aReturn[j] || !oSettings.bSortCellsTop) )
- {
- aReturn[j] = aLayout[i][j].cell;
- }
- }
- }
-
- return aReturn;
- }
-
- /**
- * Create an Ajax call based on the table's settings, taking into account that
- * parameters can have multiple forms, and backwards compatibility.
- *
- * @param {object} oSettings dataTables settings object
- * @param {array} data Data to send to the server, required by
- * DataTables - may be augmented by developer callbacks
- * @param {function} fn Callback function to run when data is obtained
- */
- function _fnBuildAjax( oSettings, data, fn )
- {
- // Compatibility with 1.9-, allow fnServerData and event to manipulate
- _fnCallbackFire( oSettings, 'aoServerParams', 'serverParams', [data] );
-
- // Convert to object based for 1.10+ if using the old array scheme which can
- // come from server-side processing or serverParams
- if ( data && $.isArray(data) ) {
- var tmp = {};
- var rbracket = /(.*?)\[\]$/;
-
- $.each( data, function (key, val) {
- var match = val.name.match(rbracket);
-
- if ( match ) {
- // Support for arrays
- var name = match[0];
-
- if ( ! tmp[ name ] ) {
- tmp[ name ] = [];
- }
- tmp[ name ].push( val.value );
- }
- else {
- tmp[val.name] = val.value;
- }
- } );
- data = tmp;
- }
-
- var ajaxData;
- var ajax = oSettings.ajax;
- var instance = oSettings.oInstance;
- var callback = function ( json ) {
- _fnCallbackFire( oSettings, null, 'xhr', [oSettings, json, oSettings.jqXHR] );
- fn( json );
- };
-
- if ( $.isPlainObject( ajax ) && ajax.data )
- {
- ajaxData = ajax.data;
-
- var newData = typeof ajaxData === 'function' ?
- ajaxData( data, oSettings ) : // fn can manipulate data or return
- ajaxData; // an object object or array to merge
-
- // If the function returned something, use that alone
- data = typeof ajaxData === 'function' && newData ?
- newData :
- $.extend( true, data, newData );
-
- // Remove the data property as we've resolved it already and don't want
- // jQuery to do it again (it is restored at the end of the function)
- delete ajax.data;
- }
-
- var baseAjax = {
- "data": data,
- "success": function (json) {
- var error = json.error || json.sError;
- if ( error ) {
- _fnLog( oSettings, 0, error );
- }
-
- oSettings.json = json;
- callback( json );
- },
- "dataType": "json",
- "cache": false,
- "type": oSettings.sServerMethod,
- "error": function (xhr, error, thrown) {
- var ret = _fnCallbackFire( oSettings, null, 'xhr', [oSettings, null, oSettings.jqXHR] );
-
- if ( $.inArray( true, ret ) === -1 ) {
- if ( error == "parsererror" ) {
- _fnLog( oSettings, 0, 'Invalid JSON response', 1 );
- }
- else if ( xhr.readyState === 4 ) {
- _fnLog( oSettings, 0, 'Ajax error', 7 );
- }
- }
-
- _fnProcessingDisplay( oSettings, false );
- }
- };
-
- // Store the data submitted for the API
- oSettings.oAjaxData = data;
-
- // Allow plug-ins and external processes to modify the data
- _fnCallbackFire( oSettings, null, 'preXhr', [oSettings, data] );
-
- if ( oSettings.fnServerData )
- {
- // DataTables 1.9- compatibility
- oSettings.fnServerData.call( instance,
- oSettings.sAjaxSource,
- $.map( data, function (val, key) { // Need to convert back to 1.9 trad format
- return { name: key, value: val };
- } ),
- callback,
- oSettings
- );
- }
- else if ( oSettings.sAjaxSource || typeof ajax === 'string' )
- {
- // DataTables 1.9- compatibility
- oSettings.jqXHR = $.ajax( $.extend( baseAjax, {
- url: ajax || oSettings.sAjaxSource
- } ) );
- }
- else if ( typeof ajax === 'function' )
- {
- // Is a function - let the caller define what needs to be done
- oSettings.jqXHR = ajax.call( instance, data, callback, oSettings );
- }
- else
- {
- // Object to extend the base settings
- oSettings.jqXHR = $.ajax( $.extend( baseAjax, ajax ) );
-
- // Restore for next time around
- ajax.data = ajaxData;
- }
- }
-
-
- /**
- * Update the table using an Ajax call
- * @param {object} settings dataTables settings object
- * @returns {boolean} Block the table drawing or not
- * @memberof DataTable#oApi
- */
- function _fnAjaxUpdate( settings )
- {
- if ( settings.bAjaxDataGet ) {
- settings.iDraw++;
- _fnProcessingDisplay( settings, true );
-
- _fnBuildAjax(
- settings,
- _fnAjaxParameters( settings ),
- function(json) {
- _fnAjaxUpdateDraw( settings, json );
- }
- );
-
- return false;
- }
- return true;
- }
-
-
- /**
- * Build up the parameters in an object needed for a server-side processing
- * request. Note that this is basically done twice, is different ways - a modern
- * method which is used by default in DataTables 1.10 which uses objects and
- * arrays, or the 1.9- method with is name / value pairs. 1.9 method is used if
- * the sAjaxSource option is used in the initialisation, or the legacyAjax
- * option is set.
- * @param {object} oSettings dataTables settings object
- * @returns {bool} block the table drawing or not
- * @memberof DataTable#oApi
- */
- function _fnAjaxParameters( settings )
- {
- var
- columns = settings.aoColumns,
- columnCount = columns.length,
- features = settings.oFeatures,
- preSearch = settings.oPreviousSearch,
- preColSearch = settings.aoPreSearchCols,
- i, data = [], dataProp, column, columnSearch,
- sort = _fnSortFlatten( settings ),
- displayStart = settings._iDisplayStart,
- displayLength = features.bPaginate !== false ?
- settings._iDisplayLength :
- -1;
-
- var param = function ( name, value ) {
- data.push( { 'name': name, 'value': value } );
- };
-
- // DataTables 1.9- compatible method
- param( 'sEcho', settings.iDraw );
- param( 'iColumns', columnCount );
- param( 'sColumns', _pluck( columns, 'sName' ).join(',') );
- param( 'iDisplayStart', displayStart );
- param( 'iDisplayLength', displayLength );
-
- // DataTables 1.10+ method
- var d = {
- draw: settings.iDraw,
- columns: [],
- order: [],
- start: displayStart,
- length: displayLength,
- search: {
- value: preSearch.sSearch,
- regex: preSearch.bRegex
- }
- };
-
- for ( i=0 ; i<columnCount ; i++ ) {
- column = columns[i];
- columnSearch = preColSearch[i];
- dataProp = typeof column.mData=="function" ? 'function' : column.mData ;
-
- d.columns.push( {
- data: dataProp,
- name: column.sName,
- searchable: column.bSearchable,
- orderable: column.bSortable,
- search: {
- value: columnSearch.sSearch,
- regex: columnSearch.bRegex
- }
- } );
-
- param( "mDataProp_"+i, dataProp );
-
- if ( features.bFilter ) {
- param( 'sSearch_'+i, columnSearch.sSearch );
- param( 'bRegex_'+i, columnSearch.bRegex );
- param( 'bSearchable_'+i, column.bSearchable );
- }
-
- if ( features.bSort ) {
- param( 'bSortable_'+i, column.bSortable );
- }
- }
-
- if ( features.bFilter ) {
- param( 'sSearch', preSearch.sSearch );
- param( 'bRegex', preSearch.bRegex );
- }
-
- if ( features.bSort ) {
- $.each( sort, function ( i, val ) {
- d.order.push( { column: val.col, dir: val.dir } );
-
- param( 'iSortCol_'+i, val.col );
- param( 'sSortDir_'+i, val.dir );
- } );
-
- param( 'iSortingCols', sort.length );
- }
-
- // If the legacy.ajax parameter is null, then we automatically decide which
- // form to use, based on sAjaxSource
- var legacy = DataTable.ext.legacy.ajax;
- if ( legacy === null ) {
- return settings.sAjaxSource ? data : d;
- }
-
- // Otherwise, if legacy has been specified then we use that to decide on the
- // form
- return legacy ? data : d;
- }
-
-
- /**
- * Data the data from the server (nuking the old) and redraw the table
- * @param {object} oSettings dataTables settings object
- * @param {object} json json data return from the server.
- * @param {string} json.sEcho Tracking flag for DataTables to match requests
- * @param {int} json.iTotalRecords Number of records in the data set, not accounting for filtering
- * @param {int} json.iTotalDisplayRecords Number of records in the data set, accounting for filtering
- * @param {array} json.aaData The data to display on this page
- * @param {string} [json.sColumns] Column ordering (sName, comma separated)
- * @memberof DataTable#oApi
- */
- function _fnAjaxUpdateDraw ( settings, json )
- {
- // v1.10 uses camelCase variables, while 1.9 uses Hungarian notation.
- // Support both
- var compat = function ( old, modern ) {
- return json[old] !== undefined ? json[old] : json[modern];
- };
-
- var data = _fnAjaxDataSrc( settings, json );
- var draw = compat( 'sEcho', 'draw' );
- var recordsTotal = compat( 'iTotalRecords', 'recordsTotal' );
- var recordsFiltered = compat( 'iTotalDisplayRecords', 'recordsFiltered' );
-
- if ( draw ) {
- // Protect against out of sequence returns
- if ( draw*1 < settings.iDraw ) {
- return;
- }
- settings.iDraw = draw * 1;
- }
-
- _fnClearTable( settings );
- settings._iRecordsTotal = parseInt(recordsTotal, 10);
- settings._iRecordsDisplay = parseInt(recordsFiltered, 10);
-
- for ( var i=0, ien=data.length ; i<ien ; i++ ) {
- _fnAddData( settings, data[i] );
- }
- settings.aiDisplay = settings.aiDisplayMaster.slice();
-
- settings.bAjaxDataGet = false;
- _fnDraw( settings );
-
- if ( ! settings._bInitComplete ) {
- _fnInitComplete( settings, json );
- }
-
- settings.bAjaxDataGet = true;
- _fnProcessingDisplay( settings, false );
- }
-
-
- /**
- * Get the data from the JSON data source to use for drawing a table. Using
- * `_fnGetObjectDataFn` allows the data to be sourced from a property of the
- * source object, or from a processing function.
- * @param {object} oSettings dataTables settings object
- * @param {object} json Data source object / array from the server
- * @return {array} Array of data to use
- */
- function _fnAjaxDataSrc ( oSettings, json )
- {
- var dataSrc = $.isPlainObject( oSettings.ajax ) && oSettings.ajax.dataSrc !== undefined ?
- oSettings.ajax.dataSrc :
- oSettings.sAjaxDataProp; // Compatibility with 1.9-.
-
- // Compatibility with 1.9-. In order to read from aaData, check if the
- // default has been changed, if not, check for aaData
- if ( dataSrc === 'data' ) {
- return json.aaData || json[dataSrc];
- }
-
- return dataSrc !== "" ?
- _fnGetObjectDataFn( dataSrc )( json ) :
- json;
- }
-
- /**
- * Generate the node required for filtering text
- * @returns {node} Filter control element
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlFilter ( settings )
- {
- var classes = settings.oClasses;
- var tableId = settings.sTableId;
- var language = settings.oLanguage;
- var previousSearch = settings.oPreviousSearch;
- var features = settings.aanFeatures;
- var input = '<input type="search" class="'+classes.sFilterInput+'"/>';
-
- var str = language.sSearch;
- str = str.match(/_INPUT_/) ?
- str.replace('_INPUT_', input) :
- str+input;
-
- var filter = $('<div/>', {
- 'id': ! features.f ? tableId+'_filter' : null,
- 'class': classes.sFilter
- } )
- .append( $('<label/>' ).append( str ) );
-
- var searchFn = function() {
- /* Update all other filter input elements for the new display */
- var n = features.f;
- var val = !this.value ? "" : this.value; // mental IE8 fix :-(
-
- /* Now do the filter */
- if ( val != previousSearch.sSearch ) {
- _fnFilterComplete( settings, {
- "sSearch": val,
- "bRegex": previousSearch.bRegex,
- "bSmart": previousSearch.bSmart ,
- "bCaseInsensitive": previousSearch.bCaseInsensitive
- } );
-
- // Need to redraw, without resorting
- settings._iDisplayStart = 0;
- _fnDraw( settings );
- }
- };
-
- var searchDelay = settings.searchDelay !== null ?
- settings.searchDelay :
- _fnDataSource( settings ) === 'ssp' ?
- 400 :
- 0;
-
- var jqFilter = $('input', filter)
- .val( previousSearch.sSearch )
- .attr( 'placeholder', language.sSearchPlaceholder )
- .on(
- 'keyup.DT search.DT input.DT paste.DT cut.DT',
- searchDelay ?
- _fnThrottle( searchFn, searchDelay ) :
- searchFn
- )
- .on( 'keypress.DT', function(e) {
- /* Prevent form submission */
- if ( e.keyCode == 13 ) {
- return false;
- }
- } )
- .attr('aria-controls', tableId);
-
- // Update the input elements whenever the table is filtered
- $(settings.nTable).on( 'search.dt.DT', function ( ev, s ) {
- if ( settings === s ) {
- // IE9 throws an 'unknown error' if document.activeElement is used
- // inside an iframe or frame...
- try {
- if ( jqFilter[0] !== document.activeElement ) {
- jqFilter.val( previousSearch.sSearch );
- }
- }
- catch ( e ) {}
- }
- } );
-
- return filter[0];
- }
-
-
- /**
- * Filter the table using both the global filter and column based filtering
- * @param {object} oSettings dataTables settings object
- * @param {object} oSearch search information
- * @param {int} [iForce] force a research of the master array (1) or not (undefined or 0)
- * @memberof DataTable#oApi
- */
- function _fnFilterComplete ( oSettings, oInput, iForce )
- {
- var oPrevSearch = oSettings.oPreviousSearch;
- var aoPrevSearch = oSettings.aoPreSearchCols;
- var fnSaveFilter = function ( oFilter ) {
- /* Save the filtering values */
- oPrevSearch.sSearch = oFilter.sSearch;
- oPrevSearch.bRegex = oFilter.bRegex;
- oPrevSearch.bSmart = oFilter.bSmart;
- oPrevSearch.bCaseInsensitive = oFilter.bCaseInsensitive;
- };
- var fnRegex = function ( o ) {
- // Backwards compatibility with the bEscapeRegex option
- return o.bEscapeRegex !== undefined ? !o.bEscapeRegex : o.bRegex;
- };
-
- // Resolve any column types that are unknown due to addition or invalidation
- // @todo As per sort - can this be moved into an event handler?
- _fnColumnTypes( oSettings );
-
- /* In server-side processing all filtering is done by the server, so no point hanging around here */
- if ( _fnDataSource( oSettings ) != 'ssp' )
- {
- /* Global filter */
- _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive );
- fnSaveFilter( oInput );
-
- /* Now do the individual column filter */
- for ( var i=0 ; i<aoPrevSearch.length ; i++ )
- {
- _fnFilterColumn( oSettings, aoPrevSearch[i].sSearch, i, fnRegex(aoPrevSearch[i]),
- aoPrevSearch[i].bSmart, aoPrevSearch[i].bCaseInsensitive );
- }
-
- /* Custom filtering */
- _fnFilterCustom( oSettings );
- }
- else
- {
- fnSaveFilter( oInput );
- }
-
- /* Tell the draw function we have been filtering */
- oSettings.bFiltered = true;
- _fnCallbackFire( oSettings, null, 'search', [oSettings] );
- }
-
-
- /**
- * Apply custom filtering functions
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnFilterCustom( settings )
- {
- var filters = DataTable.ext.search;
- var displayRows = settings.aiDisplay;
- var row, rowIdx;
-
- for ( var i=0, ien=filters.length ; i<ien ; i++ ) {
- var rows = [];
-
- // Loop over each row and see if it should be included
- for ( var j=0, jen=displayRows.length ; j<jen ; j++ ) {
- rowIdx = displayRows[ j ];
- row = settings.aoData[ rowIdx ];
-
- if ( filters[i]( settings, row._aFilterData, rowIdx, row._aData, j ) ) {
- rows.push( rowIdx );
- }
- }
-
- // So the array reference doesn't break set the results into the
- // existing array
- displayRows.length = 0;
- $.merge( displayRows, rows );
- }
- }
-
-
- /**
- * Filter the table on a per-column basis
- * @param {object} oSettings dataTables settings object
- * @param {string} sInput string to filter on
- * @param {int} iColumn column to filter
- * @param {bool} bRegex treat search string as a regular expression or not
- * @param {bool} bSmart use smart filtering or not
- * @param {bool} bCaseInsensitive Do case insenstive matching or not
- * @memberof DataTable#oApi
- */
- function _fnFilterColumn ( settings, searchStr, colIdx, regex, smart, caseInsensitive )
- {
- if ( searchStr === '' ) {
- return;
- }
-
- var data;
- var out = [];
- var display = settings.aiDisplay;
- var rpSearch = _fnFilterCreateSearch( searchStr, regex, smart, caseInsensitive );
-
- for ( var i=0 ; i<display.length ; i++ ) {
- data = settings.aoData[ display[i] ]._aFilterData[ colIdx ];
-
- if ( rpSearch.test( data ) ) {
- out.push( display[i] );
- }
- }
-
- settings.aiDisplay = out;
- }
-
-
- /**
- * Filter the data table based on user input and draw the table
- * @param {object} settings dataTables settings object
- * @param {string} input string to filter on
- * @param {int} force optional - force a research of the master array (1) or not (undefined or 0)
- * @param {bool} regex treat as a regular expression or not
- * @param {bool} smart perform smart filtering or not
- * @param {bool} caseInsensitive Do case insenstive matching or not
- * @memberof DataTable#oApi
- */
- function _fnFilter( settings, input, force, regex, smart, caseInsensitive )
- {
- var rpSearch = _fnFilterCreateSearch( input, regex, smart, caseInsensitive );
- var prevSearch = settings.oPreviousSearch.sSearch;
- var displayMaster = settings.aiDisplayMaster;
- var display, invalidated, i;
- var filtered = [];
-
- // Need to take account of custom filtering functions - always filter
- if ( DataTable.ext.search.length !== 0 ) {
- force = true;
- }
-
- // Check if any of the rows were invalidated
- invalidated = _fnFilterData( settings );
-
- // If the input is blank - we just want the full data set
- if ( input.length <= 0 ) {
- settings.aiDisplay = displayMaster.slice();
- }
- else {
- // New search - start from the master array
- if ( invalidated ||
- force ||
- prevSearch.length > input.length ||
- input.indexOf(prevSearch) !== 0 ||
- settings.bSorted // On resort, the display master needs to be
- // re-filtered since indexes will have changed
- ) {
- settings.aiDisplay = displayMaster.slice();
- }
-
- // Search the display array
- display = settings.aiDisplay;
-
- for ( i=0 ; i<display.length ; i++ ) {
- if ( rpSearch.test( settings.aoData[ display[i] ]._sFilterRow ) ) {
- filtered.push( display[i] );
- }
- }
-
- settings.aiDisplay = filtered;
- }
- }
-
-
- /**
- * Build a regular expression object suitable for searching a table
- * @param {string} sSearch string to search for
- * @param {bool} bRegex treat as a regular expression or not
- * @param {bool} bSmart perform smart filtering or not
- * @param {bool} bCaseInsensitive Do case insensitive matching or not
- * @returns {RegExp} constructed object
- * @memberof DataTable#oApi
- */
- function _fnFilterCreateSearch( search, regex, smart, caseInsensitive )
- {
- search = regex ?
- search :
- _fnEscapeRegex( search );
-
- if ( smart ) {
- /* For smart filtering we want to allow the search to work regardless of
- * word order. We also want double quoted text to be preserved, so word
- * order is important - a la google. So this is what we want to
- * generate:
- *
- * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$
- */
- var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || [''], function ( word ) {
- if ( word.charAt(0) === '"' ) {
- var m = word.match( /^"(.*)"$/ );
- word = m ? m[1] : word;
- }
-
- return word.replace('"', '');
- } );
-
- search = '^(?=.*?'+a.join( ')(?=.*?' )+').*$';
- }
-
- return new RegExp( search, caseInsensitive ? 'i' : '' );
- }
-
-
- /**
- * Escape a string such that it can be used in a regular expression
- * @param {string} sVal string to escape
- * @returns {string} escaped string
- * @memberof DataTable#oApi
- */
- var _fnEscapeRegex = DataTable.util.escapeRegex;
-
- var __filter_div = $('<div>')[0];
- var __filter_div_textContent = __filter_div.textContent !== undefined;
-
- // Update the filtering data for each row if needed (by invalidation or first run)
- function _fnFilterData ( settings )
- {
- var columns = settings.aoColumns;
- var column;
- var i, j, ien, jen, filterData, cellData, row;
- var fomatters = DataTable.ext.type.search;
- var wasInvalidated = false;
-
- for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
- row = settings.aoData[i];
-
- if ( ! row._aFilterData ) {
- filterData = [];
-
- for ( j=0, jen=columns.length ; j<jen ; j++ ) {
- column = columns[j];
-
- if ( column.bSearchable ) {
- cellData = _fnGetCellData( settings, i, j, 'filter' );
-
- if ( fomatters[ column.sType ] ) {
- cellData = fomatters[ column.sType ]( cellData );
- }
-
- // Search in DataTables 1.10 is string based. In 1.11 this
- // should be altered to also allow strict type checking.
- if ( cellData === null ) {
- cellData = '';
- }
-
- if ( typeof cellData !== 'string' && cellData.toString ) {
- cellData = cellData.toString();
- }
- }
- else {
- cellData = '';
- }
-
- // If it looks like there is an HTML entity in the string,
- // attempt to decode it so sorting works as expected. Note that
- // we could use a single line of jQuery to do this, but the DOM
- // method used here is much faster http://jsperf.com/html-decode
- if ( cellData.indexOf && cellData.indexOf('&') !== -1 ) {
- __filter_div.innerHTML = cellData;
- cellData = __filter_div_textContent ?
- __filter_div.textContent :
- __filter_div.innerText;
- }
-
- if ( cellData.replace ) {
- cellData = cellData.replace(/[\r\n]/g, '');
- }
-
- filterData.push( cellData );
- }
-
- row._aFilterData = filterData;
- row._sFilterRow = filterData.join(' ');
- wasInvalidated = true;
- }
- }
-
- return wasInvalidated;
- }
-
-
- /**
- * Convert from the internal Hungarian notation to camelCase for external
- * interaction
- * @param {object} obj Object to convert
- * @returns {object} Inverted object
- * @memberof DataTable#oApi
- */
- function _fnSearchToCamel ( obj )
- {
- return {
- search: obj.sSearch,
- smart: obj.bSmart,
- regex: obj.bRegex,
- caseInsensitive: obj.bCaseInsensitive
- };
- }
-
-
-
- /**
- * Convert from camelCase notation to the internal Hungarian. We could use the
- * Hungarian convert function here, but this is cleaner
- * @param {object} obj Object to convert
- * @returns {object} Inverted object
- * @memberof DataTable#oApi
- */
- function _fnSearchToHung ( obj )
- {
- return {
- sSearch: obj.search,
- bSmart: obj.smart,
- bRegex: obj.regex,
- bCaseInsensitive: obj.caseInsensitive
- };
- }
-
- /**
- * Generate the node required for the info display
- * @param {object} oSettings dataTables settings object
- * @returns {node} Information element
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlInfo ( settings )
- {
- var
- tid = settings.sTableId,
- nodes = settings.aanFeatures.i,
- n = $('<div/>', {
- 'class': settings.oClasses.sInfo,
- 'id': ! nodes ? tid+'_info' : null
- } );
-
- if ( ! nodes ) {
- // Update display on each draw
- settings.aoDrawCallback.push( {
- "fn": _fnUpdateInfo,
- "sName": "information"
- } );
-
- n
- .attr( 'role', 'status' )
- .attr( 'aria-live', 'polite' );
-
- // Table is described by our info div
- $(settings.nTable).attr( 'aria-describedby', tid+'_info' );
- }
-
- return n[0];
- }
-
-
- /**
- * Update the information elements in the display
- * @param {object} settings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnUpdateInfo ( settings )
- {
- /* Show information about the table */
- var nodes = settings.aanFeatures.i;
- if ( nodes.length === 0 ) {
- return;
- }
-
- var
- lang = settings.oLanguage,
- start = settings._iDisplayStart+1,
- end = settings.fnDisplayEnd(),
- max = settings.fnRecordsTotal(),
- total = settings.fnRecordsDisplay(),
- out = total ?
- lang.sInfo :
- lang.sInfoEmpty;
-
- if ( total !== max ) {
- /* Record set after filtering */
- out += ' ' + lang.sInfoFiltered;
- }
-
- // Convert the macros
- out += lang.sInfoPostFix;
- out = _fnInfoMacros( settings, out );
-
- var callback = lang.fnInfoCallback;
- if ( callback !== null ) {
- out = callback.call( settings.oInstance,
- settings, start, end, max, total, out
- );
- }
-
- $(nodes).html( out );
- }
-
-
- function _fnInfoMacros ( settings, str )
- {
- // When infinite scrolling, we are always starting at 1. _iDisplayStart is used only
- // internally
- var
- formatter = settings.fnFormatNumber,
- start = settings._iDisplayStart+1,
- len = settings._iDisplayLength,
- vis = settings.fnRecordsDisplay(),
- all = len === -1;
-
- return str.
- replace(/_START_/g, formatter.call( settings, start ) ).
- replace(/_END_/g, formatter.call( settings, settings.fnDisplayEnd() ) ).
- replace(/_MAX_/g, formatter.call( settings, settings.fnRecordsTotal() ) ).
- replace(/_TOTAL_/g, formatter.call( settings, vis ) ).
- replace(/_PAGE_/g, formatter.call( settings, all ? 1 : Math.ceil( start / len ) ) ).
- replace(/_PAGES_/g, formatter.call( settings, all ? 1 : Math.ceil( vis / len ) ) );
- }
-
-
-
- /**
- * Draw the table for the first time, adding all required features
- * @param {object} settings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnInitialise ( settings )
- {
- var i, iLen, iAjaxStart=settings.iInitDisplayStart;
- var columns = settings.aoColumns, column;
- var features = settings.oFeatures;
- var deferLoading = settings.bDeferLoading; // value modified by the draw
-
- /* Ensure that the table data is fully initialised */
- if ( ! settings.bInitialised ) {
- setTimeout( function(){ _fnInitialise( settings ); }, 200 );
- return;
- }
-
- /* Show the display HTML options */
- _fnAddOptionsHtml( settings );
-
- /* Build and draw the header / footer for the table */
- _fnBuildHead( settings );
- _fnDrawHead( settings, settings.aoHeader );
- _fnDrawHead( settings, settings.aoFooter );
-
- /* Okay to show that something is going on now */
- _fnProcessingDisplay( settings, true );
-
- /* Calculate sizes for columns */
- if ( features.bAutoWidth ) {
- _fnCalculateColumnWidths( settings );
- }
-
- for ( i=0, iLen=columns.length ; i<iLen ; i++ ) {
- column = columns[i];
-
- if ( column.sWidth ) {
- column.nTh.style.width = _fnStringToCss( column.sWidth );
- }
- }
-
- _fnCallbackFire( settings, null, 'preInit', [settings] );
-
- // If there is default sorting required - let's do it. The sort function
- // will do the drawing for us. Otherwise we draw the table regardless of the
- // Ajax source - this allows the table to look initialised for Ajax sourcing
- // data (show 'loading' message possibly)
- _fnReDraw( settings );
-
- // Server-side processing init complete is done by _fnAjaxUpdateDraw
- var dataSrc = _fnDataSource( settings );
- if ( dataSrc != 'ssp' || deferLoading ) {
- // if there is an ajax source load the data
- if ( dataSrc == 'ajax' ) {
- _fnBuildAjax( settings, [], function(json) {
- var aData = _fnAjaxDataSrc( settings, json );
-
- // Got the data - add it to the table
- for ( i=0 ; i<aData.length ; i++ ) {
- _fnAddData( settings, aData[i] );
- }
-
- // Reset the init display for cookie saving. We've already done
- // a filter, and therefore cleared it before. So we need to make
- // it appear 'fresh'
- settings.iInitDisplayStart = iAjaxStart;
-
- _fnReDraw( settings );
-
- _fnProcessingDisplay( settings, false );
- _fnInitComplete( settings, json );
- }, settings );
- }
- else {
- _fnProcessingDisplay( settings, false );
- _fnInitComplete( settings );
- }
- }
- }
-
-
- /**
- * Draw the table for the first time, adding all required features
- * @param {object} oSettings dataTables settings object
- * @param {object} [json] JSON from the server that completed the table, if using Ajax source
- * with client-side processing (optional)
- * @memberof DataTable#oApi
- */
- function _fnInitComplete ( settings, json )
- {
- settings._bInitComplete = true;
-
- // When data was added after the initialisation (data or Ajax) we need to
- // calculate the column sizing
- if ( json || settings.oInit.aaData ) {
- _fnAdjustColumnSizing( settings );
- }
-
- _fnCallbackFire( settings, null, 'plugin-init', [settings, json] );
- _fnCallbackFire( settings, 'aoInitComplete', 'init', [settings, json] );
- }
-
-
- function _fnLengthChange ( settings, val )
- {
- var len = parseInt( val, 10 );
- settings._iDisplayLength = len;
-
- _fnLengthOverflow( settings );
-
- // Fire length change event
- _fnCallbackFire( settings, null, 'length', [settings, len] );
- }
-
-
- /**
- * Generate the node required for user display length changing
- * @param {object} settings dataTables settings object
- * @returns {node} Display length feature node
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlLength ( settings )
- {
- var
- classes = settings.oClasses,
- tableId = settings.sTableId,
- menu = settings.aLengthMenu,
- d2 = $.isArray( menu[0] ),
- lengths = d2 ? menu[0] : menu,
- language = d2 ? menu[1] : menu;
-
- var select = $('<select/>', {
- 'name': tableId+'_length',
- 'aria-controls': tableId,
- 'class': classes.sLengthSelect
- } );
-
- for ( var i=0, ien=lengths.length ; i<ien ; i++ ) {
- select[0][ i ] = new Option(
- typeof language[i] === 'number' ?
- settings.fnFormatNumber( language[i] ) :
- language[i],
- lengths[i]
- );
- }
-
- var div = $('<div><label/></div>').addClass( classes.sLength );
- if ( ! settings.aanFeatures.l ) {
- div[0].id = tableId+'_length';
- }
-
- div.children().append(
- settings.oLanguage.sLengthMenu.replace( '_MENU_', select[0].outerHTML )
- );
-
- // Can't use `select` variable as user might provide their own and the
- // reference is broken by the use of outerHTML
- $('select', div)
- .val( settings._iDisplayLength )
- .on( 'change.DT', function(e) {
- _fnLengthChange( settings, $(this).val() );
- _fnDraw( settings );
- } );
-
- // Update node value whenever anything changes the table's length
- $(settings.nTable).on( 'length.dt.DT', function (e, s, len) {
- if ( settings === s ) {
- $('select', div).val( len );
- }
- } );
-
- return div[0];
- }
-
-
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Note that most of the paging logic is done in
- * DataTable.ext.pager
- */
-
- /**
- * Generate the node required for default pagination
- * @param {object} oSettings dataTables settings object
- * @returns {node} Pagination feature node
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlPaginate ( settings )
- {
- var
- type = settings.sPaginationType,
- plugin = DataTable.ext.pager[ type ],
- modern = typeof plugin === 'function',
- redraw = function( settings ) {
- _fnDraw( settings );
- },
- node = $('<div/>').addClass( settings.oClasses.sPaging + type )[0],
- features = settings.aanFeatures;
-
- if ( ! modern ) {
- plugin.fnInit( settings, node, redraw );
- }
-
- /* Add a draw callback for the pagination on first instance, to update the paging display */
- if ( ! features.p )
- {
- node.id = settings.sTableId+'_paginate';
-
- settings.aoDrawCallback.push( {
- "fn": function( settings ) {
- if ( modern ) {
- var
- start = settings._iDisplayStart,
- len = settings._iDisplayLength,
- visRecords = settings.fnRecordsDisplay(),
- all = len === -1,
- page = all ? 0 : Math.ceil( start / len ),
- pages = all ? 1 : Math.ceil( visRecords / len ),
- buttons = plugin(page, pages),
- i, ien;
-
- for ( i=0, ien=features.p.length ; i<ien ; i++ ) {
- _fnRenderer( settings, 'pageButton' )(
- settings, features.p[i], i, buttons, page, pages
- );
- }
- }
- else {
- plugin.fnUpdate( settings, redraw );
- }
- },
- "sName": "pagination"
- } );
- }
-
- return node;
- }
-
-
- /**
- * Alter the display settings to change the page
- * @param {object} settings DataTables settings object
- * @param {string|int} action Paging action to take: "first", "previous",
- * "next" or "last" or page number to jump to (integer)
- * @param [bool] redraw Automatically draw the update or not
- * @returns {bool} true page has changed, false - no change
- * @memberof DataTable#oApi
- */
- function _fnPageChange ( settings, action, redraw )
- {
- var
- start = settings._iDisplayStart,
- len = settings._iDisplayLength,
- records = settings.fnRecordsDisplay();
-
- if ( records === 0 || len === -1 )
- {
- start = 0;
- }
- else if ( typeof action === "number" )
- {
- start = action * len;
-
- if ( start > records )
- {
- start = 0;
- }
- }
- else if ( action == "first" )
- {
- start = 0;
- }
- else if ( action == "previous" )
- {
- start = len >= 0 ?
- start - len :
- 0;
-
- if ( start < 0 )
- {
- start = 0;
- }
- }
- else if ( action == "next" )
- {
- if ( start + len < records )
- {
- start += len;
- }
- }
- else if ( action == "last" )
- {
- start = Math.floor( (records-1) / len) * len;
- }
- else
- {
- _fnLog( settings, 0, "Unknown paging action: "+action, 5 );
- }
-
- var changed = settings._iDisplayStart !== start;
- settings._iDisplayStart = start;
-
- if ( changed ) {
- _fnCallbackFire( settings, null, 'page', [settings] );
-
- if ( redraw ) {
- _fnDraw( settings );
- }
- }
-
- return changed;
- }
-
-
-
- /**
- * Generate the node required for the processing node
- * @param {object} settings dataTables settings object
- * @returns {node} Processing element
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlProcessing ( settings )
- {
- return $('<div/>', {
- 'id': ! settings.aanFeatures.r ? settings.sTableId+'_processing' : null,
- 'class': settings.oClasses.sProcessing
- } )
- .html( settings.oLanguage.sProcessing )
- .insertBefore( settings.nTable )[0];
- }
-
-
- /**
- * Display or hide the processing indicator
- * @param {object} settings dataTables settings object
- * @param {bool} show Show the processing indicator (true) or not (false)
- * @memberof DataTable#oApi
- */
- function _fnProcessingDisplay ( settings, show )
- {
- if ( settings.oFeatures.bProcessing ) {
- $(settings.aanFeatures.r).css( 'display', show ? 'block' : 'none' );
- }
-
- _fnCallbackFire( settings, null, 'processing', [settings, show] );
- }
-
- /**
- * Add any control elements for the table - specifically scrolling
- * @param {object} settings dataTables settings object
- * @returns {node} Node to add to the DOM
- * @memberof DataTable#oApi
- */
- function _fnFeatureHtmlTable ( settings )
- {
- var table = $(settings.nTable);
-
- // Add the ARIA grid role to the table
- table.attr( 'role', 'grid' );
-
- // Scrolling from here on in
- var scroll = settings.oScroll;
-
- if ( scroll.sX === '' && scroll.sY === '' ) {
- return settings.nTable;
- }
-
- var scrollX = scroll.sX;
- var scrollY = scroll.sY;
- var classes = settings.oClasses;
- var caption = table.children('caption');
- var captionSide = caption.length ? caption[0]._captionSide : null;
- var headerClone = $( table[0].cloneNode(false) );
- var footerClone = $( table[0].cloneNode(false) );
- var footer = table.children('tfoot');
- var _div = '<div/>';
- var size = function ( s ) {
- return !s ? null : _fnStringToCss( s );
- };
-
- if ( ! footer.length ) {
- footer = null;
- }
-
- /*
- * The HTML structure that we want to generate in this function is:
- * div - scroller
- * div - scroll head
- * div - scroll head inner
- * table - scroll head table
- * thead - thead
- * div - scroll body
- * table - table (master table)
- * thead - thead clone for sizing
- * tbody - tbody
- * div - scroll foot
- * div - scroll foot inner
- * table - scroll foot table
- * tfoot - tfoot
- */
- var scroller = $( _div, { 'class': classes.sScrollWrapper } )
- .append(
- $(_div, { 'class': classes.sScrollHead } )
- .css( {
- overflow: 'hidden',
- position: 'relative',
- border: 0,
- width: scrollX ? size(scrollX) : '100%'
- } )
- .append(
- $(_div, { 'class': classes.sScrollHeadInner } )
- .css( {
- 'box-sizing': 'content-box',
- width: scroll.sXInner || '100%'
- } )
- .append(
- headerClone
- .removeAttr('id')
- .css( 'margin-left', 0 )
- .append( captionSide === 'top' ? caption : null )
- .append(
- table.children('thead')
- )
- )
- )
- )
- .append(
- $(_div, { 'class': classes.sScrollBody } )
- .css( {
- position: 'relative',
- overflow: 'auto',
- width: size( scrollX )
- } )
- .append( table )
- );
-
- if ( footer ) {
- scroller.append(
- $(_div, { 'class': classes.sScrollFoot } )
- .css( {
- overflow: 'hidden',
- border: 0,
- width: scrollX ? size(scrollX) : '100%'
- } )
- .append(
- $(_div, { 'class': classes.sScrollFootInner } )
- .append(
- footerClone
- .removeAttr('id')
- .css( 'margin-left', 0 )
- .append( captionSide === 'bottom' ? caption : null )
- .append(
- table.children('tfoot')
- )
- )
- )
- );
- }
-
- var children = scroller.children();
- var scrollHead = children[0];
- var scrollBody = children[1];
- var scrollFoot = footer ? children[2] : null;
-
- // When the body is scrolled, then we also want to scroll the headers
- if ( scrollX ) {
- $(scrollBody).on( 'scroll.DT', function (e) {
- var scrollLeft = this.scrollLeft;
-
- scrollHead.scrollLeft = scrollLeft;
-
- if ( footer ) {
- scrollFoot.scrollLeft = scrollLeft;
- }
- } );
- }
-
- $(scrollBody).css(
- scrollY && scroll.bCollapse ? 'max-height' : 'height',
- scrollY
- );
-
- settings.nScrollHead = scrollHead;
- settings.nScrollBody = scrollBody;
- settings.nScrollFoot = scrollFoot;
-
- // On redraw - align columns
- settings.aoDrawCallback.push( {
- "fn": _fnScrollDraw,
- "sName": "scrolling"
- } );
-
- return scroller[0];
- }
-
-
-
- /**
- * Update the header, footer and body tables for resizing - i.e. column
- * alignment.
- *
- * Welcome to the most horrible function DataTables. The process that this
- * function follows is basically:
- * 1. Re-create the table inside the scrolling div
- * 2. Take live measurements from the DOM
- * 3. Apply the measurements to align the columns
- * 4. Clean up
- *
- * @param {object} settings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnScrollDraw ( settings )
- {
- // Given that this is such a monster function, a lot of variables are use
- // to try and keep the minimised size as small as possible
- var
- scroll = settings.oScroll,
- scrollX = scroll.sX,
- scrollXInner = scroll.sXInner,
- scrollY = scroll.sY,
- barWidth = scroll.iBarWidth,
- divHeader = $(settings.nScrollHead),
- divHeaderStyle = divHeader[0].style,
- divHeaderInner = divHeader.children('div'),
- divHeaderInnerStyle = divHeaderInner[0].style,
- divHeaderTable = divHeaderInner.children('table'),
- divBodyEl = settings.nScrollBody,
- divBody = $(divBodyEl),
- divBodyStyle = divBodyEl.style,
- divFooter = $(settings.nScrollFoot),
- divFooterInner = divFooter.children('div'),
- divFooterTable = divFooterInner.children('table'),
- header = $(settings.nTHead),
- table = $(settings.nTable),
- tableEl = table[0],
- tableStyle = tableEl.style,
- footer = settings.nTFoot ? $(settings.nTFoot) : null,
- browser = settings.oBrowser,
- ie67 = browser.bScrollOversize,
- dtHeaderCells = _pluck( settings.aoColumns, 'nTh' ),
- headerTrgEls, footerTrgEls,
- headerSrcEls, footerSrcEls,
- headerCopy, footerCopy,
- headerWidths=[], footerWidths=[],
- headerContent=[], footerContent=[],
- idx, correction, sanityWidth,
- zeroOut = function(nSizer) {
- var style = nSizer.style;
- style.paddingTop = "0";
- style.paddingBottom = "0";
- style.borderTopWidth = "0";
- style.borderBottomWidth = "0";
- style.height = 0;
- };
-
- // If the scrollbar visibility has changed from the last draw, we need to
- // adjust the column sizes as the table width will have changed to account
- // for the scrollbar
- var scrollBarVis = divBodyEl.scrollHeight > divBodyEl.clientHeight;
-
- if ( settings.scrollBarVis !== scrollBarVis && settings.scrollBarVis !== undefined ) {
- settings.scrollBarVis = scrollBarVis;
- _fnAdjustColumnSizing( settings );
- return; // adjust column sizing will call this function again
- }
- else {
- settings.scrollBarVis = scrollBarVis;
- }
-
- /*
- * 1. Re-create the table inside the scrolling div
- */
-
- // Remove the old minimised thead and tfoot elements in the inner table
- table.children('thead, tfoot').remove();
-
- if ( footer ) {
- footerCopy = footer.clone().prependTo( table );
- footerTrgEls = footer.find('tr'); // the original tfoot is in its own table and must be sized
- footerSrcEls = footerCopy.find('tr');
- }
-
- // Clone the current header and footer elements and then place it into the inner table
- headerCopy = header.clone().prependTo( table );
- headerTrgEls = header.find('tr'); // original header is in its own table
- headerSrcEls = headerCopy.find('tr');
- headerCopy.find('th, td').removeAttr('tabindex');
-
-
- /*
- * 2. Take live measurements from the DOM - do not alter the DOM itself!
- */
-
- // Remove old sizing and apply the calculated column widths
- // Get the unique column headers in the newly created (cloned) header. We want to apply the
- // calculated sizes to this header
- if ( ! scrollX )
- {
- divBodyStyle.width = '100%';
- divHeader[0].style.width = '100%';
- }
-
- $.each( _fnGetUniqueThs( settings, headerCopy ), function ( i, el ) {
- idx = _fnVisibleToColumnIndex( settings, i );
- el.style.width = settings.aoColumns[idx].sWidth;
- } );
-
- if ( footer ) {
- _fnApplyToChildren( function(n) {
- n.style.width = "";
- }, footerSrcEls );
- }
-
- // Size the table as a whole
- sanityWidth = table.outerWidth();
- if ( scrollX === "" ) {
- // No x scrolling
- tableStyle.width = "100%";
-
- // IE7 will make the width of the table when 100% include the scrollbar
- // - which is shouldn't. When there is a scrollbar we need to take this
- // into account.
- if ( ie67 && (table.find('tbody').height() > divBodyEl.offsetHeight ||
- divBody.css('overflow-y') == "scroll")
- ) {
- tableStyle.width = _fnStringToCss( table.outerWidth() - barWidth);
- }
-
- // Recalculate the sanity width
- sanityWidth = table.outerWidth();
- }
- else if ( scrollXInner !== "" ) {
- // legacy x scroll inner has been given - use it
- tableStyle.width = _fnStringToCss(scrollXInner);
-
- // Recalculate the sanity width
- sanityWidth = table.outerWidth();
- }
-
- // Hidden header should have zero height, so remove padding and borders. Then
- // set the width based on the real headers
-
- // Apply all styles in one pass
- _fnApplyToChildren( zeroOut, headerSrcEls );
-
- // Read all widths in next pass
- _fnApplyToChildren( function(nSizer) {
- headerContent.push( nSizer.innerHTML );
- headerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
- }, headerSrcEls );
-
- // Apply all widths in final pass
- _fnApplyToChildren( function(nToSize, i) {
- // Only apply widths to the DataTables detected header cells - this
- // prevents complex headers from having contradictory sizes applied
- if ( $.inArray( nToSize, dtHeaderCells ) !== -1 ) {
- nToSize.style.width = headerWidths[i];
- }
- }, headerTrgEls );
-
- $(headerSrcEls).height(0);
-
- /* Same again with the footer if we have one */
- if ( footer )
- {
- _fnApplyToChildren( zeroOut, footerSrcEls );
-
- _fnApplyToChildren( function(nSizer) {
- footerContent.push( nSizer.innerHTML );
- footerWidths.push( _fnStringToCss( $(nSizer).css('width') ) );
- }, footerSrcEls );
-
- _fnApplyToChildren( function(nToSize, i) {
- nToSize.style.width = footerWidths[i];
- }, footerTrgEls );
-
- $(footerSrcEls).height(0);
- }
-
-
- /*
- * 3. Apply the measurements
- */
-
- // "Hide" the header and footer that we used for the sizing. We need to keep
- // the content of the cell so that the width applied to the header and body
- // both match, but we want to hide it completely. We want to also fix their
- // width to what they currently are
- _fnApplyToChildren( function(nSizer, i) {
- nSizer.innerHTML = '<div class="dataTables_sizing">'+headerContent[i]+'</div>';
- nSizer.childNodes[0].style.height = "0";
- nSizer.childNodes[0].style.overflow = "hidden";
- nSizer.style.width = headerWidths[i];
- }, headerSrcEls );
-
- if ( footer )
- {
- _fnApplyToChildren( function(nSizer, i) {
- nSizer.innerHTML = '<div class="dataTables_sizing">'+footerContent[i]+'</div>';
- nSizer.childNodes[0].style.height = "0";
- nSizer.childNodes[0].style.overflow = "hidden";
- nSizer.style.width = footerWidths[i];
- }, footerSrcEls );
- }
-
- // Sanity check that the table is of a sensible width. If not then we are going to get
- // misalignment - try to prevent this by not allowing the table to shrink below its min width
- if ( table.outerWidth() < sanityWidth )
- {
- // The min width depends upon if we have a vertical scrollbar visible or not */
- correction = ((divBodyEl.scrollHeight > divBodyEl.offsetHeight ||
- divBody.css('overflow-y') == "scroll")) ?
- sanityWidth+barWidth :
- sanityWidth;
-
- // IE6/7 are a law unto themselves...
- if ( ie67 && (divBodyEl.scrollHeight >
- divBodyEl.offsetHeight || divBody.css('overflow-y') == "scroll")
- ) {
- tableStyle.width = _fnStringToCss( correction-barWidth );
- }
-
- // And give the user a warning that we've stopped the table getting too small
- if ( scrollX === "" || scrollXInner !== "" ) {
- _fnLog( settings, 1, 'Possible column misalignment', 6 );
- }
- }
- else
- {
- correction = '100%';
- }
-
- // Apply to the container elements
- divBodyStyle.width = _fnStringToCss( correction );
- divHeaderStyle.width = _fnStringToCss( correction );
-
- if ( footer ) {
- settings.nScrollFoot.style.width = _fnStringToCss( correction );
- }
-
-
- /*
- * 4. Clean up
- */
- if ( ! scrollY ) {
- /* IE7< puts a vertical scrollbar in place (when it shouldn't be) due to subtracting
- * the scrollbar height from the visible display, rather than adding it on. We need to
- * set the height in order to sort this. Don't want to do it in any other browsers.
- */
- if ( ie67 ) {
- divBodyStyle.height = _fnStringToCss( tableEl.offsetHeight+barWidth );
- }
- }
-
- /* Finally set the width's of the header and footer tables */
- var iOuterWidth = table.outerWidth();
- divHeaderTable[0].style.width = _fnStringToCss( iOuterWidth );
- divHeaderInnerStyle.width = _fnStringToCss( iOuterWidth );
-
- // Figure out if there are scrollbar present - if so then we need a the header and footer to
- // provide a bit more space to allow "overflow" scrolling (i.e. past the scrollbar)
- var bScrolling = table.height() > divBodyEl.clientHeight || divBody.css('overflow-y') == "scroll";
- var padding = 'padding' + (browser.bScrollbarLeft ? 'Left' : 'Right' );
- divHeaderInnerStyle[ padding ] = bScrolling ? barWidth+"px" : "0px";
-
- if ( footer ) {
- divFooterTable[0].style.width = _fnStringToCss( iOuterWidth );
- divFooterInner[0].style.width = _fnStringToCss( iOuterWidth );
- divFooterInner[0].style[padding] = bScrolling ? barWidth+"px" : "0px";
- }
-
- // Correct DOM ordering for colgroup - comes before the thead
- table.children('colgroup').insertBefore( table.children('thead') );
-
- /* Adjust the position of the header in case we loose the y-scrollbar */
- divBody.scroll();
-
- // If sorting or filtering has occurred, jump the scrolling back to the top
- // only if we aren't holding the position
- if ( (settings.bSorted || settings.bFiltered) && ! settings._drawHold ) {
- divBodyEl.scrollTop = 0;
- }
- }
-
-
-
- /**
- * Apply a given function to the display child nodes of an element array (typically
- * TD children of TR rows
- * @param {function} fn Method to apply to the objects
- * @param array {nodes} an1 List of elements to look through for display children
- * @param array {nodes} an2 Another list (identical structure to the first) - optional
- * @memberof DataTable#oApi
- */
- function _fnApplyToChildren( fn, an1, an2 )
- {
- var index=0, i=0, iLen=an1.length;
- var nNode1, nNode2;
-
- while ( i < iLen ) {
- nNode1 = an1[i].firstChild;
- nNode2 = an2 ? an2[i].firstChild : null;
-
- while ( nNode1 ) {
- if ( nNode1.nodeType === 1 ) {
- if ( an2 ) {
- fn( nNode1, nNode2, index );
- }
- else {
- fn( nNode1, index );
- }
-
- index++;
- }
-
- nNode1 = nNode1.nextSibling;
- nNode2 = an2 ? nNode2.nextSibling : null;
- }
-
- i++;
- }
- }
-
-
-
- var __re_html_remove = /<.*?>/g;
-
-
- /**
- * Calculate the width of columns for the table
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnCalculateColumnWidths ( oSettings )
- {
- var
- table = oSettings.nTable,
- columns = oSettings.aoColumns,
- scroll = oSettings.oScroll,
- scrollY = scroll.sY,
- scrollX = scroll.sX,
- scrollXInner = scroll.sXInner,
- columnCount = columns.length,
- visibleColumns = _fnGetColumns( oSettings, 'bVisible' ),
- headerCells = $('th', oSettings.nTHead),
- tableWidthAttr = table.getAttribute('width'), // from DOM element
- tableContainer = table.parentNode,
- userInputs = false,
- i, column, columnIdx, width, outerWidth,
- browser = oSettings.oBrowser,
- ie67 = browser.bScrollOversize;
-
- var styleWidth = table.style.width;
- if ( styleWidth && styleWidth.indexOf('%') !== -1 ) {
- tableWidthAttr = styleWidth;
- }
-
- /* Convert any user input sizes into pixel sizes */
- for ( i=0 ; i<visibleColumns.length ; i++ ) {
- column = columns[ visibleColumns[i] ];
-
- if ( column.sWidth !== null ) {
- column.sWidth = _fnConvertToWidth( column.sWidthOrig, tableContainer );
-
- userInputs = true;
- }
- }
-
- /* If the number of columns in the DOM equals the number that we have to
- * process in DataTables, then we can use the offsets that are created by
- * the web- browser. No custom sizes can be set in order for this to happen,
- * nor scrolling used
- */
- if ( ie67 || ! userInputs && ! scrollX && ! scrollY &&
- columnCount == _fnVisbleColumns( oSettings ) &&
- columnCount == headerCells.length
- ) {
- for ( i=0 ; i<columnCount ; i++ ) {
- var colIdx = _fnVisibleToColumnIndex( oSettings, i );
-
- if ( colIdx !== null ) {
- columns[ colIdx ].sWidth = _fnStringToCss( headerCells.eq(i).width() );
- }
- }
- }
- else
- {
- // Otherwise construct a single row, worst case, table with the widest
- // node in the data, assign any user defined widths, then insert it into
- // the DOM and allow the browser to do all the hard work of calculating
- // table widths
- var tmpTable = $(table).clone() // don't use cloneNode - IE8 will remove events on the main table
- .css( 'visibility', 'hidden' )
- .removeAttr( 'id' );
-
- // Clean up the table body
- tmpTable.find('tbody tr').remove();
- var tr = $('<tr/>').appendTo( tmpTable.find('tbody') );
-
- // Clone the table header and footer - we can't use the header / footer
- // from the cloned table, since if scrolling is active, the table's
- // real header and footer are contained in different table tags
- tmpTable.find('thead, tfoot').remove();
- tmpTable
- .append( $(oSettings.nTHead).clone() )
- .append( $(oSettings.nTFoot).clone() );
-
- // Remove any assigned widths from the footer (from scrolling)
- tmpTable.find('tfoot th, tfoot td').css('width', '');
-
- // Apply custom sizing to the cloned header
- headerCells = _fnGetUniqueThs( oSettings, tmpTable.find('thead')[0] );
-
- for ( i=0 ; i<visibleColumns.length ; i++ ) {
- column = columns[ visibleColumns[i] ];
-
- headerCells[i].style.width = column.sWidthOrig !== null && column.sWidthOrig !== '' ?
- _fnStringToCss( column.sWidthOrig ) :
- '';
-
- // For scrollX we need to force the column width otherwise the
- // browser will collapse it. If this width is smaller than the
- // width the column requires, then it will have no effect
- if ( column.sWidthOrig && scrollX ) {
- $( headerCells[i] ).append( $('<div/>').css( {
- width: column.sWidthOrig,
- margin: 0,
- padding: 0,
- border: 0,
- height: 1
- } ) );
- }
- }
-
- // Find the widest cell for each column and put it into the table
- if ( oSettings.aoData.length ) {
- for ( i=0 ; i<visibleColumns.length ; i++ ) {
- columnIdx = visibleColumns[i];
- column = columns[ columnIdx ];
-
- $( _fnGetWidestNode( oSettings, columnIdx ) )
- .clone( false )
- .append( column.sContentPadding )
- .appendTo( tr );
- }
- }
-
- // Tidy the temporary table - remove name attributes so there aren't
- // duplicated in the dom (radio elements for example)
- $('[name]', tmpTable).removeAttr('name');
-
- // Table has been built, attach to the document so we can work with it.
- // A holding element is used, positioned at the top of the container
- // with minimal height, so it has no effect on if the container scrolls
- // or not. Otherwise it might trigger scrolling when it actually isn't
- // needed
- var holder = $('<div/>').css( scrollX || scrollY ?
- {
- position: 'absolute',
- top: 0,
- left: 0,
- height: 1,
- right: 0,
- overflow: 'hidden'
- } :
- {}
- )
- .append( tmpTable )
- .appendTo( tableContainer );
-
- // When scrolling (X or Y) we want to set the width of the table as
- // appropriate. However, when not scrolling leave the table width as it
- // is. This results in slightly different, but I think correct behaviour
- if ( scrollX && scrollXInner ) {
- tmpTable.width( scrollXInner );
- }
- else if ( scrollX ) {
- tmpTable.css( 'width', 'auto' );
- tmpTable.removeAttr('width');
-
- // If there is no width attribute or style, then allow the table to
- // collapse
- if ( tmpTable.width() < tableContainer.clientWidth && tableWidthAttr ) {
- tmpTable.width( tableContainer.clientWidth );
- }
- }
- else if ( scrollY ) {
- tmpTable.width( tableContainer.clientWidth );
- }
- else if ( tableWidthAttr ) {
- tmpTable.width( tableWidthAttr );
- }
-
- // Get the width of each column in the constructed table - we need to
- // know the inner width (so it can be assigned to the other table's
- // cells) and the outer width so we can calculate the full width of the
- // table. This is safe since DataTables requires a unique cell for each
- // column, but if ever a header can span multiple columns, this will
- // need to be modified.
- var total = 0;
- for ( i=0 ; i<visibleColumns.length ; i++ ) {
- var cell = $(headerCells[i]);
- var border = cell.outerWidth() - cell.width();
-
- // Use getBounding... where possible (not IE8-) because it can give
- // sub-pixel accuracy, which we then want to round up!
- var bounding = browser.bBounding ?
- Math.ceil( headerCells[i].getBoundingClientRect().width ) :
- cell.outerWidth();
-
- // Total is tracked to remove any sub-pixel errors as the outerWidth
- // of the table might not equal the total given here (IE!).
- total += bounding;
-
- // Width for each column to use
- columns[ visibleColumns[i] ].sWidth = _fnStringToCss( bounding - border );
- }
-
- table.style.width = _fnStringToCss( total );
-
- // Finished with the table - ditch it
- holder.remove();
- }
-
- // If there is a width attr, we want to attach an event listener which
- // allows the table sizing to automatically adjust when the window is
- // resized. Use the width attr rather than CSS, since we can't know if the
- // CSS is a relative value or absolute - DOM read is always px.
- if ( tableWidthAttr ) {
- table.style.width = _fnStringToCss( tableWidthAttr );
- }
-
- if ( (tableWidthAttr || scrollX) && ! oSettings._reszEvt ) {
- var bindResize = function () {
- $(window).on('resize.DT-'+oSettings.sInstance, _fnThrottle( function () {
- _fnAdjustColumnSizing( oSettings );
- } ) );
- };
-
- // IE6/7 will crash if we bind a resize event handler on page load.
- // To be removed in 1.11 which drops IE6/7 support
- if ( ie67 ) {
- setTimeout( bindResize, 1000 );
- }
- else {
- bindResize();
- }
-
- oSettings._reszEvt = true;
- }
- }
-
-
- /**
- * Throttle the calls to a function. Arguments and context are maintained for
- * the throttled function
- * @param {function} fn Function to be called
- * @param {int} [freq=200] call frequency in mS
- * @returns {function} wrapped function
- * @memberof DataTable#oApi
- */
- var _fnThrottle = DataTable.util.throttle;
-
-
- /**
- * Convert a CSS unit width to pixels (e.g. 2em)
- * @param {string} width width to be converted
- * @param {node} parent parent to get the with for (required for relative widths) - optional
- * @returns {int} width in pixels
- * @memberof DataTable#oApi
- */
- function _fnConvertToWidth ( width, parent )
- {
- if ( ! width ) {
- return 0;
- }
-
- var n = $('<div/>')
- .css( 'width', _fnStringToCss( width ) )
- .appendTo( parent || document.body );
-
- var val = n[0].offsetWidth;
- n.remove();
-
- return val;
- }
-
-
- /**
- * Get the widest node
- * @param {object} settings dataTables settings object
- * @param {int} colIdx column of interest
- * @returns {node} widest table node
- * @memberof DataTable#oApi
- */
- function _fnGetWidestNode( settings, colIdx )
- {
- var idx = _fnGetMaxLenString( settings, colIdx );
- if ( idx < 0 ) {
- return null;
- }
-
- var data = settings.aoData[ idx ];
- return ! data.nTr ? // Might not have been created when deferred rendering
- $('<td/>').html( _fnGetCellData( settings, idx, colIdx, 'display' ) )[0] :
- data.anCells[ colIdx ];
- }
-
-
- /**
- * Get the maximum strlen for each data column
- * @param {object} settings dataTables settings object
- * @param {int} colIdx column of interest
- * @returns {string} max string length for each column
- * @memberof DataTable#oApi
- */
- function _fnGetMaxLenString( settings, colIdx )
- {
- var s, max=-1, maxIdx = -1;
-
- for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
- s = _fnGetCellData( settings, i, colIdx, 'display' )+'';
- s = s.replace( __re_html_remove, '' );
- s = s.replace( / /g, ' ' );
-
- if ( s.length > max ) {
- max = s.length;
- maxIdx = i;
- }
- }
-
- return maxIdx;
- }
-
-
- /**
- * Append a CSS unit (only if required) to a string
- * @param {string} value to css-ify
- * @returns {string} value with css unit
- * @memberof DataTable#oApi
- */
- function _fnStringToCss( s )
- {
- if ( s === null ) {
- return '0px';
- }
-
- if ( typeof s == 'number' ) {
- return s < 0 ?
- '0px' :
- s+'px';
- }
-
- // Check it has a unit character already
- return s.match(/\d$/) ?
- s+'px' :
- s;
- }
-
-
-
- function _fnSortFlatten ( settings )
- {
- var
- i, iLen, k, kLen,
- aSort = [],
- aiOrig = [],
- aoColumns = settings.aoColumns,
- aDataSort, iCol, sType, srcCol,
- fixed = settings.aaSortingFixed,
- fixedObj = $.isPlainObject( fixed ),
- nestedSort = [],
- add = function ( a ) {
- if ( a.length && ! $.isArray( a[0] ) ) {
- // 1D array
- nestedSort.push( a );
- }
- else {
- // 2D array
- $.merge( nestedSort, a );
- }
- };
-
- // Build the sort array, with pre-fix and post-fix options if they have been
- // specified
- if ( $.isArray( fixed ) ) {
- add( fixed );
- }
-
- if ( fixedObj && fixed.pre ) {
- add( fixed.pre );
- }
-
- add( settings.aaSorting );
-
- if (fixedObj && fixed.post ) {
- add( fixed.post );
- }
-
- for ( i=0 ; i<nestedSort.length ; i++ )
- {
- srcCol = nestedSort[i][0];
- aDataSort = aoColumns[ srcCol ].aDataSort;
-
- for ( k=0, kLen=aDataSort.length ; k<kLen ; k++ )
- {
- iCol = aDataSort[k];
- sType = aoColumns[ iCol ].sType || 'string';
-
- if ( nestedSort[i]._idx === undefined ) {
- nestedSort[i]._idx = $.inArray( nestedSort[i][1], aoColumns[iCol].asSorting );
- }
-
- aSort.push( {
- src: srcCol,
- col: iCol,
- dir: nestedSort[i][1],
- index: nestedSort[i]._idx,
- type: sType,
- formatter: DataTable.ext.type.order[ sType+"-pre" ]
- } );
- }
- }
-
- return aSort;
- }
-
- /**
- * Change the order of the table
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- * @todo This really needs split up!
- */
- function _fnSort ( oSettings )
- {
- var
- i, ien, iLen, j, jLen, k, kLen,
- sDataType, nTh,
- aiOrig = [],
- oExtSort = DataTable.ext.type.order,
- aoData = oSettings.aoData,
- aoColumns = oSettings.aoColumns,
- aDataSort, data, iCol, sType, oSort,
- formatters = 0,
- sortCol,
- displayMaster = oSettings.aiDisplayMaster,
- aSort;
-
- // Resolve any column types that are unknown due to addition or invalidation
- // @todo Can this be moved into a 'data-ready' handler which is called when
- // data is going to be used in the table?
- _fnColumnTypes( oSettings );
-
- aSort = _fnSortFlatten( oSettings );
-
- for ( i=0, ien=aSort.length ; i<ien ; i++ ) {
- sortCol = aSort[i];
-
- // Track if we can use the fast sort algorithm
- if ( sortCol.formatter ) {
- formatters++;
- }
-
- // Load the data needed for the sort, for each cell
- _fnSortData( oSettings, sortCol.col );
- }
-
- /* No sorting required if server-side or no sorting array */
- if ( _fnDataSource( oSettings ) != 'ssp' && aSort.length !== 0 )
- {
- // Create a value - key array of the current row positions such that we can use their
- // current position during the sort, if values match, in order to perform stable sorting
- for ( i=0, iLen=displayMaster.length ; i<iLen ; i++ ) {
- aiOrig[ displayMaster[i] ] = i;
- }
-
- /* Do the sort - here we want multi-column sorting based on a given data source (column)
- * and sorting function (from oSort) in a certain direction. It's reasonably complex to
- * follow on it's own, but this is what we want (example two column sorting):
- * fnLocalSorting = function(a,b){
- * var iTest;
- * iTest = oSort['string-asc']('data11', 'data12');
- * if (iTest !== 0)
- * return iTest;
- * iTest = oSort['numeric-desc']('data21', 'data22');
- * if (iTest !== 0)
- * return iTest;
- * return oSort['numeric-asc']( aiOrig[a], aiOrig[b] );
- * }
- * Basically we have a test for each sorting column, if the data in that column is equal,
- * test the next column. If all columns match, then we use a numeric sort on the row
- * positions in the original data array to provide a stable sort.
- *
- * Note - I know it seems excessive to have two sorting methods, but the first is around
- * 15% faster, so the second is only maintained for backwards compatibility with sorting
- * methods which do not have a pre-sort formatting function.
- */
- if ( formatters === aSort.length ) {
- // All sort types have formatting functions
- displayMaster.sort( function ( a, b ) {
- var
- x, y, k, test, sort,
- len=aSort.length,
- dataA = aoData[a]._aSortData,
- dataB = aoData[b]._aSortData;
-
- for ( k=0 ; k<len ; k++ ) {
- sort = aSort[k];
-
- x = dataA[ sort.col ];
- y = dataB[ sort.col ];
-
- test = x<y ? -1 : x>y ? 1 : 0;
- if ( test !== 0 ) {
- return sort.dir === 'asc' ? test : -test;
- }
- }
-
- x = aiOrig[a];
- y = aiOrig[b];
- return x<y ? -1 : x>y ? 1 : 0;
- } );
- }
- else {
- // Depreciated - remove in 1.11 (providing a plug-in option)
- // Not all sort types have formatting methods, so we have to call their sorting
- // methods.
- displayMaster.sort( function ( a, b ) {
- var
- x, y, k, l, test, sort, fn,
- len=aSort.length,
- dataA = aoData[a]._aSortData,
- dataB = aoData[b]._aSortData;
-
- for ( k=0 ; k<len ; k++ ) {
- sort = aSort[k];
-
- x = dataA[ sort.col ];
- y = dataB[ sort.col ];
-
- fn = oExtSort[ sort.type+"-"+sort.dir ] || oExtSort[ "string-"+sort.dir ];
- test = fn( x, y );
- if ( test !== 0 ) {
- return test;
- }
- }
-
- x = aiOrig[a];
- y = aiOrig[b];
- return x<y ? -1 : x>y ? 1 : 0;
- } );
- }
- }
-
- /* Tell the draw function that we have sorted the data */
- oSettings.bSorted = true;
- }
-
-
- function _fnSortAria ( settings )
- {
- var label;
- var nextSort;
- var columns = settings.aoColumns;
- var aSort = _fnSortFlatten( settings );
- var oAria = settings.oLanguage.oAria;
-
- // ARIA attributes - need to loop all columns, to update all (removing old
- // attributes as needed)
- for ( var i=0, iLen=columns.length ; i<iLen ; i++ )
- {
- var col = columns[i];
- var asSorting = col.asSorting;
- var sTitle = col.sTitle.replace( /<.*?>/g, "" );
- var th = col.nTh;
-
- // IE7 is throwing an error when setting these properties with jQuery's
- // attr() and removeAttr() methods...
- th.removeAttribute('aria-sort');
-
- /* In ARIA only the first sorting column can be marked as sorting - no multi-sort option */
- if ( col.bSortable ) {
- if ( aSort.length > 0 && aSort[0].col == i ) {
- th.setAttribute('aria-sort', aSort[0].dir=="asc" ? "ascending" : "descending" );
- nextSort = asSorting[ aSort[0].index+1 ] || asSorting[0];
- }
- else {
- nextSort = asSorting[0];
- }
-
- label = sTitle + ( nextSort === "asc" ?
- oAria.sSortAscending :
- oAria.sSortDescending
- );
- }
- else {
- label = sTitle;
- }
-
- th.setAttribute('aria-label', label);
- }
- }
-
-
- /**
- * Function to run on user sort request
- * @param {object} settings dataTables settings object
- * @param {node} attachTo node to attach the handler to
- * @param {int} colIdx column sorting index
- * @param {boolean} [append=false] Append the requested sort to the existing
- * sort if true (i.e. multi-column sort)
- * @param {function} [callback] callback function
- * @memberof DataTable#oApi
- */
- function _fnSortListener ( settings, colIdx, append, callback )
- {
- var col = settings.aoColumns[ colIdx ];
- var sorting = settings.aaSorting;
- var asSorting = col.asSorting;
- var nextSortIdx;
- var next = function ( a, overflow ) {
- var idx = a._idx;
- if ( idx === undefined ) {
- idx = $.inArray( a[1], asSorting );
- }
-
- return idx+1 < asSorting.length ?
- idx+1 :
- overflow ?
- null :
- 0;
- };
-
- // Convert to 2D array if needed
- if ( typeof sorting[0] === 'number' ) {
- sorting = settings.aaSorting = [ sorting ];
- }
-
- // If appending the sort then we are multi-column sorting
- if ( append && settings.oFeatures.bSortMulti ) {
- // Are we already doing some kind of sort on this column?
- var sortIdx = $.inArray( colIdx, _pluck(sorting, '0') );
-
- if ( sortIdx !== -1 ) {
- // Yes, modify the sort
- nextSortIdx = next( sorting[sortIdx], true );
-
- if ( nextSortIdx === null && sorting.length === 1 ) {
- nextSortIdx = 0; // can't remove sorting completely
- }
-
- if ( nextSortIdx === null ) {
- sorting.splice( sortIdx, 1 );
- }
- else {
- sorting[sortIdx][1] = asSorting[ nextSortIdx ];
- sorting[sortIdx]._idx = nextSortIdx;
- }
- }
- else {
- // No sort on this column yet
- sorting.push( [ colIdx, asSorting[0], 0 ] );
- sorting[sorting.length-1]._idx = 0;
- }
- }
- else if ( sorting.length && sorting[0][0] == colIdx ) {
- // Single column - already sorting on this column, modify the sort
- nextSortIdx = next( sorting[0] );
-
- sorting.length = 1;
- sorting[0][1] = asSorting[ nextSortIdx ];
- sorting[0]._idx = nextSortIdx;
- }
- else {
- // Single column - sort only on this column
- sorting.length = 0;
- sorting.push( [ colIdx, asSorting[0] ] );
- sorting[0]._idx = 0;
- }
-
- // Run the sort by calling a full redraw
- _fnReDraw( settings );
-
- // callback used for async user interaction
- if ( typeof callback == 'function' ) {
- callback( settings );
- }
- }
-
-
- /**
- * Attach a sort handler (click) to a node
- * @param {object} settings dataTables settings object
- * @param {node} attachTo node to attach the handler to
- * @param {int} colIdx column sorting index
- * @param {function} [callback] callback function
- * @memberof DataTable#oApi
- */
- function _fnSortAttachListener ( settings, attachTo, colIdx, callback )
- {
- var col = settings.aoColumns[ colIdx ];
-
- _fnBindAction( attachTo, {}, function (e) {
- /* If the column is not sortable - don't to anything */
- if ( col.bSortable === false ) {
- return;
- }
-
- // If processing is enabled use a timeout to allow the processing
- // display to be shown - otherwise to it synchronously
- if ( settings.oFeatures.bProcessing ) {
- _fnProcessingDisplay( settings, true );
-
- setTimeout( function() {
- _fnSortListener( settings, colIdx, e.shiftKey, callback );
-
- // In server-side processing, the draw callback will remove the
- // processing display
- if ( _fnDataSource( settings ) !== 'ssp' ) {
- _fnProcessingDisplay( settings, false );
- }
- }, 0 );
- }
- else {
- _fnSortListener( settings, colIdx, e.shiftKey, callback );
- }
- } );
- }
-
-
- /**
- * Set the sorting classes on table's body, Note: it is safe to call this function
- * when bSort and bSortClasses are false
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnSortingClasses( settings )
- {
- var oldSort = settings.aLastSort;
- var sortClass = settings.oClasses.sSortColumn;
- var sort = _fnSortFlatten( settings );
- var features = settings.oFeatures;
- var i, ien, colIdx;
-
- if ( features.bSort && features.bSortClasses ) {
- // Remove old sorting classes
- for ( i=0, ien=oldSort.length ; i<ien ; i++ ) {
- colIdx = oldSort[i].src;
-
- // Remove column sorting
- $( _pluck( settings.aoData, 'anCells', colIdx ) )
- .removeClass( sortClass + (i<2 ? i+1 : 3) );
- }
-
- // Add new column sorting
- for ( i=0, ien=sort.length ; i<ien ; i++ ) {
- colIdx = sort[i].src;
-
- $( _pluck( settings.aoData, 'anCells', colIdx ) )
- .addClass( sortClass + (i<2 ? i+1 : 3) );
- }
- }
-
- settings.aLastSort = sort;
- }
-
-
- // Get the data to sort a column, be it from cache, fresh (populating the
- // cache), or from a sort formatter
- function _fnSortData( settings, idx )
- {
- // Custom sorting function - provided by the sort data type
- var column = settings.aoColumns[ idx ];
- var customSort = DataTable.ext.order[ column.sSortDataType ];
- var customData;
-
- if ( customSort ) {
- customData = customSort.call( settings.oInstance, settings, idx,
- _fnColumnIndexToVisible( settings, idx )
- );
- }
-
- // Use / populate cache
- var row, cellData;
- var formatter = DataTable.ext.type.order[ column.sType+"-pre" ];
-
- for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
- row = settings.aoData[i];
-
- if ( ! row._aSortData ) {
- row._aSortData = [];
- }
-
- if ( ! row._aSortData[idx] || customSort ) {
- cellData = customSort ?
- customData[i] : // If there was a custom sort function, use data from there
- _fnGetCellData( settings, i, idx, 'sort' );
-
- row._aSortData[ idx ] = formatter ?
- formatter( cellData ) :
- cellData;
- }
- }
- }
-
-
-
- /**
- * Save the state of a table
- * @param {object} oSettings dataTables settings object
- * @memberof DataTable#oApi
- */
- function _fnSaveState ( settings )
- {
- if ( !settings.oFeatures.bStateSave || settings.bDestroying )
- {
- return;
- }
-
- /* Store the interesting variables */
- var state = {
- time: +new Date(),
- start: settings._iDisplayStart,
- length: settings._iDisplayLength,
- order: $.extend( true, [], settings.aaSorting ),
- search: _fnSearchToCamel( settings.oPreviousSearch ),
- columns: $.map( settings.aoColumns, function ( col, i ) {
- return {
- visible: col.bVisible,
- search: _fnSearchToCamel( settings.aoPreSearchCols[i] )
- };
- } )
- };
-
- _fnCallbackFire( settings, "aoStateSaveParams", 'stateSaveParams', [settings, state] );
-
- settings.oSavedState = state;
- settings.fnStateSaveCallback.call( settings.oInstance, settings, state );
- }
-
-
- /**
- * Attempt to load a saved table state
- * @param {object} oSettings dataTables settings object
- * @param {object} oInit DataTables init object so we can override settings
- * @param {function} callback Callback to execute when the state has been loaded
- * @memberof DataTable#oApi
- */
- function _fnLoadState ( settings, oInit, callback )
- {
- var i, ien;
- var columns = settings.aoColumns;
- var loaded = function ( s ) {
- if ( ! s || ! s.time ) {
- callback();
- return;
- }
-
- // Allow custom and plug-in manipulation functions to alter the saved data set and
- // cancelling of loading by returning false
- var abStateLoad = _fnCallbackFire( settings, 'aoStateLoadParams', 'stateLoadParams', [settings, s] );
- if ( $.inArray( false, abStateLoad ) !== -1 ) {
- callback();
- return;
- }
-
- // Reject old data
- var duration = settings.iStateDuration;
- if ( duration > 0 && s.time < +new Date() - (duration*1000) ) {
- callback();
- return;
- }
-
- // Number of columns have changed - all bets are off, no restore of settings
- if ( s.columns && columns.length !== s.columns.length ) {
- callback();
- return;
- }
-
- // Store the saved state so it might be accessed at any time
- settings.oLoadedState = $.extend( true, {}, s );
-
- // Restore key features - todo - for 1.11 this needs to be done by
- // subscribed events
- if ( s.start !== undefined ) {
- settings._iDisplayStart = s.start;
- settings.iInitDisplayStart = s.start;
- }
- if ( s.length !== undefined ) {
- settings._iDisplayLength = s.length;
- }
-
- // Order
- if ( s.order !== undefined ) {
- settings.aaSorting = [];
- $.each( s.order, function ( i, col ) {
- settings.aaSorting.push( col[0] >= columns.length ?
- [ 0, col[1] ] :
- col
- );
- } );
- }
-
- // Search
- if ( s.search !== undefined ) {
- $.extend( settings.oPreviousSearch, _fnSearchToHung( s.search ) );
- }
-
- // Columns
- //
- if ( s.columns ) {
- for ( i=0, ien=s.columns.length ; i<ien ; i++ ) {
- var col = s.columns[i];
-
- // Visibility
- if ( col.visible !== undefined ) {
- columns[i].bVisible = col.visible;
- }
-
- // Search
- if ( col.search !== undefined ) {
- $.extend( settings.aoPreSearchCols[i], _fnSearchToHung( col.search ) );
- }
- }
- }
-
- _fnCallbackFire( settings, 'aoStateLoaded', 'stateLoaded', [settings, s] );
- callback();
- }
-
- if ( ! settings.oFeatures.bStateSave ) {
- callback();
- return;
- }
-
- var state = settings.fnStateLoadCallback.call( settings.oInstance, settings, loaded );
-
- if ( state !== undefined ) {
- loaded( state );
- }
- // otherwise, wait for the loaded callback to be executed
- }
-
-
- /**
- * Return the settings object for a particular table
- * @param {node} table table we are using as a dataTable
- * @returns {object} Settings object - or null if not found
- * @memberof DataTable#oApi
- */
- function _fnSettingsFromNode ( table )
- {
- var settings = DataTable.settings;
- var idx = $.inArray( table, _pluck( settings, 'nTable' ) );
-
- return idx !== -1 ?
- settings[ idx ] :
- null;
- }
-
-
- /**
- * Log an error message
- * @param {object} settings dataTables settings object
- * @param {int} level log error messages, or display them to the user
- * @param {string} msg error message
- * @param {int} tn Technical note id to get more information about the error.
- * @memberof DataTable#oApi
- */
- function _fnLog( settings, level, msg, tn )
- {
- msg = 'DataTables warning: '+
- (settings ? 'table id='+settings.sTableId+' - ' : '')+msg;
-
- if ( tn ) {
- msg += '. For more information about this error, please see '+
- 'http://datatables.net/tn/'+tn;
- }
-
- if ( ! level ) {
- // Backwards compatibility pre 1.10
- var ext = DataTable.ext;
- var type = ext.sErrMode || ext.errMode;
-
- if ( settings ) {
- _fnCallbackFire( settings, null, 'error', [ settings, tn, msg ] );
- }
-
- if ( type == 'alert' ) {
- alert( msg );
- }
- else if ( type == 'throw' ) {
- throw new Error(msg);
- }
- else if ( typeof type == 'function' ) {
- type( settings, tn, msg );
- }
- }
- else if ( window.console && console.log ) {
- console.log( msg );
- }
- }
-
-
- /**
- * See if a property is defined on one object, if so assign it to the other object
- * @param {object} ret target object
- * @param {object} src source object
- * @param {string} name property
- * @param {string} [mappedName] name to map too - optional, name used if not given
- * @memberof DataTable#oApi
- */
- function _fnMap( ret, src, name, mappedName )
- {
- if ( $.isArray( name ) ) {
- $.each( name, function (i, val) {
- if ( $.isArray( val ) ) {
- _fnMap( ret, src, val[0], val[1] );
- }
- else {
- _fnMap( ret, src, val );
- }
- } );
-
- return;
- }
-
- if ( mappedName === undefined ) {
- mappedName = name;
- }
-
- if ( src[name] !== undefined ) {
- ret[mappedName] = src[name];
- }
- }
-
-
- /**
- * Extend objects - very similar to jQuery.extend, but deep copy objects, and
- * shallow copy arrays. The reason we need to do this, is that we don't want to
- * deep copy array init values (such as aaSorting) since the dev wouldn't be
- * able to override them, but we do want to deep copy arrays.
- * @param {object} out Object to extend
- * @param {object} extender Object from which the properties will be applied to
- * out
- * @param {boolean} breakRefs If true, then arrays will be sliced to take an
- * independent copy with the exception of the `data` or `aaData` parameters
- * if they are present. This is so you can pass in a collection to
- * DataTables and have that used as your data source without breaking the
- * references
- * @returns {object} out Reference, just for convenience - out === the return.
- * @memberof DataTable#oApi
- * @todo This doesn't take account of arrays inside the deep copied objects.
- */
- function _fnExtend( out, extender, breakRefs )
- {
- var val;
-
- for ( var prop in extender ) {
- if ( extender.hasOwnProperty(prop) ) {
- val = extender[prop];
-
- if ( $.isPlainObject( val ) ) {
- if ( ! $.isPlainObject( out[prop] ) ) {
- out[prop] = {};
- }
- $.extend( true, out[prop], val );
- }
- else if ( breakRefs && prop !== 'data' && prop !== 'aaData' && $.isArray(val) ) {
- out[prop] = val.slice();
- }
- else {
- out[prop] = val;
- }
- }
- }
-
- return out;
- }
-
-
- /**
- * Bind an event handers to allow a click or return key to activate the callback.
- * This is good for accessibility since a return on the keyboard will have the
- * same effect as a click, if the element has focus.
- * @param {element} n Element to bind the action to
- * @param {object} oData Data object to pass to the triggered function
- * @param {function} fn Callback function for when the event is triggered
- * @memberof DataTable#oApi
- */
- function _fnBindAction( n, oData, fn )
- {
- $(n)
- .on( 'click.DT', oData, function (e) {
- $(n).blur(); // Remove focus outline for mouse users
- fn(e);
- } )
- .on( 'keypress.DT', oData, function (e){
- if ( e.which === 13 ) {
- e.preventDefault();
- fn(e);
- }
- } )
- .on( 'selectstart.DT', function () {
- /* Take the brutal approach to cancelling text selection */
- return false;
- } );
- }
-
-
- /**
- * Register a callback function. Easily allows a callback function to be added to
- * an array store of callback functions that can then all be called together.
- * @param {object} oSettings dataTables settings object
- * @param {string} sStore Name of the array storage for the callbacks in oSettings
- * @param {function} fn Function to be called back
- * @param {string} sName Identifying name for the callback (i.e. a label)
- * @memberof DataTable#oApi
- */
- function _fnCallbackReg( oSettings, sStore, fn, sName )
- {
- if ( fn )
- {
- oSettings[sStore].push( {
- "fn": fn,
- "sName": sName
- } );
- }
- }
-
-
- /**
- * Fire callback functions and trigger events. Note that the loop over the
- * callback array store is done backwards! Further note that you do not want to
- * fire off triggers in time sensitive applications (for example cell creation)
- * as its slow.
- * @param {object} settings dataTables settings object
- * @param {string} callbackArr Name of the array storage for the callbacks in
- * oSettings
- * @param {string} eventName Name of the jQuery custom event to trigger. If
- * null no trigger is fired
- * @param {array} args Array of arguments to pass to the callback function /
- * trigger
- * @memberof DataTable#oApi
- */
- function _fnCallbackFire( settings, callbackArr, eventName, args )
- {
- var ret = [];
-
- if ( callbackArr ) {
- ret = $.map( settings[callbackArr].slice().reverse(), function (val, i) {
- return val.fn.apply( settings.oInstance, args );
- } );
- }
-
- if ( eventName !== null ) {
- var e = $.Event( eventName+'.dt' );
-
- $(settings.nTable).trigger( e, args );
-
- ret.push( e.result );
- }
-
- return ret;
- }
-
-
- function _fnLengthOverflow ( settings )
- {
- var
- start = settings._iDisplayStart,
- end = settings.fnDisplayEnd(),
- len = settings._iDisplayLength;
-
- /* If we have space to show extra rows (backing up from the end point - then do so */
- if ( start >= end )
- {
- start = end - len;
- }
-
- // Keep the start record on the current page
- start -= (start % len);
-
- if ( len === -1 || start < 0 )
- {
- start = 0;
- }
-
- settings._iDisplayStart = start;
- }
-
-
- function _fnRenderer( settings, type )
- {
- var renderer = settings.renderer;
- var host = DataTable.ext.renderer[type];
-
- if ( $.isPlainObject( renderer ) && renderer[type] ) {
- // Specific renderer for this type. If available use it, otherwise use
- // the default.
- return host[renderer[type]] || host._;
- }
- else if ( typeof renderer === 'string' ) {
- // Common renderer - if there is one available for this type use it,
- // otherwise use the default
- return host[renderer] || host._;
- }
-
- // Use the default
- return host._;
- }
-
-
- /**
- * Detect the data source being used for the table. Used to simplify the code
- * a little (ajax) and to make it compress a little smaller.
- *
- * @param {object} settings dataTables settings object
- * @returns {string} Data source
- * @memberof DataTable#oApi
- */
- function _fnDataSource ( settings )
- {
- if ( settings.oFeatures.bServerSide ) {
- return 'ssp';
- }
- else if ( settings.ajax || settings.sAjaxSource ) {
- return 'ajax';
- }
- return 'dom';
- }
-
-
-
- /**
- * Computed structure of the DataTables API, defined by the options passed to
- * `DataTable.Api.register()` when building the API.
- *
- * The structure is built in order to speed creation and extension of the Api
- * objects since the extensions are effectively pre-parsed.
- *
- * The array is an array of objects with the following structure, where this
- * base array represents the Api prototype base:
- *
- * [
- * {
- * name: 'data' -- string - Property name
- * val: function () {}, -- function - Api method (or undefined if just an object
- * methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
- * propExt: [ ... ] -- array - Array of Api object definitions to extend the property
- * },
- * {
- * name: 'row'
- * val: {},
- * methodExt: [ ... ],
- * propExt: [
- * {
- * name: 'data'
- * val: function () {},
- * methodExt: [ ... ],
- * propExt: [ ... ]
- * },
- * ...
- * ]
- * }
- * ]
- *
- * @type {Array}
- * @ignore
- */
- var __apiStruct = [];
-
-
- /**
- * `Array.prototype` reference.
- *
- * @type object
- * @ignore
- */
- var __arrayProto = Array.prototype;
-
-
- /**
- * Abstraction for `context` parameter of the `Api` constructor to allow it to
- * take several different forms for ease of use.
- *
- * Each of the input parameter types will be converted to a DataTables settings
- * object where possible.
- *
- * @param {string|node|jQuery|object} mixed DataTable identifier. Can be one
- * of:
- *
- * * `string` - jQuery selector. Any DataTables' matching the given selector
- * with be found and used.
- * * `node` - `TABLE` node which has already been formed into a DataTable.
- * * `jQuery` - A jQuery object of `TABLE` nodes.
- * * `object` - DataTables settings object
- * * `DataTables.Api` - API instance
- * @return {array|null} Matching DataTables settings objects. `null` or
- * `undefined` is returned if no matching DataTable is found.
- * @ignore
- */
- var _toSettings = function ( mixed )
- {
- var idx, jq;
- var settings = DataTable.settings;
- var tables = $.map( settings, function (el, i) {
- return el.nTable;
- } );
-
- if ( ! mixed ) {
- return [];
- }
- else if ( mixed.nTable && mixed.oApi ) {
- // DataTables settings object
- return [ mixed ];
- }
- else if ( mixed.nodeName && mixed.nodeName.toLowerCase() === 'table' ) {
- // Table node
- idx = $.inArray( mixed, tables );
- return idx !== -1 ? [ settings[idx] ] : null;
- }
- else if ( mixed && typeof mixed.settings === 'function' ) {
- return mixed.settings().toArray();
- }
- else if ( typeof mixed === 'string' ) {
- // jQuery selector
- jq = $(mixed);
- }
- else if ( mixed instanceof $ ) {
- // jQuery object (also DataTables instance)
- jq = mixed;
- }
-
- if ( jq ) {
- return jq.map( function(i) {
- idx = $.inArray( this, tables );
- return idx !== -1 ? settings[idx] : null;
- } ).toArray();
- }
- };
-
-
- /**
- * DataTables API class - used to control and interface with one or more
- * DataTables enhanced tables.
- *
- * The API class is heavily based on jQuery, presenting a chainable interface
- * that you can use to interact with tables. Each instance of the API class has
- * a "context" - i.e. the tables that it will operate on. This could be a single
- * table, all tables on a page or a sub-set thereof.
- *
- * Additionally the API is designed to allow you to easily work with the data in
- * the tables, retrieving and manipulating it as required. This is done by
- * presenting the API class as an array like interface. The contents of the
- * array depend upon the actions requested by each method (for example
- * `rows().nodes()` will return an array of nodes, while `rows().data()` will
- * return an array of objects or arrays depending upon your table's
- * configuration). The API object has a number of array like methods (`push`,
- * `pop`, `reverse` etc) as well as additional helper methods (`each`, `pluck`,
- * `unique` etc) to assist your working with the data held in a table.
- *
- * Most methods (those which return an Api instance) are chainable, which means
- * the return from a method call also has all of the methods available that the
- * top level object had. For example, these two calls are equivalent:
- *
- * // Not chained
- * api.row.add( {...} );
- * api.draw();
- *
- * // Chained
- * api.row.add( {...} ).draw();
- *
- * @class DataTable.Api
- * @param {array|object|string|jQuery} context DataTable identifier. This is
- * used to define which DataTables enhanced tables this API will operate on.
- * Can be one of:
- *
- * * `string` - jQuery selector. Any DataTables' matching the given selector
- * with be found and used.
- * * `node` - `TABLE` node which has already been formed into a DataTable.
- * * `jQuery` - A jQuery object of `TABLE` nodes.
- * * `object` - DataTables settings object
- * @param {array} [data] Data to initialise the Api instance with.
- *
- * @example
- * // Direct initialisation during DataTables construction
- * var api = $('#example').DataTable();
- *
- * @example
- * // Initialisation using a DataTables jQuery object
- * var api = $('#example').dataTable().api();
- *
- * @example
- * // Initialisation as a constructor
- * var api = new $.fn.DataTable.Api( 'table.dataTable' );
- */
- _Api = function ( context, data )
- {
- if ( ! (this instanceof _Api) ) {
- return new _Api( context, data );
- }
-
- var settings = [];
- var ctxSettings = function ( o ) {
- var a = _toSettings( o );
- if ( a ) {
- settings = settings.concat( a );
- }
- };
-
- if ( $.isArray( context ) ) {
- for ( var i=0, ien=context.length ; i<ien ; i++ ) {
- ctxSettings( context[i] );
- }
- }
- else {
- ctxSettings( context );
- }
-
- // Remove duplicates
- this.context = _unique( settings );
-
- // Initial data
- if ( data ) {
- $.merge( this, data );
- }
-
- // selector
- this.selector = {
- rows: null,
- cols: null,
- opts: null
- };
-
- _Api.extend( this, this, __apiStruct );
- };
-
- DataTable.Api = _Api;
-
- // Don't destroy the existing prototype, just extend it. Required for jQuery 2's
- // isPlainObject.
- $.extend( _Api.prototype, {
- any: function ()
- {
- return this.count() !== 0;
- },
-
-
- concat: __arrayProto.concat,
-
-
- context: [], // array of table settings objects
-
-
- count: function ()
- {
- return this.flatten().length;
- },
-
-
- each: function ( fn )
- {
- for ( var i=0, ien=this.length ; i<ien; i++ ) {
- fn.call( this, this[i], i, this );
- }
-
- return this;
- },
-
-
- eq: function ( idx )
- {
- var ctx = this.context;
-
- return ctx.length > idx ?
- new _Api( ctx[idx], this[idx] ) :
- null;
- },
-
-
- filter: function ( fn )
- {
- var a = [];
-
- if ( __arrayProto.filter ) {
- a = __arrayProto.filter.call( this, fn, this );
- }
- else {
- // Compatibility for browsers without EMCA-252-5 (JS 1.6)
- for ( var i=0, ien=this.length ; i<ien ; i++ ) {
- if ( fn.call( this, this[i], i, this ) ) {
- a.push( this[i] );
- }
- }
- }
-
- return new _Api( this.context, a );
- },
-
-
- flatten: function ()
- {
- var a = [];
- return new _Api( this.context, a.concat.apply( a, this.toArray() ) );
- },
-
-
- join: __arrayProto.join,
-
-
- indexOf: __arrayProto.indexOf || function (obj, start)
- {
- for ( var i=(start || 0), ien=this.length ; i<ien ; i++ ) {
- if ( this[i] === obj ) {
- return i;
- }
- }
- return -1;
- },
-
- iterator: function ( flatten, type, fn, alwaysNew ) {
- var
- a = [], ret,
- i, ien, j, jen,
- context = this.context,
- rows, items, item,
- selector = this.selector;
-
- // Argument shifting
- if ( typeof flatten === 'string' ) {
- alwaysNew = fn;
- fn = type;
- type = flatten;
- flatten = false;
- }
-
- for ( i=0, ien=context.length ; i<ien ; i++ ) {
- var apiInst = new _Api( context[i] );
-
- if ( type === 'table' ) {
- ret = fn.call( apiInst, context[i], i );
-
- if ( ret !== undefined ) {
- a.push( ret );
- }
- }
- else if ( type === 'columns' || type === 'rows' ) {
- // this has same length as context - one entry for each table
- ret = fn.call( apiInst, context[i], this[i], i );
-
- if ( ret !== undefined ) {
- a.push( ret );
- }
- }
- else if ( type === 'column' || type === 'column-rows' || type === 'row' || type === 'cell' ) {
- // columns and rows share the same structure.
- // 'this' is an array of column indexes for each context
- items = this[i];
-
- if ( type === 'column-rows' ) {
- rows = _selector_row_indexes( context[i], selector.opts );
- }
-
- for ( j=0, jen=items.length ; j<jen ; j++ ) {
- item = items[j];
-
- if ( type === 'cell' ) {
- ret = fn.call( apiInst, context[i], item.row, item.column, i, j );
- }
- else {
- ret = fn.call( apiInst, context[i], item, i, j, rows );
- }
-
- if ( ret !== undefined ) {
- a.push( ret );
- }
- }
- }
- }
-
- if ( a.length || alwaysNew ) {
- var api = new _Api( context, flatten ? a.concat.apply( [], a ) : a );
- var apiSelector = api.selector;
- apiSelector.rows = selector.rows;
- apiSelector.cols = selector.cols;
- apiSelector.opts = selector.opts;
- return api;
- }
- return this;
- },
-
-
- lastIndexOf: __arrayProto.lastIndexOf || function (obj, start)
- {
- // Bit cheeky...
- return this.indexOf.apply( this.toArray.reverse(), arguments );
- },
-
-
- length: 0,
-
-
- map: function ( fn )
- {
- var a = [];
-
- if ( __arrayProto.map ) {
- a = __arrayProto.map.call( this, fn, this );
- }
- else {
- // Compatibility for browsers without EMCA-252-5 (JS 1.6)
- for ( var i=0, ien=this.length ; i<ien ; i++ ) {
- a.push( fn.call( this, this[i], i ) );
- }
- }
-
- return new _Api( this.context, a );
- },
-
-
- pluck: function ( prop )
- {
- return this.map( function ( el ) {
- return el[ prop ];
- } );
- },
-
- pop: __arrayProto.pop,
-
-
- push: __arrayProto.push,
-
-
- // Does not return an API instance
- reduce: __arrayProto.reduce || function ( fn, init )
- {
- return _fnReduce( this, fn, init, 0, this.length, 1 );
- },
-
-
- reduceRight: __arrayProto.reduceRight || function ( fn, init )
- {
- return _fnReduce( this, fn, init, this.length-1, -1, -1 );
- },
-
-
- reverse: __arrayProto.reverse,
-
-
- // Object with rows, columns and opts
- selector: null,
-
-
- shift: __arrayProto.shift,
-
-
- slice: function () {
- return new _Api( this.context, this );
- },
-
-
- sort: __arrayProto.sort, // ? name - order?
-
-
- splice: __arrayProto.splice,
-
-
- toArray: function ()
- {
- return __arrayProto.slice.call( this );
- },
-
-
- to$: function ()
- {
- return $( this );
- },
-
-
- toJQuery: function ()
- {
- return $( this );
- },
-
-
- unique: function ()
- {
- return new _Api( this.context, _unique(this) );
- },
-
-
- unshift: __arrayProto.unshift
- } );
-
-
- _Api.extend = function ( scope, obj, ext )
- {
- // Only extend API instances and static properties of the API
- if ( ! ext.length || ! obj || ( ! (obj instanceof _Api) && ! obj.__dt_wrapper ) ) {
- return;
- }
-
- var
- i, ien,
- j, jen,
- struct, inner,
- methodScoping = function ( scope, fn, struc ) {
- return function () {
- var ret = fn.apply( scope, arguments );
-
- // Method extension
- _Api.extend( ret, ret, struc.methodExt );
- return ret;
- };
- };
-
- for ( i=0, ien=ext.length ; i<ien ; i++ ) {
- struct = ext[i];
-
- // Value
- obj[ struct.name ] = typeof struct.val === 'function' ?
- methodScoping( scope, struct.val, struct ) :
- $.isPlainObject( struct.val ) ?
- {} :
- struct.val;
-
- obj[ struct.name ].__dt_wrapper = true;
-
- // Property extension
- _Api.extend( scope, obj[ struct.name ], struct.propExt );
- }
- };
-
-
- // @todo - Is there need for an augment function?
- // _Api.augment = function ( inst, name )
- // {
- // // Find src object in the structure from the name
- // var parts = name.split('.');
-
- // _Api.extend( inst, obj );
- // };
-
-
- // [
- // {
- // name: 'data' -- string - Property name
- // val: function () {}, -- function - Api method (or undefined if just an object
- // methodExt: [ ... ], -- array - Array of Api object definitions to extend the method result
- // propExt: [ ... ] -- array - Array of Api object definitions to extend the property
- // },
- // {
- // name: 'row'
- // val: {},
- // methodExt: [ ... ],
- // propExt: [
- // {
- // name: 'data'
- // val: function () {},
- // methodExt: [ ... ],
- // propExt: [ ... ]
- // },
- // ...
- // ]
- // }
- // ]
-
- _Api.register = _api_register = function ( name, val )
- {
- if ( $.isArray( name ) ) {
- for ( var j=0, jen=name.length ; j<jen ; j++ ) {
- _Api.register( name[j], val );
- }
- return;
- }
-
- var
- i, ien,
- heir = name.split('.'),
- struct = __apiStruct,
- key, method;
-
- var find = function ( src, name ) {
- for ( var i=0, ien=src.length ; i<ien ; i++ ) {
- if ( src[i].name === name ) {
- return src[i];
- }
- }
- return null;
- };
-
- for ( i=0, ien=heir.length ; i<ien ; i++ ) {
- method = heir[i].indexOf('()') !== -1;
- key = method ?
- heir[i].replace('()', '') :
- heir[i];
-
- var src = find( struct, key );
- if ( ! src ) {
- src = {
- name: key,
- val: {},
- methodExt: [],
- propExt: []
- };
- struct.push( src );
- }
-
- if ( i === ien-1 ) {
- src.val = val;
- }
- else {
- struct = method ?
- src.methodExt :
- src.propExt;
- }
- }
- };
-
-
- _Api.registerPlural = _api_registerPlural = function ( pluralName, singularName, val ) {
- _Api.register( pluralName, val );
-
- _Api.register( singularName, function () {
- var ret = val.apply( this, arguments );
-
- if ( ret === this ) {
- // Returned item is the API instance that was passed in, return it
- return this;
- }
- else if ( ret instanceof _Api ) {
- // New API instance returned, want the value from the first item
- // in the returned array for the singular result.
- return ret.length ?
- $.isArray( ret[0] ) ?
- new _Api( ret.context, ret[0] ) : // Array results are 'enhanced'
- ret[0] :
- undefined;
- }
-
- // Non-API return - just fire it back
- return ret;
- } );
- };
-
-
- /**
- * Selector for HTML tables. Apply the given selector to the give array of
- * DataTables settings objects.
- *
- * @param {string|integer} [selector] jQuery selector string or integer
- * @param {array} Array of DataTables settings objects to be filtered
- * @return {array}
- * @ignore
- */
- var __table_selector = function ( selector, a )
- {
- // Integer is used to pick out a table by index
- if ( typeof selector === 'number' ) {
- return [ a[ selector ] ];
- }
-
- // Perform a jQuery selector on the table nodes
- var nodes = $.map( a, function (el, i) {
- return el.nTable;
- } );
-
- return $(nodes)
- .filter( selector )
- .map( function (i) {
- // Need to translate back from the table node to the settings
- var idx = $.inArray( this, nodes );
- return a[ idx ];
- } )
- .toArray();
- };
-
-
-
- /**
- * Context selector for the API's context (i.e. the tables the API instance
- * refers to.
- *
- * @name DataTable.Api#tables
- * @param {string|integer} [selector] Selector to pick which tables the iterator
- * should operate on. If not given, all tables in the current context are
- * used. This can be given as a jQuery selector (for example `':gt(0)'`) to
- * select multiple tables or as an integer to select a single table.
- * @returns {DataTable.Api} Returns a new API instance if a selector is given.
- */
- _api_register( 'tables()', function ( selector ) {
- // A new instance is created if there was a selector specified
- return selector ?
- new _Api( __table_selector( selector, this.context ) ) :
- this;
- } );
-
-
- _api_register( 'table()', function ( selector ) {
- var tables = this.tables( selector );
- var ctx = tables.context;
-
- // Truncate to the first matched table
- return ctx.length ?
- new _Api( ctx[0] ) :
- tables;
- } );
-
-
- _api_registerPlural( 'tables().nodes()', 'table().node()' , function () {
- return this.iterator( 'table', function ( ctx ) {
- return ctx.nTable;
- }, 1 );
- } );
-
-
- _api_registerPlural( 'tables().body()', 'table().body()' , function () {
- return this.iterator( 'table', function ( ctx ) {
- return ctx.nTBody;
- }, 1 );
- } );
-
-
- _api_registerPlural( 'tables().header()', 'table().header()' , function () {
- return this.iterator( 'table', function ( ctx ) {
- return ctx.nTHead;
- }, 1 );
- } );
-
-
- _api_registerPlural( 'tables().footer()', 'table().footer()' , function () {
- return this.iterator( 'table', function ( ctx ) {
- return ctx.nTFoot;
- }, 1 );
- } );
-
-
- _api_registerPlural( 'tables().containers()', 'table().container()' , function () {
- return this.iterator( 'table', function ( ctx ) {
- return ctx.nTableWrapper;
- }, 1 );
- } );
-
-
-
- /**
- * Redraw the tables in the current context.
- */
- _api_register( 'draw()', function ( paging ) {
- return this.iterator( 'table', function ( settings ) {
- if ( paging === 'page' ) {
- _fnDraw( settings );
- }
- else {
- if ( typeof paging === 'string' ) {
- paging = paging === 'full-hold' ?
- false :
- true;
- }
-
- _fnReDraw( settings, paging===false );
- }
- } );
- } );
-
-
-
- /**
- * Get the current page index.
- *
- * @return {integer} Current page index (zero based)
- *//**
- * Set the current page.
- *
- * Note that if you attempt to show a page which does not exist, DataTables will
- * not throw an error, but rather reset the paging.
- *
- * @param {integer|string} action The paging action to take. This can be one of:
- * * `integer` - The page index to jump to
- * * `string` - An action to take:
- * * `first` - Jump to first page.
- * * `next` - Jump to the next page
- * * `previous` - Jump to previous page
- * * `last` - Jump to the last page.
- * @returns {DataTables.Api} this
- */
- _api_register( 'page()', function ( action ) {
- if ( action === undefined ) {
- return this.page.info().page; // not an expensive call
- }
-
- // else, have an action to take on all tables
- return this.iterator( 'table', function ( settings ) {
- _fnPageChange( settings, action );
- } );
- } );
-
-
- /**
- * Paging information for the first table in the current context.
- *
- * If you require paging information for another table, use the `table()` method
- * with a suitable selector.
- *
- * @return {object} Object with the following properties set:
- * * `page` - Current page index (zero based - i.e. the first page is `0`)
- * * `pages` - Total number of pages
- * * `start` - Display index for the first record shown on the current page
- * * `end` - Display index for the last record shown on the current page
- * * `length` - Display length (number of records). Note that generally `start
- * + length = end`, but this is not always true, for example if there are
- * only 2 records to show on the final page, with a length of 10.
- * * `recordsTotal` - Full data set length
- * * `recordsDisplay` - Data set length once the current filtering criterion
- * are applied.
- */
- _api_register( 'page.info()', function ( action ) {
- if ( this.context.length === 0 ) {
- return undefined;
- }
-
- var
- settings = this.context[0],
- start = settings._iDisplayStart,
- len = settings.oFeatures.bPaginate ? settings._iDisplayLength : -1,
- visRecords = settings.fnRecordsDisplay(),
- all = len === -1;
-
- return {
- "page": all ? 0 : Math.floor( start / len ),
- "pages": all ? 1 : Math.ceil( visRecords / len ),
- "start": start,
- "end": settings.fnDisplayEnd(),
- "length": len,
- "recordsTotal": settings.fnRecordsTotal(),
- "recordsDisplay": visRecords,
- "serverSide": _fnDataSource( settings ) === 'ssp'
- };
- } );
-
-
- /**
- * Get the current page length.
- *
- * @return {integer} Current page length. Note `-1` indicates that all records
- * are to be shown.
- *//**
- * Set the current page length.
- *
- * @param {integer} Page length to set. Use `-1` to show all records.
- * @returns {DataTables.Api} this
- */
- _api_register( 'page.len()', function ( len ) {
- // Note that we can't call this function 'length()' because `length`
- // is a Javascript property of functions which defines how many arguments
- // the function expects.
- if ( len === undefined ) {
- return this.context.length !== 0 ?
- this.context[0]._iDisplayLength :
- undefined;
- }
-
- // else, set the page length
- return this.iterator( 'table', function ( settings ) {
- _fnLengthChange( settings, len );
- } );
- } );
-
-
-
- var __reload = function ( settings, holdPosition, callback ) {
- // Use the draw event to trigger a callback
- if ( callback ) {
- var api = new _Api( settings );
-
- api.one( 'draw', function () {
- callback( api.ajax.json() );
- } );
- }
-
- if ( _fnDataSource( settings ) == 'ssp' ) {
- _fnReDraw( settings, holdPosition );
- }
- else {
- _fnProcessingDisplay( settings, true );
-
- // Cancel an existing request
- var xhr = settings.jqXHR;
- if ( xhr && xhr.readyState !== 4 ) {
- xhr.abort();
- }
-
- // Trigger xhr
- _fnBuildAjax( settings, [], function( json ) {
- _fnClearTable( settings );
-
- var data = _fnAjaxDataSrc( settings, json );
- for ( var i=0, ien=data.length ; i<ien ; i++ ) {
- _fnAddData( settings, data[i] );
- }
-
- _fnReDraw( settings, holdPosition );
- _fnProcessingDisplay( settings, false );
- } );
- }
- };
-
-
- /**
- * Get the JSON response from the last Ajax request that DataTables made to the
- * server. Note that this returns the JSON from the first table in the current
- * context.
- *
- * @return {object} JSON received from the server.
- */
- _api_register( 'ajax.json()', function () {
- var ctx = this.context;
-
- if ( ctx.length > 0 ) {
- return ctx[0].json;
- }
-
- // else return undefined;
- } );
-
-
- /**
- * Get the data submitted in the last Ajax request
- */
- _api_register( 'ajax.params()', function () {
- var ctx = this.context;
-
- if ( ctx.length > 0 ) {
- return ctx[0].oAjaxData;
- }
-
- // else return undefined;
- } );
-
-
- /**
- * Reload tables from the Ajax data source. Note that this function will
- * automatically re-draw the table when the remote data has been loaded.
- *
- * @param {boolean} [reset=true] Reset (default) or hold the current paging
- * position. A full re-sort and re-filter is performed when this method is
- * called, which is why the pagination reset is the default action.
- * @returns {DataTables.Api} this
- */
- _api_register( 'ajax.reload()', function ( callback, resetPaging ) {
- return this.iterator( 'table', function (settings) {
- __reload( settings, resetPaging===false, callback );
- } );
- } );
-
-
- /**
- * Get the current Ajax URL. Note that this returns the URL from the first
- * table in the current context.
- *
- * @return {string} Current Ajax source URL
- *//**
- * Set the Ajax URL. Note that this will set the URL for all tables in the
- * current context.
- *
- * @param {string} url URL to set.
- * @returns {DataTables.Api} this
- */
- _api_register( 'ajax.url()', function ( url ) {
- var ctx = this.context;
-
- if ( url === undefined ) {
- // get
- if ( ctx.length === 0 ) {
- return undefined;
- }
- ctx = ctx[0];
-
- return ctx.ajax ?
- $.isPlainObject( ctx.ajax ) ?
- ctx.ajax.url :
- ctx.ajax :
- ctx.sAjaxSource;
- }
-
- // set
- return this.iterator( 'table', function ( settings ) {
- if ( $.isPlainObject( settings.ajax ) ) {
- settings.ajax.url = url;
- }
- else {
- settings.ajax = url;
- }
- // No need to consider sAjaxSource here since DataTables gives priority
- // to `ajax` over `sAjaxSource`. So setting `ajax` here, renders any
- // value of `sAjaxSource` redundant.
- } );
- } );
-
-
- /**
- * Load data from the newly set Ajax URL. Note that this method is only
- * available when `ajax.url()` is used to set a URL. Additionally, this method
- * has the same effect as calling `ajax.reload()` but is provided for
- * convenience when setting a new URL. Like `ajax.reload()` it will
- * automatically redraw the table once the remote data has been loaded.
- *
- * @returns {DataTables.Api} this
- */
- _api_register( 'ajax.url().load()', function ( callback, resetPaging ) {
- // Same as a reload, but makes sense to present it for easy access after a
- // url change
- return this.iterator( 'table', function ( ctx ) {
- __reload( ctx, resetPaging===false, callback );
- } );
- } );
-
-
-
-
- var _selector_run = function ( type, selector, selectFn, settings, opts )
- {
- var
- out = [], res,
- a, i, ien, j, jen,
- selectorType = typeof selector;
-
- // Can't just check for isArray here, as an API or jQuery instance might be
- // given with their array like look
- if ( ! selector || selectorType === 'string' || selectorType === 'function' || selector.length === undefined ) {
- selector = [ selector ];
- }
-
- for ( i=0, ien=selector.length ; i<ien ; i++ ) {
- // Only split on simple strings - complex expressions will be jQuery selectors
- a = selector[i] && selector[i].split && ! selector[i].match(/[\[\(:]/) ?
- selector[i].split(',') :
- [ selector[i] ];
-
- for ( j=0, jen=a.length ; j<jen ; j++ ) {
- res = selectFn( typeof a[j] === 'string' ? $.trim(a[j]) : a[j] );
-
- if ( res && res.length ) {
- out = out.concat( res );
- }
- }
- }
-
- // selector extensions
- var ext = _ext.selector[ type ];
- if ( ext.length ) {
- for ( i=0, ien=ext.length ; i<ien ; i++ ) {
- out = ext[i]( settings, opts, out );
- }
- }
-
- return _unique( out );
- };
-
-
- var _selector_opts = function ( opts )
- {
- if ( ! opts ) {
- opts = {};
- }
-
- // Backwards compatibility for 1.9- which used the terminology filter rather
- // than search
- if ( opts.filter && opts.search === undefined ) {
- opts.search = opts.filter;
- }
-
- return $.extend( {
- search: 'none',
- order: 'current',
- page: 'all'
- }, opts );
- };
-
-
- var _selector_first = function ( inst )
- {
- // Reduce the API instance to the first item found
- for ( var i=0, ien=inst.length ; i<ien ; i++ ) {
- if ( inst[i].length > 0 ) {
- // Assign the first element to the first item in the instance
- // and truncate the instance and context
- inst[0] = inst[i];
- inst[0].length = 1;
- inst.length = 1;
- inst.context = [ inst.context[i] ];
-
- return inst;
- }
- }
-
- // Not found - return an empty instance
- inst.length = 0;
- return inst;
- };
-
-
- var _selector_row_indexes = function ( settings, opts )
- {
- var
- i, ien, tmp, a=[],
- displayFiltered = settings.aiDisplay,
- displayMaster = settings.aiDisplayMaster;
-
- var
- search = opts.search, // none, applied, removed
- order = opts.order, // applied, current, index (original - compatibility with 1.9)
- page = opts.page; // all, current
-
- if ( _fnDataSource( settings ) == 'ssp' ) {
- // In server-side processing mode, most options are irrelevant since
- // rows not shown don't exist and the index order is the applied order
- // Removed is a special case - for consistency just return an empty
- // array
- return search === 'removed' ?
- [] :
- _range( 0, displayMaster.length );
- }
- else if ( page == 'current' ) {
- // Current page implies that order=current and fitler=applied, since it is
- // fairly senseless otherwise, regardless of what order and search actually
- // are
- for ( i=settings._iDisplayStart, ien=settings.fnDisplayEnd() ; i<ien ; i++ ) {
- a.push( displayFiltered[i] );
- }
- }
- else if ( order == 'current' || order == 'applied' ) {
- if ( search == 'none') {
- a = displayMaster.slice();
- }
- else if ( search == 'applied' ) {
- a = displayFiltered.slice();
- }
- else if ( search == 'removed' ) {
- // O(n+m) solution by creating a hash map
- var displayFilteredMap = {};
-
- for ( var i=0, ien=displayFiltered.length ; i<ien ; i++ ) {
- displayFilteredMap[displayFiltered[i]] = null;
- }
-
- a = $.map( displayMaster, function (el) {
- return ! displayFilteredMap.hasOwnProperty(el) ?
- el :
- null;
- } );
- }
- }
- else if ( order == 'index' || order == 'original' ) {
- for ( i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
- if ( search == 'none' ) {
- a.push( i );
- }
- else { // applied | removed
- tmp = $.inArray( i, displayFiltered );
-
- if ((tmp === -1 && search == 'removed') ||
- (tmp >= 0 && search == 'applied') )
- {
- a.push( i );
- }
- }
- }
- }
-
- return a;
- };
-
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Rows
- *
- * {} - no selector - use all available rows
- * {integer} - row aoData index
- * {node} - TR node
- * {string} - jQuery selector to apply to the TR elements
- * {array} - jQuery array of nodes, or simply an array of TR nodes
- *
- */
- var __row_selector = function ( settings, selector, opts )
- {
- var rows;
- var run = function ( sel ) {
- var selInt = _intVal( sel );
- var i, ien;
- var aoData = settings.aoData;
-
- // Short cut - selector is a number and no options provided (default is
- // all records, so no need to check if the index is in there, since it
- // must be - dev error if the index doesn't exist).
- if ( selInt !== null && ! opts ) {
- return [ selInt ];
- }
-
- if ( ! rows ) {
- rows = _selector_row_indexes( settings, opts );
- }
-
- if ( selInt !== null && $.inArray( selInt, rows ) !== -1 ) {
- // Selector - integer
- return [ selInt ];
- }
- else if ( sel === null || sel === undefined || sel === '' ) {
- // Selector - none
- return rows;
- }
-
- // Selector - function
- if ( typeof sel === 'function' ) {
- return $.map( rows, function (idx) {
- var row = aoData[ idx ];
- return sel( idx, row._aData, row.nTr ) ? idx : null;
- } );
- }
-
- // Selector - node
- if ( sel.nodeName ) {
- var rowIdx = sel._DT_RowIndex; // Property added by DT for fast lookup
- var cellIdx = sel._DT_CellIndex;
-
- if ( rowIdx !== undefined ) {
- // Make sure that the row is actually still present in the table
- return aoData[ rowIdx ] && aoData[ rowIdx ].nTr === sel ?
- [ rowIdx ] :
- [];
- }
- else if ( cellIdx ) {
- return aoData[ cellIdx.row ] && aoData[ cellIdx.row ].nTr === sel ?
- [ cellIdx.row ] :
- [];
- }
- else {
- var host = $(sel).closest('*[data-dt-row]');
- return host.length ?
- [ host.data('dt-row') ] :
- [];
- }
- }
-
- // ID selector. Want to always be able to select rows by id, regardless
- // of if the tr element has been created or not, so can't rely upon
- // jQuery here - hence a custom implementation. This does not match
- // Sizzle's fast selector or HTML4 - in HTML5 the ID can be anything,
- // but to select it using a CSS selector engine (like Sizzle or
- // querySelect) it would need to need to be escaped for some characters.
- // DataTables simplifies this for row selectors since you can select
- // only a row. A # indicates an id any anything that follows is the id -
- // unescaped.
- if ( typeof sel === 'string' && sel.charAt(0) === '#' ) {
- // get row index from id
- var rowObj = settings.aIds[ sel.replace( /^#/, '' ) ];
- if ( rowObj !== undefined ) {
- return [ rowObj.idx ];
- }
-
- // need to fall through to jQuery in case there is DOM id that
- // matches
- }
-
- // Get nodes in the order from the `rows` array with null values removed
- var nodes = _removeEmpty(
- _pluck_order( settings.aoData, rows, 'nTr' )
- );
-
- // Selector - jQuery selector string, array of nodes or jQuery object/
- // As jQuery's .filter() allows jQuery objects to be passed in filter,
- // it also allows arrays, so this will cope with all three options
- return $(nodes)
- .filter( sel )
- .map( function () {
- return this._DT_RowIndex;
- } )
- .toArray();
- };
-
- return _selector_run( 'row', selector, run, settings, opts );
- };
-
-
- _api_register( 'rows()', function ( selector, opts ) {
- // argument shifting
- if ( selector === undefined ) {
- selector = '';
- }
- else if ( $.isPlainObject( selector ) ) {
- opts = selector;
- selector = '';
- }
-
- opts = _selector_opts( opts );
-
- var inst = this.iterator( 'table', function ( settings ) {
- return __row_selector( settings, selector, opts );
- }, 1 );
-
- // Want argument shifting here and in __row_selector?
- inst.selector.rows = selector;
- inst.selector.opts = opts;
-
- return inst;
- } );
-
- _api_register( 'rows().nodes()', function () {
- return this.iterator( 'row', function ( settings, row ) {
- return settings.aoData[ row ].nTr || undefined;
- }, 1 );
- } );
-
- _api_register( 'rows().data()', function () {
- return this.iterator( true, 'rows', function ( settings, rows ) {
- return _pluck_order( settings.aoData, rows, '_aData' );
- }, 1 );
- } );
-
- _api_registerPlural( 'rows().cache()', 'row().cache()', function ( type ) {
- return this.iterator( 'row', function ( settings, row ) {
- var r = settings.aoData[ row ];
- return type === 'search' ? r._aFilterData : r._aSortData;
- }, 1 );
- } );
-
- _api_registerPlural( 'rows().invalidate()', 'row().invalidate()', function ( src ) {
- return this.iterator( 'row', function ( settings, row ) {
- _fnInvalidate( settings, row, src );
- } );
- } );
-
- _api_registerPlural( 'rows().indexes()', 'row().index()', function () {
- return this.iterator( 'row', function ( settings, row ) {
- return row;
- }, 1 );
- } );
-
- _api_registerPlural( 'rows().ids()', 'row().id()', function ( hash ) {
- var a = [];
- var context = this.context;
-
- // `iterator` will drop undefined values, but in this case we want them
- for ( var i=0, ien=context.length ; i<ien ; i++ ) {
- for ( var j=0, jen=this[i].length ; j<jen ; j++ ) {
- var id = context[i].rowIdFn( context[i].aoData[ this[i][j] ]._aData );
- a.push( (hash === true ? '#' : '' )+ id );
- }
- }
-
- return new _Api( context, a );
- } );
-
- _api_registerPlural( 'rows().remove()', 'row().remove()', function () {
- var that = this;
-
- this.iterator( 'row', function ( settings, row, thatIdx ) {
- var data = settings.aoData;
- var rowData = data[ row ];
- var i, ien, j, jen;
- var loopRow, loopCells;
-
- data.splice( row, 1 );
-
- // Update the cached indexes
- for ( i=0, ien=data.length ; i<ien ; i++ ) {
- loopRow = data[i];
- loopCells = loopRow.anCells;
-
- // Rows
- if ( loopRow.nTr !== null ) {
- loopRow.nTr._DT_RowIndex = i;
- }
-
- // Cells
- if ( loopCells !== null ) {
- for ( j=0, jen=loopCells.length ; j<jen ; j++ ) {
- loopCells[j]._DT_CellIndex.row = i;
- }
- }
- }
-
- // Delete from the display arrays
- _fnDeleteIndex( settings.aiDisplayMaster, row );
- _fnDeleteIndex( settings.aiDisplay, row );
- _fnDeleteIndex( that[ thatIdx ], row, false ); // maintain local indexes
-
- // For server-side processing tables - subtract the deleted row from the count
- if ( settings._iRecordsDisplay > 0 ) {
- settings._iRecordsDisplay--;
- }
-
- // Check for an 'overflow' they case for displaying the table
- _fnLengthOverflow( settings );
-
- // Remove the row's ID reference if there is one
- var id = settings.rowIdFn( rowData._aData );
- if ( id !== undefined ) {
- delete settings.aIds[ id ];
- }
- } );
-
- this.iterator( 'table', function ( settings ) {
- for ( var i=0, ien=settings.aoData.length ; i<ien ; i++ ) {
- settings.aoData[i].idx = i;
- }
- } );
-
- return this;
- } );
-
-
- _api_register( 'rows.add()', function ( rows ) {
- var newRows = this.iterator( 'table', function ( settings ) {
- var row, i, ien;
- var out = [];
-
- for ( i=0, ien=rows.length ; i<ien ; i++ ) {
- row = rows[i];
-
- if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
- out.push( _fnAddTr( settings, row )[0] );
- }
- else {
- out.push( _fnAddData( settings, row ) );
- }
- }
-
- return out;
- }, 1 );
-
- // Return an Api.rows() extended instance, so rows().nodes() etc can be used
- var modRows = this.rows( -1 );
- modRows.pop();
- $.merge( modRows, newRows );
-
- return modRows;
- } );
-
-
-
-
-
- /**
- *
- */
- _api_register( 'row()', function ( selector, opts ) {
- return _selector_first( this.rows( selector, opts ) );
- } );
-
-
- _api_register( 'row().data()', function ( data ) {
- var ctx = this.context;
-
- if ( data === undefined ) {
- // Get
- return ctx.length && this.length ?
- ctx[0].aoData[ this[0] ]._aData :
- undefined;
- }
-
- // Set
- var row = ctx[0].aoData[ this[0] ];
- row._aData = data;
-
- // If the DOM has an id, and the data source is an array
- if ( $.isArray( data ) && row.nTr.id ) {
- _fnSetObjectDataFn( ctx[0].rowId )( data, row.nTr.id );
- }
-
- // Automatically invalidate
- _fnInvalidate( ctx[0], this[0], 'data' );
-
- return this;
- } );
-
-
- _api_register( 'row().node()', function () {
- var ctx = this.context;
-
- return ctx.length && this.length ?
- ctx[0].aoData[ this[0] ].nTr || null :
- null;
- } );
-
-
- _api_register( 'row.add()', function ( row ) {
- // Allow a jQuery object to be passed in - only a single row is added from
- // it though - the first element in the set
- if ( row instanceof $ && row.length ) {
- row = row[0];
- }
-
- var rows = this.iterator( 'table', function ( settings ) {
- if ( row.nodeName && row.nodeName.toUpperCase() === 'TR' ) {
- return _fnAddTr( settings, row )[0];
- }
- return _fnAddData( settings, row );
- } );
-
- // Return an Api.rows() extended instance, with the newly added row selected
- return this.row( rows[0] );
- } );
-
-
-
- var __details_add = function ( ctx, row, data, klass )
- {
- // Convert to array of TR elements
- var rows = [];
- var addRow = function ( r, k ) {
- // Recursion to allow for arrays of jQuery objects
- if ( $.isArray( r ) || r instanceof $ ) {
- for ( var i=0, ien=r.length ; i<ien ; i++ ) {
- addRow( r[i], k );
- }
- return;
- }
-
- // If we get a TR element, then just add it directly - up to the dev
- // to add the correct number of columns etc
- if ( r.nodeName && r.nodeName.toLowerCase() === 'tr' ) {
- rows.push( r );
- }
- else {
- // Otherwise create a row with a wrapper
- var created = $('<tr><td/></tr>').addClass( k );
- $('td', created)
- .addClass( k )
- .html( r )
- [0].colSpan = _fnVisbleColumns( ctx );
-
- rows.push( created[0] );
- }
- };
-
- addRow( data, klass );
-
- if ( row._details ) {
- row._details.detach();
- }
-
- row._details = $(rows);
-
- // If the children were already shown, that state should be retained
- if ( row._detailsShow ) {
- row._details.insertAfter( row.nTr );
- }
- };
-
-
- var __details_remove = function ( api, idx )
- {
- var ctx = api.context;
-
- if ( ctx.length ) {
- var row = ctx[0].aoData[ idx !== undefined ? idx : api[0] ];
-
- if ( row && row._details ) {
- row._details.remove();
-
- row._detailsShow = undefined;
- row._details = undefined;
- }
- }
- };
-
-
- var __details_display = function ( api, show ) {
- var ctx = api.context;
-
- if ( ctx.length && api.length ) {
- var row = ctx[0].aoData[ api[0] ];
-
- if ( row._details ) {
- row._detailsShow = show;
-
- if ( show ) {
- row._details.insertAfter( row.nTr );
- }
- else {
- row._details.detach();
- }
-
- __details_events( ctx[0] );
- }
- }
- };
-
-
- var __details_events = function ( settings )
- {
- var api = new _Api( settings );
- var namespace = '.dt.DT_details';
- var drawEvent = 'draw'+namespace;
- var colvisEvent = 'column-visibility'+namespace;
- var destroyEvent = 'destroy'+namespace;
- var data = settings.aoData;
-
- api.off( drawEvent +' '+ colvisEvent +' '+ destroyEvent );
-
- if ( _pluck( data, '_details' ).length > 0 ) {
- // On each draw, insert the required elements into the document
- api.on( drawEvent, function ( e, ctx ) {
- if ( settings !== ctx ) {
- return;
- }
-
- api.rows( {page:'current'} ).eq(0).each( function (idx) {
- // Internal data grab
- var row = data[ idx ];
-
- if ( row._detailsShow ) {
- row._details.insertAfter( row.nTr );
- }
- } );
- } );
-
- // Column visibility change - update the colspan
- api.on( colvisEvent, function ( e, ctx, idx, vis ) {
- if ( settings !== ctx ) {
- return;
- }
-
- // Update the colspan for the details rows (note, only if it already has
- // a colspan)
- var row, visible = _fnVisbleColumns( ctx );
-
- for ( var i=0, ien=data.length ; i<ien ; i++ ) {
- row = data[i];
-
- if ( row._details ) {
- row._details.children('td[colspan]').attr('colspan', visible );
- }
- }
- } );
-
- // Table destroyed - nuke any child rows
- api.on( destroyEvent, function ( e, ctx ) {
- if ( settings !== ctx ) {
- return;
- }
-
- for ( var i=0, ien=data.length ; i<ien ; i++ ) {
- if ( data[i]._details ) {
- __details_remove( api, i );
- }
- }
- } );
- }
- };
-
- // Strings for the method names to help minification
- var _emp = '';
- var _child_obj = _emp+'row().child';
- var _child_mth = _child_obj+'()';
-
- // data can be:
- // tr
- // string
- // jQuery or array of any of the above
- _api_register( _child_mth, function ( data, klass ) {
- var ctx = this.context;
-
- if ( data === undefined ) {
- // get
- return ctx.length && this.length ?
- ctx[0].aoData[ this[0] ]._details :
- undefined;
- }
- else if ( data === true ) {
- // show
- this.child.show();
- }
- else if ( data === false ) {
- // remove
- __details_remove( this );
- }
- else if ( ctx.length && this.length ) {
- // set
- __details_add( ctx[0], ctx[0].aoData[ this[0] ], data, klass );
- }
-
- return this;
- } );
-
-
- _api_register( [
- _child_obj+'.show()',
- _child_mth+'.show()' // only when `child()` was called with parameters (without
- ], function ( show ) { // it returns an object and this method is not executed)
- __details_display( this, true );
- return this;
- } );
-
-
- _api_register( [
- _child_obj+'.hide()',
- _child_mth+'.hide()' // only when `child()` was called with parameters (without
- ], function () { // it returns an object and this method is not executed)
- __details_display( this, false );
- return this;
- } );
-
-
- _api_register( [
- _child_obj+'.remove()',
- _child_mth+'.remove()' // only when `child()` was called with parameters (without
- ], function () { // it returns an object and this method is not executed)
- __details_remove( this );
- return this;
- } );
-
-
- _api_register( _child_obj+'.isShown()', function () {
- var ctx = this.context;
-
- if ( ctx.length && this.length ) {
- // _detailsShown as false or undefined will fall through to return false
- return ctx[0].aoData[ this[0] ]._detailsShow || false;
- }
- return false;
- } );
-
-
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Columns
- *
- * {integer} - column index (>=0 count from left, <0 count from right)
- * "{integer}:visIdx" - visible column index (i.e. translate to column index) (>=0 count from left, <0 count from right)
- * "{integer}:visible" - alias for {integer}:visIdx (>=0 count from left, <0 count from right)
- * "{string}:name" - column name
- * "{string}" - jQuery selector on column header nodes
- *
- */
-
- // can be an array of these items, comma separated list, or an array of comma
- // separated lists
-
- var __re_column_selector = /^([^:]+):(name|visIdx|visible)$/;
-
-
- // r1 and r2 are redundant - but it means that the parameters match for the
- // iterator callback in columns().data()
- var __columnData = function ( settings, column, r1, r2, rows ) {
- var a = [];
- for ( var row=0, ien=rows.length ; row<ien ; row++ ) {
- a.push( _fnGetCellData( settings, rows[row], column ) );
- }
- return a;
- };
-
-
- var __column_selector = function ( settings, selector, opts )
- {
- var
- columns = settings.aoColumns,
- names = _pluck( columns, 'sName' ),
- nodes = _pluck( columns, 'nTh' );
-
- var run = function ( s ) {
- var selInt = _intVal( s );
-
- // Selector - all
- if ( s === '' ) {
- return _range( columns.length );
- }
-
- // Selector - index
- if ( selInt !== null ) {
- return [ selInt >= 0 ?
- selInt : // Count from left
- columns.length + selInt // Count from right (+ because its a negative value)
- ];
- }
-
- // Selector = function
- if ( typeof s === 'function' ) {
- var rows = _selector_row_indexes( settings, opts );
-
- return $.map( columns, function (col, idx) {
- return s(
- idx,
- __columnData( settings, idx, 0, 0, rows ),
- nodes[ idx ]
- ) ? idx : null;
- } );
- }
-
- // jQuery or string selector
- var match = typeof s === 'string' ?
- s.match( __re_column_selector ) :
- '';
-
- if ( match ) {
- switch( match[2] ) {
- case 'visIdx':
- case 'visible':
- var idx = parseInt( match[1], 10 );
- // Visible index given, convert to column index
- if ( idx < 0 ) {
- // Counting from the right
- var visColumns = $.map( columns, function (col,i) {
- return col.bVisible ? i : null;
- } );
- return [ visColumns[ visColumns.length + idx ] ];
- }
- // Counting from the left
- return [ _fnVisibleToColumnIndex( settings, idx ) ];
-
- case 'name':
- // match by name. `names` is column index complete and in order
- return $.map( names, function (name, i) {
- return name === match[1] ? i : null;
- } );
-
- default:
- return [];
- }
- }
-
- // Cell in the table body
- if ( s.nodeName && s._DT_CellIndex ) {
- return [ s._DT_CellIndex.column ];
- }
-
- // jQuery selector on the TH elements for the columns
- var jqResult = $( nodes )
- .filter( s )
- .map( function () {
- return $.inArray( this, nodes ); // `nodes` is column index complete and in order
- } )
- .toArray();
-
- if ( jqResult.length || ! s.nodeName ) {
- return jqResult;
- }
-
- // Otherwise a node which might have a `dt-column` data attribute, or be
- // a child or such an element
- var host = $(s).closest('*[data-dt-column]');
- return host.length ?
- [ host.data('dt-column') ] :
- [];
- };
-
- return _selector_run( 'column', selector, run, settings, opts );
- };
-
-
- var __setColumnVis = function ( settings, column, vis ) {
- var
- cols = settings.aoColumns,
- col = cols[ column ],
- data = settings.aoData,
- row, cells, i, ien, tr;
-
- // Get
- if ( vis === undefined ) {
- return col.bVisible;
- }
-
- // Set
- // No change
- if ( col.bVisible === vis ) {
- return;
- }
-
- if ( vis ) {
- // Insert column
- // Need to decide if we should use appendChild or insertBefore
- var insertBefore = $.inArray( true, _pluck(cols, 'bVisible'), column+1 );
-
- for ( i=0, ien=data.length ; i<ien ; i++ ) {
- tr = data[i].nTr;
- cells = data[i].anCells;
-
- if ( tr ) {
- // insertBefore can act like appendChild if 2nd arg is null
- tr.insertBefore( cells[ column ], cells[ insertBefore ] || null );
- }
- }
- }
- else {
- // Remove column
- $( _pluck( settings.aoData, 'anCells', column ) ).detach();
- }
-
- // Common actions
- col.bVisible = vis;
- _fnDrawHead( settings, settings.aoHeader );
- _fnDrawHead( settings, settings.aoFooter );
-
- // Update colspan for no records display. Child rows and extensions will use their own
- // listeners to do this - only need to update the empty table item here
- if ( ! settings.aiDisplay.length ) {
- $(settings.nTBody).find('td[colspan]').attr('colspan', _fnVisbleColumns(settings));
- }
-
- _fnSaveState( settings );
- };
-
-
- _api_register( 'columns()', function ( selector, opts ) {
- // argument shifting
- if ( selector === undefined ) {
- selector = '';
- }
- else if ( $.isPlainObject( selector ) ) {
- opts = selector;
- selector = '';
- }
-
- opts = _selector_opts( opts );
-
- var inst = this.iterator( 'table', function ( settings ) {
- return __column_selector( settings, selector, opts );
- }, 1 );
-
- // Want argument shifting here and in _row_selector?
- inst.selector.cols = selector;
- inst.selector.opts = opts;
-
- return inst;
- } );
-
- _api_registerPlural( 'columns().header()', 'column().header()', function ( selector, opts ) {
- return this.iterator( 'column', function ( settings, column ) {
- return settings.aoColumns[column].nTh;
- }, 1 );
- } );
-
- _api_registerPlural( 'columns().footer()', 'column().footer()', function ( selector, opts ) {
- return this.iterator( 'column', function ( settings, column ) {
- return settings.aoColumns[column].nTf;
- }, 1 );
- } );
-
- _api_registerPlural( 'columns().data()', 'column().data()', function () {
- return this.iterator( 'column-rows', __columnData, 1 );
- } );
-
- _api_registerPlural( 'columns().dataSrc()', 'column().dataSrc()', function () {
- return this.iterator( 'column', function ( settings, column ) {
- return settings.aoColumns[column].mData;
- }, 1 );
- } );
-
- _api_registerPlural( 'columns().cache()', 'column().cache()', function ( type ) {
- return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
- return _pluck_order( settings.aoData, rows,
- type === 'search' ? '_aFilterData' : '_aSortData', column
- );
- }, 1 );
- } );
-
- _api_registerPlural( 'columns().nodes()', 'column().nodes()', function () {
- return this.iterator( 'column-rows', function ( settings, column, i, j, rows ) {
- return _pluck_order( settings.aoData, rows, 'anCells', column ) ;
- }, 1 );
- } );
-
- _api_registerPlural( 'columns().visible()', 'column().visible()', function ( vis, calc ) {
- var ret = this.iterator( 'column', function ( settings, column ) {
- if ( vis === undefined ) {
- return settings.aoColumns[ column ].bVisible;
- } // else
- __setColumnVis( settings, column, vis );
- } );
-
- // Group the column visibility changes
- if ( vis !== undefined ) {
- // Second loop once the first is done for events
- this.iterator( 'column', function ( settings, column ) {
- _fnCallbackFire( settings, null, 'column-visibility', [settings, column, vis, calc] );
- } );
-
- if ( calc === undefined || calc ) {
- this.columns.adjust();
- }
- }
-
- return ret;
- } );
-
- _api_registerPlural( 'columns().indexes()', 'column().index()', function ( type ) {
- return this.iterator( 'column', function ( settings, column ) {
- return type === 'visible' ?
- _fnColumnIndexToVisible( settings, column ) :
- column;
- }, 1 );
- } );
-
- _api_register( 'columns.adjust()', function () {
- return this.iterator( 'table', function ( settings ) {
- _fnAdjustColumnSizing( settings );
- }, 1 );
- } );
-
- _api_register( 'column.index()', function ( type, idx ) {
- if ( this.context.length !== 0 ) {
- var ctx = this.context[0];
-
- if ( type === 'fromVisible' || type === 'toData' ) {
- return _fnVisibleToColumnIndex( ctx, idx );
- }
- else if ( type === 'fromData' || type === 'toVisible' ) {
- return _fnColumnIndexToVisible( ctx, idx );
- }
- }
- } );
-
- _api_register( 'column()', function ( selector, opts ) {
- return _selector_first( this.columns( selector, opts ) );
- } );
-
-
-
- var __cell_selector = function ( settings, selector, opts )
- {
- var data = settings.aoData;
- var rows = _selector_row_indexes( settings, opts );
- var cells = _removeEmpty( _pluck_order( data, rows, 'anCells' ) );
- var allCells = $( [].concat.apply([], cells) );
- var row;
- var columns = settings.aoColumns.length;
- var a, i, ien, j, o, host;
-
- var run = function ( s ) {
- var fnSelector = typeof s === 'function';
-
- if ( s === null || s === undefined || fnSelector ) {
- // All cells and function selectors
- a = [];
-
- for ( i=0, ien=rows.length ; i<ien ; i++ ) {
- row = rows[i];
-
- for ( j=0 ; j<columns ; j++ ) {
- o = {
- row: row,
- column: j
- };
-
- if ( fnSelector ) {
- // Selector - function
- host = data[ row ];
-
- if ( s( o, _fnGetCellData(settings, row, j), host.anCells ? host.anCells[j] : null ) ) {
- a.push( o );
- }
- }
- else {
- // Selector - all
- a.push( o );
- }
- }
- }
-
- return a;
- }
-
- // Selector - index
- if ( $.isPlainObject( s ) ) {
- // Valid cell index and its in the array of selectable rows
- return s.column !== undefined && s.row !== undefined && $.inArray( s.row, rows ) !== -1 ?
- [s] :
- [];
- }
-
- // Selector - jQuery filtered cells
- var jqResult = allCells
- .filter( s )
- .map( function (i, el) {
- return { // use a new object, in case someone changes the values
- row: el._DT_CellIndex.row,
- column: el._DT_CellIndex.column
- };
- } )
- .toArray();
-
- if ( jqResult.length || ! s.nodeName ) {
- return jqResult;
- }
-
- // Otherwise the selector is a node, and there is one last option - the
- // element might be a child of an element which has dt-row and dt-column
- // data attributes
- host = $(s).closest('*[data-dt-row]');
- return host.length ?
- [ {
- row: host.data('dt-row'),
- column: host.data('dt-column')
- } ] :
- [];
- };
-
- return _selector_run( 'cell', selector, run, settings, opts );
- };
-
-
-
-
- _api_register( 'cells()', function ( rowSelector, columnSelector, opts ) {
- // Argument shifting
- if ( $.isPlainObject( rowSelector ) ) {
- // Indexes
- if ( rowSelector.row === undefined ) {
- // Selector options in first parameter
- opts = rowSelector;
- rowSelector = null;
- }
- else {
- // Cell index objects in first parameter
- opts = columnSelector;
- columnSelector = null;
- }
- }
- if ( $.isPlainObject( columnSelector ) ) {
- opts = columnSelector;
- columnSelector = null;
- }
-
- // Cell selector
- if ( columnSelector === null || columnSelector === undefined ) {
- return this.iterator( 'table', function ( settings ) {
- return __cell_selector( settings, rowSelector, _selector_opts( opts ) );
- } );
- }
-
- // Row + column selector
- var columns = this.columns( columnSelector );
- var rows = this.rows( rowSelector );
- var a, i, ien, j, jen;
-
- this.iterator( 'table', function ( settings, idx ) {
- a = [];
-
- for ( i=0, ien=rows[idx].length ; i<ien ; i++ ) {
- for ( j=0, jen=columns[idx].length ; j<jen ; j++ ) {
- a.push( {
- row: rows[idx][i],
- column: columns[idx][j]
- } );
- }
- }
- }, 1 );
-
- // Now pass through the cell selector for options
- var cells = this.cells( a, opts );
-
- $.extend( cells.selector, {
- cols: columnSelector,
- rows: rowSelector,
- opts: opts
- } );
-
- return cells;
- } );
-
-
- _api_registerPlural( 'cells().nodes()', 'cell().node()', function () {
- return this.iterator( 'cell', function ( settings, row, column ) {
- var data = settings.aoData[ row ];
-
- return data && data.anCells ?
- data.anCells[ column ] :
- undefined;
- }, 1 );
- } );
-
-
- _api_register( 'cells().data()', function () {
- return this.iterator( 'cell', function ( settings, row, column ) {
- return _fnGetCellData( settings, row, column );
- }, 1 );
- } );
-
-
- _api_registerPlural( 'cells().cache()', 'cell().cache()', function ( type ) {
- type = type === 'search' ? '_aFilterData' : '_aSortData';
-
- return this.iterator( 'cell', function ( settings, row, column ) {
- return settings.aoData[ row ][ type ][ column ];
- }, 1 );
- } );
-
-
- _api_registerPlural( 'cells().render()', 'cell().render()', function ( type ) {
- return this.iterator( 'cell', function ( settings, row, column ) {
- return _fnGetCellData( settings, row, column, type );
- }, 1 );
- } );
-
-
- _api_registerPlural( 'cells().indexes()', 'cell().index()', function () {
- return this.iterator( 'cell', function ( settings, row, column ) {
- return {
- row: row,
- column: column,
- columnVisible: _fnColumnIndexToVisible( settings, column )
- };
- }, 1 );
- } );
-
-
- _api_registerPlural( 'cells().invalidate()', 'cell().invalidate()', function ( src ) {
- return this.iterator( 'cell', function ( settings, row, column ) {
- _fnInvalidate( settings, row, src, column );
- } );
- } );
-
-
-
- _api_register( 'cell()', function ( rowSelector, columnSelector, opts ) {
- return _selector_first( this.cells( rowSelector, columnSelector, opts ) );
- } );
-
-
- _api_register( 'cell().data()', function ( data ) {
- var ctx = this.context;
- var cell = this[0];
-
- if ( data === undefined ) {
- // Get
- return ctx.length && cell.length ?
- _fnGetCellData( ctx[0], cell[0].row, cell[0].column ) :
- undefined;
- }
-
- // Set
- _fnSetCellData( ctx[0], cell[0].row, cell[0].column, data );
- _fnInvalidate( ctx[0], cell[0].row, 'data', cell[0].column );
-
- return this;
- } );
-
-
-
- /**
- * Get current ordering (sorting) that has been applied to the table.
- *
- * @returns {array} 2D array containing the sorting information for the first
- * table in the current context. Each element in the parent array represents
- * a column being sorted upon (i.e. multi-sorting with two columns would have
- * 2 inner arrays). The inner arrays may have 2 or 3 elements. The first is
- * the column index that the sorting condition applies to, the second is the
- * direction of the sort (`desc` or `asc`) and, optionally, the third is the
- * index of the sorting order from the `column.sorting` initialisation array.
- *//**
- * Set the ordering for the table.
- *
- * @param {integer} order Column index to sort upon.
- * @param {string} direction Direction of the sort to be applied (`asc` or `desc`)
- * @returns {DataTables.Api} this
- *//**
- * Set the ordering for the table.
- *
- * @param {array} order 1D array of sorting information to be applied.
- * @param {array} [...] Optional additional sorting conditions
- * @returns {DataTables.Api} this
- *//**
- * Set the ordering for the table.
- *
- * @param {array} order 2D array of sorting information to be applied.
- * @returns {DataTables.Api} this
- */
- _api_register( 'order()', function ( order, dir ) {
- var ctx = this.context;
-
- if ( order === undefined ) {
- // get
- return ctx.length !== 0 ?
- ctx[0].aaSorting :
- undefined;
- }
-
- // set
- if ( typeof order === 'number' ) {
- // Simple column / direction passed in
- order = [ [ order, dir ] ];
- }
- else if ( order.length && ! $.isArray( order[0] ) ) {
- // Arguments passed in (list of 1D arrays)
- order = Array.prototype.slice.call( arguments );
- }
- // otherwise a 2D array was passed in
-
- return this.iterator( 'table', function ( settings ) {
- settings.aaSorting = order.slice();
- } );
- } );
-
-
- /**
- * Attach a sort listener to an element for a given column
- *
- * @param {node|jQuery|string} node Identifier for the element(s) to attach the
- * listener to. This can take the form of a single DOM node, a jQuery
- * collection of nodes or a jQuery selector which will identify the node(s).
- * @param {integer} column the column that a click on this node will sort on
- * @param {function} [callback] callback function when sort is run
- * @returns {DataTables.Api} this
- */
- _api_register( 'order.listener()', function ( node, column, callback ) {
- return this.iterator( 'table', function ( settings ) {
- _fnSortAttachListener( settings, node, column, callback );
- } );
- } );
-
-
- _api_register( 'order.fixed()', function ( set ) {
- if ( ! set ) {
- var ctx = this.context;
- var fixed = ctx.length ?
- ctx[0].aaSortingFixed :
- undefined;
-
- return $.isArray( fixed ) ?
- { pre: fixed } :
- fixed;
- }
-
- return this.iterator( 'table', function ( settings ) {
- settings.aaSortingFixed = $.extend( true, {}, set );
- } );
- } );
-
-
- // Order by the selected column(s)
- _api_register( [
- 'columns().order()',
- 'column().order()'
- ], function ( dir ) {
- var that = this;
-
- return this.iterator( 'table', function ( settings, i ) {
- var sort = [];
-
- $.each( that[i], function (j, col) {
- sort.push( [ col, dir ] );
- } );
-
- settings.aaSorting = sort;
- } );
- } );
-
-
-
- _api_register( 'search()', function ( input, regex, smart, caseInsen ) {
- var ctx = this.context;
-
- if ( input === undefined ) {
- // get
- return ctx.length !== 0 ?
- ctx[0].oPreviousSearch.sSearch :
- undefined;
- }
-
- // set
- return this.iterator( 'table', function ( settings ) {
- if ( ! settings.oFeatures.bFilter ) {
- return;
- }
-
- _fnFilterComplete( settings, $.extend( {}, settings.oPreviousSearch, {
- "sSearch": input+"",
- "bRegex": regex === null ? false : regex,
- "bSmart": smart === null ? true : smart,
- "bCaseInsensitive": caseInsen === null ? true : caseInsen
- } ), 1 );
- } );
- } );
-
-
- _api_registerPlural(
- 'columns().search()',
- 'column().search()',
- function ( input, regex, smart, caseInsen ) {
- return this.iterator( 'column', function ( settings, column ) {
- var preSearch = settings.aoPreSearchCols;
-
- if ( input === undefined ) {
- // get
- return preSearch[ column ].sSearch;
- }
-
- // set
- if ( ! settings.oFeatures.bFilter ) {
- return;
- }
-
- $.extend( preSearch[ column ], {
- "sSearch": input+"",
- "bRegex": regex === null ? false : regex,
- "bSmart": smart === null ? true : smart,
- "bCaseInsensitive": caseInsen === null ? true : caseInsen
- } );
-
- _fnFilterComplete( settings, settings.oPreviousSearch, 1 );
- } );
- }
- );
-
- /*
- * State API methods
- */
-
- _api_register( 'state()', function () {
- return this.context.length ?
- this.context[0].oSavedState :
- null;
- } );
-
-
- _api_register( 'state.clear()', function () {
- return this.iterator( 'table', function ( settings ) {
- // Save an empty object
- settings.fnStateSaveCallback.call( settings.oInstance, settings, {} );
- } );
- } );
-
-
- _api_register( 'state.loaded()', function () {
- return this.context.length ?
- this.context[0].oLoadedState :
- null;
- } );
-
-
- _api_register( 'state.save()', function () {
- return this.iterator( 'table', function ( settings ) {
- _fnSaveState( settings );
- } );
- } );
-
-
-
- /**
- * Provide a common method for plug-ins to check the version of DataTables being
- * used, in order to ensure compatibility.
- *
- * @param {string} version Version string to check for, in the format "X.Y.Z".
- * Note that the formats "X" and "X.Y" are also acceptable.
- * @returns {boolean} true if this version of DataTables is greater or equal to
- * the required version, or false if this version of DataTales is not
- * suitable
- * @static
- * @dtopt API-Static
- *
- * @example
- * alert( $.fn.dataTable.versionCheck( '1.9.0' ) );
- */
- DataTable.versionCheck = DataTable.fnVersionCheck = function( version )
- {
- var aThis = DataTable.version.split('.');
- var aThat = version.split('.');
- var iThis, iThat;
-
- for ( var i=0, iLen=aThat.length ; i<iLen ; i++ ) {
- iThis = parseInt( aThis[i], 10 ) || 0;
- iThat = parseInt( aThat[i], 10 ) || 0;
-
- // Parts are the same, keep comparing
- if (iThis === iThat) {
- continue;
- }
-
- // Parts are different, return immediately
- return iThis > iThat;
- }
-
- return true;
- };
-
-
- /**
- * Check if a `<table>` node is a DataTable table already or not.
- *
- * @param {node|jquery|string} table Table node, jQuery object or jQuery
- * selector for the table to test. Note that if more than more than one
- * table is passed on, only the first will be checked
- * @returns {boolean} true the table given is a DataTable, or false otherwise
- * @static
- * @dtopt API-Static
- *
- * @example
- * if ( ! $.fn.DataTable.isDataTable( '#example' ) ) {
- * $('#example').dataTable();
- * }
- */
- DataTable.isDataTable = DataTable.fnIsDataTable = function ( table )
- {
- var t = $(table).get(0);
- var is = false;
-
- if ( table instanceof DataTable.Api ) {
- return true;
- }
-
- $.each( DataTable.settings, function (i, o) {
- var head = o.nScrollHead ? $('table', o.nScrollHead)[0] : null;
- var foot = o.nScrollFoot ? $('table', o.nScrollFoot)[0] : null;
-
- if ( o.nTable === t || head === t || foot === t ) {
- is = true;
- }
- } );
-
- return is;
- };
-
-
- /**
- * Get all DataTable tables that have been initialised - optionally you can
- * select to get only currently visible tables.
- *
- * @param {boolean} [visible=false] Flag to indicate if you want all (default)
- * or visible tables only.
- * @returns {array} Array of `table` nodes (not DataTable instances) which are
- * DataTables
- * @static
- * @dtopt API-Static
- *
- * @example
- * $.each( $.fn.dataTable.tables(true), function () {
- * $(table).DataTable().columns.adjust();
- * } );
- */
- DataTable.tables = DataTable.fnTables = function ( visible )
- {
- var api = false;
-
- if ( $.isPlainObject( visible ) ) {
- api = visible.api;
- visible = visible.visible;
- }
-
- var a = $.map( DataTable.settings, function (o) {
- if ( !visible || (visible && $(o.nTable).is(':visible')) ) {
- return o.nTable;
- }
- } );
-
- return api ?
- new _Api( a ) :
- a;
- };
-
-
- /**
- * Convert from camel case parameters to Hungarian notation. This is made public
- * for the extensions to provide the same ability as DataTables core to accept
- * either the 1.9 style Hungarian notation, or the 1.10+ style camelCase
- * parameters.
- *
- * @param {object} src The model object which holds all parameters that can be
- * mapped.
- * @param {object} user The object to convert from camel case to Hungarian.
- * @param {boolean} force When set to `true`, properties which already have a
- * Hungarian value in the `user` object will be overwritten. Otherwise they
- * won't be.
- */
- DataTable.camelToHungarian = _fnCamelToHungarian;
-
-
-
- /**
- *
- */
- _api_register( '$()', function ( selector, opts ) {
- var
- rows = this.rows( opts ).nodes(), // Get all rows
- jqRows = $(rows);
-
- return $( [].concat(
- jqRows.filter( selector ).toArray(),
- jqRows.find( selector ).toArray()
- ) );
- } );
-
-
- // jQuery functions to operate on the tables
- $.each( [ 'on', 'one', 'off' ], function (i, key) {
- _api_register( key+'()', function ( /* event, handler */ ) {
- var args = Array.prototype.slice.call(arguments);
-
- // Add the `dt` namespace automatically if it isn't already present
- args[0] = $.map( args[0].split( /\s/ ), function ( e ) {
- return ! e.match(/\.dt\b/) ?
- e+'.dt' :
- e;
- } ).join( ' ' );
-
- var inst = $( this.tables().nodes() );
- inst[key].apply( inst, args );
- return this;
- } );
- } );
-
-
- _api_register( 'clear()', function () {
- return this.iterator( 'table', function ( settings ) {
- _fnClearTable( settings );
- } );
- } );
-
-
- _api_register( 'settings()', function () {
- return new _Api( this.context, this.context );
- } );
-
-
- _api_register( 'init()', function () {
- var ctx = this.context;
- return ctx.length ? ctx[0].oInit : null;
- } );
-
-
- _api_register( 'data()', function () {
- return this.iterator( 'table', function ( settings ) {
- return _pluck( settings.aoData, '_aData' );
- } ).flatten();
- } );
-
-
- _api_register( 'destroy()', function ( remove ) {
- remove = remove || false;
-
- return this.iterator( 'table', function ( settings ) {
- var orig = settings.nTableWrapper.parentNode;
- var classes = settings.oClasses;
- var table = settings.nTable;
- var tbody = settings.nTBody;
- var thead = settings.nTHead;
- var tfoot = settings.nTFoot;
- var jqTable = $(table);
- var jqTbody = $(tbody);
- var jqWrapper = $(settings.nTableWrapper);
- var rows = $.map( settings.aoData, function (r) { return r.nTr; } );
- var i, ien;
-
- // Flag to note that the table is currently being destroyed - no action
- // should be taken
- settings.bDestroying = true;
-
- // Fire off the destroy callbacks for plug-ins etc
- _fnCallbackFire( settings, "aoDestroyCallback", "destroy", [settings] );
-
- // If not being removed from the document, make all columns visible
- if ( ! remove ) {
- new _Api( settings ).columns().visible( true );
- }
-
- // Blitz all `DT` namespaced events (these are internal events, the
- // lowercase, `dt` events are user subscribed and they are responsible
- // for removing them
- jqWrapper.off('.DT').find(':not(tbody *)').off('.DT');
- $(window).off('.DT-'+settings.sInstance);
-
- // When scrolling we had to break the table up - restore it
- if ( table != thead.parentNode ) {
- jqTable.children('thead').detach();
- jqTable.append( thead );
- }
-
- if ( tfoot && table != tfoot.parentNode ) {
- jqTable.children('tfoot').detach();
- jqTable.append( tfoot );
- }
-
- settings.aaSorting = [];
- settings.aaSortingFixed = [];
- _fnSortingClasses( settings );
-
- $( rows ).removeClass( settings.asStripeClasses.join(' ') );
-
- $('th, td', thead).removeClass( classes.sSortable+' '+
- classes.sSortableAsc+' '+classes.sSortableDesc+' '+classes.sSortableNone
- );
-
- // Add the TR elements back into the table in their original order
- jqTbody.children().detach();
- jqTbody.append( rows );
-
- // Remove the DataTables generated nodes, events and classes
- var removedMethod = remove ? 'remove' : 'detach';
- jqTable[ removedMethod ]();
- jqWrapper[ removedMethod ]();
-
- // If we need to reattach the table to the document
- if ( ! remove && orig ) {
- // insertBefore acts like appendChild if !arg[1]
- orig.insertBefore( table, settings.nTableReinsertBefore );
-
- // Restore the width of the original table - was read from the style property,
- // so we can restore directly to that
- jqTable
- .css( 'width', settings.sDestroyWidth )
- .removeClass( classes.sTable );
-
- // If the were originally stripe classes - then we add them back here.
- // Note this is not fool proof (for example if not all rows had stripe
- // classes - but it's a good effort without getting carried away
- ien = settings.asDestroyStripes.length;
-
- if ( ien ) {
- jqTbody.children().each( function (i) {
- $(this).addClass( settings.asDestroyStripes[i % ien] );
- } );
- }
- }
-
- /* Remove the settings object from the settings array */
- var idx = $.inArray( settings, DataTable.settings );
- if ( idx !== -1 ) {
- DataTable.settings.splice( idx, 1 );
- }
- } );
- } );
-
-
- // Add the `every()` method for rows, columns and cells in a compact form
- $.each( [ 'column', 'row', 'cell' ], function ( i, type ) {
- _api_register( type+'s().every()', function ( fn ) {
- var opts = this.selector.opts;
- var api = this;
-
- return this.iterator( type, function ( settings, arg1, arg2, arg3, arg4 ) {
- // Rows and columns:
- // arg1 - index
- // arg2 - table counter
- // arg3 - loop counter
- // arg4 - undefined
- // Cells:
- // arg1 - row index
- // arg2 - column index
- // arg3 - table counter
- // arg4 - loop counter
- fn.call(
- api[ type ](
- arg1,
- type==='cell' ? arg2 : opts,
- type==='cell' ? opts : undefined
- ),
- arg1, arg2, arg3, arg4
- );
- } );
- } );
- } );
-
-
- // i18n method for extensions to be able to use the language object from the
- // DataTable
- _api_register( 'i18n()', function ( token, def, plural ) {
- var ctx = this.context[0];
- var resolved = _fnGetObjectDataFn( token )( ctx.oLanguage );
-
- if ( resolved === undefined ) {
- resolved = def;
- }
-
- if ( plural !== undefined && $.isPlainObject( resolved ) ) {
- resolved = resolved[ plural ] !== undefined ?
- resolved[ plural ] :
- resolved._;
- }
-
- return resolved.replace( '%d', plural ); // nb: plural might be undefined,
- } );
- /**
- * Version string for plug-ins to check compatibility. Allowed format is
- * `a.b.c-d` where: a:int, b:int, c:int, d:string(dev|beta|alpha). `d` is used
- * only for non-release builds. See http://semver.org/ for more information.
- * @member
- * @type string
- * @default Version number
- */
- DataTable.version = "1.10.19";
- /**
- * Private data store, containing all of the settings objects that are
- * created for the tables on a given page.
- *
- * Note that the `DataTable.settings` object is aliased to
- * `jQuery.fn.dataTableExt` through which it may be accessed and
- * manipulated, or `jQuery.fn.dataTable.settings`.
- * @member
- * @type array
- * @default []
- * @private
- */
- DataTable.settings = [];
- /**
- * Object models container, for the various models that DataTables has
- * available to it. These models define the objects that are used to hold
- * the active state and configuration of the table.
- * @namespace
- */
- DataTable.models = {};
-
-
-
- /**
- * Template object for the way in which DataTables holds information about
- * search information for the global filter and individual column filters.
- * @namespace
- */
- DataTable.models.oSearch = {
- /**
- * Flag to indicate if the filtering should be case insensitive or not
- * @type boolean
- * @default true
- */
- "bCaseInsensitive": true,
-
- /**
- * Applied search term
- * @type string
- * @default <i>Empty string</i>
- */
- "sSearch": "",
-
- /**
- * Flag to indicate if the search term should be interpreted as a
- * regular expression (true) or not (false) and therefore and special
- * regex characters escaped.
- * @type boolean
- * @default false
- */
- "bRegex": false,
-
- /**
- * Flag to indicate if DataTables is to use its smart filtering or not.
- * @type boolean
- * @default true
- */
- "bSmart": true
- };
-
-
-
-
- /**
- * Template object for the way in which DataTables holds information about
- * each individual row. This is the object format used for the settings
- * aoData array.
- * @namespace
- */
- DataTable.models.oRow = {
- /**
- * TR element for the row
- * @type node
- * @default null
- */
- "nTr": null,
-
- /**
- * Array of TD elements for each row. This is null until the row has been
- * created.
- * @type array nodes
- * @default []
- */
- "anCells": null,
-
- /**
- * Data object from the original data source for the row. This is either
- * an array if using the traditional form of DataTables, or an object if
- * using mData options. The exact type will depend on the passed in
- * data from the data source, or will be an array if using DOM a data
- * source.
- * @type array|object
- * @default []
- */
- "_aData": [],
-
- /**
- * Sorting data cache - this array is ostensibly the same length as the
- * number of columns (although each index is generated only as it is
- * needed), and holds the data that is used for sorting each column in the
- * row. We do this cache generation at the start of the sort in order that
- * the formatting of the sort data need be done only once for each cell
- * per sort. This array should not be read from or written to by anything
- * other than the master sorting methods.
- * @type array
- * @default null
- * @private
- */
- "_aSortData": null,
-
- /**
- * Per cell filtering data cache. As per the sort data cache, used to
- * increase the performance of the filtering in DataTables
- * @type array
- * @default null
- * @private
- */
- "_aFilterData": null,
-
- /**
- * Filtering data cache. This is the same as the cell filtering cache, but
- * in this case a string rather than an array. This is easily computed with
- * a join on `_aFilterData`, but is provided as a cache so the join isn't
- * needed on every search (memory traded for performance)
- * @type array
- * @default null
- * @private
- */
- "_sFilterRow": null,
-
- /**
- * Cache of the class name that DataTables has applied to the row, so we
- * can quickly look at this variable rather than needing to do a DOM check
- * on className for the nTr property.
- * @type string
- * @default <i>Empty string</i>
- * @private
- */
- "_sRowStripe": "",
-
- /**
- * Denote if the original data source was from the DOM, or the data source
- * object. This is used for invalidating data, so DataTables can
- * automatically read data from the original source, unless uninstructed
- * otherwise.
- * @type string
- * @default null
- * @private
- */
- "src": null,
-
- /**
- * Index in the aoData array. This saves an indexOf lookup when we have the
- * object, but want to know the index
- * @type integer
- * @default -1
- * @private
- */
- "idx": -1
- };
-
-
- /**
- * Template object for the column information object in DataTables. This object
- * is held in the settings aoColumns array and contains all the information that
- * DataTables needs about each individual column.
- *
- * Note that this object is related to {@link DataTable.defaults.column}
- * but this one is the internal data store for DataTables's cache of columns.
- * It should NOT be manipulated outside of DataTables. Any configuration should
- * be done through the initialisation options.
- * @namespace
- */
- DataTable.models.oColumn = {
- /**
- * Column index. This could be worked out on-the-fly with $.inArray, but it
- * is faster to just hold it as a variable
- * @type integer
- * @default null
- */
- "idx": null,
-
- /**
- * A list of the columns that sorting should occur on when this column
- * is sorted. That this property is an array allows multi-column sorting
- * to be defined for a column (for example first name / last name columns
- * would benefit from this). The values are integers pointing to the
- * columns to be sorted on (typically it will be a single integer pointing
- * at itself, but that doesn't need to be the case).
- * @type array
- */
- "aDataSort": null,
-
- /**
- * Define the sorting directions that are applied to the column, in sequence
- * as the column is repeatedly sorted upon - i.e. the first value is used
- * as the sorting direction when the column if first sorted (clicked on).
- * Sort it again (click again) and it will move on to the next index.
- * Repeat until loop.
- * @type array
- */
- "asSorting": null,
-
- /**
- * Flag to indicate if the column is searchable, and thus should be included
- * in the filtering or not.
- * @type boolean
- */
- "bSearchable": null,
-
- /**
- * Flag to indicate if the column is sortable or not.
- * @type boolean
- */
- "bSortable": null,
-
- /**
- * Flag to indicate if the column is currently visible in the table or not
- * @type boolean
- */
- "bVisible": null,
-
- /**
- * Store for manual type assignment using the `column.type` option. This
- * is held in store so we can manipulate the column's `sType` property.
- * @type string
- * @default null
- * @private
- */
- "_sManualType": null,
-
- /**
- * Flag to indicate if HTML5 data attributes should be used as the data
- * source for filtering or sorting. True is either are.
- * @type boolean
- * @default false
- * @private
- */
- "_bAttrSrc": false,
-
- /**
- * Developer definable function that is called whenever a cell is created (Ajax source,
- * etc) or processed for input (DOM source). This can be used as a compliment to mRender
- * allowing you to modify the DOM element (add background colour for example) when the
- * element is available.
- * @type function
- * @param {element} nTd The TD node that has been created
- * @param {*} sData The Data for the cell
- * @param {array|object} oData The data for the whole row
- * @param {int} iRow The row index for the aoData data store
- * @default null
- */
- "fnCreatedCell": null,
-
- /**
- * Function to get data from a cell in a column. You should <b>never</b>
- * access data directly through _aData internally in DataTables - always use
- * the method attached to this property. It allows mData to function as
- * required. This function is automatically assigned by the column
- * initialisation method
- * @type function
- * @param {array|object} oData The data array/object for the array
- * (i.e. aoData[]._aData)
- * @param {string} sSpecific The specific data type you want to get -
- * 'display', 'type' 'filter' 'sort'
- * @returns {*} The data for the cell from the given row's data
- * @default null
- */
- "fnGetData": null,
-
- /**
- * Function to set data for a cell in the column. You should <b>never</b>
- * set the data directly to _aData internally in DataTables - always use
- * this method. It allows mData to function as required. This function
- * is automatically assigned by the column initialisation method
- * @type function
- * @param {array|object} oData The data array/object for the array
- * (i.e. aoData[]._aData)
- * @param {*} sValue Value to set
- * @default null
- */
- "fnSetData": null,
-
- /**
- * Property to read the value for the cells in the column from the data
- * source array / object. If null, then the default content is used, if a
- * function is given then the return from the function is used.
- * @type function|int|string|null
- * @default null
- */
- "mData": null,
-
- /**
- * Partner property to mData which is used (only when defined) to get
- * the data - i.e. it is basically the same as mData, but without the
- * 'set' option, and also the data fed to it is the result from mData.
- * This is the rendering method to match the data method of mData.
- * @type function|int|string|null
- * @default null
- */
- "mRender": null,
-
- /**
- * Unique header TH/TD element for this column - this is what the sorting
- * listener is attached to (if sorting is enabled.)
- * @type node
- * @default null
- */
- "nTh": null,
-
- /**
- * Unique footer TH/TD element for this column (if there is one). Not used
- * in DataTables as such, but can be used for plug-ins to reference the
- * footer for each column.
- * @type node
- * @default null
- */
- "nTf": null,
-
- /**
- * The class to apply to all TD elements in the table's TBODY for the column
- * @type string
- * @default null
- */
- "sClass": null,
-
- /**
- * When DataTables calculates the column widths to assign to each column,
- * it finds the longest string in each column and then constructs a
- * temporary table and reads the widths from that. The problem with this
- * is that "mmm" is much wider then "iiii", but the latter is a longer
- * string - thus the calculation can go wrong (doing it properly and putting
- * it into an DOM object and measuring that is horribly(!) slow). Thus as
- * a "work around" we provide this option. It will append its value to the
- * text that is found to be the longest string for the column - i.e. padding.
- * @type string
- */
- "sContentPadding": null,
-
- /**
- * Allows a default value to be given for a column's data, and will be used
- * whenever a null data source is encountered (this can be because mData
- * is set to null, or because the data source itself is null).
- * @type string
- * @default null
- */
- "sDefaultContent": null,
-
- /**
- * Name for the column, allowing reference to the column by name as well as
- * by index (needs a lookup to work by name).
- * @type string
- */
- "sName": null,
-
- /**
- * Custom sorting data type - defines which of the available plug-ins in
- * afnSortData the custom sorting will use - if any is defined.
- * @type string
- * @default std
- */
- "sSortDataType": 'std',
-
- /**
- * Class to be applied to the header element when sorting on this column
- * @type string
- * @default null
- */
- "sSortingClass": null,
-
- /**
- * Class to be applied to the header element when sorting on this column -
- * when jQuery UI theming is used.
- * @type string
- * @default null
- */
- "sSortingClassJUI": null,
-
- /**
- * Title of the column - what is seen in the TH element (nTh).
- * @type string
- */
- "sTitle": null,
-
- /**
- * Column sorting and filtering type
- * @type string
- * @default null
- */
- "sType": null,
-
- /**
- * Width of the column
- * @type string
- * @default null
- */
- "sWidth": null,
-
- /**
- * Width of the column when it was first "encountered"
- * @type string
- * @default null
- */
- "sWidthOrig": null
- };
-
-
- /*
- * Developer note: The properties of the object below are given in Hungarian
- * notation, that was used as the interface for DataTables prior to v1.10, however
- * from v1.10 onwards the primary interface is camel case. In order to avoid
- * breaking backwards compatibility utterly with this change, the Hungarian
- * version is still, internally the primary interface, but is is not documented
- * - hence the @name tags in each doc comment. This allows a Javascript function
- * to create a map from Hungarian notation to camel case (going the other direction
- * would require each property to be listed, which would at around 3K to the size
- * of DataTables, while this method is about a 0.5K hit.
- *
- * Ultimately this does pave the way for Hungarian notation to be dropped
- * completely, but that is a massive amount of work and will break current
- * installs (therefore is on-hold until v2).
- */
-
- /**
- * Initialisation options that can be given to DataTables at initialisation
- * time.
- * @namespace
- */
- DataTable.defaults = {
- /**
- * An array of data to use for the table, passed in at initialisation which
- * will be used in preference to any data which is already in the DOM. This is
- * particularly useful for constructing tables purely in Javascript, for
- * example with a custom Ajax call.
- * @type array
- * @default null
- *
- * @dtopt Option
- * @name DataTable.defaults.data
- *
- * @example
- * // Using a 2D array data source
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "data": [
- * ['Trident', 'Internet Explorer 4.0', 'Win 95+', 4, 'X'],
- * ['Trident', 'Internet Explorer 5.0', 'Win 95+', 5, 'C'],
- * ],
- * "columns": [
- * { "title": "Engine" },
- * { "title": "Browser" },
- * { "title": "Platform" },
- * { "title": "Version" },
- * { "title": "Grade" }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using an array of objects as a data source (`data`)
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "data": [
- * {
- * "engine": "Trident",
- * "browser": "Internet Explorer 4.0",
- * "platform": "Win 95+",
- * "version": 4,
- * "grade": "X"
- * },
- * {
- * "engine": "Trident",
- * "browser": "Internet Explorer 5.0",
- * "platform": "Win 95+",
- * "version": 5,
- * "grade": "C"
- * }
- * ],
- * "columns": [
- * { "title": "Engine", "data": "engine" },
- * { "title": "Browser", "data": "browser" },
- * { "title": "Platform", "data": "platform" },
- * { "title": "Version", "data": "version" },
- * { "title": "Grade", "data": "grade" }
- * ]
- * } );
- * } );
- */
- "aaData": null,
-
-
- /**
- * If ordering is enabled, then DataTables will perform a first pass sort on
- * initialisation. You can define which column(s) the sort is performed
- * upon, and the sorting direction, with this variable. The `sorting` array
- * should contain an array for each column to be sorted initially containing
- * the column's index and a direction string ('asc' or 'desc').
- * @type array
- * @default [[0,'asc']]
- *
- * @dtopt Option
- * @name DataTable.defaults.order
- *
- * @example
- * // Sort by 3rd column first, and then 4th column
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "order": [[2,'asc'], [3,'desc']]
- * } );
- * } );
- *
- * // No initial sorting
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "order": []
- * } );
- * } );
- */
- "aaSorting": [[0,'asc']],
-
-
- /**
- * This parameter is basically identical to the `sorting` parameter, but
- * cannot be overridden by user interaction with the table. What this means
- * is that you could have a column (visible or hidden) which the sorting
- * will always be forced on first - any sorting after that (from the user)
- * will then be performed as required. This can be useful for grouping rows
- * together.
- * @type array
- * @default null
- *
- * @dtopt Option
- * @name DataTable.defaults.orderFixed
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "orderFixed": [[0,'asc']]
- * } );
- * } )
- */
- "aaSortingFixed": [],
-
-
- /**
- * DataTables can be instructed to load data to display in the table from a
- * Ajax source. This option defines how that Ajax call is made and where to.
- *
- * The `ajax` property has three different modes of operation, depending on
- * how it is defined. These are:
- *
- * * `string` - Set the URL from where the data should be loaded from.
- * * `object` - Define properties for `jQuery.ajax`.
- * * `function` - Custom data get function
- *
- * `string`
- * --------
- *
- * As a string, the `ajax` property simply defines the URL from which
- * DataTables will load data.
- *
- * `object`
- * --------
- *
- * As an object, the parameters in the object are passed to
- * [jQuery.ajax](http://api.jquery.com/jQuery.ajax/) allowing fine control
- * of the Ajax request. DataTables has a number of default parameters which
- * you can override using this option. Please refer to the jQuery
- * documentation for a full description of the options available, although
- * the following parameters provide additional options in DataTables or
- * require special consideration:
- *
- * * `data` - As with jQuery, `data` can be provided as an object, but it
- * can also be used as a function to manipulate the data DataTables sends
- * to the server. The function takes a single parameter, an object of
- * parameters with the values that DataTables has readied for sending. An
- * object may be returned which will be merged into the DataTables
- * defaults, or you can add the items to the object that was passed in and
- * not return anything from the function. This supersedes `fnServerParams`
- * from DataTables 1.9-.
- *
- * * `dataSrc` - By default DataTables will look for the property `data` (or
- * `aaData` for compatibility with DataTables 1.9-) when obtaining data
- * from an Ajax source or for server-side processing - this parameter
- * allows that property to be changed. You can use Javascript dotted
- * object notation to get a data source for multiple levels of nesting, or
- * it my be used as a function. As a function it takes a single parameter,
- * the JSON returned from the server, which can be manipulated as
- * required, with the returned value being that used by DataTables as the
- * data source for the table. This supersedes `sAjaxDataProp` from
- * DataTables 1.9-.
- *
- * * `success` - Should not be overridden it is used internally in
- * DataTables. To manipulate / transform the data returned by the server
- * use `ajax.dataSrc`, or use `ajax` as a function (see below).
- *
- * `function`
- * ----------
- *
- * As a function, making the Ajax call is left up to yourself allowing
- * complete control of the Ajax request. Indeed, if desired, a method other
- * than Ajax could be used to obtain the required data, such as Web storage
- * or an AIR database.
- *
- * The function is given four parameters and no return is required. The
- * parameters are:
- *
- * 1. _object_ - Data to send to the server
- * 2. _function_ - Callback function that must be executed when the required
- * data has been obtained. That data should be passed into the callback
- * as the only parameter
- * 3. _object_ - DataTables settings object for the table
- *
- * Note that this supersedes `fnServerData` from DataTables 1.9-.
- *
- * @type string|object|function
- * @default null
- *
- * @dtopt Option
- * @name DataTable.defaults.ajax
- * @since 1.10.0
- *
- * @example
- * // Get JSON data from a file via Ajax.
- * // Note DataTables expects data in the form `{ data: [ ...data... ] }` by default).
- * $('#example').dataTable( {
- * "ajax": "data.json"
- * } );
- *
- * @example
- * // Get JSON data from a file via Ajax, using `dataSrc` to change
- * // `data` to `tableData` (i.e. `{ tableData: [ ...data... ] }`)
- * $('#example').dataTable( {
- * "ajax": {
- * "url": "data.json",
- * "dataSrc": "tableData"
- * }
- * } );
- *
- * @example
- * // Get JSON data from a file via Ajax, using `dataSrc` to read data
- * // from a plain array rather than an array in an object
- * $('#example').dataTable( {
- * "ajax": {
- * "url": "data.json",
- * "dataSrc": ""
- * }
- * } );
- *
- * @example
- * // Manipulate the data returned from the server - add a link to data
- * // (note this can, should, be done using `render` for the column - this
- * // is just a simple example of how the data can be manipulated).
- * $('#example').dataTable( {
- * "ajax": {
- * "url": "data.json",
- * "dataSrc": function ( json ) {
- * for ( var i=0, ien=json.length ; i<ien ; i++ ) {
- * json[i][0] = '<a href="/message/'+json[i][0]+'>View message</a>';
- * }
- * return json;
- * }
- * }
- * } );
- *
- * @example
- * // Add data to the request
- * $('#example').dataTable( {
- * "ajax": {
- * "url": "data.json",
- * "data": function ( d ) {
- * return {
- * "extra_search": $('#extra').val()
- * };
- * }
- * }
- * } );
- *
- * @example
- * // Send request as POST
- * $('#example').dataTable( {
- * "ajax": {
- * "url": "data.json",
- * "type": "POST"
- * }
- * } );
- *
- * @example
- * // Get the data from localStorage (could interface with a form for
- * // adding, editing and removing rows).
- * $('#example').dataTable( {
- * "ajax": function (data, callback, settings) {
- * callback(
- * JSON.parse( localStorage.getItem('dataTablesData') )
- * );
- * }
- * } );
- */
- "ajax": null,
-
-
- /**
- * This parameter allows you to readily specify the entries in the length drop
- * down menu that DataTables shows when pagination is enabled. It can be
- * either a 1D array of options which will be used for both the displayed
- * option and the value, or a 2D array which will use the array in the first
- * position as the value, and the array in the second position as the
- * displayed options (useful for language strings such as 'All').
- *
- * Note that the `pageLength` property will be automatically set to the
- * first value given in this array, unless `pageLength` is also provided.
- * @type array
- * @default [ 10, 25, 50, 100 ]
- *
- * @dtopt Option
- * @name DataTable.defaults.lengthMenu
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "lengthMenu": [[10, 25, 50, -1], [10, 25, 50, "All"]]
- * } );
- * } );
- */
- "aLengthMenu": [ 10, 25, 50, 100 ],
-
-
- /**
- * The `columns` option in the initialisation parameter allows you to define
- * details about the way individual columns behave. For a full list of
- * column options that can be set, please see
- * {@link DataTable.defaults.column}. Note that if you use `columns` to
- * define your columns, you must have an entry in the array for every single
- * column that you have in your table (these can be null if you don't which
- * to specify any options).
- * @member
- *
- * @name DataTable.defaults.column
- */
- "aoColumns": null,
-
- /**
- * Very similar to `columns`, `columnDefs` allows you to target a specific
- * column, multiple columns, or all columns, using the `targets` property of
- * each object in the array. This allows great flexibility when creating
- * tables, as the `columnDefs` arrays can be of any length, targeting the
- * columns you specifically want. `columnDefs` may use any of the column
- * options available: {@link DataTable.defaults.column}, but it _must_
- * have `targets` defined in each object in the array. Values in the `targets`
- * array may be:
- * <ul>
- * <li>a string - class name will be matched on the TH for the column</li>
- * <li>0 or a positive integer - column index counting from the left</li>
- * <li>a negative integer - column index counting from the right</li>
- * <li>the string "_all" - all columns (i.e. assign a default)</li>
- * </ul>
- * @member
- *
- * @name DataTable.defaults.columnDefs
- */
- "aoColumnDefs": null,
-
-
- /**
- * Basically the same as `search`, this parameter defines the individual column
- * filtering state at initialisation time. The array must be of the same size
- * as the number of columns, and each element be an object with the parameters
- * `search` and `escapeRegex` (the latter is optional). 'null' is also
- * accepted and the default will be used.
- * @type array
- * @default []
- *
- * @dtopt Option
- * @name DataTable.defaults.searchCols
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "searchCols": [
- * null,
- * { "search": "My filter" },
- * null,
- * { "search": "^[0-9]", "escapeRegex": false }
- * ]
- * } );
- * } )
- */
- "aoSearchCols": [],
-
-
- /**
- * An array of CSS classes that should be applied to displayed rows. This
- * array may be of any length, and DataTables will apply each class
- * sequentially, looping when required.
- * @type array
- * @default null <i>Will take the values determined by the `oClasses.stripe*`
- * options</i>
- *
- * @dtopt Option
- * @name DataTable.defaults.stripeClasses
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stripeClasses": [ 'strip1', 'strip2', 'strip3' ]
- * } );
- * } )
- */
- "asStripeClasses": null,
-
-
- /**
- * Enable or disable automatic column width calculation. This can be disabled
- * as an optimisation (it takes some time to calculate the widths) if the
- * tables widths are passed in using `columns`.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.autoWidth
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "autoWidth": false
- * } );
- * } );
- */
- "bAutoWidth": true,
-
-
- /**
- * Deferred rendering can provide DataTables with a huge speed boost when you
- * are using an Ajax or JS data source for the table. This option, when set to
- * true, will cause DataTables to defer the creation of the table elements for
- * each row until they are needed for a draw - saving a significant amount of
- * time.
- * @type boolean
- * @default false
- *
- * @dtopt Features
- * @name DataTable.defaults.deferRender
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "ajax": "sources/arrays.txt",
- * "deferRender": true
- * } );
- * } );
- */
- "bDeferRender": false,
-
-
- /**
- * Replace a DataTable which matches the given selector and replace it with
- * one which has the properties of the new initialisation object passed. If no
- * table matches the selector, then the new DataTable will be constructed as
- * per normal.
- * @type boolean
- * @default false
- *
- * @dtopt Options
- * @name DataTable.defaults.destroy
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "srollY": "200px",
- * "paginate": false
- * } );
- *
- * // Some time later....
- * $('#example').dataTable( {
- * "filter": false,
- * "destroy": true
- * } );
- * } );
- */
- "bDestroy": false,
-
-
- /**
- * Enable or disable filtering of data. Filtering in DataTables is "smart" in
- * that it allows the end user to input multiple words (space separated) and
- * will match a row containing those words, even if not in the order that was
- * specified (this allow matching across multiple columns). Note that if you
- * wish to use filtering in DataTables this must remain 'true' - to remove the
- * default filtering input box and retain filtering abilities, please use
- * {@link DataTable.defaults.dom}.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.searching
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "searching": false
- * } );
- * } );
- */
- "bFilter": true,
-
-
- /**
- * Enable or disable the table information display. This shows information
- * about the data that is currently visible on the page, including information
- * about filtered data if that action is being performed.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.info
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "info": false
- * } );
- * } );
- */
- "bInfo": true,
-
-
- /**
- * Allows the end user to select the size of a formatted page from a select
- * menu (sizes are 10, 25, 50 and 100). Requires pagination (`paginate`).
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.lengthChange
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "lengthChange": false
- * } );
- * } );
- */
- "bLengthChange": true,
-
-
- /**
- * Enable or disable pagination.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.paging
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "paging": false
- * } );
- * } );
- */
- "bPaginate": true,
-
-
- /**
- * Enable or disable the display of a 'processing' indicator when the table is
- * being processed (e.g. a sort). This is particularly useful for tables with
- * large amounts of data where it can take a noticeable amount of time to sort
- * the entries.
- * @type boolean
- * @default false
- *
- * @dtopt Features
- * @name DataTable.defaults.processing
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "processing": true
- * } );
- * } );
- */
- "bProcessing": false,
-
-
- /**
- * Retrieve the DataTables object for the given selector. Note that if the
- * table has already been initialised, this parameter will cause DataTables
- * to simply return the object that has already been set up - it will not take
- * account of any changes you might have made to the initialisation object
- * passed to DataTables (setting this parameter to true is an acknowledgement
- * that you understand this). `destroy` can be used to reinitialise a table if
- * you need.
- * @type boolean
- * @default false
- *
- * @dtopt Options
- * @name DataTable.defaults.retrieve
- *
- * @example
- * $(document).ready( function() {
- * initTable();
- * tableActions();
- * } );
- *
- * function initTable ()
- * {
- * return $('#example').dataTable( {
- * "scrollY": "200px",
- * "paginate": false,
- * "retrieve": true
- * } );
- * }
- *
- * function tableActions ()
- * {
- * var table = initTable();
- * // perform API operations with oTable
- * }
- */
- "bRetrieve": false,
-
-
- /**
- * When vertical (y) scrolling is enabled, DataTables will force the height of
- * the table's viewport to the given height at all times (useful for layout).
- * However, this can look odd when filtering data down to a small data set,
- * and the footer is left "floating" further down. This parameter (when
- * enabled) will cause DataTables to collapse the table's viewport down when
- * the result set will fit within the given Y height.
- * @type boolean
- * @default false
- *
- * @dtopt Options
- * @name DataTable.defaults.scrollCollapse
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "scrollY": "200",
- * "scrollCollapse": true
- * } );
- * } );
- */
- "bScrollCollapse": false,
-
-
- /**
- * Configure DataTables to use server-side processing. Note that the
- * `ajax` parameter must also be given in order to give DataTables a
- * source to obtain the required data for each draw.
- * @type boolean
- * @default false
- *
- * @dtopt Features
- * @dtopt Server-side
- * @name DataTable.defaults.serverSide
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "serverSide": true,
- * "ajax": "xhr.php"
- * } );
- * } );
- */
- "bServerSide": false,
-
-
- /**
- * Enable or disable sorting of columns. Sorting of individual columns can be
- * disabled by the `sortable` option for each column.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.ordering
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "ordering": false
- * } );
- * } );
- */
- "bSort": true,
-
-
- /**
- * Enable or display DataTables' ability to sort multiple columns at the
- * same time (activated by shift-click by the user).
- * @type boolean
- * @default true
- *
- * @dtopt Options
- * @name DataTable.defaults.orderMulti
- *
- * @example
- * // Disable multiple column sorting ability
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "orderMulti": false
- * } );
- * } );
- */
- "bSortMulti": true,
-
-
- /**
- * Allows control over whether DataTables should use the top (true) unique
- * cell that is found for a single column, or the bottom (false - default).
- * This is useful when using complex headers.
- * @type boolean
- * @default false
- *
- * @dtopt Options
- * @name DataTable.defaults.orderCellsTop
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "orderCellsTop": true
- * } );
- * } );
- */
- "bSortCellsTop": false,
-
-
- /**
- * Enable or disable the addition of the classes `sorting\_1`, `sorting\_2` and
- * `sorting\_3` to the columns which are currently being sorted on. This is
- * presented as a feature switch as it can increase processing time (while
- * classes are removed and added) so for large data sets you might want to
- * turn this off.
- * @type boolean
- * @default true
- *
- * @dtopt Features
- * @name DataTable.defaults.orderClasses
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "orderClasses": false
- * } );
- * } );
- */
- "bSortClasses": true,
-
-
- /**
- * Enable or disable state saving. When enabled HTML5 `localStorage` will be
- * used to save table display information such as pagination information,
- * display length, filtering and sorting. As such when the end user reloads
- * the page the display display will match what thy had previously set up.
- *
- * Due to the use of `localStorage` the default state saving is not supported
- * in IE6 or 7. If state saving is required in those browsers, use
- * `stateSaveCallback` to provide a storage solution such as cookies.
- * @type boolean
- * @default false
- *
- * @dtopt Features
- * @name DataTable.defaults.stateSave
- *
- * @example
- * $(document).ready( function () {
- * $('#example').dataTable( {
- * "stateSave": true
- * } );
- * } );
- */
- "bStateSave": false,
-
-
- /**
- * This function is called when a TR element is created (and all TD child
- * elements have been inserted), or registered if using a DOM source, allowing
- * manipulation of the TR element (adding classes etc).
- * @type function
- * @param {node} row "TR" element for the current row
- * @param {array} data Raw data array for this row
- * @param {int} dataIndex The index of this row in the internal aoData array
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.createdRow
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "createdRow": function( row, data, dataIndex ) {
- * // Bold the grade for all 'A' grade browsers
- * if ( data[4] == "A" )
- * {
- * $('td:eq(4)', row).html( '<b>A</b>' );
- * }
- * }
- * } );
- * } );
- */
- "fnCreatedRow": null,
-
-
- /**
- * This function is called on every 'draw' event, and allows you to
- * dynamically modify any aspect you want about the created DOM.
- * @type function
- * @param {object} settings DataTables settings object
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.drawCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "drawCallback": function( settings ) {
- * alert( 'DataTables has redrawn the table' );
- * }
- * } );
- * } );
- */
- "fnDrawCallback": null,
-
-
- /**
- * Identical to fnHeaderCallback() but for the table footer this function
- * allows you to modify the table footer on every 'draw' event.
- * @type function
- * @param {node} foot "TR" element for the footer
- * @param {array} data Full table data (as derived from the original HTML)
- * @param {int} start Index for the current display starting point in the
- * display array
- * @param {int} end Index for the current display ending point in the
- * display array
- * @param {array int} display Index array to translate the visual position
- * to the full data array
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.footerCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "footerCallback": function( tfoot, data, start, end, display ) {
- * tfoot.getElementsByTagName('th')[0].innerHTML = "Starting index is "+start;
- * }
- * } );
- * } )
- */
- "fnFooterCallback": null,
-
-
- /**
- * When rendering large numbers in the information element for the table
- * (i.e. "Showing 1 to 10 of 57 entries") DataTables will render large numbers
- * to have a comma separator for the 'thousands' units (e.g. 1 million is
- * rendered as "1,000,000") to help readability for the end user. This
- * function will override the default method DataTables uses.
- * @type function
- * @member
- * @param {int} toFormat number to be formatted
- * @returns {string} formatted string for DataTables to show the number
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.formatNumber
- *
- * @example
- * // Format a number using a single quote for the separator (note that
- * // this can also be done with the language.thousands option)
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "formatNumber": function ( toFormat ) {
- * return toFormat.toString().replace(
- * /\B(?=(\d{3})+(?!\d))/g, "'"
- * );
- * };
- * } );
- * } );
- */
- "fnFormatNumber": function ( toFormat ) {
- return toFormat.toString().replace(
- /\B(?=(\d{3})+(?!\d))/g,
- this.oLanguage.sThousands
- );
- },
-
-
- /**
- * This function is called on every 'draw' event, and allows you to
- * dynamically modify the header row. This can be used to calculate and
- * display useful information about the table.
- * @type function
- * @param {node} head "TR" element for the header
- * @param {array} data Full table data (as derived from the original HTML)
- * @param {int} start Index for the current display starting point in the
- * display array
- * @param {int} end Index for the current display ending point in the
- * display array
- * @param {array int} display Index array to translate the visual position
- * to the full data array
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.headerCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "fheaderCallback": function( head, data, start, end, display ) {
- * head.getElementsByTagName('th')[0].innerHTML = "Displaying "+(end-start)+" records";
- * }
- * } );
- * } )
- */
- "fnHeaderCallback": null,
-
-
- /**
- * The information element can be used to convey information about the current
- * state of the table. Although the internationalisation options presented by
- * DataTables are quite capable of dealing with most customisations, there may
- * be times where you wish to customise the string further. This callback
- * allows you to do exactly that.
- * @type function
- * @param {object} oSettings DataTables settings object
- * @param {int} start Starting position in data for the draw
- * @param {int} end End position in data for the draw
- * @param {int} max Total number of rows in the table (regardless of
- * filtering)
- * @param {int} total Total number of rows in the data set, after filtering
- * @param {string} pre The string that DataTables has formatted using it's
- * own rules
- * @returns {string} The string to be displayed in the information element.
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.infoCallback
- *
- * @example
- * $('#example').dataTable( {
- * "infoCallback": function( settings, start, end, max, total, pre ) {
- * return start +" to "+ end;
- * }
- * } );
- */
- "fnInfoCallback": null,
-
-
- /**
- * Called when the table has been initialised. Normally DataTables will
- * initialise sequentially and there will be no need for this function,
- * however, this does not hold true when using external language information
- * since that is obtained using an async XHR call.
- * @type function
- * @param {object} settings DataTables settings object
- * @param {object} json The JSON object request from the server - only
- * present if client-side Ajax sourced data is used
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.initComplete
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "initComplete": function(settings, json) {
- * alert( 'DataTables has finished its initialisation.' );
- * }
- * } );
- * } )
- */
- "fnInitComplete": null,
-
-
- /**
- * Called at the very start of each table draw and can be used to cancel the
- * draw by returning false, any other return (including undefined) results in
- * the full draw occurring).
- * @type function
- * @param {object} settings DataTables settings object
- * @returns {boolean} False will cancel the draw, anything else (including no
- * return) will allow it to complete.
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.preDrawCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "preDrawCallback": function( settings ) {
- * if ( $('#test').val() == 1 ) {
- * return false;
- * }
- * }
- * } );
- * } );
- */
- "fnPreDrawCallback": null,
-
-
- /**
- * This function allows you to 'post process' each row after it have been
- * generated for each table draw, but before it is rendered on screen. This
- * function might be used for setting the row class name etc.
- * @type function
- * @param {node} row "TR" element for the current row
- * @param {array} data Raw data array for this row
- * @param {int} displayIndex The display index for the current table draw
- * @param {int} displayIndexFull The index of the data in the full list of
- * rows (after filtering)
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.rowCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "rowCallback": function( row, data, displayIndex, displayIndexFull ) {
- * // Bold the grade for all 'A' grade browsers
- * if ( data[4] == "A" ) {
- * $('td:eq(4)', row).html( '<b>A</b>' );
- * }
- * }
- * } );
- * } );
- */
- "fnRowCallback": null,
-
-
- /**
- * __Deprecated__ The functionality provided by this parameter has now been
- * superseded by that provided through `ajax`, which should be used instead.
- *
- * This parameter allows you to override the default function which obtains
- * the data from the server so something more suitable for your application.
- * For example you could use POST data, or pull information from a Gears or
- * AIR database.
- * @type function
- * @member
- * @param {string} source HTTP source to obtain the data from (`ajax`)
- * @param {array} data A key/value pair object containing the data to send
- * to the server
- * @param {function} callback to be called on completion of the data get
- * process that will draw the data on the page.
- * @param {object} settings DataTables settings object
- *
- * @dtopt Callbacks
- * @dtopt Server-side
- * @name DataTable.defaults.serverData
- *
- * @deprecated 1.10. Please use `ajax` for this functionality now.
- */
- "fnServerData": null,
-
-
- /**
- * __Deprecated__ The functionality provided by this parameter has now been
- * superseded by that provided through `ajax`, which should be used instead.
- *
- * It is often useful to send extra data to the server when making an Ajax
- * request - for example custom filtering information, and this callback
- * function makes it trivial to send extra information to the server. The
- * passed in parameter is the data set that has been constructed by
- * DataTables, and you can add to this or modify it as you require.
- * @type function
- * @param {array} data Data array (array of objects which are name/value
- * pairs) that has been constructed by DataTables and will be sent to the
- * server. In the case of Ajax sourced data with server-side processing
- * this will be an empty array, for server-side processing there will be a
- * significant number of parameters!
- * @returns {undefined} Ensure that you modify the data array passed in,
- * as this is passed by reference.
- *
- * @dtopt Callbacks
- * @dtopt Server-side
- * @name DataTable.defaults.serverParams
- *
- * @deprecated 1.10. Please use `ajax` for this functionality now.
- */
- "fnServerParams": null,
-
-
- /**
- * Load the table state. With this function you can define from where, and how, the
- * state of a table is loaded. By default DataTables will load from `localStorage`
- * but you might wish to use a server-side database or cookies.
- * @type function
- * @member
- * @param {object} settings DataTables settings object
- * @param {object} callback Callback that can be executed when done. It
- * should be passed the loaded state object.
- * @return {object} The DataTables state object to be loaded
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.stateLoadCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateLoadCallback": function (settings, callback) {
- * $.ajax( {
- * "url": "/state_load",
- * "dataType": "json",
- * "success": function (json) {
- * callback( json );
- * }
- * } );
- * }
- * } );
- * } );
- */
- "fnStateLoadCallback": function ( settings ) {
- try {
- return JSON.parse(
- (settings.iStateDuration === -1 ? sessionStorage : localStorage).getItem(
- 'DataTables_'+settings.sInstance+'_'+location.pathname
- )
- );
- } catch (e) {}
- },
-
-
- /**
- * Callback which allows modification of the saved state prior to loading that state.
- * This callback is called when the table is loading state from the stored data, but
- * prior to the settings object being modified by the saved state. Note that for
- * plug-in authors, you should use the `stateLoadParams` event to load parameters for
- * a plug-in.
- * @type function
- * @param {object} settings DataTables settings object
- * @param {object} data The state object that is to be loaded
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.stateLoadParams
- *
- * @example
- * // Remove a saved filter, so filtering is never loaded
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateLoadParams": function (settings, data) {
- * data.oSearch.sSearch = "";
- * }
- * } );
- * } );
- *
- * @example
- * // Disallow state loading by returning false
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateLoadParams": function (settings, data) {
- * return false;
- * }
- * } );
- * } );
- */
- "fnStateLoadParams": null,
-
-
- /**
- * Callback that is called when the state has been loaded from the state saving method
- * and the DataTables settings object has been modified as a result of the loaded state.
- * @type function
- * @param {object} settings DataTables settings object
- * @param {object} data The state object that was loaded
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.stateLoaded
- *
- * @example
- * // Show an alert with the filtering value that was saved
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateLoaded": function (settings, data) {
- * alert( 'Saved filter was: '+data.oSearch.sSearch );
- * }
- * } );
- * } );
- */
- "fnStateLoaded": null,
-
-
- /**
- * Save the table state. This function allows you to define where and how the state
- * information for the table is stored By default DataTables will use `localStorage`
- * but you might wish to use a server-side database or cookies.
- * @type function
- * @member
- * @param {object} settings DataTables settings object
- * @param {object} data The state object to be saved
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.stateSaveCallback
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateSaveCallback": function (settings, data) {
- * // Send an Ajax request to the server with the state object
- * $.ajax( {
- * "url": "/state_save",
- * "data": data,
- * "dataType": "json",
- * "method": "POST"
- * "success": function () {}
- * } );
- * }
- * } );
- * } );
- */
- "fnStateSaveCallback": function ( settings, data ) {
- try {
- (settings.iStateDuration === -1 ? sessionStorage : localStorage).setItem(
- 'DataTables_'+settings.sInstance+'_'+location.pathname,
- JSON.stringify( data )
- );
- } catch (e) {}
- },
-
-
- /**
- * Callback which allows modification of the state to be saved. Called when the table
- * has changed state a new state save is required. This method allows modification of
- * the state saving object prior to actually doing the save, including addition or
- * other state properties or modification. Note that for plug-in authors, you should
- * use the `stateSaveParams` event to save parameters for a plug-in.
- * @type function
- * @param {object} settings DataTables settings object
- * @param {object} data The state object to be saved
- *
- * @dtopt Callbacks
- * @name DataTable.defaults.stateSaveParams
- *
- * @example
- * // Remove a saved filter, so filtering is never saved
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateSave": true,
- * "stateSaveParams": function (settings, data) {
- * data.oSearch.sSearch = "";
- * }
- * } );
- * } );
- */
- "fnStateSaveParams": null,
-
-
- /**
- * Duration for which the saved state information is considered valid. After this period
- * has elapsed the state will be returned to the default.
- * Value is given in seconds.
- * @type int
- * @default 7200 <i>(2 hours)</i>
- *
- * @dtopt Options
- * @name DataTable.defaults.stateDuration
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "stateDuration": 60*60*24; // 1 day
- * } );
- * } )
- */
- "iStateDuration": 7200,
-
-
- /**
- * When enabled DataTables will not make a request to the server for the first
- * page draw - rather it will use the data already on the page (no sorting etc
- * will be applied to it), thus saving on an XHR at load time. `deferLoading`
- * is used to indicate that deferred loading is required, but it is also used
- * to tell DataTables how many records there are in the full table (allowing
- * the information element and pagination to be displayed correctly). In the case
- * where a filtering is applied to the table on initial load, this can be
- * indicated by giving the parameter as an array, where the first element is
- * the number of records available after filtering and the second element is the
- * number of records without filtering (allowing the table information element
- * to be shown correctly).
- * @type int | array
- * @default null
- *
- * @dtopt Options
- * @name DataTable.defaults.deferLoading
- *
- * @example
- * // 57 records available in the table, no filtering applied
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "serverSide": true,
- * "ajax": "scripts/server_processing.php",
- * "deferLoading": 57
- * } );
- * } );
- *
- * @example
- * // 57 records after filtering, 100 without filtering (an initial filter applied)
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "serverSide": true,
- * "ajax": "scripts/server_processing.php",
- * "deferLoading": [ 57, 100 ],
- * "search": {
- * "search": "my_filter"
- * }
- * } );
- * } );
- */
- "iDeferLoading": null,
-
-
- /**
- * Number of rows to display on a single page when using pagination. If
- * feature enabled (`lengthChange`) then the end user will be able to override
- * this to a custom setting using a pop-up menu.
- * @type int
- * @default 10
- *
- * @dtopt Options
- * @name DataTable.defaults.pageLength
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "pageLength": 50
- * } );
- * } )
- */
- "iDisplayLength": 10,
-
-
- /**
- * Define the starting point for data display when using DataTables with
- * pagination. Note that this parameter is the number of records, rather than
- * the page number, so if you have 10 records per page and want to start on
- * the third page, it should be "20".
- * @type int
- * @default 0
- *
- * @dtopt Options
- * @name DataTable.defaults.displayStart
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "displayStart": 20
- * } );
- * } )
- */
- "iDisplayStart": 0,
-
-
- /**
- * By default DataTables allows keyboard navigation of the table (sorting, paging,
- * and filtering) by adding a `tabindex` attribute to the required elements. This
- * allows you to tab through the controls and press the enter key to activate them.
- * The tabindex is default 0, meaning that the tab follows the flow of the document.
- * You can overrule this using this parameter if you wish. Use a value of -1 to
- * disable built-in keyboard navigation.
- * @type int
- * @default 0
- *
- * @dtopt Options
- * @name DataTable.defaults.tabIndex
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "tabIndex": 1
- * } );
- * } );
- */
- "iTabIndex": 0,
-
-
- /**
- * Classes that DataTables assigns to the various components and features
- * that it adds to the HTML table. This allows classes to be configured
- * during initialisation in addition to through the static
- * {@link DataTable.ext.oStdClasses} object).
- * @namespace
- * @name DataTable.defaults.classes
- */
- "oClasses": {},
-
-
- /**
- * All strings that DataTables uses in the user interface that it creates
- * are defined in this object, allowing you to modified them individually or
- * completely replace them all as required.
- * @namespace
- * @name DataTable.defaults.language
- */
- "oLanguage": {
- /**
- * Strings that are used for WAI-ARIA labels and controls only (these are not
- * actually visible on the page, but will be read by screenreaders, and thus
- * must be internationalised as well).
- * @namespace
- * @name DataTable.defaults.language.aria
- */
- "oAria": {
- /**
- * ARIA label that is added to the table headers when the column may be
- * sorted ascending by activing the column (click or return when focused).
- * Note that the column header is prefixed to this string.
- * @type string
- * @default : activate to sort column ascending
- *
- * @dtopt Language
- * @name DataTable.defaults.language.aria.sortAscending
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "aria": {
- * "sortAscending": " - click/return to sort ascending"
- * }
- * }
- * } );
- * } );
- */
- "sSortAscending": ": activate to sort column ascending",
-
- /**
- * ARIA label that is added to the table headers when the column may be
- * sorted descending by activing the column (click or return when focused).
- * Note that the column header is prefixed to this string.
- * @type string
- * @default : activate to sort column ascending
- *
- * @dtopt Language
- * @name DataTable.defaults.language.aria.sortDescending
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "aria": {
- * "sortDescending": " - click/return to sort descending"
- * }
- * }
- * } );
- * } );
- */
- "sSortDescending": ": activate to sort column descending"
- },
-
- /**
- * Pagination string used by DataTables for the built-in pagination
- * control types.
- * @namespace
- * @name DataTable.defaults.language.paginate
- */
- "oPaginate": {
- /**
- * Text to use when using the 'full_numbers' type of pagination for the
- * button to take the user to the first page.
- * @type string
- * @default First
- *
- * @dtopt Language
- * @name DataTable.defaults.language.paginate.first
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "paginate": {
- * "first": "First page"
- * }
- * }
- * } );
- * } );
- */
- "sFirst": "First",
-
-
- /**
- * Text to use when using the 'full_numbers' type of pagination for the
- * button to take the user to the last page.
- * @type string
- * @default Last
- *
- * @dtopt Language
- * @name DataTable.defaults.language.paginate.last
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "paginate": {
- * "last": "Last page"
- * }
- * }
- * } );
- * } );
- */
- "sLast": "Last",
-
-
- /**
- * Text to use for the 'next' pagination button (to take the user to the
- * next page).
- * @type string
- * @default Next
- *
- * @dtopt Language
- * @name DataTable.defaults.language.paginate.next
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "paginate": {
- * "next": "Next page"
- * }
- * }
- * } );
- * } );
- */
- "sNext": "Next",
-
-
- /**
- * Text to use for the 'previous' pagination button (to take the user to
- * the previous page).
- * @type string
- * @default Previous
- *
- * @dtopt Language
- * @name DataTable.defaults.language.paginate.previous
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "paginate": {
- * "previous": "Previous page"
- * }
- * }
- * } );
- * } );
- */
- "sPrevious": "Previous"
- },
-
- /**
- * This string is shown in preference to `zeroRecords` when the table is
- * empty of data (regardless of filtering). Note that this is an optional
- * parameter - if it is not given, the value of `zeroRecords` will be used
- * instead (either the default or given value).
- * @type string
- * @default No data available in table
- *
- * @dtopt Language
- * @name DataTable.defaults.language.emptyTable
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "emptyTable": "No data available in table"
- * }
- * } );
- * } );
- */
- "sEmptyTable": "No data available in table",
-
-
- /**
- * This string gives information to the end user about the information
- * that is current on display on the page. The following tokens can be
- * used in the string and will be dynamically replaced as the table
- * display updates. This tokens can be placed anywhere in the string, or
- * removed as needed by the language requires:
- *
- * * `\_START\_` - Display index of the first record on the current page
- * * `\_END\_` - Display index of the last record on the current page
- * * `\_TOTAL\_` - Number of records in the table after filtering
- * * `\_MAX\_` - Number of records in the table without filtering
- * * `\_PAGE\_` - Current page number
- * * `\_PAGES\_` - Total number of pages of data in the table
- *
- * @type string
- * @default Showing _START_ to _END_ of _TOTAL_ entries
- *
- * @dtopt Language
- * @name DataTable.defaults.language.info
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "info": "Showing page _PAGE_ of _PAGES_"
- * }
- * } );
- * } );
- */
- "sInfo": "Showing _START_ to _END_ of _TOTAL_ entries",
-
-
- /**
- * Display information string for when the table is empty. Typically the
- * format of this string should match `info`.
- * @type string
- * @default Showing 0 to 0 of 0 entries
- *
- * @dtopt Language
- * @name DataTable.defaults.language.infoEmpty
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "infoEmpty": "No entries to show"
- * }
- * } );
- * } );
- */
- "sInfoEmpty": "Showing 0 to 0 of 0 entries",
-
-
- /**
- * When a user filters the information in a table, this string is appended
- * to the information (`info`) to give an idea of how strong the filtering
- * is. The variable _MAX_ is dynamically updated.
- * @type string
- * @default (filtered from _MAX_ total entries)
- *
- * @dtopt Language
- * @name DataTable.defaults.language.infoFiltered
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "infoFiltered": " - filtering from _MAX_ records"
- * }
- * } );
- * } );
- */
- "sInfoFiltered": "(filtered from _MAX_ total entries)",
-
-
- /**
- * If can be useful to append extra information to the info string at times,
- * and this variable does exactly that. This information will be appended to
- * the `info` (`infoEmpty` and `infoFiltered` in whatever combination they are
- * being used) at all times.
- * @type string
- * @default <i>Empty string</i>
- *
- * @dtopt Language
- * @name DataTable.defaults.language.infoPostFix
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "infoPostFix": "All records shown are derived from real information."
- * }
- * } );
- * } );
- */
- "sInfoPostFix": "",
-
-
- /**
- * This decimal place operator is a little different from the other
- * language options since DataTables doesn't output floating point
- * numbers, so it won't ever use this for display of a number. Rather,
- * what this parameter does is modify the sort methods of the table so
- * that numbers which are in a format which has a character other than
- * a period (`.`) as a decimal place will be sorted numerically.
- *
- * Note that numbers with different decimal places cannot be shown in
- * the same table and still be sortable, the table must be consistent.
- * However, multiple different tables on the page can use different
- * decimal place characters.
- * @type string
- * @default
- *
- * @dtopt Language
- * @name DataTable.defaults.language.decimal
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "decimal": ","
- * "thousands": "."
- * }
- * } );
- * } );
- */
- "sDecimal": "",
-
-
- /**
- * DataTables has a build in number formatter (`formatNumber`) which is
- * used to format large numbers that are used in the table information.
- * By default a comma is used, but this can be trivially changed to any
- * character you wish with this parameter.
- * @type string
- * @default ,
- *
- * @dtopt Language
- * @name DataTable.defaults.language.thousands
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "thousands": "'"
- * }
- * } );
- * } );
- */
- "sThousands": ",",
-
-
- /**
- * Detail the action that will be taken when the drop down menu for the
- * pagination length option is changed. The '_MENU_' variable is replaced
- * with a default select list of 10, 25, 50 and 100, and can be replaced
- * with a custom select box if required.
- * @type string
- * @default Show _MENU_ entries
- *
- * @dtopt Language
- * @name DataTable.defaults.language.lengthMenu
- *
- * @example
- * // Language change only
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "lengthMenu": "Display _MENU_ records"
- * }
- * } );
- * } );
- *
- * @example
- * // Language and options change
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "lengthMenu": 'Display <select>'+
- * '<option value="10">10</option>'+
- * '<option value="20">20</option>'+
- * '<option value="30">30</option>'+
- * '<option value="40">40</option>'+
- * '<option value="50">50</option>'+
- * '<option value="-1">All</option>'+
- * '</select> records'
- * }
- * } );
- * } );
- */
- "sLengthMenu": "Show _MENU_ entries",
-
-
- /**
- * When using Ajax sourced data and during the first draw when DataTables is
- * gathering the data, this message is shown in an empty row in the table to
- * indicate to the end user the the data is being loaded. Note that this
- * parameter is not used when loading data by server-side processing, just
- * Ajax sourced data with client-side processing.
- * @type string
- * @default Loading...
- *
- * @dtopt Language
- * @name DataTable.defaults.language.loadingRecords
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "loadingRecords": "Please wait - loading..."
- * }
- * } );
- * } );
- */
- "sLoadingRecords": "Loading...",
-
-
- /**
- * Text which is displayed when the table is processing a user action
- * (usually a sort command or similar).
- * @type string
- * @default Processing...
- *
- * @dtopt Language
- * @name DataTable.defaults.language.processing
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "processing": "DataTables is currently busy"
- * }
- * } );
- * } );
- */
- "sProcessing": "Processing...",
-
-
- /**
- * Details the actions that will be taken when the user types into the
- * filtering input text box. The variable "_INPUT_", if used in the string,
- * is replaced with the HTML text box for the filtering input allowing
- * control over where it appears in the string. If "_INPUT_" is not given
- * then the input box is appended to the string automatically.
- * @type string
- * @default Search:
- *
- * @dtopt Language
- * @name DataTable.defaults.language.search
- *
- * @example
- * // Input text box will be appended at the end automatically
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "search": "Filter records:"
- * }
- * } );
- * } );
- *
- * @example
- * // Specify where the filter should appear
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "search": "Apply filter _INPUT_ to table"
- * }
- * } );
- * } );
- */
- "sSearch": "Search:",
-
-
- /**
- * Assign a `placeholder` attribute to the search `input` element
- * @type string
- * @default
- *
- * @dtopt Language
- * @name DataTable.defaults.language.searchPlaceholder
- */
- "sSearchPlaceholder": "",
-
-
- /**
- * All of the language information can be stored in a file on the
- * server-side, which DataTables will look up if this parameter is passed.
- * It must store the URL of the language file, which is in a JSON format,
- * and the object has the same properties as the oLanguage object in the
- * initialiser object (i.e. the above parameters). Please refer to one of
- * the example language files to see how this works in action.
- * @type string
- * @default <i>Empty string - i.e. disabled</i>
- *
- * @dtopt Language
- * @name DataTable.defaults.language.url
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "url": "http://www.sprymedia.co.uk/dataTables/lang.txt"
- * }
- * } );
- * } );
- */
- "sUrl": "",
-
-
- /**
- * Text shown inside the table records when the is no information to be
- * displayed after filtering. `emptyTable` is shown when there is simply no
- * information in the table at all (regardless of filtering).
- * @type string
- * @default No matching records found
- *
- * @dtopt Language
- * @name DataTable.defaults.language.zeroRecords
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "language": {
- * "zeroRecords": "No records to display"
- * }
- * } );
- * } );
- */
- "sZeroRecords": "No matching records found"
- },
-
-
- /**
- * This parameter allows you to have define the global filtering state at
- * initialisation time. As an object the `search` parameter must be
- * defined, but all other parameters are optional. When `regex` is true,
- * the search string will be treated as a regular expression, when false
- * (default) it will be treated as a straight string. When `smart`
- * DataTables will use it's smart filtering methods (to word match at
- * any point in the data), when false this will not be done.
- * @namespace
- * @extends DataTable.models.oSearch
- *
- * @dtopt Options
- * @name DataTable.defaults.search
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "search": {"search": "Initial search"}
- * } );
- * } )
- */
- "oSearch": $.extend( {}, DataTable.models.oSearch ),
-
-
- /**
- * __Deprecated__ The functionality provided by this parameter has now been
- * superseded by that provided through `ajax`, which should be used instead.
- *
- * By default DataTables will look for the property `data` (or `aaData` for
- * compatibility with DataTables 1.9-) when obtaining data from an Ajax
- * source or for server-side processing - this parameter allows that
- * property to be changed. You can use Javascript dotted object notation to
- * get a data source for multiple levels of nesting.
- * @type string
- * @default data
- *
- * @dtopt Options
- * @dtopt Server-side
- * @name DataTable.defaults.ajaxDataProp
- *
- * @deprecated 1.10. Please use `ajax` for this functionality now.
- */
- "sAjaxDataProp": "data",
-
-
- /**
- * __Deprecated__ The functionality provided by this parameter has now been
- * superseded by that provided through `ajax`, which should be used instead.
- *
- * You can instruct DataTables to load data from an external
- * source using this parameter (use aData if you want to pass data in you
- * already have). Simply provide a url a JSON object can be obtained from.
- * @type string
- * @default null
- *
- * @dtopt Options
- * @dtopt Server-side
- * @name DataTable.defaults.ajaxSource
- *
- * @deprecated 1.10. Please use `ajax` for this functionality now.
- */
- "sAjaxSource": null,
-
-
- /**
- * This initialisation variable allows you to specify exactly where in the
- * DOM you want DataTables to inject the various controls it adds to the page
- * (for example you might want the pagination controls at the top of the
- * table). DIV elements (with or without a custom class) can also be added to
- * aid styling. The follow syntax is used:
- * <ul>
- * <li>The following options are allowed:
- * <ul>
- * <li>'l' - Length changing</li>
- * <li>'f' - Filtering input</li>
- * <li>'t' - The table!</li>
- * <li>'i' - Information</li>
- * <li>'p' - Pagination</li>
- * <li>'r' - pRocessing</li>
- * </ul>
- * </li>
- * <li>The following constants are allowed:
- * <ul>
- * <li>'H' - jQueryUI theme "header" classes ('fg-toolbar ui-widget-header ui-corner-tl ui-corner-tr ui-helper-clearfix')</li>
- * <li>'F' - jQueryUI theme "footer" classes ('fg-toolbar ui-widget-header ui-corner-bl ui-corner-br ui-helper-clearfix')</li>
- * </ul>
- * </li>
- * <li>The following syntax is expected:
- * <ul>
- * <li>'<' and '>' - div elements</li>
- * <li>'<"class" and '>' - div with a class</li>
- * <li>'<"#id" and '>' - div with an ID</li>
- * </ul>
- * </li>
- * <li>Examples:
- * <ul>
- * <li>'<"wrapper"flipt>'</li>
- * <li>'<lf<t>ip>'</li>
- * </ul>
- * </li>
- * </ul>
- * @type string
- * @default lfrtip <i>(when `jQueryUI` is false)</i> <b>or</b>
- * <"H"lfr>t<"F"ip> <i>(when `jQueryUI` is true)</i>
- *
- * @dtopt Options
- * @name DataTable.defaults.dom
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "dom": '<"top"i>rt<"bottom"flp><"clear">'
- * } );
- * } );
- */
- "sDom": "lfrtip",
-
-
- /**
- * Search delay option. This will throttle full table searches that use the
- * DataTables provided search input element (it does not effect calls to
- * `dt-api search()`, providing a delay before the search is made.
- * @type integer
- * @default 0
- *
- * @dtopt Options
- * @name DataTable.defaults.searchDelay
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "searchDelay": 200
- * } );
- * } )
- */
- "searchDelay": null,
-
-
- /**
- * DataTables features six different built-in options for the buttons to
- * display for pagination control:
- *
- * * `numbers` - Page number buttons only
- * * `simple` - 'Previous' and 'Next' buttons only
- * * 'simple_numbers` - 'Previous' and 'Next' buttons, plus page numbers
- * * `full` - 'First', 'Previous', 'Next' and 'Last' buttons
- * * `full_numbers` - 'First', 'Previous', 'Next' and 'Last' buttons, plus page numbers
- * * `first_last_numbers` - 'First' and 'Last' buttons, plus page numbers
- *
- * Further methods can be added using {@link DataTable.ext.oPagination}.
- * @type string
- * @default simple_numbers
- *
- * @dtopt Options
- * @name DataTable.defaults.pagingType
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "pagingType": "full_numbers"
- * } );
- * } )
- */
- "sPaginationType": "simple_numbers",
-
-
- /**
- * Enable horizontal scrolling. When a table is too wide to fit into a
- * certain layout, or you have a large number of columns in the table, you
- * can enable x-scrolling to show the table in a viewport, which can be
- * scrolled. This property can be `true` which will allow the table to
- * scroll horizontally when needed, or any CSS unit, or a number (in which
- * case it will be treated as a pixel measurement). Setting as simply `true`
- * is recommended.
- * @type boolean|string
- * @default <i>blank string - i.e. disabled</i>
- *
- * @dtopt Features
- * @name DataTable.defaults.scrollX
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "scrollX": true,
- * "scrollCollapse": true
- * } );
- * } );
- */
- "sScrollX": "",
-
-
- /**
- * This property can be used to force a DataTable to use more width than it
- * might otherwise do when x-scrolling is enabled. For example if you have a
- * table which requires to be well spaced, this parameter is useful for
- * "over-sizing" the table, and thus forcing scrolling. This property can by
- * any CSS unit, or a number (in which case it will be treated as a pixel
- * measurement).
- * @type string
- * @default <i>blank string - i.e. disabled</i>
- *
- * @dtopt Options
- * @name DataTable.defaults.scrollXInner
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "scrollX": "100%",
- * "scrollXInner": "110%"
- * } );
- * } );
- */
- "sScrollXInner": "",
-
-
- /**
- * Enable vertical scrolling. Vertical scrolling will constrain the DataTable
- * to the given height, and enable scrolling for any data which overflows the
- * current viewport. This can be used as an alternative to paging to display
- * a lot of data in a small area (although paging and scrolling can both be
- * enabled at the same time). This property can be any CSS unit, or a number
- * (in which case it will be treated as a pixel measurement).
- * @type string
- * @default <i>blank string - i.e. disabled</i>
- *
- * @dtopt Features
- * @name DataTable.defaults.scrollY
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "scrollY": "200px",
- * "paginate": false
- * } );
- * } );
- */
- "sScrollY": "",
-
-
- /**
- * __Deprecated__ The functionality provided by this parameter has now been
- * superseded by that provided through `ajax`, which should be used instead.
- *
- * Set the HTTP method that is used to make the Ajax call for server-side
- * processing or Ajax sourced data.
- * @type string
- * @default GET
- *
- * @dtopt Options
- * @dtopt Server-side
- * @name DataTable.defaults.serverMethod
- *
- * @deprecated 1.10. Please use `ajax` for this functionality now.
- */
- "sServerMethod": "GET",
-
-
- /**
- * DataTables makes use of renderers when displaying HTML elements for
- * a table. These renderers can be added or modified by plug-ins to
- * generate suitable mark-up for a site. For example the Bootstrap
- * integration plug-in for DataTables uses a paging button renderer to
- * display pagination buttons in the mark-up required by Bootstrap.
- *
- * For further information about the renderers available see
- * DataTable.ext.renderer
- * @type string|object
- * @default null
- *
- * @name DataTable.defaults.renderer
- *
- */
- "renderer": null,
-
-
- /**
- * Set the data property name that DataTables should use to get a row's id
- * to set as the `id` property in the node.
- * @type string
- * @default DT_RowId
- *
- * @name DataTable.defaults.rowId
- */
- "rowId": "DT_RowId"
- };
-
- _fnHungarianMap( DataTable.defaults );
-
-
-
- /*
- * Developer note - See note in model.defaults.js about the use of Hungarian
- * notation and camel case.
- */
-
- /**
- * Column options that can be given to DataTables at initialisation time.
- * @namespace
- */
- DataTable.defaults.column = {
- /**
- * Define which column(s) an order will occur on for this column. This
- * allows a column's ordering to take multiple columns into account when
- * doing a sort or use the data from a different column. For example first
- * name / last name columns make sense to do a multi-column sort over the
- * two columns.
- * @type array|int
- * @default null <i>Takes the value of the column index automatically</i>
- *
- * @name DataTable.defaults.column.orderData
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "orderData": [ 0, 1 ], "targets": [ 0 ] },
- * { "orderData": [ 1, 0 ], "targets": [ 1 ] },
- * { "orderData": 2, "targets": [ 2 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "orderData": [ 0, 1 ] },
- * { "orderData": [ 1, 0 ] },
- * { "orderData": 2 },
- * null,
- * null
- * ]
- * } );
- * } );
- */
- "aDataSort": null,
- "iDataSort": -1,
-
-
- /**
- * You can control the default ordering direction, and even alter the
- * behaviour of the sort handler (i.e. only allow ascending ordering etc)
- * using this parameter.
- * @type array
- * @default [ 'asc', 'desc' ]
- *
- * @name DataTable.defaults.column.orderSequence
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "orderSequence": [ "asc" ], "targets": [ 1 ] },
- * { "orderSequence": [ "desc", "asc", "asc" ], "targets": [ 2 ] },
- * { "orderSequence": [ "desc" ], "targets": [ 3 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * null,
- * { "orderSequence": [ "asc" ] },
- * { "orderSequence": [ "desc", "asc", "asc" ] },
- * { "orderSequence": [ "desc" ] },
- * null
- * ]
- * } );
- * } );
- */
- "asSorting": [ 'asc', 'desc' ],
-
-
- /**
- * Enable or disable filtering on the data in this column.
- * @type boolean
- * @default true
- *
- * @name DataTable.defaults.column.searchable
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "searchable": false, "targets": [ 0 ] }
- * ] } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "searchable": false },
- * null,
- * null,
- * null,
- * null
- * ] } );
- * } );
- */
- "bSearchable": true,
-
-
- /**
- * Enable or disable ordering on this column.
- * @type boolean
- * @default true
- *
- * @name DataTable.defaults.column.orderable
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "orderable": false, "targets": [ 0 ] }
- * ] } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "orderable": false },
- * null,
- * null,
- * null,
- * null
- * ] } );
- * } );
- */
- "bSortable": true,
-
-
- /**
- * Enable or disable the display of this column.
- * @type boolean
- * @default true
- *
- * @name DataTable.defaults.column.visible
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "visible": false, "targets": [ 0 ] }
- * ] } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "visible": false },
- * null,
- * null,
- * null,
- * null
- * ] } );
- * } );
- */
- "bVisible": true,
-
-
- /**
- * Developer definable function that is called whenever a cell is created (Ajax source,
- * etc) or processed for input (DOM source). This can be used as a compliment to mRender
- * allowing you to modify the DOM element (add background colour for example) when the
- * element is available.
- * @type function
- * @param {element} td The TD node that has been created
- * @param {*} cellData The Data for the cell
- * @param {array|object} rowData The data for the whole row
- * @param {int} row The row index for the aoData data store
- * @param {int} col The column index for aoColumns
- *
- * @name DataTable.defaults.column.createdCell
- * @dtopt Columns
- *
- * @example
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [3],
- * "createdCell": function (td, cellData, rowData, row, col) {
- * if ( cellData == "1.7" ) {
- * $(td).css('color', 'blue')
- * }
- * }
- * } ]
- * });
- * } );
- */
- "fnCreatedCell": null,
-
-
- /**
- * This parameter has been replaced by `data` in DataTables to ensure naming
- * consistency. `dataProp` can still be used, as there is backwards
- * compatibility in DataTables for this option, but it is strongly
- * recommended that you use `data` in preference to `dataProp`.
- * @name DataTable.defaults.column.dataProp
- */
-
-
- /**
- * This property can be used to read data from any data source property,
- * including deeply nested objects / properties. `data` can be given in a
- * number of different ways which effect its behaviour:
- *
- * * `integer` - treated as an array index for the data source. This is the
- * default that DataTables uses (incrementally increased for each column).
- * * `string` - read an object property from the data source. There are
- * three 'special' options that can be used in the string to alter how
- * DataTables reads the data from the source object:
- * * `.` - Dotted Javascript notation. Just as you use a `.` in
- * Javascript to read from nested objects, so to can the options
- * specified in `data`. For example: `browser.version` or
- * `browser.name`. If your object parameter name contains a period, use
- * `\\` to escape it - i.e. `first\\.name`.
- * * `[]` - Array notation. DataTables can automatically combine data
- * from and array source, joining the data with the characters provided
- * between the two brackets. For example: `name[, ]` would provide a
- * comma-space separated list from the source array. If no characters
- * are provided between the brackets, the original array source is
- * returned.
- * * `()` - Function notation. Adding `()` to the end of a parameter will
- * execute a function of the name given. For example: `browser()` for a
- * simple function on the data source, `browser.version()` for a
- * function in a nested property or even `browser().version` to get an
- * object property if the function called returns an object. Note that
- * function notation is recommended for use in `render` rather than
- * `data` as it is much simpler to use as a renderer.
- * * `null` - use the original data source for the row rather than plucking
- * data directly from it. This action has effects on two other
- * initialisation options:
- * * `defaultContent` - When null is given as the `data` option and
- * `defaultContent` is specified for the column, the value defined by
- * `defaultContent` will be used for the cell.
- * * `render` - When null is used for the `data` option and the `render`
- * option is specified for the column, the whole data source for the
- * row is used for the renderer.
- * * `function` - the function given will be executed whenever DataTables
- * needs to set or get the data for a cell in the column. The function
- * takes three parameters:
- * * Parameters:
- * * `{array|object}` The data source for the row
- * * `{string}` The type call data requested - this will be 'set' when
- * setting data or 'filter', 'display', 'type', 'sort' or undefined
- * when gathering data. Note that when `undefined` is given for the
- * type DataTables expects to get the raw data for the object back<
- * * `{*}` Data to set when the second parameter is 'set'.
- * * Return:
- * * The return value from the function is not required when 'set' is
- * the type of call, but otherwise the return is what will be used
- * for the data requested.
- *
- * Note that `data` is a getter and setter option. If you just require
- * formatting of data for output, you will likely want to use `render` which
- * is simply a getter and thus simpler to use.
- *
- * Note that prior to DataTables 1.9.2 `data` was called `mDataProp`. The
- * name change reflects the flexibility of this property and is consistent
- * with the naming of mRender. If 'mDataProp' is given, then it will still
- * be used by DataTables, as it automatically maps the old name to the new
- * if required.
- *
- * @type string|int|function|null
- * @default null <i>Use automatically calculated column index</i>
- *
- * @name DataTable.defaults.column.data
- * @dtopt Columns
- *
- * @example
- * // Read table data from objects
- * // JSON structure for each row:
- * // {
- * // "engine": {value},
- * // "browser": {value},
- * // "platform": {value},
- * // "version": {value},
- * // "grade": {value}
- * // }
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "ajaxSource": "sources/objects.txt",
- * "columns": [
- * { "data": "engine" },
- * { "data": "browser" },
- * { "data": "platform" },
- * { "data": "version" },
- * { "data": "grade" }
- * ]
- * } );
- * } );
- *
- * @example
- * // Read information from deeply nested objects
- * // JSON structure for each row:
- * // {
- * // "engine": {value},
- * // "browser": {value},
- * // "platform": {
- * // "inner": {value}
- * // },
- * // "details": [
- * // {value}, {value}
- * // ]
- * // }
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "ajaxSource": "sources/deep.txt",
- * "columns": [
- * { "data": "engine" },
- * { "data": "browser" },
- * { "data": "platform.inner" },
- * { "data": "details.0" },
- * { "data": "details.1" }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `data` as a function to provide different information for
- * // sorting, filtering and display. In this case, currency (price)
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "data": function ( source, type, val ) {
- * if (type === 'set') {
- * source.price = val;
- * // Store the computed dislay and filter values for efficiency
- * source.price_display = val=="" ? "" : "$"+numberFormat(val);
- * source.price_filter = val=="" ? "" : "$"+numberFormat(val)+" "+val;
- * return;
- * }
- * else if (type === 'display') {
- * return source.price_display;
- * }
- * else if (type === 'filter') {
- * return source.price_filter;
- * }
- * // 'sort', 'type' and undefined all just use the integer
- * return source.price;
- * }
- * } ]
- * } );
- * } );
- *
- * @example
- * // Using default content
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "data": null,
- * "defaultContent": "Click to edit"
- * } ]
- * } );
- * } );
- *
- * @example
- * // Using array notation - outputting a list from an array
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "data": "name[, ]"
- * } ]
- * } );
- * } );
- *
- */
- "mData": null,
-
-
- /**
- * This property is the rendering partner to `data` and it is suggested that
- * when you want to manipulate data for display (including filtering,
- * sorting etc) without altering the underlying data for the table, use this
- * property. `render` can be considered to be the the read only companion to
- * `data` which is read / write (then as such more complex). Like `data`
- * this option can be given in a number of different ways to effect its
- * behaviour:
- *
- * * `integer` - treated as an array index for the data source. This is the
- * default that DataTables uses (incrementally increased for each column).
- * * `string` - read an object property from the data source. There are
- * three 'special' options that can be used in the string to alter how
- * DataTables reads the data from the source object:
- * * `.` - Dotted Javascript notation. Just as you use a `.` in
- * Javascript to read from nested objects, so to can the options
- * specified in `data`. For example: `browser.version` or
- * `browser.name`. If your object parameter name contains a period, use
- * `\\` to escape it - i.e. `first\\.name`.
- * * `[]` - Array notation. DataTables can automatically combine data
- * from and array source, joining the data with the characters provided
- * between the two brackets. For example: `name[, ]` would provide a
- * comma-space separated list from the source array. If no characters
- * are provided between the brackets, the original array source is
- * returned.
- * * `()` - Function notation. Adding `()` to the end of a parameter will
- * execute a function of the name given. For example: `browser()` for a
- * simple function on the data source, `browser.version()` for a
- * function in a nested property or even `browser().version` to get an
- * object property if the function called returns an object.
- * * `object` - use different data for the different data types requested by
- * DataTables ('filter', 'display', 'type' or 'sort'). The property names
- * of the object is the data type the property refers to and the value can
- * defined using an integer, string or function using the same rules as
- * `render` normally does. Note that an `_` option _must_ be specified.
- * This is the default value to use if you haven't specified a value for
- * the data type requested by DataTables.
- * * `function` - the function given will be executed whenever DataTables
- * needs to set or get the data for a cell in the column. The function
- * takes three parameters:
- * * Parameters:
- * * {array|object} The data source for the row (based on `data`)
- * * {string} The type call data requested - this will be 'filter',
- * 'display', 'type' or 'sort'.
- * * {array|object} The full data source for the row (not based on
- * `data`)
- * * Return:
- * * The return value from the function is what will be used for the
- * data requested.
- *
- * @type string|int|function|object|null
- * @default null Use the data source value.
- *
- * @name DataTable.defaults.column.render
- * @dtopt Columns
- *
- * @example
- * // Create a comma separated list from an array of objects
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "ajaxSource": "sources/deep.txt",
- * "columns": [
- * { "data": "engine" },
- * { "data": "browser" },
- * {
- * "data": "platform",
- * "render": "[, ].name"
- * }
- * ]
- * } );
- * } );
- *
- * @example
- * // Execute a function to obtain data
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "data": null, // Use the full data source object for the renderer's source
- * "render": "browserName()"
- * } ]
- * } );
- * } );
- *
- * @example
- * // As an object, extracting different data for the different types
- * // This would be used with a data source such as:
- * // { "phone": 5552368, "phone_filter": "5552368 555-2368", "phone_display": "555-2368" }
- * // Here the `phone` integer is used for sorting and type detection, while `phone_filter`
- * // (which has both forms) is used for filtering for if a user inputs either format, while
- * // the formatted phone number is the one that is shown in the table.
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "data": null, // Use the full data source object for the renderer's source
- * "render": {
- * "_": "phone",
- * "filter": "phone_filter",
- * "display": "phone_display"
- * }
- * } ]
- * } );
- * } );
- *
- * @example
- * // Use as a function to create a link from the data source
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "data": "download_link",
- * "render": function ( data, type, full ) {
- * return '<a href="'+data+'">Download</a>';
- * }
- * } ]
- * } );
- * } );
- */
- "mRender": null,
-
-
- /**
- * Change the cell type created for the column - either TD cells or TH cells. This
- * can be useful as TH cells have semantic meaning in the table body, allowing them
- * to act as a header for a row (you may wish to add scope='row' to the TH elements).
- * @type string
- * @default td
- *
- * @name DataTable.defaults.column.cellType
- * @dtopt Columns
- *
- * @example
- * // Make the first column use TH cells
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [ {
- * "targets": [ 0 ],
- * "cellType": "th"
- * } ]
- * } );
- * } );
- */
- "sCellType": "td",
-
-
- /**
- * Class to give to each cell in this column.
- * @type string
- * @default <i>Empty string</i>
- *
- * @name DataTable.defaults.column.class
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "class": "my_class", "targets": [ 0 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "class": "my_class" },
- * null,
- * null,
- * null,
- * null
- * ]
- * } );
- * } );
- */
- "sClass": "",
-
- /**
- * When DataTables calculates the column widths to assign to each column,
- * it finds the longest string in each column and then constructs a
- * temporary table and reads the widths from that. The problem with this
- * is that "mmm" is much wider then "iiii", but the latter is a longer
- * string - thus the calculation can go wrong (doing it properly and putting
- * it into an DOM object and measuring that is horribly(!) slow). Thus as
- * a "work around" we provide this option. It will append its value to the
- * text that is found to be the longest string for the column - i.e. padding.
- * Generally you shouldn't need this!
- * @type string
- * @default <i>Empty string<i>
- *
- * @name DataTable.defaults.column.contentPadding
- * @dtopt Columns
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * null,
- * null,
- * null,
- * {
- * "contentPadding": "mmm"
- * }
- * ]
- * } );
- * } );
- */
- "sContentPadding": "",
-
-
- /**
- * Allows a default value to be given for a column's data, and will be used
- * whenever a null data source is encountered (this can be because `data`
- * is set to null, or because the data source itself is null).
- * @type string
- * @default null
- *
- * @name DataTable.defaults.column.defaultContent
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * {
- * "data": null,
- * "defaultContent": "Edit",
- * "targets": [ -1 ]
- * }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * null,
- * null,
- * null,
- * {
- * "data": null,
- * "defaultContent": "Edit"
- * }
- * ]
- * } );
- * } );
- */
- "sDefaultContent": null,
-
-
- /**
- * This parameter is only used in DataTables' server-side processing. It can
- * be exceptionally useful to know what columns are being displayed on the
- * client side, and to map these to database fields. When defined, the names
- * also allow DataTables to reorder information from the server if it comes
- * back in an unexpected order (i.e. if you switch your columns around on the
- * client-side, your server-side code does not also need updating).
- * @type string
- * @default <i>Empty string</i>
- *
- * @name DataTable.defaults.column.name
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "name": "engine", "targets": [ 0 ] },
- * { "name": "browser", "targets": [ 1 ] },
- * { "name": "platform", "targets": [ 2 ] },
- * { "name": "version", "targets": [ 3 ] },
- * { "name": "grade", "targets": [ 4 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "name": "engine" },
- * { "name": "browser" },
- * { "name": "platform" },
- * { "name": "version" },
- * { "name": "grade" }
- * ]
- * } );
- * } );
- */
- "sName": "",
-
-
- /**
- * Defines a data source type for the ordering which can be used to read
- * real-time information from the table (updating the internally cached
- * version) prior to ordering. This allows ordering to occur on user
- * editable elements such as form inputs.
- * @type string
- * @default std
- *
- * @name DataTable.defaults.column.orderDataType
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "orderDataType": "dom-text", "targets": [ 2, 3 ] },
- * { "type": "numeric", "targets": [ 3 ] },
- * { "orderDataType": "dom-select", "targets": [ 4 ] },
- * { "orderDataType": "dom-checkbox", "targets": [ 5 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * null,
- * null,
- * { "orderDataType": "dom-text" },
- * { "orderDataType": "dom-text", "type": "numeric" },
- * { "orderDataType": "dom-select" },
- * { "orderDataType": "dom-checkbox" }
- * ]
- * } );
- * } );
- */
- "sSortDataType": "std",
-
-
- /**
- * The title of this column.
- * @type string
- * @default null <i>Derived from the 'TH' value for this column in the
- * original HTML table.</i>
- *
- * @name DataTable.defaults.column.title
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "title": "My column title", "targets": [ 0 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "title": "My column title" },
- * null,
- * null,
- * null,
- * null
- * ]
- * } );
- * } );
- */
- "sTitle": null,
-
-
- /**
- * The type allows you to specify how the data for this column will be
- * ordered. Four types (string, numeric, date and html (which will strip
- * HTML tags before ordering)) are currently available. Note that only date
- * formats understood by Javascript's Date() object will be accepted as type
- * date. For example: "Mar 26, 2008 5:03 PM". May take the values: 'string',
- * 'numeric', 'date' or 'html' (by default). Further types can be adding
- * through plug-ins.
- * @type string
- * @default null <i>Auto-detected from raw data</i>
- *
- * @name DataTable.defaults.column.type
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "type": "html", "targets": [ 0 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "type": "html" },
- * null,
- * null,
- * null,
- * null
- * ]
- * } );
- * } );
- */
- "sType": null,
-
-
- /**
- * Defining the width of the column, this parameter may take any CSS value
- * (3em, 20px etc). DataTables applies 'smart' widths to columns which have not
- * been given a specific width through this interface ensuring that the table
- * remains readable.
- * @type string
- * @default null <i>Automatic</i>
- *
- * @name DataTable.defaults.column.width
- * @dtopt Columns
- *
- * @example
- * // Using `columnDefs`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columnDefs": [
- * { "width": "20%", "targets": [ 0 ] }
- * ]
- * } );
- * } );
- *
- * @example
- * // Using `columns`
- * $(document).ready( function() {
- * $('#example').dataTable( {
- * "columns": [
- * { "width": "20%" },
- * null,
- * null,
- * null,
- * null
- * ]
- * } );
- * } );
- */
- "sWidth": null
- };
-
- _fnHungarianMap( DataTable.defaults.column );
-
-
-
- /**
- * DataTables settings object - this holds all the information needed for a
- * given table, including configuration, data and current application of the
- * table options. DataTables does not have a single instance for each DataTable
- * with the settings attached to that instance, but rather instances of the
- * DataTable "class" are created on-the-fly as needed (typically by a
- * $().dataTable() call) and the settings object is then applied to that
- * instance.
- *
- * Note that this object is related to {@link DataTable.defaults} but this
- * one is the internal data store for DataTables's cache of columns. It should
- * NOT be manipulated outside of DataTables. Any configuration should be done
- * through the initialisation options.
- * @namespace
- * @todo Really should attach the settings object to individual instances so we
- * don't need to create new instances on each $().dataTable() call (if the
- * table already exists). It would also save passing oSettings around and
- * into every single function. However, this is a very significant
- * architecture change for DataTables and will almost certainly break
- * backwards compatibility with older installations. This is something that
- * will be done in 2.0.
- */
- DataTable.models.oSettings = {
- /**
- * Primary features of DataTables and their enablement state.
- * @namespace
- */
- "oFeatures": {
-
- /**
- * Flag to say if DataTables should automatically try to calculate the
- * optimum table and columns widths (true) or not (false).
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bAutoWidth": null,
-
- /**
- * Delay the creation of TR and TD elements until they are actually
- * needed by a driven page draw. This can give a significant speed
- * increase for Ajax source and Javascript source data, but makes no
- * difference at all fro DOM and server-side processing tables.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bDeferRender": null,
-
- /**
- * Enable filtering on the table or not. Note that if this is disabled
- * then there is no filtering at all on the table, including fnFilter.
- * To just remove the filtering input use sDom and remove the 'f' option.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bFilter": null,
-
- /**
- * Table information element (the 'Showing x of y records' div) enable
- * flag.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bInfo": null,
-
- /**
- * Present a user control allowing the end user to change the page size
- * when pagination is enabled.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bLengthChange": null,
-
- /**
- * Pagination enabled or not. Note that if this is disabled then length
- * changing must also be disabled.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bPaginate": null,
-
- /**
- * Processing indicator enable flag whenever DataTables is enacting a
- * user request - typically an Ajax request for server-side processing.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bProcessing": null,
-
- /**
- * Server-side processing enabled flag - when enabled DataTables will
- * get all data from the server for every draw - there is no filtering,
- * sorting or paging done on the client-side.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bServerSide": null,
-
- /**
- * Sorting enablement flag.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bSort": null,
-
- /**
- * Multi-column sorting
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bSortMulti": null,
-
- /**
- * Apply a class to the columns which are being sorted to provide a
- * visual highlight or not. This can slow things down when enabled since
- * there is a lot of DOM interaction.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bSortClasses": null,
-
- /**
- * State saving enablement flag.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bStateSave": null
- },
-
-
- /**
- * Scrolling settings for a table.
- * @namespace
- */
- "oScroll": {
- /**
- * When the table is shorter in height than sScrollY, collapse the
- * table container down to the height of the table (when true).
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bCollapse": null,
-
- /**
- * Width of the scrollbar for the web-browser's platform. Calculated
- * during table initialisation.
- * @type int
- * @default 0
- */
- "iBarWidth": 0,
-
- /**
- * Viewport width for horizontal scrolling. Horizontal scrolling is
- * disabled if an empty string.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- */
- "sX": null,
-
- /**
- * Width to expand the table to when using x-scrolling. Typically you
- * should not need to use this.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- * @deprecated
- */
- "sXInner": null,
-
- /**
- * Viewport height for vertical scrolling. Vertical scrolling is disabled
- * if an empty string.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- */
- "sY": null
- },
-
- /**
- * Language information for the table.
- * @namespace
- * @extends DataTable.defaults.oLanguage
- */
- "oLanguage": {
- /**
- * Information callback function. See
- * {@link DataTable.defaults.fnInfoCallback}
- * @type function
- * @default null
- */
- "fnInfoCallback": null
- },
-
- /**
- * Browser support parameters
- * @namespace
- */
- "oBrowser": {
- /**
- * Indicate if the browser incorrectly calculates width:100% inside a
- * scrolling element (IE6/7)
- * @type boolean
- * @default false
- */
- "bScrollOversize": false,
-
- /**
- * Determine if the vertical scrollbar is on the right or left of the
- * scrolling container - needed for rtl language layout, although not
- * all browsers move the scrollbar (Safari).
- * @type boolean
- * @default false
- */
- "bScrollbarLeft": false,
-
- /**
- * Flag for if `getBoundingClientRect` is fully supported or not
- * @type boolean
- * @default false
- */
- "bBounding": false,
-
- /**
- * Browser scrollbar width
- * @type integer
- * @default 0
- */
- "barWidth": 0
- },
-
-
- "ajax": null,
-
-
- /**
- * Array referencing the nodes which are used for the features. The
- * parameters of this object match what is allowed by sDom - i.e.
- * <ul>
- * <li>'l' - Length changing</li>
- * <li>'f' - Filtering input</li>
- * <li>'t' - The table!</li>
- * <li>'i' - Information</li>
- * <li>'p' - Pagination</li>
- * <li>'r' - pRocessing</li>
- * </ul>
- * @type array
- * @default []
- */
- "aanFeatures": [],
-
- /**
- * Store data information - see {@link DataTable.models.oRow} for detailed
- * information.
- * @type array
- * @default []
- */
- "aoData": [],
-
- /**
- * Array of indexes which are in the current display (after filtering etc)
- * @type array
- * @default []
- */
- "aiDisplay": [],
-
- /**
- * Array of indexes for display - no filtering
- * @type array
- * @default []
- */
- "aiDisplayMaster": [],
-
- /**
- * Map of row ids to data indexes
- * @type object
- * @default {}
- */
- "aIds": {},
-
- /**
- * Store information about each column that is in use
- * @type array
- * @default []
- */
- "aoColumns": [],
-
- /**
- * Store information about the table's header
- * @type array
- * @default []
- */
- "aoHeader": [],
-
- /**
- * Store information about the table's footer
- * @type array
- * @default []
- */
- "aoFooter": [],
-
- /**
- * Store the applied global search information in case we want to force a
- * research or compare the old search to a new one.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @namespace
- * @extends DataTable.models.oSearch
- */
- "oPreviousSearch": {},
-
- /**
- * Store the applied search for each column - see
- * {@link DataTable.models.oSearch} for the format that is used for the
- * filtering information for each column.
- * @type array
- * @default []
- */
- "aoPreSearchCols": [],
-
- /**
- * Sorting that is applied to the table. Note that the inner arrays are
- * used in the following manner:
- * <ul>
- * <li>Index 0 - column number</li>
- * <li>Index 1 - current sorting direction</li>
- * </ul>
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type array
- * @todo These inner arrays should really be objects
- */
- "aaSorting": null,
-
- /**
- * Sorting that is always applied to the table (i.e. prefixed in front of
- * aaSorting).
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type array
- * @default []
- */
- "aaSortingFixed": [],
-
- /**
- * Classes to use for the striping of a table.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type array
- * @default []
- */
- "asStripeClasses": null,
-
- /**
- * If restoring a table - we should restore its striping classes as well
- * @type array
- * @default []
- */
- "asDestroyStripes": [],
-
- /**
- * If restoring a table - we should restore its width
- * @type int
- * @default 0
- */
- "sDestroyWidth": 0,
-
- /**
- * Callback functions array for every time a row is inserted (i.e. on a draw).
- * @type array
- * @default []
- */
- "aoRowCallback": [],
-
- /**
- * Callback functions for the header on each draw.
- * @type array
- * @default []
- */
- "aoHeaderCallback": [],
-
- /**
- * Callback function for the footer on each draw.
- * @type array
- * @default []
- */
- "aoFooterCallback": [],
-
- /**
- * Array of callback functions for draw callback functions
- * @type array
- * @default []
- */
- "aoDrawCallback": [],
-
- /**
- * Array of callback functions for row created function
- * @type array
- * @default []
- */
- "aoRowCreatedCallback": [],
-
- /**
- * Callback functions for just before the table is redrawn. A return of
- * false will be used to cancel the draw.
- * @type array
- * @default []
- */
- "aoPreDrawCallback": [],
-
- /**
- * Callback functions for when the table has been initialised.
- * @type array
- * @default []
- */
- "aoInitComplete": [],
-
-
- /**
- * Callbacks for modifying the settings to be stored for state saving, prior to
- * saving state.
- * @type array
- * @default []
- */
- "aoStateSaveParams": [],
-
- /**
- * Callbacks for modifying the settings that have been stored for state saving
- * prior to using the stored values to restore the state.
- * @type array
- * @default []
- */
- "aoStateLoadParams": [],
-
- /**
- * Callbacks for operating on the settings object once the saved state has been
- * loaded
- * @type array
- * @default []
- */
- "aoStateLoaded": [],
-
- /**
- * Cache the table ID for quick access
- * @type string
- * @default <i>Empty string</i>
- */
- "sTableId": "",
-
- /**
- * The TABLE node for the main table
- * @type node
- * @default null
- */
- "nTable": null,
-
- /**
- * Permanent ref to the thead element
- * @type node
- * @default null
- */
- "nTHead": null,
-
- /**
- * Permanent ref to the tfoot element - if it exists
- * @type node
- * @default null
- */
- "nTFoot": null,
-
- /**
- * Permanent ref to the tbody element
- * @type node
- * @default null
- */
- "nTBody": null,
-
- /**
- * Cache the wrapper node (contains all DataTables controlled elements)
- * @type node
- * @default null
- */
- "nTableWrapper": null,
-
- /**
- * Indicate if when using server-side processing the loading of data
- * should be deferred until the second draw.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- * @default false
- */
- "bDeferLoading": false,
-
- /**
- * Indicate if all required information has been read in
- * @type boolean
- * @default false
- */
- "bInitialised": false,
-
- /**
- * Information about open rows. Each object in the array has the parameters
- * 'nTr' and 'nParent'
- * @type array
- * @default []
- */
- "aoOpenRows": [],
-
- /**
- * Dictate the positioning of DataTables' control elements - see
- * {@link DataTable.model.oInit.sDom}.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- * @default null
- */
- "sDom": null,
-
- /**
- * Search delay (in mS)
- * @type integer
- * @default null
- */
- "searchDelay": null,
-
- /**
- * Which type of pagination should be used.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- * @default two_button
- */
- "sPaginationType": "two_button",
-
- /**
- * The state duration (for `stateSave`) in seconds.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type int
- * @default 0
- */
- "iStateDuration": 0,
-
- /**
- * Array of callback functions for state saving. Each array element is an
- * object with the following parameters:
- * <ul>
- * <li>function:fn - function to call. Takes two parameters, oSettings
- * and the JSON string to save that has been thus far created. Returns
- * a JSON string to be inserted into a json object
- * (i.e. '"param": [ 0, 1, 2]')</li>
- * <li>string:sName - name of callback</li>
- * </ul>
- * @type array
- * @default []
- */
- "aoStateSave": [],
-
- /**
- * Array of callback functions for state loading. Each array element is an
- * object with the following parameters:
- * <ul>
- * <li>function:fn - function to call. Takes two parameters, oSettings
- * and the object stored. May return false to cancel state loading</li>
- * <li>string:sName - name of callback</li>
- * </ul>
- * @type array
- * @default []
- */
- "aoStateLoad": [],
-
- /**
- * State that was saved. Useful for back reference
- * @type object
- * @default null
- */
- "oSavedState": null,
-
- /**
- * State that was loaded. Useful for back reference
- * @type object
- * @default null
- */
- "oLoadedState": null,
-
- /**
- * Source url for AJAX data for the table.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- * @default null
- */
- "sAjaxSource": null,
-
- /**
- * Property from a given object from which to read the table data from. This
- * can be an empty string (when not server-side processing), in which case
- * it is assumed an an array is given directly.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- */
- "sAjaxDataProp": null,
-
- /**
- * Note if draw should be blocked while getting data
- * @type boolean
- * @default true
- */
- "bAjaxDataGet": true,
-
- /**
- * The last jQuery XHR object that was used for server-side data gathering.
- * This can be used for working with the XHR information in one of the
- * callbacks
- * @type object
- * @default null
- */
- "jqXHR": null,
-
- /**
- * JSON returned from the server in the last Ajax request
- * @type object
- * @default undefined
- */
- "json": undefined,
-
- /**
- * Data submitted as part of the last Ajax request
- * @type object
- * @default undefined
- */
- "oAjaxData": undefined,
-
- /**
- * Function to get the server-side data.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type function
- */
- "fnServerData": null,
-
- /**
- * Functions which are called prior to sending an Ajax request so extra
- * parameters can easily be sent to the server
- * @type array
- * @default []
- */
- "aoServerParams": [],
-
- /**
- * Send the XHR HTTP method - GET or POST (could be PUT or DELETE if
- * required).
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type string
- */
- "sServerMethod": null,
-
- /**
- * Format numbers for display.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type function
- */
- "fnFormatNumber": null,
-
- /**
- * List of options that can be used for the user selectable length menu.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type array
- * @default []
- */
- "aLengthMenu": null,
-
- /**
- * Counter for the draws that the table does. Also used as a tracker for
- * server-side processing
- * @type int
- * @default 0
- */
- "iDraw": 0,
-
- /**
- * Indicate if a redraw is being done - useful for Ajax
- * @type boolean
- * @default false
- */
- "bDrawing": false,
-
- /**
- * Draw index (iDraw) of the last error when parsing the returned data
- * @type int
- * @default -1
- */
- "iDrawError": -1,
-
- /**
- * Paging display length
- * @type int
- * @default 10
- */
- "_iDisplayLength": 10,
-
- /**
- * Paging start point - aiDisplay index
- * @type int
- * @default 0
- */
- "_iDisplayStart": 0,
-
- /**
- * Server-side processing - number of records in the result set
- * (i.e. before filtering), Use fnRecordsTotal rather than
- * this property to get the value of the number of records, regardless of
- * the server-side processing setting.
- * @type int
- * @default 0
- * @private
- */
- "_iRecordsTotal": 0,
-
- /**
- * Server-side processing - number of records in the current display set
- * (i.e. after filtering). Use fnRecordsDisplay rather than
- * this property to get the value of the number of records, regardless of
- * the server-side processing setting.
- * @type boolean
- * @default 0
- * @private
- */
- "_iRecordsDisplay": 0,
-
- /**
- * The classes to use for the table
- * @type object
- * @default {}
- */
- "oClasses": {},
-
- /**
- * Flag attached to the settings object so you can check in the draw
- * callback if filtering has been done in the draw. Deprecated in favour of
- * events.
- * @type boolean
- * @default false
- * @deprecated
- */
- "bFiltered": false,
-
- /**
- * Flag attached to the settings object so you can check in the draw
- * callback if sorting has been done in the draw. Deprecated in favour of
- * events.
- * @type boolean
- * @default false
- * @deprecated
- */
- "bSorted": false,
-
- /**
- * Indicate that if multiple rows are in the header and there is more than
- * one unique cell per column, if the top one (true) or bottom one (false)
- * should be used for sorting / title by DataTables.
- * Note that this parameter will be set by the initialisation routine. To
- * set a default use {@link DataTable.defaults}.
- * @type boolean
- */
- "bSortCellsTop": null,
-
- /**
- * Initialisation object that is used for the table
- * @type object
- * @default null
- */
- "oInit": null,
-
- /**
- * Destroy callback functions - for plug-ins to attach themselves to the
- * destroy so they can clean up markup and events.
- * @type array
- * @default []
- */
- "aoDestroyCallback": [],
-
-
- /**
- * Get the number of records in the current record set, before filtering
- * @type function
- */
- "fnRecordsTotal": function ()
- {
- return _fnDataSource( this ) == 'ssp' ?
- this._iRecordsTotal * 1 :
- this.aiDisplayMaster.length;
- },
-
- /**
- * Get the number of records in the current record set, after filtering
- * @type function
- */
- "fnRecordsDisplay": function ()
- {
- return _fnDataSource( this ) == 'ssp' ?
- this._iRecordsDisplay * 1 :
- this.aiDisplay.length;
- },
-
- /**
- * Get the display end point - aiDisplay index
- * @type function
- */
- "fnDisplayEnd": function ()
- {
- var
- len = this._iDisplayLength,
- start = this._iDisplayStart,
- calc = start + len,
- records = this.aiDisplay.length,
- features = this.oFeatures,
- paginate = features.bPaginate;
-
- if ( features.bServerSide ) {
- return paginate === false || len === -1 ?
- start + records :
- Math.min( start+len, this._iRecordsDisplay );
- }
- else {
- return ! paginate || calc>records || len===-1 ?
- records :
- calc;
- }
- },
-
- /**
- * The DataTables object for this table
- * @type object
- * @default null
- */
- "oInstance": null,
-
- /**
- * Unique identifier for each instance of the DataTables object. If there
- * is an ID on the table node, then it takes that value, otherwise an
- * incrementing internal counter is used.
- * @type string
- * @default null
- */
- "sInstance": null,
-
- /**
- * tabindex attribute value that is added to DataTables control elements, allowing
- * keyboard navigation of the table and its controls.
- */
- "iTabIndex": 0,
-
- /**
- * DIV container for the footer scrolling table if scrolling
- */
- "nScrollHead": null,
-
- /**
- * DIV container for the footer scrolling table if scrolling
- */
- "nScrollFoot": null,
-
- /**
- * Last applied sort
- * @type array
- * @default []
- */
- "aLastSort": [],
-
- /**
- * Stored plug-in instances
- * @type object
- * @default {}
- */
- "oPlugins": {},
-
- /**
- * Function used to get a row's id from the row's data
- * @type function
- * @default null
- */
- "rowIdFn": null,
-
- /**
- * Data location where to store a row's id
- * @type string
- * @default null
- */
- "rowId": null
- };
- /**
- * Extension object for DataTables that is used to provide all extension
- * options.
- *
- * Note that the `DataTable.ext` object is available through
- * `jQuery.fn.dataTable.ext` where it may be accessed and manipulated. It is
- * also aliased to `jQuery.fn.dataTableExt` for historic reasons.
- * @namespace
- * @extends DataTable.models.ext
- */
-
-
- /**
- * DataTables extensions
- *
- * This namespace acts as a collection area for plug-ins that can be used to
- * extend DataTables capabilities. Indeed many of the build in methods
- * use this method to provide their own capabilities (sorting methods for
- * example).
- *
- * Note that this namespace is aliased to `jQuery.fn.dataTableExt` for legacy
- * reasons
- *
- * @namespace
- */
- DataTable.ext = _ext = {
- /**
- * Buttons. For use with the Buttons extension for DataTables. This is
- * defined here so other extensions can define buttons regardless of load
- * order. It is _not_ used by DataTables core.
- *
- * @type object
- * @default {}
- */
- buttons: {},
-
-
- /**
- * Element class names
- *
- * @type object
- * @default {}
- */
- classes: {},
-
-
- /**
- * DataTables build type (expanded by the download builder)
- *
- * @type string
- */
- builder: "-source-",
-
-
- /**
- * Error reporting.
- *
- * How should DataTables report an error. Can take the value 'alert',
- * 'throw', 'none' or a function.
- *
- * @type string|function
- * @default alert
- */
- errMode: "alert",
-
-
- /**
- * Feature plug-ins.
- *
- * This is an array of objects which describe the feature plug-ins that are
- * available to DataTables. These feature plug-ins are then available for
- * use through the `dom` initialisation option.
- *
- * Each feature plug-in is described by an object which must have the
- * following properties:
- *
- * * `fnInit` - function that is used to initialise the plug-in,
- * * `cFeature` - a character so the feature can be enabled by the `dom`
- * instillation option. This is case sensitive.
- *
- * The `fnInit` function has the following input parameters:
- *
- * 1. `{object}` DataTables settings object: see
- * {@link DataTable.models.oSettings}
- *
- * And the following return is expected:
- *
- * * {node|null} The element which contains your feature. Note that the
- * return may also be void if your plug-in does not require to inject any
- * DOM elements into DataTables control (`dom`) - for example this might
- * be useful when developing a plug-in which allows table control via
- * keyboard entry
- *
- * @type array
- *
- * @example
- * $.fn.dataTable.ext.features.push( {
- * "fnInit": function( oSettings ) {
- * return new TableTools( { "oDTSettings": oSettings } );
- * },
- * "cFeature": "T"
- * } );
- */
- feature: [],
-
-
- /**
- * Row searching.
- *
- * This method of searching is complimentary to the default type based
- * searching, and a lot more comprehensive as it allows you complete control
- * over the searching logic. Each element in this array is a function
- * (parameters described below) that is called for every row in the table,
- * and your logic decides if it should be included in the searching data set
- * or not.
- *
- * Searching functions have the following input parameters:
- *
- * 1. `{object}` DataTables settings object: see
- * {@link DataTable.models.oSettings}
- * 2. `{array|object}` Data for the row to be processed (same as the
- * original format that was passed in as the data source, or an array
- * from a DOM data source
- * 3. `{int}` Row index ({@link DataTable.models.oSettings.aoData}), which
- * can be useful to retrieve the `TR` element if you need DOM interaction.
- *
- * And the following return is expected:
- *
- * * {boolean} Include the row in the searched result set (true) or not
- * (false)
- *
- * Note that as with the main search ability in DataTables, technically this
- * is "filtering", since it is subtractive. However, for consistency in
- * naming we call it searching here.
- *
- * @type array
- * @default []
- *
- * @example
- * // The following example shows custom search being applied to the
- * // fourth column (i.e. the data[3] index) based on two input values
- * // from the end-user, matching the data in a certain range.
- * $.fn.dataTable.ext.search.push(
- * function( settings, data, dataIndex ) {
- * var min = document.getElementById('min').value * 1;
- * var max = document.getElementById('max').value * 1;
- * var version = data[3] == "-" ? 0 : data[3]*1;
- *
- * if ( min == "" && max == "" ) {
- * return true;
- * }
- * else if ( min == "" && version < max ) {
- * return true;
- * }
- * else if ( min < version && "" == max ) {
- * return true;
- * }
- * else if ( min < version && version < max ) {
- * return true;
- * }
- * return false;
- * }
- * );
- */
- search: [],
-
-
- /**
- * Selector extensions
- *
- * The `selector` option can be used to extend the options available for the
- * selector modifier options (`selector-modifier` object data type) that
- * each of the three built in selector types offer (row, column and cell +
- * their plural counterparts). For example the Select extension uses this
- * mechanism to provide an option to select only rows, columns and cells
- * that have been marked as selected by the end user (`{selected: true}`),
- * which can be used in conjunction with the existing built in selector
- * options.
- *
- * Each property is an array to which functions can be pushed. The functions
- * take three attributes:
- *
- * * Settings object for the host table
- * * Options object (`selector-modifier` object type)
- * * Array of selected item indexes
- *
- * The return is an array of the resulting item indexes after the custom
- * selector has been applied.
- *
- * @type object
- */
- selector: {
- cell: [],
- column: [],
- row: []
- },
-
-
- /**
- * Internal functions, exposed for used in plug-ins.
- *
- * Please note that you should not need to use the internal methods for
- * anything other than a plug-in (and even then, try to avoid if possible).
- * The internal function may change between releases.
- *
- * @type object
- * @default {}
- */
- internal: {},
-
-
- /**
- * Legacy configuration options. Enable and disable legacy options that
- * are available in DataTables.
- *
- * @type object
- */
- legacy: {
- /**
- * Enable / disable DataTables 1.9 compatible server-side processing
- * requests
- *
- * @type boolean
- * @default null
- */
- ajax: null
- },
-
-
- /**
- * Pagination plug-in methods.
- *
- * Each entry in this object is a function and defines which buttons should
- * be shown by the pagination rendering method that is used for the table:
- * {@link DataTable.ext.renderer.pageButton}. The renderer addresses how the
- * buttons are displayed in the document, while the functions here tell it
- * what buttons to display. This is done by returning an array of button
- * descriptions (what each button will do).
- *
- * Pagination types (the four built in options and any additional plug-in
- * options defined here) can be used through the `paginationType`
- * initialisation parameter.
- *
- * The functions defined take two parameters:
- *
- * 1. `{int} page` The current page index
- * 2. `{int} pages` The number of pages in the table
- *
- * Each function is expected to return an array where each element of the
- * array can be one of:
- *
- * * `first` - Jump to first page when activated
- * * `last` - Jump to last page when activated
- * * `previous` - Show previous page when activated
- * * `next` - Show next page when activated
- * * `{int}` - Show page of the index given
- * * `{array}` - A nested array containing the above elements to add a
- * containing 'DIV' element (might be useful for styling).
- *
- * Note that DataTables v1.9- used this object slightly differently whereby
- * an object with two functions would be defined for each plug-in. That
- * ability is still supported by DataTables 1.10+ to provide backwards
- * compatibility, but this option of use is now decremented and no longer
- * documented in DataTables 1.10+.
- *
- * @type object
- * @default {}
- *
- * @example
- * // Show previous, next and current page buttons only
- * $.fn.dataTableExt.oPagination.current = function ( page, pages ) {
- * return [ 'previous', page, 'next' ];
- * };
- */
- pager: {},
-
-
- renderer: {
- pageButton: {},
- header: {}
- },
-
-
- /**
- * Ordering plug-ins - custom data source
- *
- * The extension options for ordering of data available here is complimentary
- * to the default type based ordering that DataTables typically uses. It
- * allows much greater control over the the data that is being used to
- * order a column, but is necessarily therefore more complex.
- *
- * This type of ordering is useful if you want to do ordering based on data
- * live from the DOM (for example the contents of an 'input' element) rather
- * than just the static string that DataTables knows of.
- *
- * The way these plug-ins work is that you create an array of the values you
- * wish to be ordering for the column in question and then return that
- * array. The data in the array much be in the index order of the rows in
- * the table (not the currently ordering order!). Which order data gathering
- * function is run here depends on the `dt-init columns.orderDataType`
- * parameter that is used for the column (if any).
- *
- * The functions defined take two parameters:
- *
- * 1. `{object}` DataTables settings object: see
- * {@link DataTable.models.oSettings}
- * 2. `{int}` Target column index
- *
- * Each function is expected to return an array:
- *
- * * `{array}` Data for the column to be ordering upon
- *
- * @type array
- *
- * @example
- * // Ordering using `input` node values
- * $.fn.dataTable.ext.order['dom-text'] = function ( settings, col )
- * {
- * return this.api().column( col, {order:'index'} ).nodes().map( function ( td, i ) {
- * return $('input', td).val();
- * } );
- * }
- */
- order: {},
-
-
- /**
- * Type based plug-ins.
- *
- * Each column in DataTables has a type assigned to it, either by automatic
- * detection or by direct assignment using the `type` option for the column.
- * The type of a column will effect how it is ordering and search (plug-ins
- * can also make use of the column type if required).
- *
- * @namespace
- */
- type: {
- /**
- * Type detection functions.
- *
- * The functions defined in this object are used to automatically detect
- * a column's type, making initialisation of DataTables super easy, even
- * when complex data is in the table.
- *
- * The functions defined take two parameters:
- *
- * 1. `{*}` Data from the column cell to be analysed
- * 2. `{settings}` DataTables settings object. This can be used to
- * perform context specific type detection - for example detection
- * based on language settings such as using a comma for a decimal
- * place. Generally speaking the options from the settings will not
- * be required
- *
- * Each function is expected to return:
- *
- * * `{string|null}` Data type detected, or null if unknown (and thus
- * pass it on to the other type detection functions.
- *
- * @type array
- *
- * @example
- * // Currency type detection plug-in:
- * $.fn.dataTable.ext.type.detect.push(
- * function ( data, settings ) {
- * // Check the numeric part
- * if ( ! data.substring(1).match(/[0-9]/) ) {
- * return null;
- * }
- *
- * // Check prefixed by currency
- * if ( data.charAt(0) == '$' || data.charAt(0) == '£' ) {
- * return 'currency';
- * }
- * return null;
- * }
- * );
- */
- detect: [],
-
-
- /**
- * Type based search formatting.
- *
- * The type based searching functions can be used to pre-format the
- * data to be search on. For example, it can be used to strip HTML
- * tags or to de-format telephone numbers for numeric only searching.
- *
- * Note that is a search is not defined for a column of a given type,
- * no search formatting will be performed.
- *
- * Pre-processing of searching data plug-ins - When you assign the sType
- * for a column (or have it automatically detected for you by DataTables
- * or a type detection plug-in), you will typically be using this for
- * custom sorting, but it can also be used to provide custom searching
- * by allowing you to pre-processing the data and returning the data in
- * the format that should be searched upon. This is done by adding
- * functions this object with a parameter name which matches the sType
- * for that target column. This is the corollary of <i>afnSortData</i>
- * for searching data.
- *
- * The functions defined take a single parameter:
- *
- * 1. `{*}` Data from the column cell to be prepared for searching
- *
- * Each function is expected to return:
- *
- * * `{string|null}` Formatted string that will be used for the searching.
- *
- * @type object
- * @default {}
- *
- * @example
- * $.fn.dataTable.ext.type.search['title-numeric'] = function ( d ) {
- * return d.replace(/\n/g," ").replace( /<.*?>/g, "" );
- * }
- */
- search: {},
-
-
- /**
- * Type based ordering.
- *
- * The column type tells DataTables what ordering to apply to the table
- * when a column is sorted upon. The order for each type that is defined,
- * is defined by the functions available in this object.
- *
- * Each ordering option can be described by three properties added to
- * this object:
- *
- * * `{type}-pre` - Pre-formatting function
- * * `{type}-asc` - Ascending order function
- * * `{type}-desc` - Descending order function
- *
- * All three can be used together, only `{type}-pre` or only
- * `{type}-asc` and `{type}-desc` together. It is generally recommended
- * that only `{type}-pre` is used, as this provides the optimal
- * implementation in terms of speed, although the others are provided
- * for compatibility with existing Javascript sort functions.
- *
- * `{type}-pre`: Functions defined take a single parameter:
- *
- * 1. `{*}` Data from the column cell to be prepared for ordering
- *
- * And return:
- *
- * * `{*}` Data to be sorted upon
- *
- * `{type}-asc` and `{type}-desc`: Functions are typical Javascript sort
- * functions, taking two parameters:
- *
- * 1. `{*}` Data to compare to the second parameter
- * 2. `{*}` Data to compare to the first parameter
- *
- * And returning:
- *
- * * `{*}` Ordering match: <0 if first parameter should be sorted lower
- * than the second parameter, ===0 if the two parameters are equal and
- * >0 if the first parameter should be sorted height than the second
- * parameter.
- *
- * @type object
- * @default {}
- *
- * @example
- * // Numeric ordering of formatted numbers with a pre-formatter
- * $.extend( $.fn.dataTable.ext.type.order, {
- * "string-pre": function(x) {
- * a = (a === "-" || a === "") ? 0 : a.replace( /[^\d\-\.]/g, "" );
- * return parseFloat( a );
- * }
- * } );
- *
- * @example
- * // Case-sensitive string ordering, with no pre-formatting method
- * $.extend( $.fn.dataTable.ext.order, {
- * "string-case-asc": function(x,y) {
- * return ((x < y) ? -1 : ((x > y) ? 1 : 0));
- * },
- * "string-case-desc": function(x,y) {
- * return ((x < y) ? 1 : ((x > y) ? -1 : 0));
- * }
- * } );
- */
- order: {}
- },
-
- /**
- * Unique DataTables instance counter
- *
- * @type int
- * @private
- */
- _unique: 0,
-
-
- //
- // Depreciated
- // The following properties are retained for backwards compatiblity only.
- // The should not be used in new projects and will be removed in a future
- // version
- //
-
- /**
- * Version check function.
- * @type function
- * @depreciated Since 1.10
- */
- fnVersionCheck: DataTable.fnVersionCheck,
-
-
- /**
- * Index for what 'this' index API functions should use
- * @type int
- * @deprecated Since v1.10
- */
- iApiIndex: 0,
-
-
- /**
- * jQuery UI class container
- * @type object
- * @deprecated Since v1.10
- */
- oJUIClasses: {},
-
-
- /**
- * Software version
- * @type string
- * @deprecated Since v1.10
- */
- sVersion: DataTable.version
- };
-
-
- //
- // Backwards compatibility. Alias to pre 1.10 Hungarian notation counter parts
- //
- $.extend( _ext, {
- afnFiltering: _ext.search,
- aTypes: _ext.type.detect,
- ofnSearch: _ext.type.search,
- oSort: _ext.type.order,
- afnSortData: _ext.order,
- aoFeatures: _ext.feature,
- oApi: _ext.internal,
- oStdClasses: _ext.classes,
- oPagination: _ext.pager
- } );
-
-
- $.extend( DataTable.ext.classes, {
- "sTable": "dataTable",
- "sNoFooter": "no-footer",
-
- /* Paging buttons */
- "sPageButton": "paginate_button",
- "sPageButtonActive": "current",
- "sPageButtonDisabled": "disabled",
-
- /* Striping classes */
- "sStripeOdd": "odd",
- "sStripeEven": "even",
-
- /* Empty row */
- "sRowEmpty": "dataTables_empty",
-
- /* Features */
- "sWrapper": "dataTables_wrapper",
- "sFilter": "dataTables_filter",
- "sInfo": "dataTables_info",
- "sPaging": "dataTables_paginate paging_", /* Note that the type is postfixed */
- "sLength": "dataTables_length",
- "sProcessing": "dataTables_processing",
-
- /* Sorting */
- "sSortAsc": "sorting_asc",
- "sSortDesc": "sorting_desc",
- "sSortable": "sorting", /* Sortable in both directions */
- "sSortableAsc": "sorting_asc_disabled",
- "sSortableDesc": "sorting_desc_disabled",
- "sSortableNone": "sorting_disabled",
- "sSortColumn": "sorting_", /* Note that an int is postfixed for the sorting order */
-
- /* Filtering */
- "sFilterInput": "",
-
- /* Page length */
- "sLengthSelect": "",
-
- /* Scrolling */
- "sScrollWrapper": "dataTables_scroll",
- "sScrollHead": "dataTables_scrollHead",
- "sScrollHeadInner": "dataTables_scrollHeadInner",
- "sScrollBody": "dataTables_scrollBody",
- "sScrollFoot": "dataTables_scrollFoot",
- "sScrollFootInner": "dataTables_scrollFootInner",
-
- /* Misc */
- "sHeaderTH": "",
- "sFooterTH": "",
-
- // Deprecated
- "sSortJUIAsc": "",
- "sSortJUIDesc": "",
- "sSortJUI": "",
- "sSortJUIAscAllowed": "",
- "sSortJUIDescAllowed": "",
- "sSortJUIWrapper": "",
- "sSortIcon": "",
- "sJUIHeader": "",
- "sJUIFooter": ""
- } );
-
-
- var extPagination = DataTable.ext.pager;
-
- function _numbers ( page, pages ) {
- var
- numbers = [],
- buttons = extPagination.numbers_length,
- half = Math.floor( buttons / 2 ),
- i = 1;
-
- if ( pages <= buttons ) {
- numbers = _range( 0, pages );
- }
- else if ( page <= half ) {
- numbers = _range( 0, buttons-2 );
- numbers.push( 'ellipsis' );
- numbers.push( pages-1 );
- }
- else if ( page >= pages - 1 - half ) {
- numbers = _range( pages-(buttons-2), pages );
- numbers.splice( 0, 0, 'ellipsis' ); // no unshift in ie6
- numbers.splice( 0, 0, 0 );
- }
- else {
- numbers = _range( page-half+2, page+half-1 );
- numbers.push( 'ellipsis' );
- numbers.push( pages-1 );
- numbers.splice( 0, 0, 'ellipsis' );
- numbers.splice( 0, 0, 0 );
- }
-
- numbers.DT_el = 'span';
- return numbers;
- }
-
-
- $.extend( extPagination, {
- simple: function ( page, pages ) {
- return [ 'previous', 'next' ];
- },
-
- full: function ( page, pages ) {
- return [ 'first', 'previous', 'next', 'last' ];
- },
-
- numbers: function ( page, pages ) {
- return [ _numbers(page, pages) ];
- },
-
- simple_numbers: function ( page, pages ) {
- return [ 'previous', _numbers(page, pages), 'next' ];
- },
-
- full_numbers: function ( page, pages ) {
- return [ 'first', 'previous', _numbers(page, pages), 'next', 'last' ];
- },
-
- first_last_numbers: function (page, pages) {
- return ['first', _numbers(page, pages), 'last'];
- },
-
- // For testing and plug-ins to use
- _numbers: _numbers,
-
- // Number of number buttons (including ellipsis) to show. _Must be odd!_
- numbers_length: 7
- } );
-
-
- $.extend( true, DataTable.ext.renderer, {
- pageButton: {
- _: function ( settings, host, idx, buttons, page, pages ) {
- var classes = settings.oClasses;
- var lang = settings.oLanguage.oPaginate;
- var aria = settings.oLanguage.oAria.paginate || {};
- var btnDisplay, btnClass, counter=0;
-
- var attach = function( container, buttons ) {
- var i, ien, node, button;
- var clickHandler = function ( e ) {
- _fnPageChange( settings, e.data.action, true );
- };
-
- for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
- button = buttons[i];
-
- if ( $.isArray( button ) ) {
- var inner = $( '<'+(button.DT_el || 'div')+'/>' )
- .appendTo( container );
- attach( inner, button );
- }
- else {
- btnDisplay = null;
- btnClass = '';
-
- switch ( button ) {
- case 'ellipsis':
- container.append('<span class="ellipsis">…</span>');
- break;
-
- case 'first':
- btnDisplay = lang.sFirst;
- btnClass = button + (page > 0 ?
- '' : ' '+classes.sPageButtonDisabled);
- break;
-
- case 'previous':
- btnDisplay = lang.sPrevious;
- btnClass = button + (page > 0 ?
- '' : ' '+classes.sPageButtonDisabled);
- break;
-
- case 'next':
- btnDisplay = lang.sNext;
- btnClass = button + (page < pages-1 ?
- '' : ' '+classes.sPageButtonDisabled);
- break;
-
- case 'last':
- btnDisplay = lang.sLast;
- btnClass = button + (page < pages-1 ?
- '' : ' '+classes.sPageButtonDisabled);
- break;
-
- default:
- btnDisplay = button + 1;
- btnClass = page === button ?
- classes.sPageButtonActive : '';
- break;
- }
-
- if ( btnDisplay !== null ) {
- node = $('<a>', {
- 'class': classes.sPageButton+' '+btnClass,
- 'aria-controls': settings.sTableId,
- 'aria-label': aria[ button ],
- 'data-dt-idx': counter,
- 'tabindex': settings.iTabIndex,
- 'id': idx === 0 && typeof button === 'string' ?
- settings.sTableId +'_'+ button :
- null
- } )
- .html( btnDisplay )
- .appendTo( container );
-
- _fnBindAction(
- node, {action: button}, clickHandler
- );
-
- counter++;
- }
- }
- }
- };
-
- // IE9 throws an 'unknown error' if document.activeElement is used
- // inside an iframe or frame. Try / catch the error. Not good for
- // accessibility, but neither are frames.
- var activeEl;
-
- try {
- // Because this approach is destroying and recreating the paging
- // elements, focus is lost on the select button which is bad for
- // accessibility. So we want to restore focus once the draw has
- // completed
- activeEl = $(host).find(document.activeElement).data('dt-idx');
- }
- catch (e) {}
-
- attach( $(host).empty(), buttons );
-
- if ( activeEl !== undefined ) {
- $(host).find( '[data-dt-idx='+activeEl+']' ).focus();
- }
- }
- }
- } );
-
-
-
- // Built in type detection. See model.ext.aTypes for information about
- // what is required from this methods.
- $.extend( DataTable.ext.type.detect, [
- // Plain numbers - first since V8 detects some plain numbers as dates
- // e.g. Date.parse('55') (but not all, e.g. Date.parse('22')...).
- function ( d, settings )
- {
- var decimal = settings.oLanguage.sDecimal;
- return _isNumber( d, decimal ) ? 'num'+decimal : null;
- },
-
- // Dates (only those recognised by the browser's Date.parse)
- function ( d, settings )
- {
- // V8 tries _very_ hard to make a string passed into `Date.parse()`
- // valid, so we need to use a regex to restrict date formats. Use a
- // plug-in for anything other than ISO8601 style strings
- if ( d && !(d instanceof Date) && ! _re_date.test(d) ) {
- return null;
- }
- var parsed = Date.parse(d);
- return (parsed !== null && !isNaN(parsed)) || _empty(d) ? 'date' : null;
- },
-
- // Formatted numbers
- function ( d, settings )
- {
- var decimal = settings.oLanguage.sDecimal;
- return _isNumber( d, decimal, true ) ? 'num-fmt'+decimal : null;
- },
-
- // HTML numeric
- function ( d, settings )
- {
- var decimal = settings.oLanguage.sDecimal;
- return _htmlNumeric( d, decimal ) ? 'html-num'+decimal : null;
- },
-
- // HTML numeric, formatted
- function ( d, settings )
- {
- var decimal = settings.oLanguage.sDecimal;
- return _htmlNumeric( d, decimal, true ) ? 'html-num-fmt'+decimal : null;
- },
-
- // HTML (this is strict checking - there must be html)
- function ( d, settings )
- {
- return _empty( d ) || (typeof d === 'string' && d.indexOf('<') !== -1) ?
- 'html' : null;
- }
- ] );
-
-
-
- // Filter formatting functions. See model.ext.ofnSearch for information about
- // what is required from these methods.
- //
- // Note that additional search methods are added for the html numbers and
- // html formatted numbers by `_addNumericSort()` when we know what the decimal
- // place is
-
-
- $.extend( DataTable.ext.type.search, {
- html: function ( data ) {
- return _empty(data) ?
- data :
- typeof data === 'string' ?
- data
- .replace( _re_new_lines, " " )
- .replace( _re_html, "" ) :
- '';
- },
-
- string: function ( data ) {
- return _empty(data) ?
- data :
- typeof data === 'string' ?
- data.replace( _re_new_lines, " " ) :
- data;
- }
- } );
-
-
-
- var __numericReplace = function ( d, decimalPlace, re1, re2 ) {
- if ( d !== 0 && (!d || d === '-') ) {
- return -Infinity;
- }
-
- // If a decimal place other than `.` is used, it needs to be given to the
- // function so we can detect it and replace with a `.` which is the only
- // decimal place Javascript recognises - it is not locale aware.
- if ( decimalPlace ) {
- d = _numToDecimal( d, decimalPlace );
- }
-
- if ( d.replace ) {
- if ( re1 ) {
- d = d.replace( re1, '' );
- }
-
- if ( re2 ) {
- d = d.replace( re2, '' );
- }
- }
-
- return d * 1;
- };
-
-
- // Add the numeric 'deformatting' functions for sorting and search. This is done
- // in a function to provide an easy ability for the language options to add
- // additional methods if a non-period decimal place is used.
- function _addNumericSort ( decimalPlace ) {
- $.each(
- {
- // Plain numbers
- "num": function ( d ) {
- return __numericReplace( d, decimalPlace );
- },
-
- // Formatted numbers
- "num-fmt": function ( d ) {
- return __numericReplace( d, decimalPlace, _re_formatted_numeric );
- },
-
- // HTML numeric
- "html-num": function ( d ) {
- return __numericReplace( d, decimalPlace, _re_html );
- },
-
- // HTML numeric, formatted
- "html-num-fmt": function ( d ) {
- return __numericReplace( d, decimalPlace, _re_html, _re_formatted_numeric );
- }
- },
- function ( key, fn ) {
- // Add the ordering method
- _ext.type.order[ key+decimalPlace+'-pre' ] = fn;
-
- // For HTML types add a search formatter that will strip the HTML
- if ( key.match(/^html\-/) ) {
- _ext.type.search[ key+decimalPlace ] = _ext.type.search.html;
- }
- }
- );
- }
-
-
- // Default sort methods
- $.extend( _ext.type.order, {
- // Dates
- "date-pre": function ( d ) {
- var ts = Date.parse( d );
- return isNaN(ts) ? -Infinity : ts;
- },
-
- // html
- "html-pre": function ( a ) {
- return _empty(a) ?
- '' :
- a.replace ?
- a.replace( /<.*?>/g, "" ).toLowerCase() :
- a+'';
- },
-
- // string
- "string-pre": function ( a ) {
- // This is a little complex, but faster than always calling toString,
- // http://jsperf.com/tostring-v-check
- return _empty(a) ?
- '' :
- typeof a === 'string' ?
- a.toLowerCase() :
- ! a.toString ?
- '' :
- a.toString();
- },
-
- // string-asc and -desc are retained only for compatibility with the old
- // sort methods
- "string-asc": function ( x, y ) {
- return ((x < y) ? -1 : ((x > y) ? 1 : 0));
- },
-
- "string-desc": function ( x, y ) {
- return ((x < y) ? 1 : ((x > y) ? -1 : 0));
- }
- } );
-
-
- // Numeric sorting types - order doesn't matter here
- _addNumericSort( '' );
-
-
- $.extend( true, DataTable.ext.renderer, {
- header: {
- _: function ( settings, cell, column, classes ) {
- // No additional mark-up required
- // Attach a sort listener to update on sort - note that using the
- // `DT` namespace will allow the event to be removed automatically
- // on destroy, while the `dt` namespaced event is the one we are
- // listening for
- $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
- if ( settings !== ctx ) { // need to check this this is the host
- return; // table, not a nested one
- }
-
- var colIdx = column.idx;
-
- cell
- .removeClass(
- column.sSortingClass +' '+
- classes.sSortAsc +' '+
- classes.sSortDesc
- )
- .addClass( columns[ colIdx ] == 'asc' ?
- classes.sSortAsc : columns[ colIdx ] == 'desc' ?
- classes.sSortDesc :
- column.sSortingClass
- );
- } );
- },
-
- jqueryui: function ( settings, cell, column, classes ) {
- $('<div/>')
- .addClass( classes.sSortJUIWrapper )
- .append( cell.contents() )
- .append( $('<span/>')
- .addClass( classes.sSortIcon+' '+column.sSortingClassJUI )
- )
- .appendTo( cell );
-
- // Attach a sort listener to update on sort
- $(settings.nTable).on( 'order.dt.DT', function ( e, ctx, sorting, columns ) {
- if ( settings !== ctx ) {
- return;
- }
-
- var colIdx = column.idx;
-
- cell
- .removeClass( classes.sSortAsc +" "+classes.sSortDesc )
- .addClass( columns[ colIdx ] == 'asc' ?
- classes.sSortAsc : columns[ colIdx ] == 'desc' ?
- classes.sSortDesc :
- column.sSortingClass
- );
-
- cell
- .find( 'span.'+classes.sSortIcon )
- .removeClass(
- classes.sSortJUIAsc +" "+
- classes.sSortJUIDesc +" "+
- classes.sSortJUI +" "+
- classes.sSortJUIAscAllowed +" "+
- classes.sSortJUIDescAllowed
- )
- .addClass( columns[ colIdx ] == 'asc' ?
- classes.sSortJUIAsc : columns[ colIdx ] == 'desc' ?
- classes.sSortJUIDesc :
- column.sSortingClassJUI
- );
- } );
- }
- }
- } );
-
- /*
- * Public helper functions. These aren't used internally by DataTables, or
- * called by any of the options passed into DataTables, but they can be used
- * externally by developers working with DataTables. They are helper functions
- * to make working with DataTables a little bit easier.
- */
-
- var __htmlEscapeEntities = function ( d ) {
- return typeof d === 'string' ?
- d.replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"') :
- d;
- };
-
- /**
- * Helpers for `columns.render`.
- *
- * The options defined here can be used with the `columns.render` initialisation
- * option to provide a display renderer. The following functions are defined:
- *
- * * `number` - Will format numeric data (defined by `columns.data`) for
- * display, retaining the original unformatted data for sorting and filtering.
- * It takes 5 parameters:
- * * `string` - Thousands grouping separator
- * * `string` - Decimal point indicator
- * * `integer` - Number of decimal points to show
- * * `string` (optional) - Prefix.
- * * `string` (optional) - Postfix (/suffix).
- * * `text` - Escape HTML to help prevent XSS attacks. It has no optional
- * parameters.
- *
- * @example
- * // Column definition using the number renderer
- * {
- * data: "salary",
- * render: $.fn.dataTable.render.number( '\'', '.', 0, '$' )
- * }
- *
- * @namespace
- */
- DataTable.render = {
- number: function ( thousands, decimal, precision, prefix, postfix ) {
- return {
- display: function ( d ) {
- if ( typeof d !== 'number' && typeof d !== 'string' ) {
- return d;
- }
-
- var negative = d < 0 ? '-' : '';
- var flo = parseFloat( d );
-
- // If NaN then there isn't much formatting that we can do - just
- // return immediately, escaping any HTML (this was supposed to
- // be a number after all)
- if ( isNaN( flo ) ) {
- return __htmlEscapeEntities( d );
- }
-
- flo = flo.toFixed( precision );
- d = Math.abs( flo );
-
- var intPart = parseInt( d, 10 );
- var floatPart = precision ?
- decimal+(d - intPart).toFixed( precision ).substring( 2 ):
- '';
-
- return negative + (prefix||'') +
- intPart.toString().replace(
- /\B(?=(\d{3})+(?!\d))/g, thousands
- ) +
- floatPart +
- (postfix||'');
- }
- };
- },
-
- text: function () {
- return {
- display: __htmlEscapeEntities,
- filter: __htmlEscapeEntities
- };
- }
- };
-
-
- /*
- * This is really a good bit rubbish this method of exposing the internal methods
- * publicly... - To be fixed in 2.0 using methods on the prototype
- */
-
-
- /**
- * Create a wrapper function for exporting an internal functions to an external API.
- * @param {string} fn API function name
- * @returns {function} wrapped function
- * @memberof DataTable#internal
- */
- function _fnExternApiFunc (fn)
- {
- return function() {
- var args = [_fnSettingsFromNode( this[DataTable.ext.iApiIndex] )].concat(
- Array.prototype.slice.call(arguments)
- );
- return DataTable.ext.internal[fn].apply( this, args );
- };
- }
-
-
- /**
- * Reference to internal functions for use by plug-in developers. Note that
- * these methods are references to internal functions and are considered to be
- * private. If you use these methods, be aware that they are liable to change
- * between versions.
- * @namespace
- */
- $.extend( DataTable.ext.internal, {
- _fnExternApiFunc: _fnExternApiFunc,
- _fnBuildAjax: _fnBuildAjax,
- _fnAjaxUpdate: _fnAjaxUpdate,
- _fnAjaxParameters: _fnAjaxParameters,
- _fnAjaxUpdateDraw: _fnAjaxUpdateDraw,
- _fnAjaxDataSrc: _fnAjaxDataSrc,
- _fnAddColumn: _fnAddColumn,
- _fnColumnOptions: _fnColumnOptions,
- _fnAdjustColumnSizing: _fnAdjustColumnSizing,
- _fnVisibleToColumnIndex: _fnVisibleToColumnIndex,
- _fnColumnIndexToVisible: _fnColumnIndexToVisible,
- _fnVisbleColumns: _fnVisbleColumns,
- _fnGetColumns: _fnGetColumns,
- _fnColumnTypes: _fnColumnTypes,
- _fnApplyColumnDefs: _fnApplyColumnDefs,
- _fnHungarianMap: _fnHungarianMap,
- _fnCamelToHungarian: _fnCamelToHungarian,
- _fnLanguageCompat: _fnLanguageCompat,
- _fnBrowserDetect: _fnBrowserDetect,
- _fnAddData: _fnAddData,
- _fnAddTr: _fnAddTr,
- _fnNodeToDataIndex: _fnNodeToDataIndex,
- _fnNodeToColumnIndex: _fnNodeToColumnIndex,
- _fnGetCellData: _fnGetCellData,
- _fnSetCellData: _fnSetCellData,
- _fnSplitObjNotation: _fnSplitObjNotation,
- _fnGetObjectDataFn: _fnGetObjectDataFn,
- _fnSetObjectDataFn: _fnSetObjectDataFn,
- _fnGetDataMaster: _fnGetDataMaster,
- _fnClearTable: _fnClearTable,
- _fnDeleteIndex: _fnDeleteIndex,
- _fnInvalidate: _fnInvalidate,
- _fnGetRowElements: _fnGetRowElements,
- _fnCreateTr: _fnCreateTr,
- _fnBuildHead: _fnBuildHead,
- _fnDrawHead: _fnDrawHead,
- _fnDraw: _fnDraw,
- _fnReDraw: _fnReDraw,
- _fnAddOptionsHtml: _fnAddOptionsHtml,
- _fnDetectHeader: _fnDetectHeader,
- _fnGetUniqueThs: _fnGetUniqueThs,
- _fnFeatureHtmlFilter: _fnFeatureHtmlFilter,
- _fnFilterComplete: _fnFilterComplete,
- _fnFilterCustom: _fnFilterCustom,
- _fnFilterColumn: _fnFilterColumn,
- _fnFilter: _fnFilter,
- _fnFilterCreateSearch: _fnFilterCreateSearch,
- _fnEscapeRegex: _fnEscapeRegex,
- _fnFilterData: _fnFilterData,
- _fnFeatureHtmlInfo: _fnFeatureHtmlInfo,
- _fnUpdateInfo: _fnUpdateInfo,
- _fnInfoMacros: _fnInfoMacros,
- _fnInitialise: _fnInitialise,
- _fnInitComplete: _fnInitComplete,
- _fnLengthChange: _fnLengthChange,
- _fnFeatureHtmlLength: _fnFeatureHtmlLength,
- _fnFeatureHtmlPaginate: _fnFeatureHtmlPaginate,
- _fnPageChange: _fnPageChange,
- _fnFeatureHtmlProcessing: _fnFeatureHtmlProcessing,
- _fnProcessingDisplay: _fnProcessingDisplay,
- _fnFeatureHtmlTable: _fnFeatureHtmlTable,
- _fnScrollDraw: _fnScrollDraw,
- _fnApplyToChildren: _fnApplyToChildren,
- _fnCalculateColumnWidths: _fnCalculateColumnWidths,
- _fnThrottle: _fnThrottle,
- _fnConvertToWidth: _fnConvertToWidth,
- _fnGetWidestNode: _fnGetWidestNode,
- _fnGetMaxLenString: _fnGetMaxLenString,
- _fnStringToCss: _fnStringToCss,
- _fnSortFlatten: _fnSortFlatten,
- _fnSort: _fnSort,
- _fnSortAria: _fnSortAria,
- _fnSortListener: _fnSortListener,
- _fnSortAttachListener: _fnSortAttachListener,
- _fnSortingClasses: _fnSortingClasses,
- _fnSortData: _fnSortData,
- _fnSaveState: _fnSaveState,
- _fnLoadState: _fnLoadState,
- _fnSettingsFromNode: _fnSettingsFromNode,
- _fnLog: _fnLog,
- _fnMap: _fnMap,
- _fnBindAction: _fnBindAction,
- _fnCallbackReg: _fnCallbackReg,
- _fnCallbackFire: _fnCallbackFire,
- _fnLengthOverflow: _fnLengthOverflow,
- _fnRenderer: _fnRenderer,
- _fnDataSource: _fnDataSource,
- _fnRowAttributes: _fnRowAttributes,
- _fnExtend: _fnExtend,
- _fnCalculateEnd: function () {} // Used by a lot of plug-ins, but redundant
- // in 1.10, so this dead-end function is
- // added to prevent errors
- } );
-
- // jQuery access
- $.fn.dataTable = DataTable;
- // Provide access to the host jQuery object (circular reference)
- DataTable.$ = $;
- // Legacy aliases
- $.fn.dataTableSettings = DataTable.settings;
- $.fn.dataTableExt = DataTable.ext;
- // With a capital `D` we return a DataTables API instance rather than a
- // jQuery object
- $.fn.DataTable = function ( opts ) {
- return $(this).dataTable( opts ).api();
- };
- // All properties that are available to $.fn.dataTable should also be
- // available on $.fn.DataTable
- $.each( DataTable, function ( prop, val ) {
- $.fn.DataTable[ prop ] = val;
- } );
- // Information about events fired by DataTables - for documentation.
- /**
- * Draw event, fired whenever the table is redrawn on the page, at the same
- * point as fnDrawCallback. This may be useful for binding events or
- * performing calculations when the table is altered at all.
- * @name DataTable#draw.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
- */
- /**
- * Search event, fired when the searching applied to the table (using the
- * built-in global search, or column filters) is altered.
- * @name DataTable#search.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
- */
- /**
- * Page change event, fired when the paging of the table is altered.
- * @name DataTable#page.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
- */
- /**
- * Order event, fired when the ordering applied to the table is altered.
- * @name DataTable#order.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
- */
- /**
- * DataTables initialisation complete event, fired when the table is fully
- * drawn, including Ajax data loaded, if Ajax data is required.
- * @name DataTable#init.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} oSettings DataTables settings object
- * @param {object} json The JSON object request from the server - only
- * present if client-side Ajax sourced data is used</li></ol>
- */
- /**
- * State save event, fired when the table has changed state a new state save
- * is required. This event allows modification of the state saving object
- * prior to actually doing the save, including addition or other state
- * properties (for plug-ins) or modification of a DataTables core property.
- * @name DataTable#stateSaveParams.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} oSettings DataTables settings object
- * @param {object} json The state information to be saved
- */
- /**
- * State load event, fired when the table is loading state from the stored
- * data, but prior to the settings object being modified by the saved state
- * - allowing modification of the saved state is required or loading of
- * state for a plug-in.
- * @name DataTable#stateLoadParams.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} oSettings DataTables settings object
- * @param {object} json The saved state information
- */
- /**
- * State loaded event, fired when state has been loaded from stored data and
- * the settings object has been modified by the loaded data.
- * @name DataTable#stateLoaded.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} oSettings DataTables settings object
- * @param {object} json The saved state information
- */
- /**
- * Processing event, fired when DataTables is doing some kind of processing
- * (be it, order, searcg or anything else). It can be used to indicate to
- * the end user that there is something happening, or that something has
- * finished.
- * @name DataTable#processing.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} oSettings DataTables settings object
- * @param {boolean} bShow Flag for if DataTables is doing processing or not
- */
- /**
- * Ajax (XHR) event, fired whenever an Ajax request is completed from a
- * request to made to the server for new data. This event is called before
- * DataTables processed the returned data, so it can also be used to pre-
- * process the data returned from the server, if needed.
- *
- * Note that this trigger is called in `fnServerData`, if you override
- * `fnServerData` and which to use this event, you need to trigger it in you
- * success function.
- * @name DataTable#xhr.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
- * @param {object} json JSON returned from the server
- *
- * @example
- * // Use a custom property returned from the server in another DOM element
- * $('#table').dataTable().on('xhr.dt', function (e, settings, json) {
- * $('#status').html( json.status );
- * } );
- *
- * @example
- * // Pre-process the data returned from the server
- * $('#table').dataTable().on('xhr.dt', function (e, settings, json) {
- * for ( var i=0, ien=json.aaData.length ; i<ien ; i++ ) {
- * json.aaData[i].sum = json.aaData[i].one + json.aaData[i].two;
- * }
- * // Note no return - manipulate the data directly in the JSON object.
- * } );
- */
- /**
- * Destroy event, fired when the DataTable is destroyed by calling fnDestroy
- * or passing the bDestroy:true parameter in the initialisation object. This
- * can be used to remove bound events, added DOM nodes, etc.
- * @name DataTable#destroy.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
- */
- /**
- * Page length change event, fired when number of records to show on each
- * page (the length) is changed.
- * @name DataTable#length.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
- * @param {integer} len New length
- */
- /**
- * Column sizing has changed.
- * @name DataTable#column-sizing.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
- */
- /**
- * Column visibility has changed.
- * @name DataTable#column-visibility.dt
- * @event
- * @param {event} e jQuery event object
- * @param {object} o DataTables settings object {@link DataTable.models.oSettings}
- * @param {int} column Column index
- * @param {bool} vis `false` if column now hidden, or `true` if visible
- */
- return $.fn.dataTable;
- }));
- /*! DataTables Bootstrap 4 integration
- * ©2011-2017 SpryMedia Ltd - datatables.net/license
- */
- /**
- * DataTables integration for Bootstrap 4. This requires Bootstrap 4 and
- * DataTables 1.10 or newer.
- *
- * This file sets the defaults and adds options to DataTables to style its
- * controls using Bootstrap. See http://datatables.net/manual/styling/bootstrap
- * for further information.
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- // Require DataTables, which attaches to jQuery, including
- // jQuery if needed and have a $ property so we can access the
- // jQuery object that is used
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- /* Set the defaults for DataTables initialisation */
- $.extend( true, DataTable.defaults, {
- dom:
- "<'row'<'col-sm-12 col-md-6'l><'col-sm-12 col-md-6'f>>" +
- "<'row'<'col-sm-12'tr>>" +
- "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
- renderer: 'bootstrap'
- } );
- /* Default class modification */
- $.extend( DataTable.ext.classes, {
- sWrapper: "dataTables_wrapper dt-bootstrap4",
- sFilterInput: "form-control form-control-sm",
- sLengthSelect: "custom-select custom-select-sm form-control form-control-sm",
- sProcessing: "dataTables_processing card",
- sPageButton: "paginate_button page-item"
- } );
- /* Bootstrap paging button renderer */
- DataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, buttons, page, pages ) {
- var api = new DataTable.Api( settings );
- var classes = settings.oClasses;
- var lang = settings.oLanguage.oPaginate;
- var aria = settings.oLanguage.oAria.paginate || {};
- var btnDisplay, btnClass, counter=0;
- var attach = function( container, buttons ) {
- var i, ien, node, button;
- var clickHandler = function ( e ) {
- e.preventDefault();
- if ( !$(e.currentTarget).hasClass('disabled') && api.page() != e.data.action ) {
- api.page( e.data.action ).draw( 'page' );
- }
- };
- for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
- button = buttons[i];
- if ( $.isArray( button ) ) {
- attach( container, button );
- }
- else {
- btnDisplay = '';
- btnClass = '';
- switch ( button ) {
- case 'ellipsis':
- btnDisplay = '…';
- btnClass = 'disabled';
- break;
- case 'first':
- btnDisplay = lang.sFirst;
- btnClass = button + (page > 0 ?
- '' : ' disabled');
- break;
- case 'previous':
- btnDisplay = lang.sPrevious;
- btnClass = button + (page > 0 ?
- '' : ' disabled');
- break;
- case 'next':
- btnDisplay = lang.sNext;
- btnClass = button + (page < pages-1 ?
- '' : ' disabled');
- break;
- case 'last':
- btnDisplay = lang.sLast;
- btnClass = button + (page < pages-1 ?
- '' : ' disabled');
- break;
- default:
- btnDisplay = button + 1;
- btnClass = page === button ?
- 'active' : '';
- break;
- }
- if ( btnDisplay ) {
- node = $('<li>', {
- 'class': classes.sPageButton+' '+btnClass,
- 'id': idx === 0 && typeof button === 'string' ?
- settings.sTableId +'_'+ button :
- null
- } )
- .append( $('<a>', {
- 'href': '#',
- 'aria-controls': settings.sTableId,
- 'aria-label': aria[ button ],
- 'data-dt-idx': counter,
- 'tabindex': settings.iTabIndex,
- 'class': 'page-link'
- } )
- .html( btnDisplay )
- )
- .appendTo( container );
- settings.oApi._fnBindAction(
- node, {action: button}, clickHandler
- );
- counter++;
- }
- }
- }
- };
- // IE9 throws an 'unknown error' if document.activeElement is used
- // inside an iframe or frame.
- var activeEl;
- try {
- // Because this approach is destroying and recreating the paging
- // elements, focus is lost on the select button which is bad for
- // accessibility. So we want to restore focus once the draw has
- // completed
- activeEl = $(host).find(document.activeElement).data('dt-idx');
- }
- catch (e) {}
- attach(
- $(host).empty().html('<ul class="pagination"/>').children('ul'),
- buttons
- );
- if ( activeEl !== undefined ) {
- $(host).find( '[data-dt-idx='+activeEl+']' ).focus();
- }
- };
- return DataTable;
- }));
- /* Set the defaults for DataTables extended classes */
- $.extend(true, $.fn.dataTableExt.oStdClasses, {
- "sFilterInput": "form-control border-top-left-radius-0 border-bottom-left-radius-0 ml-0 width-lg shadow-inset-1",
- "sLengthSelect": "form-control custom-select"
- });
- /* Set the defaults for DataTables initialisation */
- $.extend(true, $.fn.dataTable.defaults, {
- /* --- Layout Structure
- --- Options
- l - length changing input control
- f - filtering input
- t - The table!
- i - Table information summary
- p - pagination control
- r - processing display element
- B - buttons
- R - ColReorder
- S - Select
- --- Markup
- < and > - div element
- <"class" and > - div with a class
- <"#id" and > - div with an ID
- <"#id.class" and > - div with an ID and a class
- --- Further reading
- https://datatables.net/reference/option/dom
- --------------------------------------
- */
- dom: "<'row mb-3'<'col-sm-12 col-md-6 d-flex align-items-center justify-content-start'f><'col-sm-12 col-md-6 d-flex align-items-center justify-content-end'l>>" +
- "<'row'<'col-sm-12'tr>>" +
- "<'row'<'col-sm-12 col-md-5'i><'col-sm-12 col-md-7'p>>",
- lengthMenu: [[10, 15, 25, 50, 100, -1], [10, 15, 25, 50, 100, "All"]],
- language: {
- /* change the default text for 'next' and 'previous' with icons */
- paginate: {
- previous: "<i class='fal fa-chevron-left'></i>",
- next: "<i class='fal fa-chevron-right'></i>"
- },
- processing: '<div class="d-flex align-items-center justify-content-center fs-lg"><div class="spinner-border spinner-border-sm text-primary mr-2" role="status"><span class="sr-only"> Loading...</span></div> Processing...</div>',
- /* replace the default search lable text with a nice icon */
- search: '<div class="input-group-text d-inline-flex width-3 align-items-center justify-content-center border-bottom-right-radius-0 border-top-right-radius-0 border-right-0"><i class="fal fa-search"></i></div>',
- /* add search filter */
- searchPlaceholder: "Search",
- /* change text for zero records */
- zeroRecords: "No records to display"
- },
- initComplete: function(settings, json) {
- initApp.appForms('.dataTables_filter', 'has-length', 'has-disabled');
- }
- });
- /*! AutoFill 2.3.3
- * ©2008-2018 SpryMedia Ltd - datatables.net/license
- */
- /**
- * @summary AutoFill
- * @description Add Excel like click and drag auto-fill options to DataTables
- * @version 2.3.3
- * @file dataTables.autoFill.js
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
- * @contact www.sprymedia.co.uk/contact
- * @copyright Copyright 2010-2018 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license/mit
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- var _instance = 0;
- /**
- * AutoFill provides Excel like auto-fill features for a DataTable
- *
- * @class AutoFill
- * @constructor
- * @param {object} oTD DataTables settings object
- * @param {object} oConfig Configuration object for AutoFill
- */
- var AutoFill = function( dt, opts )
- {
- if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) {
- throw( "Warning: AutoFill requires DataTables 1.10.8 or greater");
- }
- // User and defaults configuration object
- this.c = $.extend( true, {},
- DataTable.defaults.autoFill,
- AutoFill.defaults,
- opts
- );
- /**
- * @namespace Settings object which contains customisable information for AutoFill instance
- */
- this.s = {
- /** @type {DataTable.Api} DataTables' API instance */
- dt: new DataTable.Api( dt ),
- /** @type {String} Unique namespace for events attached to the document */
- namespace: '.autoFill'+(_instance++),
- /** @type {Object} Cached dimension information for use in the mouse move event handler */
- scroll: {},
- /** @type {integer} Interval object used for smooth scrolling */
- scrollInterval: null,
- handle: {
- height: 0,
- width: 0
- },
- /**
- * Enabled setting
- * @type {Boolean}
- */
- enabled: false
- };
- /**
- * @namespace Common and useful DOM elements for the class instance
- */
- this.dom = {
- /** @type {jQuery} AutoFill handle */
- handle: $('<div class="dt-autofill-handle"/>'),
- /**
- * @type {Object} Selected cells outline - Need to use 4 elements,
- * otherwise the mouse over if you back into the selected rectangle
- * will be over that element, rather than the cells!
- */
- select: {
- top: $('<div class="dt-autofill-select top"/>'),
- right: $('<div class="dt-autofill-select right"/>'),
- bottom: $('<div class="dt-autofill-select bottom"/>'),
- left: $('<div class="dt-autofill-select left"/>')
- },
- /** @type {jQuery} Fill type chooser background */
- background: $('<div class="dt-autofill-background"/>'),
- /** @type {jQuery} Fill type chooser */
- list: $('<div class="dt-autofill-list">'+this.s.dt.i18n('autoFill.info', '')+'<ul/></div>'),
- /** @type {jQuery} DataTables scrolling container */
- dtScroll: null,
- /** @type {jQuery} Offset parent element */
- offsetParent: null
- };
- /* Constructor logic */
- this._constructor();
- };
- $.extend( AutoFill.prototype, {
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Public methods (exposed via the DataTables API below)
- */
- enabled: function ()
- {
- return this.s.enabled;
- },
- enable: function ( flag )
- {
- var that = this;
- if ( flag === false ) {
- return this.disable();
- }
- this.s.enabled = true;
- this._focusListener();
- this.dom.handle.on( 'mousedown', function (e) {
- that._mousedown( e );
- return false;
- } );
- return this;
- },
- disable: function ()
- {
- this.s.enabled = false;
- this._focusListenerRemove();
- return this;
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constructor
- */
- /**
- * Initialise the RowReorder instance
- *
- * @private
- */
- _constructor: function ()
- {
- var that = this;
- var dt = this.s.dt;
- var dtScroll = $('div.dataTables_scrollBody', this.s.dt.table().container());
- // Make the instance accessible to the API
- dt.settings()[0].autoFill = this;
- if ( dtScroll.length ) {
- this.dom.dtScroll = dtScroll;
- // Need to scroll container to be the offset parent
- if ( dtScroll.css('position') === 'static' ) {
- dtScroll.css( 'position', 'relative' );
- }
- }
- if ( this.c.enable !== false ) {
- this.enable();
- }
- dt.on( 'destroy.autoFill', function () {
- that._focusListenerRemove();
- } );
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Private methods
- */
- /**
- * Display the AutoFill drag handle by appending it to a table cell. This
- * is the opposite of the _detach method.
- *
- * @param {node} node TD/TH cell to insert the handle into
- * @private
- */
- _attach: function ( node )
- {
- var dt = this.s.dt;
- var idx = dt.cell( node ).index();
- var handle = this.dom.handle;
- var handleDim = this.s.handle;
- if ( ! idx || dt.columns( this.c.columns ).indexes().indexOf( idx.column ) === -1 ) {
- this._detach();
- return;
- }
- if ( ! this.dom.offsetParent ) {
- // We attach to the table's offset parent
- this.dom.offsetParent = $( dt.table().node() ).offsetParent();
- }
- if ( ! handleDim.height || ! handleDim.width ) {
- // Append to document so we can get its size. Not expecting it to
- // change during the life time of the page
- handle.appendTo( 'body' );
- handleDim.height = handle.outerHeight();
- handleDim.width = handle.outerWidth();
- }
- // Might need to go through multiple offset parents
- var offset = this._getPosition( node, this.dom.offsetParent );
- this.dom.attachedTo = node;
- handle
- .css( {
- top: offset.top + node.offsetHeight - handleDim.height,
- left: offset.left + node.offsetWidth - handleDim.width
- } )
- .appendTo( this.dom.offsetParent );
- },
- /**
- * Determine can the fill type should be. This can be automatic, or ask the
- * end user.
- *
- * @param {array} cells Information about the selected cells from the key
- * up function
- * @private
- */
- _actionSelector: function ( cells )
- {
- var that = this;
- var dt = this.s.dt;
- var actions = AutoFill.actions;
- var available = [];
- // "Ask" each plug-in if it wants to handle this data
- $.each( actions, function ( key, action ) {
- if ( action.available( dt, cells ) ) {
- available.push( key );
- }
- } );
- if ( available.length === 1 && this.c.alwaysAsk === false ) {
- // Only one action available - enact it immediately
- var result = actions[ available[0] ].execute( dt, cells );
- this._update( result, cells );
- }
- else {
- // Multiple actions available - ask the end user what they want to do
- var list = this.dom.list.children('ul').empty();
- // Add a cancel option
- available.push( 'cancel' );
- $.each( available, function ( i, name ) {
- list.append( $('<li/>')
- .append(
- '<div class="dt-autofill-question">'+
- actions[ name ].option( dt, cells )+
- '<div>'
- )
- .append( $('<div class="dt-autofill-button">' )
- .append( $('<button class="'+AutoFill.classes.btn+'">'+dt.i18n('autoFill.button', '>')+'</button>')
- .on( 'click', function () {
- var result = actions[ name ].execute(
- dt, cells, $(this).closest('li')
- );
- that._update( result, cells );
- that.dom.background.remove();
- that.dom.list.remove();
- } )
- )
- )
- );
- } );
- this.dom.background.appendTo( 'body' );
- this.dom.list.appendTo( 'body' );
- this.dom.list.css( 'margin-top', this.dom.list.outerHeight()/2 * -1 );
- }
- },
- /**
- * Remove the AutoFill handle from the document
- *
- * @private
- */
- _detach: function ()
- {
- this.dom.attachedTo = null;
- this.dom.handle.detach();
- },
- /**
- * Draw the selection outline by calculating the range between the start
- * and end cells, then placing the highlighting elements to draw a rectangle
- *
- * @param {node} target End cell
- * @param {object} e Originating event
- * @private
- */
- _drawSelection: function ( target, e )
- {
- // Calculate boundary for start cell to this one
- var dt = this.s.dt;
- var start = this.s.start;
- var startCell = $(this.dom.start);
- var end = {
- row: this.c.vertical ?
- dt.rows( { page: 'current' } ).nodes().indexOf( target.parentNode ) :
- start.row,
- column: this.c.horizontal ?
- $(target).index() :
- start.column
- };
- var colIndx = dt.column.index( 'toData', end.column );
- var endRow = dt.row( ':eq('+end.row+')', { page: 'current' } ); // Workaround for M581
- var endCell = $( dt.cell( endRow.index(), colIndx ).node() );
- // Be sure that is a DataTables controlled cell
- if ( ! dt.cell( endCell ).any() ) {
- return;
- }
- // if target is not in the columns available - do nothing
- if ( dt.columns( this.c.columns ).indexes().indexOf( colIndx ) === -1 ) {
- return;
- }
- this.s.end = end;
- var top, bottom, left, right, height, width;
- top = start.row < end.row ? startCell : endCell;
- bottom = start.row < end.row ? endCell : startCell;
- left = start.column < end.column ? startCell : endCell;
- right = start.column < end.column ? endCell : startCell;
- top = this._getPosition( top.get(0) ).top;
- left = this._getPosition( left.get(0) ).left;
- height = this._getPosition( bottom.get(0) ).top + bottom.outerHeight() - top;
- width = this._getPosition( right.get(0) ).left + right.outerWidth() - left;
- var select = this.dom.select;
- select.top.css( {
- top: top,
- left: left,
- width: width
- } );
- select.left.css( {
- top: top,
- left: left,
- height: height
- } );
- select.bottom.css( {
- top: top + height,
- left: left,
- width: width
- } );
- select.right.css( {
- top: top,
- left: left + width,
- height: height
- } );
- },
- /**
- * Use the Editor API to perform an update based on the new data for the
- * cells
- *
- * @param {array} cells Information about the selected cells from the key
- * up function
- * @private
- */
- _editor: function ( cells )
- {
- var dt = this.s.dt;
- var editor = this.c.editor;
- if ( ! editor ) {
- return;
- }
- // Build the object structure for Editor's multi-row editing
- var idValues = {};
- var nodes = [];
- var fields = editor.fields();
- for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
- for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
- var cell = cells[i][j];
- // Determine the field name for the cell being edited
- var col = dt.settings()[0].aoColumns[ cell.index.column ];
- var fieldName = col.editField;
- if ( fieldName === undefined ) {
- var dataSrc = col.mData;
- // dataSrc is the `field.data` property, but we need to set
- // using the field name, so we need to translate from the
- // data to the name
- for ( var k=0, ken=fields.length ; k<ken ; k++ ) {
- var field = editor.field( fields[k] );
- if ( field.dataSrc() === dataSrc ) {
- fieldName = field.name();
- break;
- }
- }
- }
- if ( ! fieldName ) {
- throw 'Could not automatically determine field data. '+
- 'Please see https://datatables.net/tn/11';
- }
- if ( ! idValues[ fieldName ] ) {
- idValues[ fieldName ] = {};
- }
- var id = dt.row( cell.index.row ).id();
- idValues[ fieldName ][ id ] = cell.set;
- // Keep a list of cells so we can activate the bubble editing
- // with them
- nodes.push( cell.index );
- }
- }
- // Perform the edit using bubble editing as it allows us to specify
- // the cells to be edited, rather than using full rows
- editor
- .bubble( nodes, false )
- .multiSet( idValues )
- .submit();
- },
- /**
- * Emit an event on the DataTable for listeners
- *
- * @param {string} name Event name
- * @param {array} args Event arguments
- * @private
- */
- _emitEvent: function ( name, args )
- {
- this.s.dt.iterator( 'table', function ( ctx, i ) {
- $(ctx.nTable).triggerHandler( name+'.dt', args );
- } );
- },
- /**
- * Attach suitable listeners (based on the configuration) that will attach
- * and detach the AutoFill handle in the document.
- *
- * @private
- */
- _focusListener: function ()
- {
- var that = this;
- var dt = this.s.dt;
- var namespace = this.s.namespace;
- var focus = this.c.focus !== null ?
- this.c.focus :
- dt.init().keys || dt.settings()[0].keytable ?
- 'focus' :
- 'hover';
- // All event listeners attached here are removed in the `destroy`
- // callback in the constructor
- if ( focus === 'focus' ) {
- dt
- .on( 'key-focus.autoFill', function ( e, dt, cell ) {
- that._attach( cell.node() );
- } )
- .on( 'key-blur.autoFill', function ( e, dt, cell ) {
- that._detach();
- } );
- }
- else if ( focus === 'click' ) {
- $(dt.table().body()).on( 'click'+namespace, 'td, th', function (e) {
- that._attach( this );
- } );
- $(document.body).on( 'click'+namespace, function (e) {
- if ( ! $(e.target).parents().filter( dt.table().body() ).length ) {
- that._detach();
- }
- } );
- }
- else {
- $(dt.table().body())
- .on( 'mouseenter'+namespace, 'td, th', function (e) {
- that._attach( this );
- } )
- .on( 'mouseleave'+namespace, function (e) {
- if ( $(e.relatedTarget).hasClass('dt-autofill-handle') ) {
- return;
- }
- that._detach();
- } );
- }
- },
- _focusListenerRemove: function ()
- {
- var dt = this.s.dt;
- dt.off( '.autoFill' );
- $(dt.table().body()).off( this.s.namespace );
- $(document.body).off( this.s.namespace );
- },
- /**
- * Get the position of a node, relative to another, including any scrolling
- * offsets.
- * @param {Node} node Node to get the position of
- * @param {jQuery} targetParent Node to use as the parent
- * @return {object} Offset calculation
- * @private
- */
- _getPosition: function ( node, targetParent )
- {
- var
- currNode = node,
- currOffsetParent,
- top = 0,
- left = 0;
- if ( ! targetParent ) {
- targetParent = $( $( this.s.dt.table().node() )[0].offsetParent );
- }
- do {
- // Don't use jQuery().position() the behaviour changes between 1.x and 3.x for
- // tables
- var positionTop = currNode.offsetTop;
- var positionLeft = currNode.offsetLeft;
- // jQuery doesn't give a `table` as the offset parent oddly, so use DOM directly
- currOffsetParent = $( currNode.offsetParent );
- top += positionTop + currOffsetParent.scrollTop();
- left += positionLeft + currOffsetParent.scrollLeft();
- top += parseInt( currOffsetParent.css('margin-top') ) * 1;
- top += parseInt( currOffsetParent.css('border-top-width') ) * 1;
- // Emergency fall back. Shouldn't happen, but just in case!
- if ( currNode.nodeName.toLowerCase() === 'body' ) {
- break;
- }
- currNode = currOffsetParent.get(0); // for next loop
- }
- while ( currOffsetParent.get(0) !== targetParent.get(0) )
- return {
- top: top,
- left: left
- };
- },
- /**
- * Start mouse drag - selects the start cell
- *
- * @param {object} e Mouse down event
- * @private
- */
- _mousedown: function ( e )
- {
- var that = this;
- var dt = this.s.dt;
- this.dom.start = this.dom.attachedTo;
- this.s.start = {
- row: dt.rows( { page: 'current' } ).nodes().indexOf( $(this.dom.start).parent()[0] ),
- column: $(this.dom.start).index()
- };
- $(document.body)
- .on( 'mousemove.autoFill', function (e) {
- that._mousemove( e );
- } )
- .on( 'mouseup.autoFill', function (e) {
- that._mouseup( e );
- } );
- var select = this.dom.select;
- var offsetParent = $( dt.table().node() ).offsetParent();
- select.top.appendTo( offsetParent );
- select.left.appendTo( offsetParent );
- select.right.appendTo( offsetParent );
- select.bottom.appendTo( offsetParent );
- this._drawSelection( this.dom.start, e );
- this.dom.handle.css( 'display', 'none' );
- // Cache scrolling information so mouse move doesn't need to read.
- // This assumes that the window and DT scroller will not change size
- // during an AutoFill drag, which I think is a fair assumption
- var scrollWrapper = this.dom.dtScroll;
- this.s.scroll = {
- windowHeight: $(window).height(),
- windowWidth: $(window).width(),
- dtTop: scrollWrapper ? scrollWrapper.offset().top : null,
- dtLeft: scrollWrapper ? scrollWrapper.offset().left : null,
- dtHeight: scrollWrapper ? scrollWrapper.outerHeight() : null,
- dtWidth: scrollWrapper ? scrollWrapper.outerWidth() : null
- };
- },
- /**
- * Mouse drag - selects the end cell and update the selection display for
- * the end user
- *
- * @param {object} e Mouse move event
- * @private
- */
- _mousemove: function ( e )
- {
- var that = this;
- var dt = this.s.dt;
- var name = e.target.nodeName.toLowerCase();
- if ( name !== 'td' && name !== 'th' ) {
- return;
- }
- this._drawSelection( e.target, e );
- this._shiftScroll( e );
- },
- /**
- * End mouse drag - perform the update actions
- *
- * @param {object} e Mouse up event
- * @private
- */
- _mouseup: function ( e )
- {
- $(document.body).off( '.autoFill' );
- var that = this;
- var dt = this.s.dt;
- var select = this.dom.select;
- select.top.remove();
- select.left.remove();
- select.right.remove();
- select.bottom.remove();
- this.dom.handle.css( 'display', 'block' );
- // Display complete - now do something useful with the selection!
- var start = this.s.start;
- var end = this.s.end;
- // Haven't selected multiple cells, so nothing to do
- if ( start.row === end.row && start.column === end.column ) {
- return;
- }
- var startDt = dt.cell( ':eq('+start.row+')', start.column+':visible', {page:'current'} );
- // If Editor is active inside this cell (inline editing) we need to wait for Editor to
- // submit and then we can loop back and trigger the fill.
- if ( $('div.DTE', startDt.node()).length ) {
- var editor = dt.editor();
- editor
- .on( 'submitSuccess.dtaf close.dtaf', function () {
- editor.off( '.dtaf');
- setTimeout( function () {
- that._mouseup( e );
- }, 100 );
- } )
- .on( 'submitComplete.dtaf preSubmitCancelled.dtaf close.dtaf', function () {
- editor.off( '.dtaf');
- } );
- // Make the current input submit
- editor.submit();
- return;
- }
- // Build a matrix representation of the selected rows
- var rows = this._range( start.row, end.row );
- var columns = this._range( start.column, end.column );
- var selected = [];
- var dtSettings = dt.settings()[0];
- var dtColumns = dtSettings.aoColumns;
- // Can't use Array.prototype.map as IE8 doesn't support it
- // Can't use $.map as jQuery flattens 2D arrays
- // Need to use a good old fashioned for loop
- for ( var rowIdx=0 ; rowIdx<rows.length ; rowIdx++ ) {
- selected.push(
- $.map( columns, function (column) {
- var row = dt.row( ':eq('+rows[rowIdx]+')', {page:'current'} ); // Workaround for M581
- var cell = dt.cell( row.index(), column+':visible' );
- var data = cell.data();
- var cellIndex = cell.index();
- var editField = dtColumns[ cellIndex.column ].editField;
- if ( editField !== undefined ) {
- data = dtSettings.oApi._fnGetObjectDataFn( editField )( dt.row( cellIndex.row ).data() );
- }
- return {
- cell: cell,
- data: data,
- label: cell.data(),
- index: cellIndex
- };
- } )
- );
- }
- this._actionSelector( selected );
-
- // Stop shiftScroll
- clearInterval( this.s.scrollInterval );
- this.s.scrollInterval = null;
- },
- /**
- * Create an array with a range of numbers defined by the start and end
- * parameters passed in (inclusive!).
- *
- * @param {integer} start Start
- * @param {integer} end End
- * @private
- */
- _range: function ( start, end )
- {
- var out = [];
- var i;
- if ( start <= end ) {
- for ( i=start ; i<=end ; i++ ) {
- out.push( i );
- }
- }
- else {
- for ( i=start ; i>=end ; i-- ) {
- out.push( i );
- }
- }
- return out;
- },
- /**
- * Move the window and DataTables scrolling during a drag to scroll new
- * content into view. This is done by proximity to the edge of the scrolling
- * container of the mouse - for example near the top edge of the window
- * should scroll up. This is a little complicated as there are two elements
- * that can be scrolled - the window and the DataTables scrolling view port
- * (if scrollX and / or scrollY is enabled).
- *
- * @param {object} e Mouse move event object
- * @private
- */
- _shiftScroll: function ( e )
- {
- var that = this;
- var dt = this.s.dt;
- var scroll = this.s.scroll;
- var runInterval = false;
- var scrollSpeed = 5;
- var buffer = 65;
- var
- windowY = e.pageY - document.body.scrollTop,
- windowX = e.pageX - document.body.scrollLeft,
- windowVert, windowHoriz,
- dtVert, dtHoriz;
- // Window calculations - based on the mouse position in the window,
- // regardless of scrolling
- if ( windowY < buffer ) {
- windowVert = scrollSpeed * -1;
- }
- else if ( windowY > scroll.windowHeight - buffer ) {
- windowVert = scrollSpeed;
- }
- if ( windowX < buffer ) {
- windowHoriz = scrollSpeed * -1;
- }
- else if ( windowX > scroll.windowWidth - buffer ) {
- windowHoriz = scrollSpeed;
- }
- // DataTables scrolling calculations - based on the table's position in
- // the document and the mouse position on the page
- if ( scroll.dtTop !== null && e.pageY < scroll.dtTop + buffer ) {
- dtVert = scrollSpeed * -1;
- }
- else if ( scroll.dtTop !== null && e.pageY > scroll.dtTop + scroll.dtHeight - buffer ) {
- dtVert = scrollSpeed;
- }
- if ( scroll.dtLeft !== null && e.pageX < scroll.dtLeft + buffer ) {
- dtHoriz = scrollSpeed * -1;
- }
- else if ( scroll.dtLeft !== null && e.pageX > scroll.dtLeft + scroll.dtWidth - buffer ) {
- dtHoriz = scrollSpeed;
- }
- // This is where it gets interesting. We want to continue scrolling
- // without requiring a mouse move, so we need an interval to be
- // triggered. The interval should continue until it is no longer needed,
- // but it must also use the latest scroll commands (for example consider
- // that the mouse might move from scrolling up to scrolling left, all
- // with the same interval running. We use the `scroll` object to "pass"
- // this information to the interval. Can't use local variables as they
- // wouldn't be the ones that are used by an already existing interval!
- if ( windowVert || windowHoriz || dtVert || dtHoriz ) {
- scroll.windowVert = windowVert;
- scroll.windowHoriz = windowHoriz;
- scroll.dtVert = dtVert;
- scroll.dtHoriz = dtHoriz;
- runInterval = true;
- }
- else if ( this.s.scrollInterval ) {
- // Don't need to scroll - remove any existing timer
- clearInterval( this.s.scrollInterval );
- this.s.scrollInterval = null;
- }
- // If we need to run the interval to scroll and there is no existing
- // interval (if there is an existing one, it will continue to run)
- if ( ! this.s.scrollInterval && runInterval ) {
- this.s.scrollInterval = setInterval( function () {
- // Don't need to worry about setting scroll <0 or beyond the
- // scroll bound as the browser will just reject that.
- if ( scroll.windowVert ) {
- document.body.scrollTop += scroll.windowVert;
- }
- if ( scroll.windowHoriz ) {
- document.body.scrollLeft += scroll.windowHoriz;
- }
- // DataTables scrolling
- if ( scroll.dtVert || scroll.dtHoriz ) {
- var scroller = that.dom.dtScroll[0];
- if ( scroll.dtVert ) {
- scroller.scrollTop += scroll.dtVert;
- }
- if ( scroll.dtHoriz ) {
- scroller.scrollLeft += scroll.dtHoriz;
- }
- }
- }, 20 );
- }
- },
- /**
- * Update the DataTable after the user has selected what they want to do
- *
- * @param {false|undefined} result Return from the `execute` method - can
- * be false internally to do nothing. This is not documented for plug-ins
- * and is used only by the cancel option.
- * @param {array} cells Information about the selected cells from the key
- * up function, argumented with the set values
- * @private
- */
- _update: function ( result, cells )
- {
- // Do nothing on `false` return from an execute function
- if ( result === false ) {
- return;
- }
- var dt = this.s.dt;
- var cell;
- var columns = dt.columns( this.c.columns ).indexes();
- // Potentially allow modifications to the cells matrix
- this._emitEvent( 'preAutoFill', [ dt, cells ] );
- this._editor( cells );
- // Automatic updates are not performed if `update` is null and the
- // `editor` parameter is passed in - the reason being that Editor will
- // update the data once submitted
- var update = this.c.update !== null ?
- this.c.update :
- this.c.editor ?
- false :
- true;
- if ( update ) {
- for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
- for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
- cell = cells[i][j];
- if ( columns.indexOf(cell.index.column) !== -1 ) {
- cell.cell.data( cell.set );
- }
- }
- }
- dt.draw(false);
- }
- this._emitEvent( 'autoFill', [ dt, cells ] );
- }
- } );
- /**
- * AutoFill actions. The options here determine how AutoFill will fill the data
- * in the table when the user has selected a range of cells. Please see the
- * documentation on the DataTables site for full details on how to create plug-
- * ins.
- *
- * @type {Object}
- */
- AutoFill.actions = {
- increment: {
- available: function ( dt, cells ) {
- var d = cells[0][0].label;
- // is numeric test based on jQuery's old `isNumeric` function
- return !isNaN( d - parseFloat( d ) );
- },
- option: function ( dt, cells ) {
- return dt.i18n(
- 'autoFill.increment',
- 'Increment / decrement each cell by: <input type="number" value="1">'
- );
- },
- execute: function ( dt, cells, node ) {
- var value = cells[0][0].data * 1;
- var increment = $('input', node).val() * 1;
- for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
- for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
- cells[i][j].set = value;
- value += increment;
- }
- }
- }
- },
- fill: {
- available: function ( dt, cells ) {
- return true;
- },
- option: function ( dt, cells ) {
- return dt.i18n('autoFill.fill', 'Fill all cells with <i>'+cells[0][0].label+'</i>' );
- },
- execute: function ( dt, cells, node ) {
- var value = cells[0][0].data;
- for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
- for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
- cells[i][j].set = value;
- }
- }
- }
- },
- fillHorizontal: {
- available: function ( dt, cells ) {
- return cells.length > 1 && cells[0].length > 1;
- },
- option: function ( dt, cells ) {
- return dt.i18n('autoFill.fillHorizontal', 'Fill cells horizontally' );
- },
- execute: function ( dt, cells, node ) {
- for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
- for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
- cells[i][j].set = cells[i][0].data;
- }
- }
- }
- },
- fillVertical: {
- available: function ( dt, cells ) {
- return cells.length > 1 && cells[0].length > 1;
- },
- option: function ( dt, cells ) {
- return dt.i18n('autoFill.fillVertical', 'Fill cells vertically' );
- },
- execute: function ( dt, cells, node ) {
- for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
- for ( var j=0, jen=cells[i].length ; j<jen ; j++ ) {
- cells[i][j].set = cells[0][j].data;
- }
- }
- }
- },
- // Special type that does not make itself available, but is added
- // automatically by AutoFill if a multi-choice list is shown. This allows
- // sensible code reuse
- cancel: {
- available: function () {
- return false;
- },
- option: function ( dt ) {
- return dt.i18n('autoFill.cancel', 'Cancel' );
- },
- execute: function () {
- return false;
- }
- }
- };
- /**
- * AutoFill version
- *
- * @static
- * @type String
- */
- AutoFill.version = '2.3.3';
- /**
- * AutoFill defaults
- *
- * @namespace
- */
- AutoFill.defaults = {
- /** @type {Boolean} Ask user what they want to do, even for a single option */
- alwaysAsk: false,
- /** @type {string|null} What will trigger a focus */
- focus: null, // focus, click, hover
- /** @type {column-selector} Columns to provide auto fill for */
- columns: '', // all
- /** @type {Boolean} Enable AutoFill on load */
- enable: true,
- /** @type {boolean|null} Update the cells after a drag */
- update: null, // false is editor given, true otherwise
- /** @type {DataTable.Editor} Editor instance for automatic submission */
- editor: null,
- /** @type {boolean} Enable vertical fill */
- vertical: true,
- /** @type {boolean} Enable horizontal fill */
- horizontal: true
- };
- /**
- * Classes used by AutoFill that are configurable
- *
- * @namespace
- */
- AutoFill.classes = {
- /** @type {String} Class used by the selection button */
- btn: 'btn'
- };
- /*
- * API
- */
- var Api = $.fn.dataTable.Api;
- // Doesn't do anything - Not documented
- Api.register( 'autoFill()', function () {
- return this;
- } );
- Api.register( 'autoFill().enabled()', function () {
- var ctx = this.context[0];
- return ctx.autoFill ?
- ctx.autoFill.enabled() :
- false;
- } );
- Api.register( 'autoFill().enable()', function ( flag ) {
- return this.iterator( 'table', function ( ctx ) {
- if ( ctx.autoFill ) {
- ctx.autoFill.enable( flag );
- }
- } );
- } );
- Api.register( 'autoFill().disable()', function () {
- return this.iterator( 'table', function ( ctx ) {
- if ( ctx.autoFill ) {
- ctx.autoFill.disable();
- }
- } );
- } );
- // Attach a listener to the document which listens for DataTables initialisation
- // events so we can automatically initialise
- $(document).on( 'preInit.dt.autofill', function (e, settings, json) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- var init = settings.oInit.autoFill;
- var defaults = DataTable.defaults.autoFill;
- if ( init || defaults ) {
- var opts = $.extend( {}, init, defaults );
- if ( init !== false ) {
- new AutoFill( settings, opts );
- }
- }
- } );
- // Alias for access
- DataTable.AutoFill = AutoFill;
- DataTable.AutoFill = AutoFill;
- return AutoFill;
- }));
- /*! Bootstrap integration for DataTables' AutoFill
- * ©2015 SpryMedia Ltd - datatables.net/license
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net-bs4', 'datatables.net-autofill'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net-bs4')(root, $).$;
- }
- if ( ! $.fn.dataTable.AutoFill ) {
- require('datatables.net-autofill')(root, $);
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- DataTable.AutoFill.classes.btn = 'btn btn-primary';
- return DataTable;
- }));
- /*! Buttons for DataTables 1.5.6
- * ©2016-2019 SpryMedia Ltd - datatables.net/license
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- // Used for namespacing events added to the document by each instance, so they
- // can be removed on destroy
- var _instCounter = 0;
- // Button namespacing counter for namespacing events on individual buttons
- var _buttonCounter = 0;
- var _dtButtons = DataTable.ext.buttons;
- /**
- * [Buttons description]
- * @param {[type]}
- * @param {[type]}
- */
- var Buttons = function( dt, config )
- {
- // If not created with a `new` keyword then we return a wrapper function that
- // will take the settings object for a DT. This allows easy use of new instances
- // with the `layout` option - e.g. `topLeft: $.fn.dataTable.Buttons( ... )`.
- if ( !(this instanceof Buttons) ) {
- return function (settings) {
- return new Buttons( settings, dt ).container();
- };
- }
- // If there is no config set it to an empty object
- if ( typeof( config ) === 'undefined' ) {
- config = {};
- }
-
- // Allow a boolean true for defaults
- if ( config === true ) {
- config = {};
- }
- // For easy configuration of buttons an array can be given
- if ( $.isArray( config ) ) {
- config = { buttons: config };
- }
- this.c = $.extend( true, {}, Buttons.defaults, config );
- // Don't want a deep copy for the buttons
- if ( config.buttons ) {
- this.c.buttons = config.buttons;
- }
- this.s = {
- dt: new DataTable.Api( dt ),
- buttons: [],
- listenKeys: '',
- namespace: 'dtb'+(_instCounter++)
- };
- this.dom = {
- container: $('<'+this.c.dom.container.tag+'/>')
- .addClass( this.c.dom.container.className )
- };
- this._constructor();
- };
- $.extend( Buttons.prototype, {
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Public methods
- */
- /**
- * Get the action of a button
- * @param {int|string} Button index
- * @return {function}
- *//**
- * Set the action of a button
- * @param {node} node Button element
- * @param {function} action Function to set
- * @return {Buttons} Self for chaining
- */
- action: function ( node, action )
- {
- var button = this._nodeToButton( node );
- if ( action === undefined ) {
- return button.conf.action;
- }
- button.conf.action = action;
- return this;
- },
- /**
- * Add an active class to the button to make to look active or get current
- * active state.
- * @param {node} node Button element
- * @param {boolean} [flag] Enable / disable flag
- * @return {Buttons} Self for chaining or boolean for getter
- */
- active: function ( node, flag ) {
- var button = this._nodeToButton( node );
- var klass = this.c.dom.button.active;
- var jqNode = $(button.node);
- if ( flag === undefined ) {
- return jqNode.hasClass( klass );
- }
- jqNode.toggleClass( klass, flag === undefined ? true : flag );
- return this;
- },
- /**
- * Add a new button
- * @param {object} config Button configuration object, base string name or function
- * @param {int|string} [idx] Button index for where to insert the button
- * @return {Buttons} Self for chaining
- */
- add: function ( config, idx )
- {
- var buttons = this.s.buttons;
- if ( typeof idx === 'string' ) {
- var split = idx.split('-');
- var base = this.s;
- for ( var i=0, ien=split.length-1 ; i<ien ; i++ ) {
- base = base.buttons[ split[i]*1 ];
- }
- buttons = base.buttons;
- idx = split[ split.length-1 ]*1;
- }
- this._expandButton( buttons, config, false, idx );
- this._draw();
- return this;
- },
- /**
- * Get the container node for the buttons
- * @return {jQuery} Buttons node
- */
- container: function ()
- {
- return this.dom.container;
- },
- /**
- * Disable a button
- * @param {node} node Button node
- * @return {Buttons} Self for chaining
- */
- disable: function ( node ) {
- var button = this._nodeToButton( node );
- $(button.node).addClass( this.c.dom.button.disabled );
- return this;
- },
- /**
- * Destroy the instance, cleaning up event handlers and removing DOM
- * elements
- * @return {Buttons} Self for chaining
- */
- destroy: function ()
- {
- // Key event listener
- $('body').off( 'keyup.'+this.s.namespace );
- // Individual button destroy (so they can remove their own events if
- // needed). Take a copy as the array is modified by `remove`
- var buttons = this.s.buttons.slice();
- var i, ien;
-
- for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
- this.remove( buttons[i].node );
- }
- // Container
- this.dom.container.remove();
- // Remove from the settings object collection
- var buttonInsts = this.s.dt.settings()[0];
- for ( i=0, ien=buttonInsts.length ; i<ien ; i++ ) {
- if ( buttonInsts.inst === this ) {
- buttonInsts.splice( i, 1 );
- break;
- }
- }
- return this;
- },
- /**
- * Enable / disable a button
- * @param {node} node Button node
- * @param {boolean} [flag=true] Enable / disable flag
- * @return {Buttons} Self for chaining
- */
- enable: function ( node, flag )
- {
- if ( flag === false ) {
- return this.disable( node );
- }
- var button = this._nodeToButton( node );
- $(button.node).removeClass( this.c.dom.button.disabled );
- return this;
- },
- /**
- * Get the instance name for the button set selector
- * @return {string} Instance name
- */
- name: function ()
- {
- return this.c.name;
- },
- /**
- * Get a button's node of the buttons container if no button is given
- * @param {node} [node] Button node
- * @return {jQuery} Button element, or container
- */
- node: function ( node )
- {
- if ( ! node ) {
- return this.dom.container;
- }
- var button = this._nodeToButton( node );
- return $(button.node);
- },
- /**
- * Set / get a processing class on the selected button
- * @param {boolean} flag true to add, false to remove, undefined to get
- * @return {boolean|Buttons} Getter value or this if a setter.
- */
- processing: function ( node, flag )
- {
- var button = this._nodeToButton( node );
- if ( flag === undefined ) {
- return $(button.node).hasClass( 'processing' );
- }
- $(button.node).toggleClass( 'processing', flag );
- return this;
- },
- /**
- * Remove a button.
- * @param {node} node Button node
- * @return {Buttons} Self for chaining
- */
- remove: function ( node )
- {
- var button = this._nodeToButton( node );
- var host = this._nodeToHost( node );
- var dt = this.s.dt;
- // Remove any child buttons first
- if ( button.buttons.length ) {
- for ( var i=button.buttons.length-1 ; i>=0 ; i-- ) {
- this.remove( button.buttons[i].node );
- }
- }
- // Allow the button to remove event handlers, etc
- if ( button.conf.destroy ) {
- button.conf.destroy.call( dt.button(node), dt, $(node), button.conf );
- }
- this._removeKey( button.conf );
- $(button.node).remove();
- var idx = $.inArray( button, host );
- host.splice( idx, 1 );
- return this;
- },
- /**
- * Get the text for a button
- * @param {int|string} node Button index
- * @return {string} Button text
- *//**
- * Set the text for a button
- * @param {int|string|function} node Button index
- * @param {string} label Text
- * @return {Buttons} Self for chaining
- */
- text: function ( node, label )
- {
- var button = this._nodeToButton( node );
- var buttonLiner = this.c.dom.collection.buttonLiner;
- var linerTag = button.inCollection && buttonLiner && buttonLiner.tag ?
- buttonLiner.tag :
- this.c.dom.buttonLiner.tag;
- var dt = this.s.dt;
- var jqNode = $(button.node);
- var text = function ( opt ) {
- return typeof opt === 'function' ?
- opt( dt, jqNode, button.conf ) :
- opt;
- };
- if ( label === undefined ) {
- return text( button.conf.text );
- }
- button.conf.text = label;
- if ( linerTag ) {
- jqNode.children( linerTag ).html( text(label) );
- }
- else {
- jqNode.html( text(label) );
- }
- return this;
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constructor
- */
- /**
- * Buttons constructor
- * @private
- */
- _constructor: function ()
- {
- var that = this;
- var dt = this.s.dt;
- var dtSettings = dt.settings()[0];
- var buttons = this.c.buttons;
- if ( ! dtSettings._buttons ) {
- dtSettings._buttons = [];
- }
- dtSettings._buttons.push( {
- inst: this,
- name: this.c.name
- } );
- for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
- this.add( buttons[i] );
- }
- dt.on( 'destroy', function ( e, settings ) {
- if ( settings === dtSettings ) {
- that.destroy();
- }
- } );
- // Global key event binding to listen for button keys
- $('body').on( 'keyup.'+this.s.namespace, function ( e ) {
- if ( ! document.activeElement || document.activeElement === document.body ) {
- // SUse a string of characters for fast lookup of if we need to
- // handle this
- var character = String.fromCharCode(e.keyCode).toLowerCase();
- if ( that.s.listenKeys.toLowerCase().indexOf( character ) !== -1 ) {
- that._keypress( character, e );
- }
- }
- } );
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Private methods
- */
- /**
- * Add a new button to the key press listener
- * @param {object} conf Resolved button configuration object
- * @private
- */
- _addKey: function ( conf )
- {
- if ( conf.key ) {
- this.s.listenKeys += $.isPlainObject( conf.key ) ?
- conf.key.key :
- conf.key;
- }
- },
- /**
- * Insert the buttons into the container. Call without parameters!
- * @param {node} [container] Recursive only - Insert point
- * @param {array} [buttons] Recursive only - Buttons array
- * @private
- */
- _draw: function ( container, buttons )
- {
- if ( ! container ) {
- container = this.dom.container;
- buttons = this.s.buttons;
- }
- container.children().detach();
- for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
- container.append( buttons[i].inserter );
- container.append( ' ' );
- if ( buttons[i].buttons && buttons[i].buttons.length ) {
- this._draw( buttons[i].collection, buttons[i].buttons );
- }
- }
- },
- /**
- * Create buttons from an array of buttons
- * @param {array} attachTo Buttons array to attach to
- * @param {object} button Button definition
- * @param {boolean} inCollection true if the button is in a collection
- * @private
- */
- _expandButton: function ( attachTo, button, inCollection, attachPoint )
- {
- var dt = this.s.dt;
- var buttonCounter = 0;
- var buttons = ! $.isArray( button ) ?
- [ button ] :
- button;
- for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
- var conf = this._resolveExtends( buttons[i] );
- if ( ! conf ) {
- continue;
- }
- // If the configuration is an array, then expand the buttons at this
- // point
- if ( $.isArray( conf ) ) {
- this._expandButton( attachTo, conf, inCollection, attachPoint );
- continue;
- }
- var built = this._buildButton( conf, inCollection );
- if ( ! built ) {
- continue;
- }
- if ( attachPoint !== undefined ) {
- attachTo.splice( attachPoint, 0, built );
- attachPoint++;
- }
- else {
- attachTo.push( built );
- }
- if ( built.conf.buttons ) {
- var collectionDom = this.c.dom.collection;
- built.collection = $('<'+collectionDom.tag+'/>')
- .addClass( collectionDom.className )
- .attr( 'role', 'menu' ) ;
- built.conf._collection = built.collection;
- this._expandButton( built.buttons, built.conf.buttons, true, attachPoint );
- }
- // init call is made here, rather than buildButton as it needs to
- // be selectable, and for that it needs to be in the buttons array
- if ( conf.init ) {
- conf.init.call( dt.button( built.node ), dt, $(built.node), conf );
- }
- buttonCounter++;
- }
- },
- /**
- * Create an individual button
- * @param {object} config Resolved button configuration
- * @param {boolean} inCollection `true` if a collection button
- * @return {jQuery} Created button node (jQuery)
- * @private
- */
- _buildButton: function ( config, inCollection )
- {
- var buttonDom = this.c.dom.button;
- var linerDom = this.c.dom.buttonLiner;
- var collectionDom = this.c.dom.collection;
- var dt = this.s.dt;
- var text = function ( opt ) {
- return typeof opt === 'function' ?
- opt( dt, button, config ) :
- opt;
- };
- if ( inCollection && collectionDom.button ) {
- buttonDom = collectionDom.button;
- }
- if ( inCollection && collectionDom.buttonLiner ) {
- linerDom = collectionDom.buttonLiner;
- }
- // Make sure that the button is available based on whatever requirements
- // it has. For example, Flash buttons require Flash
- if ( config.available && ! config.available( dt, config ) ) {
- return false;
- }
- var action = function ( e, dt, button, config ) {
- config.action.call( dt.button( button ), e, dt, button, config );
- $(dt.table().node()).triggerHandler( 'buttons-action.dt', [
- dt.button( button ), dt, button, config
- ] );
- };
- var tag = config.tag || buttonDom.tag;
- var clickBlurs = config.clickBlurs === undefined ? true : config.clickBlurs
- var button = $('<'+tag+'/>')
- .addClass( buttonDom.className )
- .attr( 'tabindex', this.s.dt.settings()[0].iTabIndex )
- .attr( 'aria-controls', this.s.dt.table().node().id )
- .on( 'click.dtb', function (e) {
- e.preventDefault();
- if ( ! button.hasClass( buttonDom.disabled ) && config.action ) {
- action( e, dt, button, config );
- }
- if( clickBlurs ) {
- button.blur();
- }
- } )
- .on( 'keyup.dtb', function (e) {
- if ( e.keyCode === 13 ) {
- if ( ! button.hasClass( buttonDom.disabled ) && config.action ) {
- action( e, dt, button, config );
- }
- }
- } );
- // Make `a` tags act like a link
- if ( tag.toLowerCase() === 'a' ) {
- button.attr( 'href', '#' );
- }
- // Button tags should have `type=button` so they don't have any default behaviour
- if ( tag.toLowerCase() === 'button' ) {
- button.attr( 'type', 'button' );
- }
- if ( linerDom.tag ) {
- var liner = $('<'+linerDom.tag+'/>')
- .html( text( config.text ) )
- .addClass( linerDom.className );
- if ( linerDom.tag.toLowerCase() === 'a' ) {
- liner.attr( 'href', '#' );
- }
- button.append( liner );
- }
- else {
- button.html( text( config.text ) );
- }
- if ( config.enabled === false ) {
- button.addClass( buttonDom.disabled );
- }
- if ( config.className ) {
- button.addClass( config.className );
- }
- if ( config.titleAttr ) {
- button.attr( 'title', text( config.titleAttr ) );
- }
- if ( config.attr ) {
- button.attr( config.attr );
- }
- if ( ! config.namespace ) {
- config.namespace = '.dt-button-'+(_buttonCounter++);
- }
- var buttonContainer = this.c.dom.buttonContainer;
- var inserter;
- if ( buttonContainer && buttonContainer.tag ) {
- inserter = $('<'+buttonContainer.tag+'/>')
- .addClass( buttonContainer.className )
- .append( button );
- }
- else {
- inserter = button;
- }
- this._addKey( config );
- // Style integration callback for DOM manipulation
- // Note that this is _not_ documented. It is currently
- // for style integration only
- if( this.c.buttonCreated ) {
- inserter = this.c.buttonCreated( config, inserter );
- }
- return {
- conf: config,
- node: button.get(0),
- inserter: inserter,
- buttons: [],
- inCollection: inCollection,
- collection: null
- };
- },
- /**
- * Get the button object from a node (recursive)
- * @param {node} node Button node
- * @param {array} [buttons] Button array, uses base if not defined
- * @return {object} Button object
- * @private
- */
- _nodeToButton: function ( node, buttons )
- {
- if ( ! buttons ) {
- buttons = this.s.buttons;
- }
- for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
- if ( buttons[i].node === node ) {
- return buttons[i];
- }
- if ( buttons[i].buttons.length ) {
- var ret = this._nodeToButton( node, buttons[i].buttons );
- if ( ret ) {
- return ret;
- }
- }
- }
- },
- /**
- * Get container array for a button from a button node (recursive)
- * @param {node} node Button node
- * @param {array} [buttons] Button array, uses base if not defined
- * @return {array} Button's host array
- * @private
- */
- _nodeToHost: function ( node, buttons )
- {
- if ( ! buttons ) {
- buttons = this.s.buttons;
- }
- for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
- if ( buttons[i].node === node ) {
- return buttons;
- }
- if ( buttons[i].buttons.length ) {
- var ret = this._nodeToHost( node, buttons[i].buttons );
- if ( ret ) {
- return ret;
- }
- }
- }
- },
- /**
- * Handle a key press - determine if any button's key configured matches
- * what was typed and trigger the action if so.
- * @param {string} character The character pressed
- * @param {object} e Key event that triggered this call
- * @private
- */
- _keypress: function ( character, e )
- {
- // Check if this button press already activated on another instance of Buttons
- if ( e._buttonsHandled ) {
- return;
- }
- var run = function ( conf, node ) {
- if ( ! conf.key ) {
- return;
- }
- if ( conf.key === character ) {
- e._buttonsHandled = true;
- $(node).click();
- }
- else if ( $.isPlainObject( conf.key ) ) {
- if ( conf.key.key !== character ) {
- return;
- }
- if ( conf.key.shiftKey && ! e.shiftKey ) {
- return;
- }
- if ( conf.key.altKey && ! e.altKey ) {
- return;
- }
- if ( conf.key.ctrlKey && ! e.ctrlKey ) {
- return;
- }
- if ( conf.key.metaKey && ! e.metaKey ) {
- return;
- }
- // Made it this far - it is good
- e._buttonsHandled = true;
- $(node).click();
- }
- };
- var recurse = function ( a ) {
- for ( var i=0, ien=a.length ; i<ien ; i++ ) {
- run( a[i].conf, a[i].node );
- if ( a[i].buttons.length ) {
- recurse( a[i].buttons );
- }
- }
- };
- recurse( this.s.buttons );
- },
- /**
- * Remove a key from the key listener for this instance (to be used when a
- * button is removed)
- * @param {object} conf Button configuration
- * @private
- */
- _removeKey: function ( conf )
- {
- if ( conf.key ) {
- var character = $.isPlainObject( conf.key ) ?
- conf.key.key :
- conf.key;
- // Remove only one character, as multiple buttons could have the
- // same listening key
- var a = this.s.listenKeys.split('');
- var idx = $.inArray( character, a );
- a.splice( idx, 1 );
- this.s.listenKeys = a.join('');
- }
- },
- /**
- * Resolve a button configuration
- * @param {string|function|object} conf Button config to resolve
- * @return {object} Button configuration
- * @private
- */
- _resolveExtends: function ( conf )
- {
- var dt = this.s.dt;
- var i, ien;
- var toConfObject = function ( base ) {
- var loop = 0;
- // Loop until we have resolved to a button configuration, or an
- // array of button configurations (which will be iterated
- // separately)
- while ( ! $.isPlainObject(base) && ! $.isArray(base) ) {
- if ( base === undefined ) {
- return;
- }
- if ( typeof base === 'function' ) {
- base = base( dt, conf );
- if ( ! base ) {
- return false;
- }
- }
- else if ( typeof base === 'string' ) {
- if ( ! _dtButtons[ base ] ) {
- throw 'Unknown button type: '+base;
- }
- base = _dtButtons[ base ];
- }
- loop++;
- if ( loop > 30 ) {
- // Protect against misconfiguration killing the browser
- throw 'Buttons: Too many iterations';
- }
- }
- return $.isArray( base ) ?
- base :
- $.extend( {}, base );
- };
- conf = toConfObject( conf );
- while ( conf && conf.extend ) {
- // Use `toConfObject` in case the button definition being extended
- // is itself a string or a function
- if ( ! _dtButtons[ conf.extend ] ) {
- throw 'Cannot extend unknown button type: '+conf.extend;
- }
- var objArray = toConfObject( _dtButtons[ conf.extend ] );
- if ( $.isArray( objArray ) ) {
- return objArray;
- }
- else if ( ! objArray ) {
- // This is a little brutal as it might be possible to have a
- // valid button without the extend, but if there is no extend
- // then the host button would be acting in an undefined state
- return false;
- }
- // Stash the current class name
- var originalClassName = objArray.className;
- conf = $.extend( {}, objArray, conf );
- // The extend will have overwritten the original class name if the
- // `conf` object also assigned a class, but we want to concatenate
- // them so they are list that is combined from all extended buttons
- if ( originalClassName && conf.className !== originalClassName ) {
- conf.className = originalClassName+' '+conf.className;
- }
- // Buttons to be added to a collection -gives the ability to define
- // if buttons should be added to the start or end of a collection
- var postfixButtons = conf.postfixButtons;
- if ( postfixButtons ) {
- if ( ! conf.buttons ) {
- conf.buttons = [];
- }
- for ( i=0, ien=postfixButtons.length ; i<ien ; i++ ) {
- conf.buttons.push( postfixButtons[i] );
- }
- conf.postfixButtons = null;
- }
- var prefixButtons = conf.prefixButtons;
- if ( prefixButtons ) {
- if ( ! conf.buttons ) {
- conf.buttons = [];
- }
- for ( i=0, ien=prefixButtons.length ; i<ien ; i++ ) {
- conf.buttons.splice( i, 0, prefixButtons[i] );
- }
- conf.prefixButtons = null;
- }
- // Although we want the `conf` object to overwrite almost all of
- // the properties of the object being extended, the `extend`
- // property should come from the object being extended
- conf.extend = objArray.extend;
- }
- return conf;
- }
- } );
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Statics
- */
- /**
- * Show / hide a background layer behind a collection
- * @param {boolean} Flag to indicate if the background should be shown or
- * hidden
- * @param {string} Class to assign to the background
- * @static
- */
- Buttons.background = function ( show, className, fade, insertPoint ) {
- if ( fade === undefined ) {
- fade = 400;
- }
- if ( ! insertPoint ) {
- insertPoint = document.body;
- }
- if ( show ) {
- $('<div/>')
- .addClass( className )
- .css( 'display', 'none' )
- .insertAfter( insertPoint )
- .stop()
- .fadeIn( fade );
- }
- else {
- $('div.'+className)
- .stop()
- .fadeOut( fade, function () {
- $(this)
- .removeClass( className )
- .remove();
- } );
- }
- };
- /**
- * Instance selector - select Buttons instances based on an instance selector
- * value from the buttons assigned to a DataTable. This is only useful if
- * multiple instances are attached to a DataTable.
- * @param {string|int|array} Instance selector - see `instance-selector`
- * documentation on the DataTables site
- * @param {array} Button instance array that was attached to the DataTables
- * settings object
- * @return {array} Buttons instances
- * @static
- */
- Buttons.instanceSelector = function ( group, buttons )
- {
- if ( ! group ) {
- return $.map( buttons, function ( v ) {
- return v.inst;
- } );
- }
- var ret = [];
- var names = $.map( buttons, function ( v ) {
- return v.name;
- } );
- // Flatten the group selector into an array of single options
- var process = function ( input ) {
- if ( $.isArray( input ) ) {
- for ( var i=0, ien=input.length ; i<ien ; i++ ) {
- process( input[i] );
- }
- return;
- }
- if ( typeof input === 'string' ) {
- if ( input.indexOf( ',' ) !== -1 ) {
- // String selector, list of names
- process( input.split(',') );
- }
- else {
- // String selector individual name
- var idx = $.inArray( $.trim(input), names );
- if ( idx !== -1 ) {
- ret.push( buttons[ idx ].inst );
- }
- }
- }
- else if ( typeof input === 'number' ) {
- // Index selector
- ret.push( buttons[ input ].inst );
- }
- };
-
- process( group );
- return ret;
- };
- /**
- * Button selector - select one or more buttons from a selector input so some
- * operation can be performed on them.
- * @param {array} Button instances array that the selector should operate on
- * @param {string|int|node|jQuery|array} Button selector - see
- * `button-selector` documentation on the DataTables site
- * @return {array} Array of objects containing `inst` and `idx` properties of
- * the selected buttons so you know which instance each button belongs to.
- * @static
- */
- Buttons.buttonSelector = function ( insts, selector )
- {
- var ret = [];
- var nodeBuilder = function ( a, buttons, baseIdx ) {
- var button;
- var idx;
- for ( var i=0, ien=buttons.length ; i<ien ; i++ ) {
- button = buttons[i];
- if ( button ) {
- idx = baseIdx !== undefined ?
- baseIdx+i :
- i+'';
- a.push( {
- node: button.node,
- name: button.conf.name,
- idx: idx
- } );
- if ( button.buttons ) {
- nodeBuilder( a, button.buttons, idx+'-' );
- }
- }
- }
- };
- var run = function ( selector, inst ) {
- var i, ien;
- var buttons = [];
- nodeBuilder( buttons, inst.s.buttons );
- var nodes = $.map( buttons, function (v) {
- return v.node;
- } );
- if ( $.isArray( selector ) || selector instanceof $ ) {
- for ( i=0, ien=selector.length ; i<ien ; i++ ) {
- run( selector[i], inst );
- }
- return;
- }
- if ( selector === null || selector === undefined || selector === '*' ) {
- // Select all
- for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
- ret.push( {
- inst: inst,
- node: buttons[i].node
- } );
- }
- }
- else if ( typeof selector === 'number' ) {
- // Main button index selector
- ret.push( {
- inst: inst,
- node: inst.s.buttons[ selector ].node
- } );
- }
- else if ( typeof selector === 'string' ) {
- if ( selector.indexOf( ',' ) !== -1 ) {
- // Split
- var a = selector.split(',');
- for ( i=0, ien=a.length ; i<ien ; i++ ) {
- run( $.trim(a[i]), inst );
- }
- }
- else if ( selector.match( /^\d+(\-\d+)*$/ ) ) {
- // Sub-button index selector
- var indexes = $.map( buttons, function (v) {
- return v.idx;
- } );
- ret.push( {
- inst: inst,
- node: buttons[ $.inArray( selector, indexes ) ].node
- } );
- }
- else if ( selector.indexOf( ':name' ) !== -1 ) {
- // Button name selector
- var name = selector.replace( ':name', '' );
- for ( i=0, ien=buttons.length ; i<ien ; i++ ) {
- if ( buttons[i].name === name ) {
- ret.push( {
- inst: inst,
- node: buttons[i].node
- } );
- }
- }
- }
- else {
- // jQuery selector on the nodes
- $( nodes ).filter( selector ).each( function () {
- ret.push( {
- inst: inst,
- node: this
- } );
- } );
- }
- }
- else if ( typeof selector === 'object' && selector.nodeName ) {
- // Node selector
- var idx = $.inArray( selector, nodes );
- if ( idx !== -1 ) {
- ret.push( {
- inst: inst,
- node: nodes[ idx ]
- } );
- }
- }
- };
- for ( var i=0, ien=insts.length ; i<ien ; i++ ) {
- var inst = insts[i];
- run( selector, inst );
- }
- return ret;
- };
- /**
- * Buttons defaults. For full documentation, please refer to the docs/option
- * directory or the DataTables site.
- * @type {Object}
- * @static
- */
- Buttons.defaults = {
- buttons: [ 'copy', 'excel', 'csv', 'pdf', 'print' ],
- name: 'main',
- tabIndex: 0,
- dom: {
- container: {
- tag: 'div',
- className: 'dt-buttons'
- },
- collection: {
- tag: 'div',
- className: 'dt-button-collection'
- },
- button: {
- // Flash buttons will not work with `<button>` in IE - it has to be `<a>`
- tag: 'ActiveXObject' in window ?
- 'a' :
- 'button',
- className: 'dt-button',
- active: 'active',
- disabled: 'disabled'
- },
- buttonLiner: {
- tag: 'span',
- className: ''
- }
- }
- };
- /**
- * Version information
- * @type {string}
- * @static
- */
- Buttons.version = '1.5.6';
- $.extend( _dtButtons, {
- collection: {
- text: function ( dt ) {
- return dt.i18n( 'buttons.collection', 'Collection' );
- },
- className: 'buttons-collection',
- init: function ( dt, button, config ) {
- button.attr( 'aria-expanded', false );
- },
- action: function ( e, dt, button, config ) {
- var close = function () {
- dt.buttons( '[aria-haspopup="true"][aria-expanded="true"]' ).nodes().each( function() {
- var collection = $(this).siblings('.dt-button-collection');
- if ( collection.length ) {
- collection.stop().fadeOut( config.fade, function () {
- collection.detach();
- } );
- }
- $(this).attr( 'aria-expanded', 'false' );
- });
- $('div.dt-button-background').off( 'click.dtb-collection' );
- Buttons.background( false, config.backgroundClassName, config.fade, insertPoint );
- $('body').off( '.dtb-collection' );
- dt.off( 'buttons-action.b-internal' );
- };
- var wasExpanded = button.attr( 'aria-expanded' ) === 'true';
- close();
- if (!wasExpanded) {
- var host = button;
- var collectionParent = $(button).parents('div.dt-button-collection');
- var hostPosition = host.position();
- var tableContainer = $( dt.table().container() );
- var multiLevel = false;
- var insertPoint = host;
- button.attr( 'aria-expanded', 'true' );
- // Remove any old collection
- if ( collectionParent.length ) {
- multiLevel = $('.dt-button-collection').position();
- insertPoint = collectionParent;
- $('body').trigger( 'click.dtb-collection' );
- }
- if ( insertPoint.parents('body')[0] !== document.body ) {
- insertPoint = document.body.lastChild;
- }
- config._collection.find('.dt-button-collection-title').remove();
- config._collection.prepend('<div class="dt-button-collection-title">'+config.collectionTitle+'</div>');
- config._collection
- .addClass( config.collectionLayout )
- .css( 'display', 'none' )
- .insertAfter( insertPoint )
- .stop()
- .fadeIn( config.fade );
- var position = config._collection.css( 'position' );
- if ( multiLevel && position === 'absolute' ) {
- config._collection.css( {
- top: multiLevel.top,
- left: multiLevel.left
- } );
- }
- else if ( position === 'absolute' ) {
- config._collection.css( {
- top: hostPosition.top + host.outerHeight(),
- left: hostPosition.left
- } );
- // calculate overflow when positioned beneath
- var tableBottom = tableContainer.offset().top + tableContainer.height();
- var listBottom = hostPosition.top + host.outerHeight() + config._collection.outerHeight();
- var bottomOverflow = listBottom - tableBottom;
- // calculate overflow when positioned above
- var listTop = hostPosition.top - config._collection.outerHeight();
- var tableTop = tableContainer.offset().top;
- var topOverflow = tableTop - listTop;
- // if bottom overflow is larger, move to the top because it fits better, or if dropup is requested
- if (bottomOverflow > topOverflow || config.dropup) {
- config._collection.css( 'top', hostPosition.top - config._collection.outerHeight() - 5);
- }
- // Right alignment is enabled on a class, e.g. bootstrap:
- // $.fn.dataTable.Buttons.defaults.dom.collection.className += " dropdown-menu-right";
- if ( config._collection.hasClass( config.rightAlignClassName ) ) {
- config._collection.css( 'left', hostPosition.left + host.outerWidth() - config._collection.outerWidth() );
- }
- // Right alignment in table container
- var listRight = hostPosition.left + config._collection.outerWidth();
- var tableRight = tableContainer.offset().left + tableContainer.width();
- if ( listRight > tableRight ) {
- config._collection.css( 'left', hostPosition.left - ( listRight - tableRight ) );
- }
- // Right alignment to window
- var listOffsetRight = host.offset().left + config._collection.outerWidth();
- if ( listOffsetRight > $(window).width() ) {
- config._collection.css( 'left', hostPosition.left - (listOffsetRight-$(window).width()) );
- }
- }
- else {
- // Fix position - centre on screen
- var top = config._collection.height() / 2;
- if ( top > $(window).height() / 2 ) {
- top = $(window).height() / 2;
- }
- config._collection.css( 'marginTop', top*-1 );
- }
- if ( config.background ) {
- Buttons.background( true, config.backgroundClassName, config.fade, insertPoint );
- }
- // Need to break the 'thread' for the collection button being
- // activated by a click - it would also trigger this event
- setTimeout( function () {
- // This is bonkers, but if we don't have a click listener on the
- // background element, iOS Safari will ignore the body click
- // listener below. An empty function here is all that is
- // required to make it work...
- $('div.dt-button-background').on( 'click.dtb-collection', function () {} );
- $('body')
- .on( 'click.dtb-collection', function (e) {
- // andSelf is deprecated in jQ1.8, but we want 1.7 compat
- var back = $.fn.addBack ? 'addBack' : 'andSelf';
- if ( ! $(e.target).parents()[back]().filter( config._collection ).length ) {
- close();
- }
- } )
- .on( 'keyup.dtb-collection', function (e) {
- if ( e.keyCode === 27 ) {
- close();
- }
- } );
- if ( config.autoClose ) {
- dt.on( 'buttons-action.b-internal', function () {
- close();
- } );
- }
- }, 10 );
- }
- },
- background: true,
- collectionLayout: '',
- collectionTitle: '',
- backgroundClassName: 'dt-button-background',
- rightAlignClassName: 'dt-button-right',
- autoClose: false,
- fade: 400,
- attr: {
- 'aria-haspopup': true
- }
- },
- copy: function ( dt, conf ) {
- if ( _dtButtons.copyHtml5 ) {
- return 'copyHtml5';
- }
- if ( _dtButtons.copyFlash && _dtButtons.copyFlash.available( dt, conf ) ) {
- return 'copyFlash';
- }
- },
- csv: function ( dt, conf ) {
- // Common option that will use the HTML5 or Flash export buttons
- if ( _dtButtons.csvHtml5 && _dtButtons.csvHtml5.available( dt, conf ) ) {
- return 'csvHtml5';
- }
- if ( _dtButtons.csvFlash && _dtButtons.csvFlash.available( dt, conf ) ) {
- return 'csvFlash';
- }
- },
- excel: function ( dt, conf ) {
- // Common option that will use the HTML5 or Flash export buttons
- if ( _dtButtons.excelHtml5 && _dtButtons.excelHtml5.available( dt, conf ) ) {
- return 'excelHtml5';
- }
- if ( _dtButtons.excelFlash && _dtButtons.excelFlash.available( dt, conf ) ) {
- return 'excelFlash';
- }
- },
- pdf: function ( dt, conf ) {
- // Common option that will use the HTML5 or Flash export buttons
- if ( _dtButtons.pdfHtml5 && _dtButtons.pdfHtml5.available( dt, conf ) ) {
- return 'pdfHtml5';
- }
- if ( _dtButtons.pdfFlash && _dtButtons.pdfFlash.available( dt, conf ) ) {
- return 'pdfFlash';
- }
- },
- pageLength: function ( dt ) {
- var lengthMenu = dt.settings()[0].aLengthMenu;
- var vals = $.isArray( lengthMenu[0] ) ? lengthMenu[0] : lengthMenu;
- var lang = $.isArray( lengthMenu[0] ) ? lengthMenu[1] : lengthMenu;
- var text = function ( dt ) {
- return dt.i18n( 'buttons.pageLength', {
- "-1": 'Show all rows',
- _: 'Show %d rows'
- }, dt.page.len() );
- };
- return {
- extend: 'collection',
- text: text,
- className: 'buttons-page-length',
- autoClose: true,
- buttons: $.map( vals, function ( val, i ) {
- return {
- text: lang[i],
- className: 'button-page-length',
- action: function ( e, dt ) {
- dt.page.len( val ).draw();
- },
- init: function ( dt, node, conf ) {
- var that = this;
- var fn = function () {
- that.active( dt.page.len() === val );
- };
- dt.on( 'length.dt'+conf.namespace, fn );
- fn();
- },
- destroy: function ( dt, node, conf ) {
- dt.off( 'length.dt'+conf.namespace );
- }
- };
- } ),
- init: function ( dt, node, conf ) {
- var that = this;
- dt.on( 'length.dt'+conf.namespace, function () {
- that.text( conf.text );
- } );
- },
- destroy: function ( dt, node, conf ) {
- dt.off( 'length.dt'+conf.namespace );
- }
- };
- }
- } );
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables API
- *
- * For complete documentation, please refer to the docs/api directory or the
- * DataTables site
- */
- // Buttons group and individual button selector
- DataTable.Api.register( 'buttons()', function ( group, selector ) {
- // Argument shifting
- if ( selector === undefined ) {
- selector = group;
- group = undefined;
- }
- this.selector.buttonGroup = group;
- var res = this.iterator( true, 'table', function ( ctx ) {
- if ( ctx._buttons ) {
- return Buttons.buttonSelector(
- Buttons.instanceSelector( group, ctx._buttons ),
- selector
- );
- }
- }, true );
- res._groupSelector = group;
- return res;
- } );
- // Individual button selector
- DataTable.Api.register( 'button()', function ( group, selector ) {
- // just run buttons() and truncate
- var buttons = this.buttons( group, selector );
- if ( buttons.length > 1 ) {
- buttons.splice( 1, buttons.length );
- }
- return buttons;
- } );
- // Active buttons
- DataTable.Api.registerPlural( 'buttons().active()', 'button().active()', function ( flag ) {
- if ( flag === undefined ) {
- return this.map( function ( set ) {
- return set.inst.active( set.node );
- } );
- }
- return this.each( function ( set ) {
- set.inst.active( set.node, flag );
- } );
- } );
- // Get / set button action
- DataTable.Api.registerPlural( 'buttons().action()', 'button().action()', function ( action ) {
- if ( action === undefined ) {
- return this.map( function ( set ) {
- return set.inst.action( set.node );
- } );
- }
- return this.each( function ( set ) {
- set.inst.action( set.node, action );
- } );
- } );
- // Enable / disable buttons
- DataTable.Api.register( ['buttons().enable()', 'button().enable()'], function ( flag ) {
- return this.each( function ( set ) {
- set.inst.enable( set.node, flag );
- } );
- } );
- // Disable buttons
- DataTable.Api.register( ['buttons().disable()', 'button().disable()'], function () {
- return this.each( function ( set ) {
- set.inst.disable( set.node );
- } );
- } );
- // Get button nodes
- DataTable.Api.registerPlural( 'buttons().nodes()', 'button().node()', function () {
- var jq = $();
- // jQuery will automatically reduce duplicates to a single entry
- $( this.each( function ( set ) {
- jq = jq.add( set.inst.node( set.node ) );
- } ) );
- return jq;
- } );
- // Get / set button processing state
- DataTable.Api.registerPlural( 'buttons().processing()', 'button().processing()', function ( flag ) {
- if ( flag === undefined ) {
- return this.map( function ( set ) {
- return set.inst.processing( set.node );
- } );
- }
- return this.each( function ( set ) {
- set.inst.processing( set.node, flag );
- } );
- } );
- // Get / set button text (i.e. the button labels)
- DataTable.Api.registerPlural( 'buttons().text()', 'button().text()', function ( label ) {
- if ( label === undefined ) {
- return this.map( function ( set ) {
- return set.inst.text( set.node );
- } );
- }
- return this.each( function ( set ) {
- set.inst.text( set.node, label );
- } );
- } );
- // Trigger a button's action
- DataTable.Api.registerPlural( 'buttons().trigger()', 'button().trigger()', function () {
- return this.each( function ( set ) {
- set.inst.node( set.node ).trigger( 'click' );
- } );
- } );
- // Get the container elements
- DataTable.Api.registerPlural( 'buttons().containers()', 'buttons().container()', function () {
- var jq = $();
- var groupSelector = this._groupSelector;
- // We need to use the group selector directly, since if there are no buttons
- // the result set will be empty
- this.iterator( true, 'table', function ( ctx ) {
- if ( ctx._buttons ) {
- var insts = Buttons.instanceSelector( groupSelector, ctx._buttons );
- for ( var i=0, ien=insts.length ; i<ien ; i++ ) {
- jq = jq.add( insts[i].container() );
- }
- }
- } );
- return jq;
- } );
- // Add a new button
- DataTable.Api.register( 'button().add()', function ( idx, conf ) {
- var ctx = this.context;
- // Don't use `this` as it could be empty - select the instances directly
- if ( ctx.length ) {
- var inst = Buttons.instanceSelector( this._groupSelector, ctx[0]._buttons );
- if ( inst.length ) {
- inst[0].add( conf, idx );
- }
- }
- return this.button( this._groupSelector, idx );
- } );
- // Destroy the button sets selected
- DataTable.Api.register( 'buttons().destroy()', function () {
- this.pluck( 'inst' ).unique().each( function ( inst ) {
- inst.destroy();
- } );
- return this;
- } );
- // Remove a button
- DataTable.Api.registerPlural( 'buttons().remove()', 'buttons().remove()', function () {
- this.each( function ( set ) {
- set.inst.remove( set.node );
- } );
- return this;
- } );
- // Information box that can be used by buttons
- var _infoTimer;
- DataTable.Api.register( 'buttons.info()', function ( title, message, time ) {
- var that = this;
- if ( title === false ) {
- $('#datatables_buttons_info').fadeOut( function () {
- $(this).remove();
- } );
- clearTimeout( _infoTimer );
- _infoTimer = null;
- return this;
- }
- if ( _infoTimer ) {
- clearTimeout( _infoTimer );
- }
- if ( $('#datatables_buttons_info').length ) {
- $('#datatables_buttons_info').remove();
- }
- title = title ? '<h2>'+title+'</h2>' : '';
- $('<div id="datatables_buttons_info" class="dt-button-info"/>')
- .html( title )
- .append( $('<div/>')[ typeof message === 'string' ? 'html' : 'append' ]( message ) )
- .css( 'display', 'none' )
- .appendTo( 'body' )
- .fadeIn();
- if ( time !== undefined && time !== 0 ) {
- _infoTimer = setTimeout( function () {
- that.buttons.info( false );
- }, time );
- }
- return this;
- } );
- // Get data from the table for export - this is common to a number of plug-in
- // buttons so it is included in the Buttons core library
- DataTable.Api.register( 'buttons.exportData()', function ( options ) {
- if ( this.context.length ) {
- return _exportData( new DataTable.Api( this.context[0] ), options );
- }
- } );
- // Get information about the export that is common to many of the export data
- // types (DRY)
- DataTable.Api.register( 'buttons.exportInfo()', function ( conf ) {
- if ( ! conf ) {
- conf = {};
- }
- return {
- filename: _filename( conf ),
- title: _title( conf ),
- messageTop: _message(this, conf.message || conf.messageTop, 'top'),
- messageBottom: _message(this, conf.messageBottom, 'bottom')
- };
- } );
- /**
- * Get the file name for an exported file.
- *
- * @param {object} config Button configuration
- * @param {boolean} incExtension Include the file name extension
- */
- var _filename = function ( config )
- {
- // Backwards compatibility
- var filename = config.filename === '*' && config.title !== '*' && config.title !== undefined && config.title !== null && config.title !== '' ?
- config.title :
- config.filename;
- if ( typeof filename === 'function' ) {
- filename = filename();
- }
- if ( filename === undefined || filename === null ) {
- return null;
- }
- if ( filename.indexOf( '*' ) !== -1 ) {
- filename = $.trim( filename.replace( '*', $('head > title').text() ) );
- }
- // Strip characters which the OS will object to
- filename = filename.replace(/[^a-zA-Z0-9_\u00A1-\uFFFF\.,\-_ !\(\)]/g, "");
- var extension = _stringOrFunction( config.extension );
- if ( ! extension ) {
- extension = '';
- }
- return filename + extension;
- };
- /**
- * Simply utility method to allow parameters to be given as a function
- *
- * @param {undefined|string|function} option Option
- * @return {null|string} Resolved value
- */
- var _stringOrFunction = function ( option )
- {
- if ( option === null || option === undefined ) {
- return null;
- }
- else if ( typeof option === 'function' ) {
- return option();
- }
- return option;
- };
- /**
- * Get the title for an exported file.
- *
- * @param {object} config Button configuration
- */
- var _title = function ( config )
- {
- var title = _stringOrFunction( config.title );
- return title === null ?
- null : title.indexOf( '*' ) !== -1 ?
- title.replace( '*', $('head > title').text() || 'Exported data' ) :
- title;
- };
- var _message = function ( dt, option, position )
- {
- var message = _stringOrFunction( option );
- if ( message === null ) {
- return null;
- }
- var caption = $('caption', dt.table().container()).eq(0);
- if ( message === '*' ) {
- var side = caption.css( 'caption-side' );
- if ( side !== position ) {
- return null;
- }
- return caption.length ?
- caption.text() :
- '';
- }
- return message;
- };
- var _exportTextarea = $('<textarea/>')[0];
- var _exportData = function ( dt, inOpts )
- {
- var config = $.extend( true, {}, {
- rows: null,
- columns: '',
- modifier: {
- search: 'applied',
- order: 'applied'
- },
- orthogonal: 'display',
- stripHtml: true,
- stripNewlines: true,
- decodeEntities: true,
- trim: true,
- format: {
- header: function ( d ) {
- return strip( d );
- },
- footer: function ( d ) {
- return strip( d );
- },
- body: function ( d ) {
- return strip( d );
- }
- },
- customizeData: null
- }, inOpts );
- var strip = function ( str ) {
- if ( typeof str !== 'string' ) {
- return str;
- }
- // Always remove script tags
- str = str.replace( /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, '' );
- // Always remove comments
- str = str.replace( /<!\-\-.*?\-\->/g, '' );
- if ( config.stripHtml ) {
- str = str.replace( /<[^>]*>/g, '' );
- }
- if ( config.trim ) {
- str = str.replace( /^\s+|\s+$/g, '' );
- }
- if ( config.stripNewlines ) {
- str = str.replace( /\n/g, ' ' );
- }
- if ( config.decodeEntities ) {
- _exportTextarea.innerHTML = str;
- str = _exportTextarea.value;
- }
- return str;
- };
- var header = dt.columns( config.columns ).indexes().map( function (idx) {
- var el = dt.column( idx ).header();
- return config.format.header( el.innerHTML, idx, el );
- } ).toArray();
- var footer = dt.table().footer() ?
- dt.columns( config.columns ).indexes().map( function (idx) {
- var el = dt.column( idx ).footer();
- return config.format.footer( el ? el.innerHTML : '', idx, el );
- } ).toArray() :
- null;
-
- // If Select is available on this table, and any rows are selected, limit the export
- // to the selected rows. If no rows are selected, all rows will be exported. Specify
- // a `selected` modifier to control directly.
- var modifier = $.extend( {}, config.modifier );
- if ( dt.select && typeof dt.select.info === 'function' && modifier.selected === undefined ) {
- if ( dt.rows( config.rows, $.extend( { selected: true }, modifier ) ).any() ) {
- $.extend( modifier, { selected: true } )
- }
- }
- var rowIndexes = dt.rows( config.rows, modifier ).indexes().toArray();
- var selectedCells = dt.cells( rowIndexes, config.columns );
- var cells = selectedCells
- .render( config.orthogonal )
- .toArray();
- var cellNodes = selectedCells
- .nodes()
- .toArray();
- var columns = header.length;
- var rows = columns > 0 ? cells.length / columns : 0;
- var body = [];
- var cellCounter = 0;
- for ( var i=0, ien=rows ; i<ien ; i++ ) {
- var row = [ columns ];
- for ( var j=0 ; j<columns ; j++ ) {
- row[j] = config.format.body( cells[ cellCounter ], i, j, cellNodes[ cellCounter ] );
- cellCounter++;
- }
- body[i] = row;
- }
- var data = {
- header: header,
- footer: footer,
- body: body
- };
- if ( config.customizeData ) {
- config.customizeData( data );
- }
- return data;
- };
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables interface
- */
- // Attach to DataTables objects for global access
- $.fn.dataTable.Buttons = Buttons;
- $.fn.DataTable.Buttons = Buttons;
- // DataTables creation - check if the buttons have been defined for this table,
- // they will have been if the `B` option was used in `dom`, otherwise we should
- // create the buttons instance here so they can be inserted into the document
- // using the API. Listen for `init` for compatibility with pre 1.10.10, but to
- // be removed in future.
- $(document).on( 'init.dt plugin-init.dt', function (e, settings) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- var opts = settings.oInit.buttons || DataTable.defaults.buttons;
- if ( opts && ! settings._buttons ) {
- new Buttons( settings, opts ).container();
- }
- } );
- function _init ( settings ) {
- var api = new DataTable.Api( settings );
- var opts = api.init().buttons || DataTable.defaults.buttons;
- return new Buttons( api, opts ).container();
- }
- // DataTables `dom` feature option
- DataTable.ext.feature.push( {
- fnInit: _init,
- cFeature: "B"
- } );
- // DataTables 2 layout feature
- if ( DataTable.ext.features ) {
- DataTable.ext.features.register( 'buttons', _init );
- }
- return Buttons;
- }));
- /*! Bootstrap integration for DataTables' Buttons
- * ©2016 SpryMedia Ltd - datatables.net/license
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net-bs4', 'datatables.net-buttons'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net-bs4')(root, $).$;
- }
- if ( ! $.fn.dataTable.Buttons ) {
- require('datatables.net-buttons')(root, $);
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- $.extend( true, DataTable.Buttons.defaults, {
- dom: {
- container: {
- className: 'dt-buttons btn-group'
- },
- button: {
- className: 'btn btn-secondary'
- },
- collection: {
- tag: 'div',
- className: 'dt-button-collection dropdown-menu',
- button: {
- tag: 'a',
- className: 'dt-button dropdown-item',
- active: 'active',
- disabled: 'disabled'
- }
- }
- },
- buttonCreated: function ( config, button ) {
- return config.buttons ?
- $('<div class="btn-group"/>').append(button) :
- button;
- }
- } );
- DataTable.ext.buttons.collection.className += ' dropdown-toggle';
- DataTable.ext.buttons.collection.rightAlignClassName = 'dropdown-menu-right';
- return DataTable.Buttons;
- }));
- /*!
- * HTML5 export buttons for Buttons and DataTables.
- * 2016 SpryMedia Ltd - datatables.net/license
- *
- * FileSaver.js (1.3.3) - MIT license
- * Copyright © 2016 Eli Grey - http://eligrey.com
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net', 'datatables.net-buttons'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $, jszip, pdfmake) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- if ( ! $.fn.dataTable.Buttons ) {
- require('datatables.net-buttons')(root, $);
- }
- return factory( $, root, root.document, jszip, pdfmake );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, jszip, pdfmake, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- // Allow the constructor to pass in JSZip and PDFMake from external requires.
- // Otherwise, use globally defined variables, if they are available.
- function _jsZip () {
- return jszip || window.JSZip;
- }
- function _pdfMake () {
- return pdfmake || window.pdfMake;
- }
- DataTable.Buttons.pdfMake = function (_) {
- if ( ! _ ) {
- return _pdfMake();
- }
- pdfmake = m_ake;
- }
- DataTable.Buttons.jszip = function (_) {
- if ( ! _ ) {
- return _jsZip();
- }
- jszip = _;
- }
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * FileSaver.js dependency
- */
- /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
- var _saveAs = (function(view) {
- "use strict";
- // IE <10 is explicitly unsupported
- if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
- return;
- }
- var
- doc = view.document
- // only get URL when necessary in case Blob.js hasn't overridden it yet
- , get_URL = function() {
- return view.URL || view.webkitURL || view;
- }
- , save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
- , can_use_save_link = "download" in save_link
- , click = function(node) {
- var event = new MouseEvent("click");
- node.dispatchEvent(event);
- }
- , is_safari = /constructor/i.test(view.HTMLElement) || view.safari
- , is_chrome_ios =/CriOS\/[\d]+/.test(navigator.userAgent)
- , throw_outside = function(ex) {
- (view.setImmediate || view.setTimeout)(function() {
- throw ex;
- }, 0);
- }
- , force_saveable_type = "application/octet-stream"
- // the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
- , arbitrary_revoke_timeout = 1000 * 40 // in ms
- , revoke = function(file) {
- var revoker = function() {
- if (typeof file === "string") { // file is an object URL
- get_URL().revokeObjectURL(file);
- } else { // file is a File
- file.remove();
- }
- };
- setTimeout(revoker, arbitrary_revoke_timeout);
- }
- , dispatch = function(filesaver, event_types, event) {
- event_types = [].concat(event_types);
- var i = event_types.length;
- while (i--) {
- var listener = filesaver["on" + event_types[i]];
- if (typeof listener === "function") {
- try {
- listener.call(filesaver, event || filesaver);
- } catch (ex) {
- throw_outside(ex);
- }
- }
- }
- }
- , auto_bom = function(blob) {
- // prepend BOM for UTF-8 XML and text/* types (including HTML)
- // note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
- if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
- return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type});
- }
- return blob;
- }
- , FileSaver = function(blob, name, no_auto_bom) {
- if (!no_auto_bom) {
- blob = auto_bom(blob);
- }
- // First try a.download, then web filesystem, then object URLs
- var
- filesaver = this
- , type = blob.type
- , force = type === force_saveable_type
- , object_url
- , dispatch_all = function() {
- dispatch(filesaver, "writestart progress write writeend".split(" "));
- }
- // on any filesys errors revert to saving with object URLs
- , fs_error = function() {
- if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
- // Safari doesn't allow downloading of blob urls
- var reader = new FileReader();
- reader.onloadend = function() {
- var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');
- var popup = view.open(url, '_blank');
- if(!popup) view.location.href = url;
- url=undefined; // release reference before dispatching
- filesaver.readyState = filesaver.DONE;
- dispatch_all();
- };
- reader.readAsDataURL(blob);
- filesaver.readyState = filesaver.INIT;
- return;
- }
- // don't create more object URLs than needed
- if (!object_url) {
- object_url = get_URL().createObjectURL(blob);
- }
- if (force) {
- view.location.href = object_url;
- } else {
- var opened = view.open(object_url, "_blank");
- if (!opened) {
- // Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
- view.location.href = object_url;
- }
- }
- filesaver.readyState = filesaver.DONE;
- dispatch_all();
- revoke(object_url);
- }
- ;
- filesaver.readyState = filesaver.INIT;
- if (can_use_save_link) {
- object_url = get_URL().createObjectURL(blob);
- setTimeout(function() {
- save_link.href = object_url;
- save_link.download = name;
- click(save_link);
- dispatch_all();
- revoke(object_url);
- filesaver.readyState = filesaver.DONE;
- });
- return;
- }
- fs_error();
- }
- , FS_proto = FileSaver.prototype
- , saveAs = function(blob, name, no_auto_bom) {
- return new FileSaver(blob, name || blob.name || "download", no_auto_bom);
- }
- ;
- // IE 10+ (native saveAs)
- if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
- return function(blob, name, no_auto_bom) {
- name = name || blob.name || "download";
- if (!no_auto_bom) {
- blob = auto_bom(blob);
- }
- return navigator.msSaveOrOpenBlob(blob, name);
- };
- }
- FS_proto.abort = function(){};
- FS_proto.readyState = FS_proto.INIT = 0;
- FS_proto.WRITING = 1;
- FS_proto.DONE = 2;
- FS_proto.error =
- FS_proto.onwritestart =
- FS_proto.onprogress =
- FS_proto.onwrite =
- FS_proto.onabort =
- FS_proto.onerror =
- FS_proto.onwriteend =
- null;
- return saveAs;
- }(
- typeof self !== "undefined" && self
- || typeof window !== "undefined" && window
- || this.content
- ));
- // Expose file saver on the DataTables API. Can't attach to `DataTables.Buttons`
- // since this file can be loaded before Button's core!
- DataTable.fileSave = _saveAs;
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Local (private) functions
- */
- /**
- * Get the sheet name for Excel exports.
- *
- * @param {object} config Button configuration
- */
- var _sheetname = function ( config )
- {
- var sheetName = 'Sheet1';
- if ( config.sheetName ) {
- sheetName = config.sheetName.replace(/[\[\]\*\/\\\?\:]/g, '');
- }
- return sheetName;
- };
- /**
- * Get the newline character(s)
- *
- * @param {object} config Button configuration
- * @return {string} Newline character
- */
- var _newLine = function ( config )
- {
- return config.newline ?
- config.newline :
- navigator.userAgent.match(/Windows/) ?
- '\r\n' :
- '\n';
- };
- /**
- * Combine the data from the `buttons.exportData` method into a string that
- * will be used in the export file.
- *
- * @param {DataTable.Api} dt DataTables API instance
- * @param {object} config Button configuration
- * @return {object} The data to export
- */
- var _exportData = function ( dt, config )
- {
- var newLine = _newLine( config );
- var data = dt.buttons.exportData( config.exportOptions );
- var boundary = config.fieldBoundary;
- var separator = config.fieldSeparator;
- var reBoundary = new RegExp( boundary, 'g' );
- var escapeChar = config.escapeChar !== undefined ?
- config.escapeChar :
- '\\';
- var join = function ( a ) {
- var s = '';
- // If there is a field boundary, then we might need to escape it in
- // the source data
- for ( var i=0, ien=a.length ; i<ien ; i++ ) {
- if ( i > 0 ) {
- s += separator;
- }
- s += boundary ?
- boundary + ('' + a[i]).replace( reBoundary, escapeChar+boundary ) + boundary :
- a[i];
- }
- return s;
- };
- var header = config.header ? join( data.header )+newLine : '';
- var footer = config.footer && data.footer ? newLine+join( data.footer ) : '';
- var body = [];
- for ( var i=0, ien=data.body.length ; i<ien ; i++ ) {
- body.push( join( data.body[i] ) );
- }
- return {
- str: header + body.join( newLine ) + footer,
- rows: body.length
- };
- };
- /**
- * Older versions of Safari (prior to tech preview 18) don't support the
- * download option required.
- *
- * @return {Boolean} `true` if old Safari
- */
- var _isDuffSafari = function ()
- {
- var safari = navigator.userAgent.indexOf('Safari') !== -1 &&
- navigator.userAgent.indexOf('Chrome') === -1 &&
- navigator.userAgent.indexOf('Opera') === -1;
- if ( ! safari ) {
- return false;
- }
- var version = navigator.userAgent.match( /AppleWebKit\/(\d+\.\d+)/ );
- if ( version && version.length > 1 && version[1]*1 < 603.1 ) {
- return true;
- }
- return false;
- };
- /**
- * Convert from numeric position to letter for column names in Excel
- * @param {int} n Column number
- * @return {string} Column letter(s) name
- */
- function createCellPos( n ){
- var ordA = 'A'.charCodeAt(0);
- var ordZ = 'Z'.charCodeAt(0);
- var len = ordZ - ordA + 1;
- var s = "";
- while( n >= 0 ) {
- s = String.fromCharCode(n % len + ordA) + s;
- n = Math.floor(n / len) - 1;
- }
- return s;
- }
- try {
- var _serialiser = new XMLSerializer();
- var _ieExcel;
- }
- catch (t) {}
- /**
- * Recursively add XML files from an object's structure to a ZIP file. This
- * allows the XSLX file to be easily defined with an object's structure matching
- * the files structure.
- *
- * @param {JSZip} zip ZIP package
- * @param {object} obj Object to add (recursive)
- */
- function _addToZip( zip, obj ) {
- if ( _ieExcel === undefined ) {
- // Detect if we are dealing with IE's _awful_ serialiser by seeing if it
- // drop attributes
- _ieExcel = _serialiser
- .serializeToString(
- $.parseXML( excelStrings['xl/worksheets/sheet1.xml'] )
- )
- .indexOf( 'xmlns:r' ) === -1;
- }
- $.each( obj, function ( name, val ) {
- if ( $.isPlainObject( val ) ) {
- var newDir = zip.folder( name );
- _addToZip( newDir, val );
- }
- else {
- if ( _ieExcel ) {
- // IE's XML serialiser will drop some name space attributes from
- // from the root node, so we need to save them. Do this by
- // replacing the namespace nodes with a regular attribute that
- // we convert back when serialised. Edge does not have this
- // issue
- var worksheet = val.childNodes[0];
- var i, ien;
- var attrs = [];
- for ( i=worksheet.attributes.length-1 ; i>=0 ; i-- ) {
- var attrName = worksheet.attributes[i].nodeName;
- var attrValue = worksheet.attributes[i].nodeValue;
- if ( attrName.indexOf( ':' ) !== -1 ) {
- attrs.push( { name: attrName, value: attrValue } );
- worksheet.removeAttribute( attrName );
- }
- }
- for ( i=0, ien=attrs.length ; i<ien ; i++ ) {
- var attr = val.createAttribute( attrs[i].name.replace( ':', '_dt_b_namespace_token_' ) );
- attr.value = attrs[i].value;
- worksheet.setAttributeNode( attr );
- }
- }
- var str = _serialiser.serializeToString(val);
- // Fix IE's XML
- if ( _ieExcel ) {
- // IE doesn't include the XML declaration
- if ( str.indexOf( '<?xml' ) === -1 ) {
- str = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+str;
- }
- // Return namespace attributes to being as such
- str = str.replace( /_dt_b_namespace_token_/g, ':' );
- // Remove testing name space that IE puts into the space preserve attr
- str = str.replace( /xmlns:NS[\d]+="" NS[\d]+:/g, '' );
- }
- // Safari, IE and Edge will put empty name space attributes onto
- // various elements making them useless. This strips them out
- str = str.replace( /<([^<>]*?) xmlns=""([^<>]*?)>/g, '<$1 $2>' );
- zip.file( name, str );
- }
- } );
- }
- /**
- * Create an XML node and add any children, attributes, etc without needing to
- * be verbose in the DOM.
- *
- * @param {object} doc XML document
- * @param {string} nodeName Node name
- * @param {object} opts Options - can be `attr` (attributes), `children`
- * (child nodes) and `text` (text content)
- * @return {node} Created node
- */
- function _createNode( doc, nodeName, opts ) {
- var tempNode = doc.createElement( nodeName );
- if ( opts ) {
- if ( opts.attr ) {
- $(tempNode).attr( opts.attr );
- }
- if ( opts.children ) {
- $.each( opts.children, function ( key, value ) {
- tempNode.appendChild( value );
- } );
- }
- if ( opts.text !== null && opts.text !== undefined ) {
- tempNode.appendChild( doc.createTextNode( opts.text ) );
- }
- }
- return tempNode;
- }
- /**
- * Get the width for an Excel column based on the contents of that column
- * @param {object} data Data for export
- * @param {int} col Column index
- * @return {int} Column width
- */
- function _excelColWidth( data, col ) {
- var max = data.header[col].length;
- var len, lineSplit, str;
- if ( data.footer && data.footer[col].length > max ) {
- max = data.footer[col].length;
- }
- for ( var i=0, ien=data.body.length ; i<ien ; i++ ) {
- var point = data.body[i][col];
- str = point !== null && point !== undefined ?
- point.toString() :
- '';
- // If there is a newline character, workout the width of the column
- // based on the longest line in the string
- if ( str.indexOf('\n') !== -1 ) {
- lineSplit = str.split('\n');
- lineSplit.sort( function (a, b) {
- return b.length - a.length;
- } );
- len = lineSplit[0].length;
- }
- else {
- len = str.length;
- }
- if ( len > max ) {
- max = len;
- }
- // Max width rather than having potentially massive column widths
- if ( max > 40 ) {
- return 54; // 40 * 1.35
- }
- }
- max *= 1.35;
- // And a min width
- return max > 6 ? max : 6;
- }
- // Excel - Pre-defined strings to build a basic XLSX file
- var excelStrings = {
- "_rels/.rels":
- '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+
- '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'+
- '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="xl/workbook.xml"/>'+
- '</Relationships>',
- "xl/_rels/workbook.xml.rels":
- '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+
- '<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">'+
- '<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/worksheet" Target="worksheets/sheet1.xml"/>'+
- '<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/styles" Target="styles.xml"/>'+
- '</Relationships>',
- "[Content_Types].xml":
- '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+
- '<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">'+
- '<Default Extension="xml" ContentType="application/xml" />'+
- '<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml" />'+
- '<Default Extension="jpeg" ContentType="image/jpeg" />'+
- '<Override PartName="/xl/workbook.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet.main+xml" />'+
- '<Override PartName="/xl/worksheets/sheet1.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.worksheet+xml" />'+
- '<Override PartName="/xl/styles.xml" ContentType="application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml" />'+
- '</Types>',
- "xl/workbook.xml":
- '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+
- '<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">'+
- '<fileVersion appName="xl" lastEdited="5" lowestEdited="5" rupBuild="24816"/>'+
- '<workbookPr showInkAnnotation="0" autoCompressPictures="0"/>'+
- '<bookViews>'+
- '<workbookView xWindow="0" yWindow="0" windowWidth="25600" windowHeight="19020" tabRatio="500"/>'+
- '</bookViews>'+
- '<sheets>'+
- '<sheet name="Sheet1" sheetId="1" r:id="rId1"/>'+
- '</sheets>'+
- '<definedNames/>'+
- '</workbook>',
- "xl/worksheets/sheet1.xml":
- '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>'+
- '<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">'+
- '<sheetData/>'+
- '<mergeCells count="0"/>'+
- '</worksheet>',
- "xl/styles.xml":
- '<?xml version="1.0" encoding="UTF-8"?>'+
- '<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="x14ac" xmlns:x14ac="http://schemas.microsoft.com/office/spreadsheetml/2009/9/ac">'+
- '<numFmts count="6">'+
- '<numFmt numFmtId="164" formatCode="#,##0.00_-\ [$$-45C]"/>'+
- '<numFmt numFmtId="165" formatCode=""£"#,##0.00"/>'+
- '<numFmt numFmtId="166" formatCode="[$€-2]\ #,##0.00"/>'+
- '<numFmt numFmtId="167" formatCode="0.0%"/>'+
- '<numFmt numFmtId="168" formatCode="#,##0;(#,##0)"/>'+
- '<numFmt numFmtId="169" formatCode="#,##0.00;(#,##0.00)"/>'+
- '</numFmts>'+
- '<fonts count="5" x14ac:knownFonts="1">'+
- '<font>'+
- '<sz val="11" />'+
- '<name val="Calibri" />'+
- '</font>'+
- '<font>'+
- '<sz val="11" />'+
- '<name val="Calibri" />'+
- '<color rgb="FFFFFFFF" />'+
- '</font>'+
- '<font>'+
- '<sz val="11" />'+
- '<name val="Calibri" />'+
- '<b />'+
- '</font>'+
- '<font>'+
- '<sz val="11" />'+
- '<name val="Calibri" />'+
- '<i />'+
- '</font>'+
- '<font>'+
- '<sz val="11" />'+
- '<name val="Calibri" />'+
- '<u />'+
- '</font>'+
- '</fonts>'+
- '<fills count="6">'+
- '<fill>'+
- '<patternFill patternType="none" />'+
- '</fill>'+
- '<fill>'+ // Excel appears to use this as a dotted background regardless of values but
- '<patternFill patternType="none" />'+ // to be valid to the schema, use a patternFill
- '</fill>'+
- '<fill>'+
- '<patternFill patternType="solid">'+
- '<fgColor rgb="FFD9D9D9" />'+
- '<bgColor indexed="64" />'+
- '</patternFill>'+
- '</fill>'+
- '<fill>'+
- '<patternFill patternType="solid">'+
- '<fgColor rgb="FFD99795" />'+
- '<bgColor indexed="64" />'+
- '</patternFill>'+
- '</fill>'+
- '<fill>'+
- '<patternFill patternType="solid">'+
- '<fgColor rgb="ffc6efce" />'+
- '<bgColor indexed="64" />'+
- '</patternFill>'+
- '</fill>'+
- '<fill>'+
- '<patternFill patternType="solid">'+
- '<fgColor rgb="ffc6cfef" />'+
- '<bgColor indexed="64" />'+
- '</patternFill>'+
- '</fill>'+
- '</fills>'+
- '<borders count="2">'+
- '<border>'+
- '<left />'+
- '<right />'+
- '<top />'+
- '<bottom />'+
- '<diagonal />'+
- '</border>'+
- '<border diagonalUp="false" diagonalDown="false">'+
- '<left style="thin">'+
- '<color auto="1" />'+
- '</left>'+
- '<right style="thin">'+
- '<color auto="1" />'+
- '</right>'+
- '<top style="thin">'+
- '<color auto="1" />'+
- '</top>'+
- '<bottom style="thin">'+
- '<color auto="1" />'+
- '</bottom>'+
- '<diagonal />'+
- '</border>'+
- '</borders>'+
- '<cellStyleXfs count="1">'+
- '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" />'+
- '</cellStyleXfs>'+
- '<cellXfs count="67">'+
- '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="1" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="2" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="3" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="4" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="0" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="1" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="2" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="3" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="4" fillId="2" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="0" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="1" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="2" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="3" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="4" fillId="3" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="0" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="1" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="2" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="3" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="4" fillId="4" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="0" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="1" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="2" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="3" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="4" fillId="5" borderId="0" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="0" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="1" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="2" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="3" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="4" fillId="0" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="0" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="1" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="2" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="3" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="4" fillId="2" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="0" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="1" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="2" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="3" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="4" fillId="3" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="0" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="1" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="2" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="3" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="4" fillId="4" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="0" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="1" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="2" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="3" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="4" fillId="5" borderId="1" applyFont="1" applyFill="1" applyBorder="1"/>'+
- '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
- '<alignment horizontal="left"/>'+
- '</xf>'+
- '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
- '<alignment horizontal="center"/>'+
- '</xf>'+
- '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
- '<alignment horizontal="right"/>'+
- '</xf>'+
- '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
- '<alignment horizontal="fill"/>'+
- '</xf>'+
- '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
- '<alignment textRotation="90"/>'+
- '</xf>'+
- '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyAlignment="1">'+
- '<alignment wrapText="1"/>'+
- '</xf>'+
- '<xf numFmtId="9" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
- '<xf numFmtId="164" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
- '<xf numFmtId="165" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
- '<xf numFmtId="166" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
- '<xf numFmtId="167" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
- '<xf numFmtId="168" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
- '<xf numFmtId="169" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
- '<xf numFmtId="3" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
- '<xf numFmtId="4" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
- '<xf numFmtId="1" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
- '<xf numFmtId="2" fontId="0" fillId="0" borderId="0" applyFont="1" applyFill="1" applyBorder="1" xfId="0" applyNumberFormat="1"/>'+
- '</cellXfs>'+
- '<cellStyles count="1">'+
- '<cellStyle name="Normal" xfId="0" builtinId="0" />'+
- '</cellStyles>'+
- '<dxfs count="0" />'+
- '<tableStyles count="0" defaultTableStyle="TableStyleMedium9" defaultPivotStyle="PivotStyleMedium4" />'+
- '</styleSheet>'
- };
- // Note we could use 3 `for` loops for the styles, but when gzipped there is
- // virtually no difference in size, since the above can be easily compressed
- // Pattern matching for special number formats. Perhaps this should be exposed
- // via an API in future?
- // Ref: section 3.8.30 - built in formatters in open spreadsheet
- // https://www.ecma-international.org/news/TC45_current_work/Office%20Open%20XML%20Part%204%20-%20Markup%20Language%20Reference.pdf
- var _excelSpecials = [
- { match: /^\-?\d+\.\d%$/, style: 60, fmt: function (d) { return d/100; } }, // Precent with d.p.
- { match: /^\-?\d+\.?\d*%$/, style: 56, fmt: function (d) { return d/100; } }, // Percent
- { match: /^\-?\$[\d,]+.?\d*$/, style: 57 }, // Dollars
- { match: /^\-?£[\d,]+.?\d*$/, style: 58 }, // Pounds
- { match: /^\-?€[\d,]+.?\d*$/, style: 59 }, // Euros
- { match: /^\-?\d+$/, style: 65 }, // Numbers without thousand separators
- { match: /^\-?\d+\.\d{2}$/, style: 66 }, // Numbers 2 d.p. without thousands separators
- { match: /^\([\d,]+\)$/, style: 61, fmt: function (d) { return -1 * d.replace(/[\(\)]/g, ''); } }, // Negative numbers indicated by brackets
- { match: /^\([\d,]+\.\d{2}\)$/, style: 62, fmt: function (d) { return -1 * d.replace(/[\(\)]/g, ''); } }, // Negative numbers indicated by brackets - 2d.p.
- { match: /^\-?[\d,]+$/, style: 63 }, // Numbers with thousand separators
- { match: /^\-?[\d,]+\.\d{2}$/, style: 64 } // Numbers with 2 d.p. and thousands separators
- ];
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Buttons
- */
- //
- // Copy to clipboard
- //
- DataTable.ext.buttons.copyHtml5 = {
- className: 'buttons-copy buttons-html5',
- text: function ( dt ) {
- return dt.i18n( 'buttons.copy', 'Copy' );
- },
- action: function ( e, dt, button, config ) {
- this.processing( true );
- var that = this;
- var exportData = _exportData( dt, config );
- var info = dt.buttons.exportInfo( config );
- var newline = _newLine(config);
- var output = exportData.str;
- var hiddenDiv = $('<div/>')
- .css( {
- height: 1,
- width: 1,
- overflow: 'hidden',
- position: 'fixed',
- top: 0,
- left: 0
- } );
- if ( info.title ) {
- output = info.title + newline + newline + output;
- }
- if ( info.messageTop ) {
- output = info.messageTop + newline + newline + output;
- }
- if ( info.messageBottom ) {
- output = output + newline + newline + info.messageBottom;
- }
- if ( config.customize ) {
- output = config.customize( output, config, dt );
- }
- var textarea = $('<textarea readonly/>')
- .val( output )
- .appendTo( hiddenDiv );
- // For browsers that support the copy execCommand, try to use it
- if ( document.queryCommandSupported('copy') ) {
- hiddenDiv.appendTo( dt.table().container() );
- textarea[0].focus();
- textarea[0].select();
- try {
- var successful = document.execCommand( 'copy' );
- hiddenDiv.remove();
- if (successful) {
- dt.buttons.info(
- dt.i18n( 'buttons.copyTitle', 'Copy to clipboard' ),
- dt.i18n( 'buttons.copySuccess', {
- 1: 'Copied one row to clipboard',
- _: 'Copied %d rows to clipboard'
- }, exportData.rows ),
- 2000
- );
- this.processing( false );
- return;
- }
- }
- catch (t) {}
- }
- // Otherwise we show the text box and instruct the user to use it
- var message = $('<span>'+dt.i18n( 'buttons.copyKeys',
- 'Press <i>ctrl</i> or <i>\u2318</i> + <i>C</i> to copy the table data<br>to your system clipboard.<br><br>'+
- 'To cancel, click this message or press escape.' )+'</span>'
- )
- .append( hiddenDiv );
- dt.buttons.info( dt.i18n( 'buttons.copyTitle', 'Copy to clipboard' ), message, 0 );
- // Select the text so when the user activates their system clipboard
- // it will copy that text
- textarea[0].focus();
- textarea[0].select();
- // Event to hide the message when the user is done
- var container = $(message).closest('.dt-button-info');
- var close = function () {
- container.off( 'click.buttons-copy' );
- $(document).off( '.buttons-copy' );
- dt.buttons.info( false );
- };
- container.on( 'click.buttons-copy', close );
- $(document)
- .on( 'keydown.buttons-copy', function (e) {
- if ( e.keyCode === 27 ) { // esc
- close();
- that.processing( false );
- }
- } )
- .on( 'copy.buttons-copy cut.buttons-copy', function () {
- close();
- that.processing( false );
- } );
- },
- exportOptions: {},
- fieldSeparator: '\t',
- fieldBoundary: '',
- header: true,
- footer: false,
- title: '*',
- messageTop: '*',
- messageBottom: '*'
- };
- //
- // CSV export
- //
- DataTable.ext.buttons.csvHtml5 = {
- bom: false,
- className: 'buttons-csv buttons-html5',
- available: function () {
- return window.FileReader !== undefined && window.Blob;
- },
- text: function ( dt ) {
- return dt.i18n( 'buttons.csv', 'CSV' );
- },
- action: function ( e, dt, button, config ) {
- this.processing( true );
- // Set the text
- var output = _exportData( dt, config ).str;
- var info = dt.buttons.exportInfo(config);
- var charset = config.charset;
- if ( config.customize ) {
- output = config.customize( output, config, dt );
- }
- if ( charset !== false ) {
- if ( ! charset ) {
- charset = document.characterSet || document.charset;
- }
- if ( charset ) {
- charset = ';charset='+charset;
- }
- }
- else {
- charset = '';
- }
- if ( config.bom ) {
- output = '\ufeff' + output;
- }
- _saveAs(
- new Blob( [output], {type: 'text/csv'+charset} ),
- info.filename,
- true
- );
- this.processing( false );
- },
- filename: '*',
- extension: '.csv',
- exportOptions: {},
- fieldSeparator: ',',
- fieldBoundary: '"',
- escapeChar: '"',
- charset: null,
- header: true,
- footer: false
- };
- //
- // Excel (xlsx) export
- //
- DataTable.ext.buttons.excelHtml5 = {
- className: 'buttons-excel buttons-html5',
- available: function () {
- return window.FileReader !== undefined && _jsZip() !== undefined && ! _isDuffSafari() && _serialiser;
- },
- text: function ( dt ) {
- return dt.i18n( 'buttons.excel', 'Excel' );
- },
- action: function ( e, dt, button, config ) {
- this.processing( true );
- var that = this;
- var rowPos = 0;
- var dataStartRow, dataEndRow;
- var getXml = function ( type ) {
- var str = excelStrings[ type ];
- //str = str.replace( /xmlns:/g, 'xmlns_' ).replace( /mc:/g, 'mc_' );
- return $.parseXML( str );
- };
- var rels = getXml('xl/worksheets/sheet1.xml');
- var relsGet = rels.getElementsByTagName( "sheetData" )[0];
- var xlsx = {
- _rels: {
- ".rels": getXml('_rels/.rels')
- },
- xl: {
- _rels: {
- "workbook.xml.rels": getXml('xl/_rels/workbook.xml.rels')
- },
- "workbook.xml": getXml('xl/workbook.xml'),
- "styles.xml": getXml('xl/styles.xml'),
- "worksheets": {
- "sheet1.xml": rels
- }
- },
- "[Content_Types].xml": getXml('[Content_Types].xml')
- };
- var data = dt.buttons.exportData( config.exportOptions );
- var currentRow, rowNode;
- var addRow = function ( row ) {
- currentRow = rowPos+1;
- rowNode = _createNode( rels, "row", { attr: {r:currentRow} } );
- for ( var i=0, ien=row.length ; i<ien ; i++ ) {
- // Concat both the Cell Columns as a letter and the Row of the cell.
- var cellId = createCellPos(i) + '' + currentRow;
- var cell = null;
- // For null, undefined of blank cell, continue so it doesn't create the _createNode
- if ( row[i] === null || row[i] === undefined || row[i] === '' ) {
- if ( config.createEmptyCells === true ) {
- row[i] = '';
- }
- else {
- continue;
- }
- }
- var originalContent = row[i];
- row[i] = $.trim( row[i] );
- // Special number formatting options
- for ( var j=0, jen=_excelSpecials.length ; j<jen ; j++ ) {
- var special = _excelSpecials[j];
- // TODO Need to provide the ability for the specials to say
- // if they are returning a string, since at the moment it is
- // assumed to be a number
- if ( row[i].match && ! row[i].match(/^0\d+/) && row[i].match( special.match ) ) {
- var val = row[i].replace(/[^\d\.\-]/g, '');
- if ( special.fmt ) {
- val = special.fmt( val );
- }
- cell = _createNode( rels, 'c', {
- attr: {
- r: cellId,
- s: special.style
- },
- children: [
- _createNode( rels, 'v', { text: val } )
- ]
- } );
- break;
- }
- }
- if ( ! cell ) {
- if ( typeof row[i] === 'number' || (
- row[i].match &&
- row[i].match(/^-?\d+(\.\d+)?$/) &&
- ! row[i].match(/^0\d+/) )
- ) {
- // Detect numbers - don't match numbers with leading zeros
- // or a negative anywhere but the start
- cell = _createNode( rels, 'c', {
- attr: {
- t: 'n',
- r: cellId
- },
- children: [
- _createNode( rels, 'v', { text: row[i] } )
- ]
- } );
- }
- else {
- // String output - replace non standard characters for text output
- var text = ! originalContent.replace ?
- originalContent :
- originalContent.replace(/[\x00-\x09\x0B\x0C\x0E-\x1F\x7F-\x9F]/g, '');
- cell = _createNode( rels, 'c', {
- attr: {
- t: 'inlineStr',
- r: cellId
- },
- children:{
- row: _createNode( rels, 'is', {
- children: {
- row: _createNode( rels, 't', {
- text: text,
- attr: {
- 'xml:space': 'preserve'
- }
- } )
- }
- } )
- }
- } );
- }
- }
- rowNode.appendChild( cell );
- }
- relsGet.appendChild(rowNode);
- rowPos++;
- };
- if ( config.customizeData ) {
- config.customizeData( data );
- }
- var mergeCells = function ( row, colspan ) {
- var mergeCells = $('mergeCells', rels);
- mergeCells[0].appendChild( _createNode( rels, 'mergeCell', {
- attr: {
- ref: 'A'+row+':'+createCellPos(colspan)+row
- }
- } ) );
- mergeCells.attr( 'count', parseFloat(mergeCells.attr( 'count' ))+1 );
- $('row:eq('+(row-1)+') c', rels).attr( 's', '51' ); // centre
- };
- // Title and top messages
- var exportInfo = dt.buttons.exportInfo( config );
- if ( exportInfo.title ) {
- addRow( [exportInfo.title], rowPos );
- mergeCells( rowPos, data.header.length-1 );
- }
- if ( exportInfo.messageTop ) {
- addRow( [exportInfo.messageTop], rowPos );
- mergeCells( rowPos, data.header.length-1 );
- }
- // Table itself
- if ( config.header ) {
- addRow( data.header, rowPos );
- $('row:last c', rels).attr( 's', '2' ); // bold
- }
-
- dataStartRow = rowPos;
- for ( var n=0, ie=data.body.length ; n<ie ; n++ ) {
- addRow( data.body[n], rowPos );
- }
-
- dataEndRow = rowPos;
- if ( config.footer && data.footer ) {
- addRow( data.footer, rowPos);
- $('row:last c', rels).attr( 's', '2' ); // bold
- }
- // Below the table
- if ( exportInfo.messageBottom ) {
- addRow( [exportInfo.messageBottom], rowPos );
- mergeCells( rowPos, data.header.length-1 );
- }
- // Set column widths
- var cols = _createNode( rels, 'cols' );
- $('worksheet', rels).prepend( cols );
- for ( var i=0, ien=data.header.length ; i<ien ; i++ ) {
- cols.appendChild( _createNode( rels, 'col', {
- attr: {
- min: i+1,
- max: i+1,
- width: _excelColWidth( data, i ),
- customWidth: 1
- }
- } ) );
- }
- // Workbook modifications
- var workbook = xlsx.xl['workbook.xml'];
- $( 'sheets sheet', workbook ).attr( 'name', _sheetname( config ) );
- // Auto filter for columns
- if ( config.autoFilter ) {
- $('mergeCells', rels).before( _createNode( rels, 'autoFilter', {
- attr: {
- ref: 'A'+dataStartRow+':'+createCellPos(data.header.length-1)+dataEndRow
- }
- } ) );
- $('definedNames', workbook).append( _createNode( workbook, 'definedName', {
- attr: {
- name: '_xlnm._FilterDatabase',
- localSheetId: '0',
- hidden: 1
- },
- text: _sheetname(config)+'!$A$'+dataStartRow+':'+createCellPos(data.header.length-1)+dataEndRow
- } ) );
- }
- // Let the developer customise the document if they want to
- if ( config.customize ) {
- config.customize( xlsx, config, dt );
- }
- // Excel doesn't like an empty mergeCells tag
- if ( $('mergeCells', rels).children().length === 0 ) {
- $('mergeCells', rels).remove();
- }
- var jszip = _jsZip();
- var zip = new jszip();
- var zipConfig = {
- type: 'blob',
- mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
- };
- _addToZip( zip, xlsx );
- if ( zip.generateAsync ) {
- // JSZip 3+
- zip
- .generateAsync( zipConfig )
- .then( function ( blob ) {
- _saveAs( blob, exportInfo.filename );
- that.processing( false );
- } );
- }
- else {
- // JSZip 2.5
- _saveAs(
- zip.generate( zipConfig ),
- exportInfo.filename
- );
- this.processing( false );
- }
- },
- filename: '*',
- extension: '.xlsx',
- exportOptions: {},
- header: true,
- footer: false,
- title: '*',
- messageTop: '*',
- messageBottom: '*',
- createEmptyCells: false,
- autoFilter: false,
- sheetName: ''
- };
- //
- // PDF export - using pdfMake - http://pdfmake.org
- //
- DataTable.ext.buttons.pdfHtml5 = {
- className: 'buttons-pdf buttons-html5',
- available: function () {
- return window.FileReader !== undefined && _pdfMake();
- },
- text: function ( dt ) {
- return dt.i18n( 'buttons.pdf', 'PDF' );
- },
- action: function ( e, dt, button, config ) {
- this.processing( true );
- var that = this;
- var data = dt.buttons.exportData( config.exportOptions );
- var info = dt.buttons.exportInfo( config );
- var rows = [];
- if ( config.header ) {
- rows.push( $.map( data.header, function ( d ) {
- return {
- text: typeof d === 'string' ? d : d+'',
- style: 'tableHeader'
- };
- } ) );
- }
- for ( var i=0, ien=data.body.length ; i<ien ; i++ ) {
- rows.push( $.map( data.body[i], function ( d ) {
- if ( d === null || d === undefined ) {
- d = '';
- }
- return {
- text: typeof d === 'string' ? d : d+'',
- style: i % 2 ? 'tableBodyEven' : 'tableBodyOdd'
- };
- } ) );
- }
- if ( config.footer && data.footer) {
- rows.push( $.map( data.footer, function ( d ) {
- return {
- text: typeof d === 'string' ? d : d+'',
- style: 'tableFooter'
- };
- } ) );
- }
- var doc = {
- pageSize: config.pageSize,
- pageOrientation: config.orientation,
- content: [
- {
- table: {
- headerRows: 1,
- body: rows
- },
- layout: 'noBorders'
- }
- ],
- styles: {
- tableHeader: {
- bold: true,
- fontSize: 11,
- color: 'white',
- fillColor: '#2d4154',
- alignment: 'center'
- },
- tableBodyEven: {},
- tableBodyOdd: {
- fillColor: '#f3f3f3'
- },
- tableFooter: {
- bold: true,
- fontSize: 11,
- color: 'white',
- fillColor: '#2d4154'
- },
- title: {
- alignment: 'center',
- fontSize: 15
- },
- message: {}
- },
- defaultStyle: {
- fontSize: 10
- }
- };
- if ( info.messageTop ) {
- doc.content.unshift( {
- text: info.messageTop,
- style: 'message',
- margin: [ 0, 0, 0, 12 ]
- } );
- }
- if ( info.messageBottom ) {
- doc.content.push( {
- text: info.messageBottom,
- style: 'message',
- margin: [ 0, 0, 0, 12 ]
- } );
- }
- if ( info.title ) {
- doc.content.unshift( {
- text: info.title,
- style: 'title',
- margin: [ 0, 0, 0, 12 ]
- } );
- }
- if ( config.customize ) {
- config.customize( doc, config, dt );
- }
- var pdf = _pdfMake().createPdf( doc );
- if ( config.download === 'open' && ! _isDuffSafari() ) {
- pdf.open();
- }
- else {
- pdf.download( info.filename );
- }
- this.processing( false );
- },
- title: '*',
- filename: '*',
- extension: '.pdf',
- exportOptions: {},
- orientation: 'portrait',
- pageSize: 'A4',
- header: true,
- footer: false,
- messageTop: '*',
- messageBottom: '*',
- customize: null,
- download: 'download'
- };
- return DataTable.Buttons;
- }));
- /*!
- * Print button for Buttons and DataTables.
- * 2016 SpryMedia Ltd - datatables.net/license
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net', 'datatables.net-buttons'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- if ( ! $.fn.dataTable.Buttons ) {
- require('datatables.net-buttons')(root, $);
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- var _link = document.createElement( 'a' );
- /**
- * Clone link and style tags, taking into account the need to change the source
- * path.
- *
- * @param {node} el Element to convert
- */
- var _styleToAbs = function( el ) {
- var url;
- var clone = $(el).clone()[0];
- var linkHost;
- if ( clone.nodeName.toLowerCase() === 'link' ) {
- clone.href = _relToAbs( clone.href );
- }
- return clone.outerHTML;
- };
- /**
- * Convert a URL from a relative to an absolute address so it will work
- * correctly in the popup window which has no base URL.
- *
- * @param {string} href URL
- */
- var _relToAbs = function( href ) {
- // Assign to a link on the original page so the browser will do all the
- // hard work of figuring out where the file actually is
- _link.href = href;
- var linkHost = _link.host;
- // IE doesn't have a trailing slash on the host
- // Chrome has it on the pathname
- if ( linkHost.indexOf('/') === -1 && _link.pathname.indexOf('/') !== 0) {
- linkHost += '/';
- }
- return _link.protocol+"//"+linkHost+_link.pathname+_link.search;
- };
- DataTable.ext.buttons.print = {
- className: 'buttons-print',
- text: function ( dt ) {
- return dt.i18n( 'buttons.print', 'Print' );
- },
- action: function ( e, dt, button, config ) {
- var data = dt.buttons.exportData(
- $.extend( {decodeEntities: false}, config.exportOptions ) // XSS protection
- );
- var exportInfo = dt.buttons.exportInfo( config );
- var columnClasses = dt
- .columns( config.exportOptions.columns )
- .flatten()
- .map( function (idx) {
- return dt.settings()[0].aoColumns[dt.column(idx).index()].sClass;
- } )
- .toArray();
- var addRow = function ( d, tag ) {
- var str = '<tr>';
- for ( var i=0, ien=d.length ; i<ien ; i++ ) {
- // null and undefined aren't useful in the print output
- var dataOut = d[i] === null || d[i] === undefined ?
- '' :
- d[i];
- var classAttr = columnClasses[i] ?
- 'class="'+columnClasses[i]+'"' :
- '';
- str += '<'+tag+' '+classAttr+'>'+dataOut+'</'+tag+'>';
- }
- return str + '</tr>';
- };
- // Construct a table for printing
- var html = '<table class="'+dt.table().node().className+'">';
- if ( config.header ) {
- html += '<thead>'+ addRow( data.header, 'th' ) +'</thead>';
- }
- html += '<tbody>';
- for ( var i=0, ien=data.body.length ; i<ien ; i++ ) {
- html += addRow( data.body[i], 'td' );
- }
- html += '</tbody>';
- if ( config.footer && data.footer ) {
- html += '<tfoot>'+ addRow( data.footer, 'th' ) +'</tfoot>';
- }
- html += '</table>';
- // Open a new window for the printable table
- var win = window.open( '', '' );
- win.document.close();
- // Inject the title and also a copy of the style and link tags from this
- // document so the table can retain its base styling. Note that we have
- // to use string manipulation as IE won't allow elements to be created
- // in the host document and then appended to the new window.
- var head = '<title>'+exportInfo.title+'</title>';
- $('style, link').each( function () {
- head += _styleToAbs( this );
- } );
- try {
- win.document.head.innerHTML = head; // Work around for Edge
- }
- catch (e) {
- $(win.document.head).html( head ); // Old IE
- }
- // Inject the table and other surrounding information
- win.document.body.innerHTML =
- '<h1>'+exportInfo.title+'</h1>'+
- '<div>'+(exportInfo.messageTop || '')+'</div>'+
- html+
- '<div>'+(exportInfo.messageBottom || '')+'</div>';
- $(win.document.body).addClass('dt-print-view');
- $('img', win.document.body).each( function ( i, img ) {
- img.setAttribute( 'src', _relToAbs( img.getAttribute('src') ) );
- } );
- if ( config.customize ) {
- config.customize( win, config, dt );
- }
- // Allow stylesheets time to load
- var autoPrint = function () {
- if ( config.autoPrint ) {
- win.print(); // blocking - so close will not
- win.close(); // execute until this is done
- }
- };
- if ( navigator.userAgent.match(/Trident\/\d.\d/) ) { // IE needs to call this without a setTimeout
- autoPrint();
- }
- else {
- win.setTimeout( autoPrint, 1000 );
- }
- },
- title: '*',
- messageTop: '*',
- messageBottom: '*',
- exportOptions: {},
- header: true,
- footer: false,
- autoPrint: true,
- customize: null
- };
- return DataTable.Buttons;
- }));
- /*!
- * Column visibility buttons for Buttons and DataTables.
- * 2016 SpryMedia Ltd - datatables.net/license
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net', 'datatables.net-buttons'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- if ( ! $.fn.dataTable.Buttons ) {
- require('datatables.net-buttons')(root, $);
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- $.extend( DataTable.ext.buttons, {
- // A collection of column visibility buttons
- colvis: function ( dt, conf ) {
- return {
- extend: 'collection',
- text: function ( dt ) {
- return dt.i18n( 'buttons.colvis', 'Column visibility' );
- },
- className: 'buttons-colvis',
- buttons: [ {
- extend: 'columnsToggle',
- columns: conf.columns,
- columnText: conf.columnText
- } ]
- };
- },
- // Selected columns with individual buttons - toggle column visibility
- columnsToggle: function ( dt, conf ) {
- var columns = dt.columns( conf.columns ).indexes().map( function ( idx ) {
- return {
- extend: 'columnToggle',
- columns: idx,
- columnText: conf.columnText
- };
- } ).toArray();
- return columns;
- },
- // Single button to toggle column visibility
- columnToggle: function ( dt, conf ) {
- return {
- extend: 'columnVisibility',
- columns: conf.columns,
- columnText: conf.columnText
- };
- },
- // Selected columns with individual buttons - set column visibility
- columnsVisibility: function ( dt, conf ) {
- var columns = dt.columns( conf.columns ).indexes().map( function ( idx ) {
- return {
- extend: 'columnVisibility',
- columns: idx,
- visibility: conf.visibility,
- columnText: conf.columnText
- };
- } ).toArray();
- return columns;
- },
- // Single button to set column visibility
- columnVisibility: {
- columns: undefined, // column selector
- text: function ( dt, button, conf ) {
- return conf._columnText( dt, conf );
- },
- className: 'buttons-columnVisibility',
- action: function ( e, dt, button, conf ) {
- var col = dt.columns( conf.columns );
- var curr = col.visible();
- col.visible( conf.visibility !== undefined ?
- conf.visibility :
- ! (curr.length ? curr[0] : false )
- );
- },
- init: function ( dt, button, conf ) {
- var that = this;
- button.attr( 'data-cv-idx', conf.columns );
- dt
- .on( 'column-visibility.dt'+conf.namespace, function (e, settings) {
- if ( ! settings.bDestroying && settings.nTable == dt.settings()[0].nTable ) {
- that.active( dt.column( conf.columns ).visible() );
- }
- } )
- .on( 'column-reorder.dt'+conf.namespace, function (e, settings, details) {
- // Don't rename buttons based on column name if the button
- // controls more than one column!
- if ( dt.columns( conf.columns ).count() !== 1 ) {
- return;
- }
- conf.columns = $.inArray( conf.columns, details.mapping );
- button.attr( 'data-cv-idx', conf.columns );
- // Reorder buttons for new table order
- button
- .parent()
- .children('[data-cv-idx]')
- .sort( function (a, b) {
- return (a.getAttribute('data-cv-idx')*1) - (b.getAttribute('data-cv-idx')*1);
- } )
- .appendTo(button.parent());
- } );
- this.active( dt.column( conf.columns ).visible() );
- },
- destroy: function ( dt, button, conf ) {
- dt
- .off( 'column-visibility.dt'+conf.namespace )
- .off( 'column-reorder.dt'+conf.namespace );
- },
- _columnText: function ( dt, conf ) {
- // Use DataTables' internal data structure until this is presented
- // is a public API. The other option is to use
- // `$( column(col).node() ).text()` but the node might not have been
- // populated when Buttons is constructed.
- var idx = dt.column( conf.columns ).index();
- var title = dt.settings()[0].aoColumns[ idx ].sTitle
- .replace(/\n/g," ") // remove new lines
- .replace(/<br\s*\/?>/gi, " ") // replace line breaks with spaces
- .replace(/<select(.*?)<\/select>/g, "") // remove select tags, including options text
- .replace(/<!\-\-.*?\-\->/g, "") // strip HTML comments
- .replace(/<.*?>/g, "") // strip HTML
- .replace(/^\s+|\s+$/g,""); // trim
- return conf.columnText ?
- conf.columnText( dt, idx, title ) :
- title;
- }
- },
- colvisRestore: {
- className: 'buttons-colvisRestore',
- text: function ( dt ) {
- return dt.i18n( 'buttons.colvisRestore', 'Restore visibility' );
- },
- init: function ( dt, button, conf ) {
- conf._visOriginal = dt.columns().indexes().map( function ( idx ) {
- return dt.column( idx ).visible();
- } ).toArray();
- },
- action: function ( e, dt, button, conf ) {
- dt.columns().every( function ( i ) {
- // Take into account that ColReorder might have disrupted our
- // indexes
- var idx = dt.colReorder && dt.colReorder.transpose ?
- dt.colReorder.transpose( i, 'toOriginal' ) :
- i;
- this.visible( conf._visOriginal[ idx ] );
- } );
- }
- },
- colvisGroup: {
- className: 'buttons-colvisGroup',
- action: function ( e, dt, button, conf ) {
- dt.columns( conf.show ).visible( true, false );
- dt.columns( conf.hide ).visible( false, false );
- dt.columns.adjust();
- },
- show: [],
- hide: []
- }
- } );
- return DataTable.Buttons;
- }));
- /* Set the defaults for DataTables buttons */
- $.extend(true, $.fn.dataTable.Buttons.defaults, {
- dom: {
- container: {
- className: 'dt-buttons'
- },
- button: {
- className: 'btn'
- }
- }
- });
- /* auto fill button class on popup */
- $.fn.dataTable.AutoFill.classes.btn = 'btn btn-primary';
- /*! ColReorder 1.5.1
- * ©2010-2018 SpryMedia Ltd - datatables.net/license
- */
- /**
- * @summary ColReorder
- * @description Provide the ability to reorder columns in a DataTable
- * @version 1.5.1
- * @file dataTables.colReorder.js
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
- * @contact www.sprymedia.co.uk/contact
- * @copyright Copyright 2010-2018 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license/mit
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- /**
- * Switch the key value pairing of an index array to be value key (i.e. the old value is now the
- * key). For example consider [ 2, 0, 1 ] this would be returned as [ 1, 2, 0 ].
- * @method fnInvertKeyValues
- * @param array aIn Array to switch around
- * @returns array
- */
- function fnInvertKeyValues( aIn )
- {
- var aRet=[];
- for ( var i=0, iLen=aIn.length ; i<iLen ; i++ )
- {
- aRet[ aIn[i] ] = i;
- }
- return aRet;
- }
- /**
- * Modify an array by switching the position of two elements
- * @method fnArraySwitch
- * @param array aArray Array to consider, will be modified by reference (i.e. no return)
- * @param int iFrom From point
- * @param int iTo Insert point
- * @returns void
- */
- function fnArraySwitch( aArray, iFrom, iTo )
- {
- var mStore = aArray.splice( iFrom, 1 )[0];
- aArray.splice( iTo, 0, mStore );
- }
- /**
- * Switch the positions of nodes in a parent node (note this is specifically designed for
- * table rows). Note this function considers all element nodes under the parent!
- * @method fnDomSwitch
- * @param string sTag Tag to consider
- * @param int iFrom Element to move
- * @param int Point to element the element to (before this point), can be null for append
- * @returns void
- */
- function fnDomSwitch( nParent, iFrom, iTo )
- {
- var anTags = [];
- for ( var i=0, iLen=nParent.childNodes.length ; i<iLen ; i++ )
- {
- if ( nParent.childNodes[i].nodeType == 1 )
- {
- anTags.push( nParent.childNodes[i] );
- }
- }
- var nStore = anTags[ iFrom ];
- if ( iTo !== null )
- {
- nParent.insertBefore( nStore, anTags[iTo] );
- }
- else
- {
- nParent.appendChild( nStore );
- }
- }
- /**
- * Plug-in for DataTables which will reorder the internal column structure by taking the column
- * from one position (iFrom) and insert it into a given point (iTo).
- * @method $.fn.dataTableExt.oApi.fnColReorder
- * @param object oSettings DataTables settings object - automatically added by DataTables!
- * @param int iFrom Take the column to be repositioned from this point
- * @param int iTo and insert it into this point
- * @param bool drop Indicate if the reorder is the final one (i.e. a drop)
- * not a live reorder
- * @param bool invalidateRows speeds up processing if false passed
- * @returns void
- */
- $.fn.dataTableExt.oApi.fnColReorder = function ( oSettings, iFrom, iTo, drop, invalidateRows )
- {
- var i, iLen, j, jLen, jen, iCols=oSettings.aoColumns.length, nTrs, oCol;
- var attrMap = function ( obj, prop, mapping ) {
- if ( ! obj[ prop ] || typeof obj[ prop ] === 'function' ) {
- return;
- }
- var a = obj[ prop ].split('.');
- var num = a.shift();
- if ( isNaN( num*1 ) ) {
- return;
- }
- obj[ prop ] = mapping[ num*1 ]+'.'+a.join('.');
- };
- /* Sanity check in the input */
- if ( iFrom == iTo )
- {
- /* Pointless reorder */
- return;
- }
- if ( iFrom < 0 || iFrom >= iCols )
- {
- this.oApi._fnLog( oSettings, 1, "ColReorder 'from' index is out of bounds: "+iFrom );
- return;
- }
- if ( iTo < 0 || iTo >= iCols )
- {
- this.oApi._fnLog( oSettings, 1, "ColReorder 'to' index is out of bounds: "+iTo );
- return;
- }
- /*
- * Calculate the new column array index, so we have a mapping between the old and new
- */
- var aiMapping = [];
- for ( i=0, iLen=iCols ; i<iLen ; i++ )
- {
- aiMapping[i] = i;
- }
- fnArraySwitch( aiMapping, iFrom, iTo );
- var aiInvertMapping = fnInvertKeyValues( aiMapping );
- /*
- * Convert all internal indexing to the new column order indexes
- */
- /* Sorting */
- for ( i=0, iLen=oSettings.aaSorting.length ; i<iLen ; i++ )
- {
- oSettings.aaSorting[i][0] = aiInvertMapping[ oSettings.aaSorting[i][0] ];
- }
- /* Fixed sorting */
- if ( oSettings.aaSortingFixed !== null )
- {
- for ( i=0, iLen=oSettings.aaSortingFixed.length ; i<iLen ; i++ )
- {
- oSettings.aaSortingFixed[i][0] = aiInvertMapping[ oSettings.aaSortingFixed[i][0] ];
- }
- }
- /* Data column sorting (the column which the sort for a given column should take place on) */
- for ( i=0, iLen=iCols ; i<iLen ; i++ )
- {
- oCol = oSettings.aoColumns[i];
- for ( j=0, jLen=oCol.aDataSort.length ; j<jLen ; j++ )
- {
- oCol.aDataSort[j] = aiInvertMapping[ oCol.aDataSort[j] ];
- }
- // Update the column indexes
- oCol.idx = aiInvertMapping[ oCol.idx ];
- }
- // Update 1.10 optimised sort class removal variable
- $.each( oSettings.aLastSort, function (i, val) {
- oSettings.aLastSort[i].src = aiInvertMapping[ val.src ];
- } );
- /* Update the Get and Set functions for each column */
- for ( i=0, iLen=iCols ; i<iLen ; i++ )
- {
- oCol = oSettings.aoColumns[i];
- if ( typeof oCol.mData == 'number' ) {
- oCol.mData = aiInvertMapping[ oCol.mData ];
- }
- else if ( $.isPlainObject( oCol.mData ) ) {
- // HTML5 data sourced
- attrMap( oCol.mData, '_', aiInvertMapping );
- attrMap( oCol.mData, 'filter', aiInvertMapping );
- attrMap( oCol.mData, 'sort', aiInvertMapping );
- attrMap( oCol.mData, 'type', aiInvertMapping );
- }
- }
- /*
- * Move the DOM elements
- */
- if ( oSettings.aoColumns[iFrom].bVisible )
- {
- /* Calculate the current visible index and the point to insert the node before. The insert
- * before needs to take into account that there might not be an element to insert before,
- * in which case it will be null, and an appendChild should be used
- */
- var iVisibleIndex = this.oApi._fnColumnIndexToVisible( oSettings, iFrom );
- var iInsertBeforeIndex = null;
- i = iTo < iFrom ? iTo : iTo + 1;
- while ( iInsertBeforeIndex === null && i < iCols )
- {
- iInsertBeforeIndex = this.oApi._fnColumnIndexToVisible( oSettings, i );
- i++;
- }
- /* Header */
- nTrs = oSettings.nTHead.getElementsByTagName('tr');
- for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
- {
- fnDomSwitch( nTrs[i], iVisibleIndex, iInsertBeforeIndex );
- }
- /* Footer */
- if ( oSettings.nTFoot !== null )
- {
- nTrs = oSettings.nTFoot.getElementsByTagName('tr');
- for ( i=0, iLen=nTrs.length ; i<iLen ; i++ )
- {
- fnDomSwitch( nTrs[i], iVisibleIndex, iInsertBeforeIndex );
- }
- }
- /* Body */
- for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
- {
- if ( oSettings.aoData[i].nTr !== null )
- {
- fnDomSwitch( oSettings.aoData[i].nTr, iVisibleIndex, iInsertBeforeIndex );
- }
- }
- }
- /*
- * Move the internal array elements
- */
- /* Columns */
- fnArraySwitch( oSettings.aoColumns, iFrom, iTo );
- // regenerate the get / set functions
- for ( i=0, iLen=iCols ; i<iLen ; i++ ) {
- oSettings.oApi._fnColumnOptions( oSettings, i, {} );
- }
- /* Search columns */
- fnArraySwitch( oSettings.aoPreSearchCols, iFrom, iTo );
- /* Array array - internal data anodes cache */
- for ( i=0, iLen=oSettings.aoData.length ; i<iLen ; i++ )
- {
- var data = oSettings.aoData[i];
- var cells = data.anCells;
- if ( cells ) {
- fnArraySwitch( cells, iFrom, iTo );
- // Longer term, should this be moved into the DataTables' invalidate
- // methods?
- for ( j=0, jen=cells.length ; j<jen ; j++ ) {
- if ( cells[j] && cells[j]._DT_CellIndex ) {
- cells[j]._DT_CellIndex.column = j;
- }
- }
- }
- // For DOM sourced data, the invalidate will reread the cell into
- // the data array, but for data sources as an array, they need to
- // be flipped
- if ( data.src !== 'dom' && $.isArray( data._aData ) ) {
- fnArraySwitch( data._aData, iFrom, iTo );
- }
- }
- /* Reposition the header elements in the header layout array */
- for ( i=0, iLen=oSettings.aoHeader.length ; i<iLen ; i++ )
- {
- fnArraySwitch( oSettings.aoHeader[i], iFrom, iTo );
- }
- if ( oSettings.aoFooter !== null )
- {
- for ( i=0, iLen=oSettings.aoFooter.length ; i<iLen ; i++ )
- {
- fnArraySwitch( oSettings.aoFooter[i], iFrom, iTo );
- }
- }
- if ( invalidateRows || invalidateRows === undefined )
- {
- $.fn.dataTable.Api( oSettings ).rows().invalidate();
- }
- /*
- * Update DataTables' event handlers
- */
- /* Sort listener */
- for ( i=0, iLen=iCols ; i<iLen ; i++ )
- {
- $(oSettings.aoColumns[i].nTh).off('.DT');
- this.oApi._fnSortAttachListener( oSettings, oSettings.aoColumns[i].nTh, i );
- }
- /* Fire an event so other plug-ins can update */
- $(oSettings.oInstance).trigger( 'column-reorder.dt', [ oSettings, {
- from: iFrom,
- to: iTo,
- mapping: aiInvertMapping,
- drop: drop,
- // Old style parameters for compatibility
- iFrom: iFrom,
- iTo: iTo,
- aiInvertMapping: aiInvertMapping
- } ] );
- };
- /**
- * ColReorder provides column visibility control for DataTables
- * @class ColReorder
- * @constructor
- * @param {object} dt DataTables settings object
- * @param {object} opts ColReorder options
- */
- var ColReorder = function( dt, opts )
- {
- var settings = new $.fn.dataTable.Api( dt ).settings()[0];
- // Ensure that we can't initialise on the same table twice
- if ( settings._colReorder ) {
- return settings._colReorder;
- }
- // Allow the options to be a boolean for defaults
- if ( opts === true ) {
- opts = {};
- }
- // Convert from camelCase to Hungarian, just as DataTables does
- var camelToHungarian = $.fn.dataTable.camelToHungarian;
- if ( camelToHungarian ) {
- camelToHungarian( ColReorder.defaults, ColReorder.defaults, true );
- camelToHungarian( ColReorder.defaults, opts || {} );
- }
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Public class variables
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * @namespace Settings object which contains customisable information for ColReorder instance
- */
- this.s = {
- /**
- * DataTables settings object
- * @property dt
- * @type Object
- * @default null
- */
- "dt": null,
- /**
- * Enable flag
- * @property dt
- * @type Object
- * @default null
- */
- "enable": null,
- /**
- * Initialisation object used for this instance
- * @property init
- * @type object
- * @default {}
- */
- "init": $.extend( true, {}, ColReorder.defaults, opts ),
- /**
- * Number of columns to fix (not allow to be reordered)
- * @property fixed
- * @type int
- * @default 0
- */
- "fixed": 0,
- /**
- * Number of columns to fix counting from right (not allow to be reordered)
- * @property fixedRight
- * @type int
- * @default 0
- */
- "fixedRight": 0,
- /**
- * Callback function for once the reorder has been done
- * @property reorderCallback
- * @type function
- * @default null
- */
- "reorderCallback": null,
- /**
- * @namespace Information used for the mouse drag
- */
- "mouse": {
- "startX": -1,
- "startY": -1,
- "offsetX": -1,
- "offsetY": -1,
- "target": -1,
- "targetIndex": -1,
- "fromIndex": -1
- },
- /**
- * Information which is used for positioning the insert cusor and knowing where to do the
- * insert. Array of objects with the properties:
- * x: x-axis position
- * to: insert point
- * @property aoTargets
- * @type array
- * @default []
- */
- "aoTargets": []
- };
- /**
- * @namespace Common and useful DOM elements for the class instance
- */
- this.dom = {
- /**
- * Dragging element (the one the mouse is moving)
- * @property drag
- * @type element
- * @default null
- */
- "drag": null,
- /**
- * The insert cursor
- * @property pointer
- * @type element
- * @default null
- */
- "pointer": null
- };
- /* Constructor logic */
- this.s.enable = this.s.init.bEnable;
- this.s.dt = settings;
- this.s.dt._colReorder = this;
- this._fnConstruct();
- return this;
- };
- $.extend( ColReorder.prototype, {
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Public methods
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * Enable / disable end user interaction
- */
- fnEnable: function ( flag )
- {
- if ( flag === false ) {
- return fnDisable();
- }
- this.s.enable = true;
- },
- /**
- * Disable end user interaction
- */
- fnDisable: function ()
- {
- this.s.enable = false;
- },
- /**
- * Reset the column ordering to the original ordering that was detected on
- * start up.
- * @return {this} Returns `this` for chaining.
- *
- * @example
- * // DataTables initialisation with ColReorder
- * var table = $('#example').dataTable( {
- * "sDom": 'Rlfrtip'
- * } );
- *
- * // Add click event to a button to reset the ordering
- * $('#resetOrdering').click( function (e) {
- * e.preventDefault();
- * $.fn.dataTable.ColReorder( table ).fnReset();
- * } );
- */
- "fnReset": function ()
- {
- this._fnOrderColumns( this.fnOrder() );
- return this;
- },
- /**
- * `Deprecated` - Get the current order of the columns, as an array.
- * @return {array} Array of column identifiers
- * @deprecated `fnOrder` should be used in preference to this method.
- * `fnOrder` acts as a getter/setter.
- */
- "fnGetCurrentOrder": function ()
- {
- return this.fnOrder();
- },
- /**
- * Get the current order of the columns, as an array. Note that the values
- * given in the array are unique identifiers for each column. Currently
- * these are the original ordering of the columns that was detected on
- * start up, but this could potentially change in future.
- * @return {array} Array of column identifiers
- *
- * @example
- * // Get column ordering for the table
- * var order = $.fn.dataTable.ColReorder( dataTable ).fnOrder();
- *//**
- * Set the order of the columns, from the positions identified in the
- * ordering array given. Note that ColReorder takes a brute force approach
- * to reordering, so it is possible multiple reordering events will occur
- * before the final order is settled upon.
- * @param {array} [set] Array of column identifiers in the new order. Note
- * that every column must be included, uniquely, in this array.
- * @return {this} Returns `this` for chaining.
- *
- * @example
- * // Swap the first and second columns
- * $.fn.dataTable.ColReorder( dataTable ).fnOrder( [1, 0, 2, 3, 4] );
- *
- * @example
- * // Move the first column to the end for the table `#example`
- * var curr = $.fn.dataTable.ColReorder( '#example' ).fnOrder();
- * var first = curr.shift();
- * curr.push( first );
- * $.fn.dataTable.ColReorder( '#example' ).fnOrder( curr );
- *
- * @example
- * // Reverse the table's order
- * $.fn.dataTable.ColReorder( '#example' ).fnOrder(
- * $.fn.dataTable.ColReorder( '#example' ).fnOrder().reverse()
- * );
- */
- "fnOrder": function ( set, original )
- {
- var a = [], i, ien, j, jen;
- var columns = this.s.dt.aoColumns;
- if ( set === undefined ){
- for ( i=0, ien=columns.length ; i<ien ; i++ ) {
- a.push( columns[i]._ColReorder_iOrigCol );
- }
- return a;
- }
- // The order given is based on the original indexes, rather than the
- // existing ones, so we need to translate from the original to current
- // before then doing the order
- if ( original ) {
- var order = this.fnOrder();
- for ( i=0, ien=set.length ; i<ien ; i++ ) {
- a.push( $.inArray( set[i], order ) );
- }
- set = a;
- }
- this._fnOrderColumns( fnInvertKeyValues( set ) );
- return this;
- },
- /**
- * Convert from the original column index, to the original
- *
- * @param {int|array} idx Index(es) to convert
- * @param {string} dir Transpose direction - `fromOriginal` / `toCurrent`
- * or `'toOriginal` / `fromCurrent`
- * @return {int|array} Converted values
- */
- fnTranspose: function ( idx, dir )
- {
- if ( ! dir ) {
- dir = 'toCurrent';
- }
- var order = this.fnOrder();
- var columns = this.s.dt.aoColumns;
- if ( dir === 'toCurrent' ) {
- // Given an original index, want the current
- return ! $.isArray( idx ) ?
- $.inArray( idx, order ) :
- $.map( idx, function ( index ) {
- return $.inArray( index, order );
- } );
- }
- else {
- // Given a current index, want the original
- return ! $.isArray( idx ) ?
- columns[idx]._ColReorder_iOrigCol :
- $.map( idx, function ( index ) {
- return columns[index]._ColReorder_iOrigCol;
- } );
- }
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Private methods (they are of course public in JS, but recommended as private)
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * Constructor logic
- * @method _fnConstruct
- * @returns void
- * @private
- */
- "_fnConstruct": function ()
- {
- var that = this;
- var iLen = this.s.dt.aoColumns.length;
- var table = this.s.dt.nTable;
- var i;
- /* Columns discounted from reordering - counting left to right */
- if ( this.s.init.iFixedColumns )
- {
- this.s.fixed = this.s.init.iFixedColumns;
- }
- if ( this.s.init.iFixedColumnsLeft )
- {
- this.s.fixed = this.s.init.iFixedColumnsLeft;
- }
- /* Columns discounted from reordering - counting right to left */
- this.s.fixedRight = this.s.init.iFixedColumnsRight ?
- this.s.init.iFixedColumnsRight :
- 0;
- /* Drop callback initialisation option */
- if ( this.s.init.fnReorderCallback )
- {
- this.s.reorderCallback = this.s.init.fnReorderCallback;
- }
- /* Add event handlers for the drag and drop, and also mark the original column order */
- for ( i = 0; i < iLen; i++ )
- {
- if ( i > this.s.fixed-1 && i < iLen - this.s.fixedRight )
- {
- this._fnMouseListener( i, this.s.dt.aoColumns[i].nTh );
- }
- /* Mark the original column order for later reference */
- this.s.dt.aoColumns[i]._ColReorder_iOrigCol = i;
- }
- /* State saving */
- this.s.dt.oApi._fnCallbackReg( this.s.dt, 'aoStateSaveParams', function (oS, oData) {
- that._fnStateSave.call( that, oData );
- }, "ColReorder_State" );
- /* An initial column order has been specified */
- var aiOrder = null;
- if ( this.s.init.aiOrder )
- {
- aiOrder = this.s.init.aiOrder.slice();
- }
- /* State loading, overrides the column order given */
- if ( this.s.dt.oLoadedState && typeof this.s.dt.oLoadedState.ColReorder != 'undefined' &&
- this.s.dt.oLoadedState.ColReorder.length == this.s.dt.aoColumns.length )
- {
- aiOrder = this.s.dt.oLoadedState.ColReorder;
- }
- /* If we have an order to apply - do so */
- if ( aiOrder )
- {
- /* We might be called during or after the DataTables initialisation. If before, then we need
- * to wait until the draw is done, if after, then do what we need to do right away
- */
- if ( !that.s.dt._bInitComplete )
- {
- var bDone = false;
- $(table).on( 'draw.dt.colReorder', function () {
- if ( !that.s.dt._bInitComplete && !bDone )
- {
- bDone = true;
- var resort = fnInvertKeyValues( aiOrder );
- that._fnOrderColumns.call( that, resort );
- }
- } );
- }
- else
- {
- var resort = fnInvertKeyValues( aiOrder );
- that._fnOrderColumns.call( that, resort );
- }
- }
- else {
- this._fnSetColumnIndexes();
- }
- // Destroy clean up
- $(table).on( 'destroy.dt.colReorder', function () {
- $(table).off( 'destroy.dt.colReorder draw.dt.colReorder' );
- $.each( that.s.dt.aoColumns, function (i, column) {
- $(column.nTh).off('.ColReorder');
- $(column.nTh).removeAttr('data-column-index');
- } );
- that.s.dt._colReorder = null;
- that.s = null;
- } );
- },
- /**
- * Set the column order from an array
- * @method _fnOrderColumns
- * @param array a An array of integers which dictate the column order that should be applied
- * @returns void
- * @private
- */
- "_fnOrderColumns": function ( a )
- {
- var changed = false;
- if ( a.length != this.s.dt.aoColumns.length )
- {
- this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "ColReorder - array reorder does not "+
- "match known number of columns. Skipping." );
- return;
- }
- for ( var i=0, iLen=a.length ; i<iLen ; i++ )
- {
- var currIndex = $.inArray( i, a );
- if ( i != currIndex )
- {
- /* Reorder our switching array */
- fnArraySwitch( a, currIndex, i );
- /* Do the column reorder in the table */
- this.s.dt.oInstance.fnColReorder( currIndex, i, true, false );
- changed = true;
- }
- }
- this._fnSetColumnIndexes();
- // Has anything actually changed? If not, then nothing else to do
- if ( ! changed ) {
- return;
- }
- $.fn.dataTable.Api( this.s.dt ).rows().invalidate();
- /* When scrolling we need to recalculate the column sizes to allow for the shift */
- if ( this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" )
- {
- this.s.dt.oInstance.fnAdjustColumnSizing( false );
- }
- /* Save the state */
- this.s.dt.oInstance.oApi._fnSaveState( this.s.dt );
- if ( this.s.reorderCallback !== null )
- {
- this.s.reorderCallback.call( this );
- }
- },
- /**
- * Because we change the indexes of columns in the table, relative to their starting point
- * we need to reorder the state columns to what they are at the starting point so we can
- * then rearrange them again on state load!
- * @method _fnStateSave
- * @param object oState DataTables state
- * @returns string JSON encoded cookie string for DataTables
- * @private
- */
- "_fnStateSave": function ( oState )
- {
- var i, iLen, aCopy, iOrigColumn;
- var oSettings = this.s.dt;
- var columns = oSettings.aoColumns;
- oState.ColReorder = [];
- /* Sorting */
- if ( oState.aaSorting ) {
- // 1.10.0-
- for ( i=0 ; i<oState.aaSorting.length ; i++ ) {
- oState.aaSorting[i][0] = columns[ oState.aaSorting[i][0] ]._ColReorder_iOrigCol;
- }
- var aSearchCopy = $.extend( true, [], oState.aoSearchCols );
- for ( i=0, iLen=columns.length ; i<iLen ; i++ )
- {
- iOrigColumn = columns[i]._ColReorder_iOrigCol;
- /* Column filter */
- oState.aoSearchCols[ iOrigColumn ] = aSearchCopy[i];
- /* Visibility */
- oState.abVisCols[ iOrigColumn ] = columns[i].bVisible;
- /* Column reordering */
- oState.ColReorder.push( iOrigColumn );
- }
- }
- else if ( oState.order ) {
- // 1.10.1+
- for ( i=0 ; i<oState.order.length ; i++ ) {
- oState.order[i][0] = columns[ oState.order[i][0] ]._ColReorder_iOrigCol;
- }
- var stateColumnsCopy = $.extend( true, [], oState.columns );
- for ( i=0, iLen=columns.length ; i<iLen ; i++ )
- {
- iOrigColumn = columns[i]._ColReorder_iOrigCol;
- /* Columns */
- oState.columns[ iOrigColumn ] = stateColumnsCopy[i];
- /* Column reordering */
- oState.ColReorder.push( iOrigColumn );
- }
- }
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Mouse drop and drag
- */
- /**
- * Add a mouse down listener to a particluar TH element
- * @method _fnMouseListener
- * @param int i Column index
- * @param element nTh TH element clicked on
- * @returns void
- * @private
- */
- "_fnMouseListener": function ( i, nTh )
- {
- var that = this;
- $(nTh)
- .on( 'mousedown.ColReorder', function (e) {
- if ( that.s.enable ) {
- that._fnMouseDown.call( that, e, nTh );
- }
- } )
- .on( 'touchstart.ColReorder', function (e) {
- if ( that.s.enable ) {
- that._fnMouseDown.call( that, e, nTh );
- }
- } );
- },
- /**
- * Mouse down on a TH element in the table header
- * @method _fnMouseDown
- * @param event e Mouse event
- * @param element nTh TH element to be dragged
- * @returns void
- * @private
- */
- "_fnMouseDown": function ( e, nTh )
- {
- var that = this;
- /* Store information about the mouse position */
- var target = $(e.target).closest('th, td');
- var offset = target.offset();
- var idx = parseInt( $(nTh).attr('data-column-index'), 10 );
- if ( idx === undefined ) {
- return;
- }
- this.s.mouse.startX = this._fnCursorPosition( e, 'pageX' );
- this.s.mouse.startY = this._fnCursorPosition( e, 'pageY' );
- this.s.mouse.offsetX = this._fnCursorPosition( e, 'pageX' ) - offset.left;
- this.s.mouse.offsetY = this._fnCursorPosition( e, 'pageY' ) - offset.top;
- this.s.mouse.target = this.s.dt.aoColumns[ idx ].nTh;//target[0];
- this.s.mouse.targetIndex = idx;
- this.s.mouse.fromIndex = idx;
- this._fnRegions();
- /* Add event handlers to the document */
- $(document)
- .on( 'mousemove.ColReorder touchmove.ColReorder', function (e) {
- that._fnMouseMove.call( that, e );
- } )
- .on( 'mouseup.ColReorder touchend.ColReorder', function (e) {
- that._fnMouseUp.call( that, e );
- } );
- },
- /**
- * Deal with a mouse move event while dragging a node
- * @method _fnMouseMove
- * @param event e Mouse event
- * @returns void
- * @private
- */
- "_fnMouseMove": function ( e )
- {
- var that = this;
- if ( this.dom.drag === null )
- {
- /* Only create the drag element if the mouse has moved a specific distance from the start
- * point - this allows the user to make small mouse movements when sorting and not have a
- * possibly confusing drag element showing up
- */
- if ( Math.pow(
- Math.pow(this._fnCursorPosition( e, 'pageX') - this.s.mouse.startX, 2) +
- Math.pow(this._fnCursorPosition( e, 'pageY') - this.s.mouse.startY, 2), 0.5 ) < 5 )
- {
- return;
- }
- this._fnCreateDragNode();
- }
- /* Position the element - we respect where in the element the click occured */
- this.dom.drag.css( {
- left: this._fnCursorPosition( e, 'pageX' ) - this.s.mouse.offsetX,
- top: this._fnCursorPosition( e, 'pageY' ) - this.s.mouse.offsetY
- } );
- /* Based on the current mouse position, calculate where the insert should go */
- var bSet = false;
- var lastToIndex = this.s.mouse.toIndex;
- for ( var i=1, iLen=this.s.aoTargets.length ; i<iLen ; i++ )
- {
- if ( this._fnCursorPosition(e, 'pageX') < this.s.aoTargets[i-1].x + ((this.s.aoTargets[i].x-this.s.aoTargets[i-1].x)/2) )
- {
- this.dom.pointer.css( 'left', this.s.aoTargets[i-1].x );
- this.s.mouse.toIndex = this.s.aoTargets[i-1].to;
- bSet = true;
- break;
- }
- }
- // The insert element wasn't positioned in the array (less than
- // operator), so we put it at the end
- if ( !bSet )
- {
- this.dom.pointer.css( 'left', this.s.aoTargets[this.s.aoTargets.length-1].x );
- this.s.mouse.toIndex = this.s.aoTargets[this.s.aoTargets.length-1].to;
- }
- // Perform reordering if realtime updating is on and the column has moved
- if ( this.s.init.bRealtime && lastToIndex !== this.s.mouse.toIndex ) {
- this.s.dt.oInstance.fnColReorder( this.s.mouse.fromIndex, this.s.mouse.toIndex );
- this.s.mouse.fromIndex = this.s.mouse.toIndex;
- // Not great for performance, but required to keep everything in alignment
- if ( this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" )
- {
- this.s.dt.oInstance.fnAdjustColumnSizing( false );
- }
- this._fnRegions();
- }
- },
- /**
- * Finish off the mouse drag and insert the column where needed
- * @method _fnMouseUp
- * @param event e Mouse event
- * @returns void
- * @private
- */
- "_fnMouseUp": function ( e )
- {
- var that = this;
- $(document).off( '.ColReorder' );
- if ( this.dom.drag !== null )
- {
- /* Remove the guide elements */
- this.dom.drag.remove();
- this.dom.pointer.remove();
- this.dom.drag = null;
- this.dom.pointer = null;
- /* Actually do the reorder */
- this.s.dt.oInstance.fnColReorder( this.s.mouse.fromIndex, this.s.mouse.toIndex, true );
- this._fnSetColumnIndexes();
- /* When scrolling we need to recalculate the column sizes to allow for the shift */
- if ( this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "" )
- {
- this.s.dt.oInstance.fnAdjustColumnSizing( false );
- }
- /* Save the state */
- this.s.dt.oInstance.oApi._fnSaveState( this.s.dt );
- if ( this.s.reorderCallback !== null )
- {
- this.s.reorderCallback.call( this );
- }
- }
- },
- /**
- * Calculate a cached array with the points of the column inserts, and the
- * 'to' points
- * @method _fnRegions
- * @returns void
- * @private
- */
- "_fnRegions": function ()
- {
- var aoColumns = this.s.dt.aoColumns;
- this.s.aoTargets.splice( 0, this.s.aoTargets.length );
- this.s.aoTargets.push( {
- "x": $(this.s.dt.nTable).offset().left,
- "to": 0
- } );
- var iToPoint = 0;
- var total = this.s.aoTargets[0].x;
- for ( var i=0, iLen=aoColumns.length ; i<iLen ; i++ )
- {
- /* For the column / header in question, we want it's position to remain the same if the
- * position is just to it's immediate left or right, so we only increment the counter for
- * other columns
- */
- if ( i != this.s.mouse.fromIndex )
- {
- iToPoint++;
- }
- if ( aoColumns[i].bVisible && aoColumns[i].nTh.style.display !=='none' )
- {
- total += $(aoColumns[i].nTh).outerWidth();
- this.s.aoTargets.push( {
- "x": total,
- "to": iToPoint
- } );
- }
- }
- /* Disallow columns for being reordered by drag and drop, counting right to left */
- if ( this.s.fixedRight !== 0 )
- {
- this.s.aoTargets.splice( this.s.aoTargets.length - this.s.fixedRight );
- }
- /* Disallow columns for being reordered by drag and drop, counting left to right */
- if ( this.s.fixed !== 0 )
- {
- this.s.aoTargets.splice( 0, this.s.fixed );
- }
- },
- /**
- * Copy the TH element that is being drags so the user has the idea that they are actually
- * moving it around the page.
- * @method _fnCreateDragNode
- * @returns void
- * @private
- */
- "_fnCreateDragNode": function ()
- {
- var scrolling = this.s.dt.oScroll.sX !== "" || this.s.dt.oScroll.sY !== "";
- var origCell = this.s.dt.aoColumns[ this.s.mouse.targetIndex ].nTh;
- var origTr = origCell.parentNode;
- var origThead = origTr.parentNode;
- var origTable = origThead.parentNode;
- var cloneCell = $(origCell).clone();
- // This is a slightly odd combination of jQuery and DOM, but it is the
- // fastest and least resource intensive way I could think of cloning
- // the table with just a single header cell in it.
- this.dom.drag = $(origTable.cloneNode(false))
- .addClass( 'DTCR_clonedTable' )
- .append(
- $(origThead.cloneNode(false)).append(
- $(origTr.cloneNode(false)).append(
- cloneCell[0]
- )
- )
- )
- .css( {
- position: 'absolute',
- top: 0,
- left: 0,
- width: $(origCell).outerWidth(),
- height: $(origCell).outerHeight()
- } )
- .appendTo( 'body' );
- this.dom.pointer = $('<div></div>')
- .addClass( 'DTCR_pointer' )
- .css( {
- position: 'absolute',
- top: scrolling ?
- $('div.dataTables_scroll', this.s.dt.nTableWrapper).offset().top :
- $(this.s.dt.nTable).offset().top,
- height : scrolling ?
- $('div.dataTables_scroll', this.s.dt.nTableWrapper).height() :
- $(this.s.dt.nTable).height()
- } )
- .appendTo( 'body' );
- },
- /**
- * Add a data attribute to the column headers, so we know the index of
- * the row to be reordered. This allows fast detection of the index, and
- * for this plug-in to work with FixedHeader which clones the nodes.
- * @private
- */
- "_fnSetColumnIndexes": function ()
- {
- $.each( this.s.dt.aoColumns, function (i, column) {
- $(column.nTh).attr('data-column-index', i);
- } );
- },
- /**
- * Get cursor position regardless of mouse or touch input
- * @param {Event} e jQuery Event
- * @param {string} prop Property to get
- * @return {number} Value
- */
- _fnCursorPosition: function ( e, prop ) {
- if ( e.type.indexOf('touch') !== -1 ) {
- return e.originalEvent.touches[0][ prop ];
- }
- return e[ prop ];
- }
- } );
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Static parameters
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * ColReorder default settings for initialisation
- * @namespace
- * @static
- */
- ColReorder.defaults = {
- /**
- * Predefined ordering for the columns that will be applied automatically
- * on initialisation. If not specified then the order that the columns are
- * found to be in the HTML is the order used.
- * @type array
- * @default null
- * @static
- */
- aiOrder: null,
- /**
- * ColReorder enable on initialisation
- * @type boolean
- * @default true
- * @static
- */
- bEnable: true,
- /**
- * Redraw the table's column ordering as the end user draws the column
- * (`true`) or wait until the mouse is released (`false` - default). Note
- * that this will perform a redraw on each reordering, which involves an
- * Ajax request each time if you are using server-side processing in
- * DataTables.
- * @type boolean
- * @default false
- * @static
- */
- bRealtime: true,
- /**
- * Indicate how many columns should be fixed in position (counting from the
- * left). This will typically be 1 if used, but can be as high as you like.
- * @type int
- * @default 0
- * @static
- */
- iFixedColumnsLeft: 0,
- /**
- * As `iFixedColumnsRight` but counting from the right.
- * @type int
- * @default 0
- * @static
- */
- iFixedColumnsRight: 0,
- /**
- * Callback function that is fired when columns are reordered. The `column-
- * reorder` event is preferred over this callback
- * @type function():void
- * @default null
- * @static
- */
- fnReorderCallback: null
- };
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constants
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * ColReorder version
- * @constant version
- * @type String
- * @default As code
- */
- ColReorder.version = "1.5.1";
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables interfaces
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- // Expose
- $.fn.dataTable.ColReorder = ColReorder;
- $.fn.DataTable.ColReorder = ColReorder;
- // Register a new feature with DataTables
- if ( typeof $.fn.dataTable == "function" &&
- typeof $.fn.dataTableExt.fnVersionCheck == "function" &&
- $.fn.dataTableExt.fnVersionCheck('1.10.8') )
- {
- $.fn.dataTableExt.aoFeatures.push( {
- "fnInit": function( settings ) {
- var table = settings.oInstance;
- if ( ! settings._colReorder ) {
- var dtInit = settings.oInit;
- var opts = dtInit.colReorder || dtInit.oColReorder || {};
- new ColReorder( settings, opts );
- }
- else {
- table.oApi._fnLog( settings, 1, "ColReorder attempted to initialise twice. Ignoring second" );
- }
- return null; /* No node for DataTables to insert */
- },
- "cFeature": "R",
- "sFeature": "ColReorder"
- } );
- }
- else {
- alert( "Warning: ColReorder requires DataTables 1.10.8 or greater - www.datatables.net/download");
- }
- // Attach a listener to the document which listens for DataTables initialisation
- // events so we can automatically initialise
- $(document).on( 'preInit.dt.colReorder', function (e, settings) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- var init = settings.oInit.colReorder;
- var defaults = DataTable.defaults.colReorder;
- if ( init || defaults ) {
- var opts = $.extend( {}, init, defaults );
- if ( init !== false ) {
- new ColReorder( settings, opts );
- }
- }
- } );
- // API augmentation
- $.fn.dataTable.Api.register( 'colReorder.reset()', function () {
- return this.iterator( 'table', function ( ctx ) {
- ctx._colReorder.fnReset();
- } );
- } );
- $.fn.dataTable.Api.register( 'colReorder.order()', function ( set, original ) {
- if ( set ) {
- return this.iterator( 'table', function ( ctx ) {
- ctx._colReorder.fnOrder( set, original );
- } );
- }
- return this.context.length ?
- this.context[0]._colReorder.fnOrder() :
- null;
- } );
- $.fn.dataTable.Api.register( 'colReorder.transpose()', function ( idx, dir ) {
- return this.context.length && this.context[0]._colReorder ?
- this.context[0]._colReorder.fnTranspose( idx, dir ) :
- idx;
- } );
- $.fn.dataTable.Api.register( 'colReorder.move()', function( from, to, drop, invalidateRows ) {
- if (this.context.length) {
- this.context[0]._colReorder.s.dt.oInstance.fnColReorder( from, to, drop, invalidateRows );
- }
- return this;
- } );
- $.fn.dataTable.Api.register( 'colReorder.enable()', function( flag ) {
- return this.iterator( 'table', function ( ctx ) {
- if ( ctx._colReorder ) {
- ctx._colReorder.fnEnable( flag );
- }
- } );
- } );
- $.fn.dataTable.Api.register( 'colReorder.disable()', function() {
- return this.iterator( 'table', function ( ctx ) {
- if ( ctx._colReorder ) {
- ctx._colReorder.fnDisable();
- }
- } );
- } );
- return ColReorder;
- }));
- /*! Bootstrap 4 styling wrapper for ColReorder
- * ©2018 SpryMedia Ltd - datatables.net/license
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net-bs4', 'datatables.net-colreorder'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net-bs4')(root, $).$;
- }
- if ( ! $.fn.dataTable.ColReorder ) {
- require('datatables.net-colreorder')(root, $);
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- return $.fn.dataTable;
- }));
- /*! FixedColumns 3.2.6
- * ©2010-2018 SpryMedia Ltd - datatables.net/license
- */
- /**
- * @summary FixedColumns
- * @description Freeze columns in place on a scrolling DataTable
- * @version 3.2.6
- * @file dataTables.fixedColumns.js
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
- * @contact www.sprymedia.co.uk/contact
- * @copyright Copyright 2010-2018 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license/mit
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- var _firefoxScroll;
- /**
- * When making use of DataTables' x-axis scrolling feature, you may wish to
- * fix the left most column in place. This plug-in for DataTables provides
- * exactly this option (note for non-scrolling tables, please use the
- * FixedHeader plug-in, which can fix headers and footers). Key
- * features include:
- *
- * * Freezes the left or right most columns to the side of the table
- * * Option to freeze two or more columns
- * * Full integration with DataTables' scrolling options
- * * Speed - FixedColumns is fast in its operation
- *
- * @class
- * @constructor
- * @global
- * @param {object} dt DataTables instance. With DataTables 1.10 this can also
- * be a jQuery collection, a jQuery selector, DataTables API instance or
- * settings object.
- * @param {object} [init={}] Configuration object for FixedColumns. Options are
- * defined by {@link FixedColumns.defaults}
- *
- * @requires jQuery 1.7+
- * @requires DataTables 1.8.0+
- *
- * @example
- * var table = $('#example').dataTable( {
- * "scrollX": "100%"
- * } );
- * new $.fn.dataTable.fixedColumns( table );
- */
- var FixedColumns = function ( dt, init ) {
- var that = this;
- /* Sanity check - you just know it will happen */
- if ( ! ( this instanceof FixedColumns ) ) {
- alert( "FixedColumns warning: FixedColumns must be initialised with the 'new' keyword." );
- return;
- }
- if ( init === undefined || init === true ) {
- init = {};
- }
- // Use the DataTables Hungarian notation mapping method, if it exists to
- // provide forwards compatibility for camel case variables
- var camelToHungarian = $.fn.dataTable.camelToHungarian;
- if ( camelToHungarian ) {
- camelToHungarian( FixedColumns.defaults, FixedColumns.defaults, true );
- camelToHungarian( FixedColumns.defaults, init );
- }
- // v1.10 allows the settings object to be got form a number of sources
- var dtSettings = new $.fn.dataTable.Api( dt ).settings()[0];
- /**
- * Settings object which contains customisable information for FixedColumns instance
- * @namespace
- * @extends FixedColumns.defaults
- * @private
- */
- this.s = {
- /**
- * DataTables settings objects
- * @type object
- * @default Obtained from DataTables instance
- */
- "dt": dtSettings,
- /**
- * Number of columns in the DataTable - stored for quick access
- * @type int
- * @default Obtained from DataTables instance
- */
- "iTableColumns": dtSettings.aoColumns.length,
- /**
- * Original outer widths of the columns as rendered by DataTables - used to calculate
- * the FixedColumns grid bounding box
- * @type array.<int>
- * @default []
- */
- "aiOuterWidths": [],
- /**
- * Original inner widths of the columns as rendered by DataTables - used to apply widths
- * to the columns
- * @type array.<int>
- * @default []
- */
- "aiInnerWidths": [],
- /**
- * Is the document layout right-to-left
- * @type boolean
- */
- rtl: $(dtSettings.nTable).css('direction') === 'rtl'
- };
- /**
- * DOM elements used by the class instance
- * @namespace
- * @private
- *
- */
- this.dom = {
- /**
- * DataTables scrolling element
- * @type node
- * @default null
- */
- "scroller": null,
- /**
- * DataTables header table
- * @type node
- * @default null
- */
- "header": null,
- /**
- * DataTables body table
- * @type node
- * @default null
- */
- "body": null,
- /**
- * DataTables footer table
- * @type node
- * @default null
- */
- "footer": null,
- /**
- * Display grid elements
- * @namespace
- */
- "grid": {
- /**
- * Grid wrapper. This is the container element for the 3x3 grid
- * @type node
- * @default null
- */
- "wrapper": null,
- /**
- * DataTables scrolling element. This element is the DataTables
- * component in the display grid (making up the main table - i.e.
- * not the fixed columns).
- * @type node
- * @default null
- */
- "dt": null,
- /**
- * Left fixed column grid components
- * @namespace
- */
- "left": {
- "wrapper": null,
- "head": null,
- "body": null,
- "foot": null
- },
- /**
- * Right fixed column grid components
- * @namespace
- */
- "right": {
- "wrapper": null,
- "head": null,
- "body": null,
- "foot": null
- }
- },
- /**
- * Cloned table nodes
- * @namespace
- */
- "clone": {
- /**
- * Left column cloned table nodes
- * @namespace
- */
- "left": {
- /**
- * Cloned header table
- * @type node
- * @default null
- */
- "header": null,
- /**
- * Cloned body table
- * @type node
- * @default null
- */
- "body": null,
- /**
- * Cloned footer table
- * @type node
- * @default null
- */
- "footer": null
- },
- /**
- * Right column cloned table nodes
- * @namespace
- */
- "right": {
- /**
- * Cloned header table
- * @type node
- * @default null
- */
- "header": null,
- /**
- * Cloned body table
- * @type node
- * @default null
- */
- "body": null,
- /**
- * Cloned footer table
- * @type node
- * @default null
- */
- "footer": null
- }
- }
- };
- if ( dtSettings._oFixedColumns ) {
- throw 'FixedColumns already initialised on this table';
- }
- /* Attach the instance to the DataTables instance so it can be accessed easily */
- dtSettings._oFixedColumns = this;
- /* Let's do it */
- if ( ! dtSettings._bInitComplete )
- {
- dtSettings.oApi._fnCallbackReg( dtSettings, 'aoInitComplete', function () {
- that._fnConstruct( init );
- }, 'FixedColumns' );
- }
- else
- {
- this._fnConstruct( init );
- }
- };
- $.extend( FixedColumns.prototype , {
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Public methods
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * Update the fixed columns - including headers and footers. Note that FixedColumns will
- * automatically update the display whenever the host DataTable redraws.
- * @returns {void}
- * @example
- * var table = $('#example').dataTable( {
- * "scrollX": "100%"
- * } );
- * var fc = new $.fn.dataTable.fixedColumns( table );
- *
- * // at some later point when the table has been manipulated....
- * fc.fnUpdate();
- */
- "fnUpdate": function ()
- {
- this._fnDraw( true );
- },
- /**
- * Recalculate the resizes of the 3x3 grid that FixedColumns uses for display of the table.
- * This is useful if you update the width of the table container. Note that FixedColumns will
- * perform this function automatically when the window.resize event is fired.
- * @returns {void}
- * @example
- * var table = $('#example').dataTable( {
- * "scrollX": "100%"
- * } );
- * var fc = new $.fn.dataTable.fixedColumns( table );
- *
- * // Resize the table container and then have FixedColumns adjust its layout....
- * $('#content').width( 1200 );
- * fc.fnRedrawLayout();
- */
- "fnRedrawLayout": function ()
- {
- this._fnColCalc();
- this._fnGridLayout();
- this.fnUpdate();
- },
- /**
- * Mark a row such that it's height should be recalculated when using 'semiauto' row
- * height matching. This function will have no effect when 'none' or 'auto' row height
- * matching is used.
- * @param {Node} nTr TR element that should have it's height recalculated
- * @returns {void}
- * @example
- * var table = $('#example').dataTable( {
- * "scrollX": "100%"
- * } );
- * var fc = new $.fn.dataTable.fixedColumns( table );
- *
- * // manipulate the table - mark the row as needing an update then update the table
- * // this allows the redraw performed by DataTables fnUpdate to recalculate the row
- * // height
- * fc.fnRecalculateHeight();
- * table.fnUpdate( $('#example tbody tr:eq(0)')[0], ["insert date", 1, 2, 3 ... ]);
- */
- "fnRecalculateHeight": function ( nTr )
- {
- delete nTr._DTTC_iHeight;
- nTr.style.height = 'auto';
- },
- /**
- * Set the height of a given row - provides cross browser compatibility
- * @param {Node} nTarget TR element that should have it's height recalculated
- * @param {int} iHeight Height in pixels to set
- * @returns {void}
- * @example
- * var table = $('#example').dataTable( {
- * "scrollX": "100%"
- * } );
- * var fc = new $.fn.dataTable.fixedColumns( table );
- *
- * // You may want to do this after manipulating a row in the fixed column
- * fc.fnSetRowHeight( $('#example tbody tr:eq(0)')[0], 50 );
- */
- "fnSetRowHeight": function ( nTarget, iHeight )
- {
- nTarget.style.height = iHeight+"px";
- },
- /**
- * Get data index information about a row or cell in the table body.
- * This function is functionally identical to fnGetPosition in DataTables,
- * taking the same parameter (TH, TD or TR node) and returning exactly the
- * the same information (data index information). THe difference between
- * the two is that this method takes into account the fixed columns in the
- * table, so you can pass in nodes from the master table, or the cloned
- * tables and get the index position for the data in the main table.
- * @param {node} node TR, TH or TD element to get the information about
- * @returns {int} If nNode is given as a TR, then a single index is
- * returned, or if given as a cell, an array of [row index, column index
- * (visible), column index (all)] is given.
- */
- "fnGetPosition": function ( node )
- {
- var idx;
- var inst = this.s.dt.oInstance;
- if ( ! $(node).parents('.DTFC_Cloned').length )
- {
- // Not in a cloned table
- return inst.fnGetPosition( node );
- }
- else
- {
- // Its in the cloned table, so need to look up position
- if ( node.nodeName.toLowerCase() === 'tr' ) {
- idx = $(node).index();
- return inst.fnGetPosition( $('tr', this.s.dt.nTBody)[ idx ] );
- }
- else
- {
- var colIdx = $(node).index();
- idx = $(node.parentNode).index();
- var row = inst.fnGetPosition( $('tr', this.s.dt.nTBody)[ idx ] );
- return [
- row,
- colIdx,
- inst.oApi._fnVisibleToColumnIndex( this.s.dt, colIdx )
- ];
- }
- }
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Private methods (they are of course public in JS, but recommended as private)
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * Initialisation for FixedColumns
- * @param {Object} oInit User settings for initialisation
- * @returns {void}
- * @private
- */
- "_fnConstruct": function ( oInit )
- {
- var i, iLen, iWidth,
- that = this;
- /* Sanity checking */
- if ( typeof this.s.dt.oInstance.fnVersionCheck != 'function' ||
- this.s.dt.oInstance.fnVersionCheck( '1.8.0' ) !== true )
- {
- alert( "FixedColumns "+FixedColumns.VERSION+" required DataTables 1.8.0 or later. "+
- "Please upgrade your DataTables installation" );
- return;
- }
- if ( this.s.dt.oScroll.sX === "" )
- {
- this.s.dt.oInstance.oApi._fnLog( this.s.dt, 1, "FixedColumns is not needed (no "+
- "x-scrolling in DataTables enabled), so no action will be taken. Use 'FixedHeader' for "+
- "column fixing when scrolling is not enabled" );
- return;
- }
- /* Apply the settings from the user / defaults */
- this.s = $.extend( true, this.s, FixedColumns.defaults, oInit );
- /* Set up the DOM as we need it and cache nodes */
- var classes = this.s.dt.oClasses;
- this.dom.grid.dt = $(this.s.dt.nTable).parents('div.'+classes.sScrollWrapper)[0];
- this.dom.scroller = $('div.'+classes.sScrollBody, this.dom.grid.dt )[0];
- /* Set up the DOM that we want for the fixed column layout grid */
- this._fnColCalc();
- this._fnGridSetup();
- /* Event handlers */
- var mouseController;
- var mouseDown = false;
- // When the mouse is down (drag scroll) the mouse controller cannot
- // change, as the browser keeps the original element as the scrolling one
- $(this.s.dt.nTableWrapper).on( 'mousedown.DTFC', function (e) {
- if ( e.button === 0 ) {
- mouseDown = true;
- $(document).one( 'mouseup', function () {
- mouseDown = false;
- } );
- }
- } );
- // When the body is scrolled - scroll the left and right columns
- $(this.dom.scroller)
- .on( 'mouseover.DTFC touchstart.DTFC', function () {
- if ( ! mouseDown ) {
- mouseController = 'main';
- }
- } )
- .on( 'scroll.DTFC', function (e) {
- if ( ! mouseController && e.originalEvent ) {
- mouseController = 'main';
- }
- if ( mouseController === 'main' ) {
- if ( that.s.iLeftColumns > 0 ) {
- that.dom.grid.left.liner.scrollTop = that.dom.scroller.scrollTop;
- }
- if ( that.s.iRightColumns > 0 ) {
- that.dom.grid.right.liner.scrollTop = that.dom.scroller.scrollTop;
- }
- }
- } );
- var wheelType = 'onwheel' in document.createElement('div') ?
- 'wheel.DTFC' :
- 'mousewheel.DTFC';
- if ( that.s.iLeftColumns > 0 ) {
- // When scrolling the left column, scroll the body and right column
- $(that.dom.grid.left.liner)
- .on( 'mouseover.DTFC touchstart.DTFC', function () {
- if ( ! mouseDown ) {
- mouseController = 'left';
- }
- } )
- .on( 'scroll.DTFC', function ( e ) {
- if ( ! mouseController && e.originalEvent ) {
- mouseController = 'left';
- }
- if ( mouseController === 'left' ) {
- that.dom.scroller.scrollTop = that.dom.grid.left.liner.scrollTop;
- if ( that.s.iRightColumns > 0 ) {
- that.dom.grid.right.liner.scrollTop = that.dom.grid.left.liner.scrollTop;
- }
- }
- } )
- .on( wheelType, function(e) {
- // Pass horizontal scrolling through
- var xDelta = e.type === 'wheel' ?
- -e.originalEvent.deltaX :
- e.originalEvent.wheelDeltaX;
- that.dom.scroller.scrollLeft -= xDelta;
- } );
- }
- if ( that.s.iRightColumns > 0 ) {
- // When scrolling the right column, scroll the body and the left column
- $(that.dom.grid.right.liner)
- .on( 'mouseover.DTFC touchstart.DTFC', function () {
- if ( ! mouseDown ) {
- mouseController = 'right';
- }
- } )
- .on( 'scroll.DTFC', function ( e ) {
- if ( ! mouseController && e.originalEvent ) {
- mouseController = 'right';
- }
- if ( mouseController === 'right' ) {
- that.dom.scroller.scrollTop = that.dom.grid.right.liner.scrollTop;
- if ( that.s.iLeftColumns > 0 ) {
- that.dom.grid.left.liner.scrollTop = that.dom.grid.right.liner.scrollTop;
- }
- }
- } )
- .on( wheelType, function(e) {
- // Pass horizontal scrolling through
- var xDelta = e.type === 'wheel' ?
- -e.originalEvent.deltaX :
- e.originalEvent.wheelDeltaX;
- that.dom.scroller.scrollLeft -= xDelta;
- } );
- }
- $(window).on( 'resize.DTFC', function () {
- that._fnGridLayout.call( that );
- } );
- var bFirstDraw = true;
- var jqTable = $(this.s.dt.nTable);
- jqTable
- .on( 'draw.dt.DTFC', function () {
- that._fnColCalc();
- that._fnDraw.call( that, bFirstDraw );
- bFirstDraw = false;
- } )
- .on( 'column-sizing.dt.DTFC', function () {
- that._fnColCalc();
- that._fnGridLayout( that );
- } )
- .on( 'column-visibility.dt.DTFC', function ( e, settings, column, vis, recalc ) {
- if ( recalc === undefined || recalc ) {
- that._fnColCalc();
- that._fnGridLayout( that );
- that._fnDraw( true );
- }
- } )
- .on( 'select.dt.DTFC deselect.dt.DTFC', function ( e, dt, type, indexes ) {
- if ( e.namespace === 'dt' ) {
- that._fnDraw( false );
- }
- } )
- .on( 'destroy.dt.DTFC', function () {
- jqTable.off( '.DTFC' );
- $(that.dom.scroller).off( '.DTFC' );
- $(window).off( '.DTFC' );
- $(that.s.dt.nTableWrapper).off( '.DTFC' );
- $(that.dom.grid.left.liner).off( '.DTFC '+wheelType );
- $(that.dom.grid.left.wrapper).remove();
- $(that.dom.grid.right.liner).off( '.DTFC '+wheelType );
- $(that.dom.grid.right.wrapper).remove();
- } );
- /* Get things right to start with - note that due to adjusting the columns, there must be
- * another redraw of the main table. It doesn't need to be a full redraw however.
- */
- this._fnGridLayout();
- this.s.dt.oInstance.fnDraw(false);
- },
- /**
- * Calculate the column widths for the grid layout
- * @returns {void}
- * @private
- */
- "_fnColCalc": function ()
- {
- var that = this;
- var iLeftWidth = 0;
- var iRightWidth = 0;
- this.s.aiInnerWidths = [];
- this.s.aiOuterWidths = [];
- $.each( this.s.dt.aoColumns, function (i, col) {
- var th = $(col.nTh);
- var border;
- if ( ! th.filter(':visible').length ) {
- that.s.aiInnerWidths.push( 0 );
- that.s.aiOuterWidths.push( 0 );
- }
- else
- {
- // Inner width is used to assign widths to cells
- // Outer width is used to calculate the container
- var iWidth = th.outerWidth();
- // When working with the left most-cell, need to add on the
- // table's border to the outerWidth, since we need to take
- // account of it, but it isn't in any cell
- if ( that.s.aiOuterWidths.length === 0 ) {
- border = $(that.s.dt.nTable).css('border-left-width');
- iWidth += typeof border === 'string' && border.indexOf('px') === -1 ?
- 1 :
- parseInt( border, 10 );
- }
- // Likewise with the final column on the right
- if ( that.s.aiOuterWidths.length === that.s.dt.aoColumns.length-1 ) {
- border = $(that.s.dt.nTable).css('border-right-width');
- iWidth += typeof border === 'string' && border.indexOf('px') === -1 ?
- 1 :
- parseInt( border, 10 );
- }
- that.s.aiOuterWidths.push( iWidth );
- that.s.aiInnerWidths.push( th.width() );
- if ( i < that.s.iLeftColumns )
- {
- iLeftWidth += iWidth;
- }
- if ( that.s.iTableColumns-that.s.iRightColumns <= i )
- {
- iRightWidth += iWidth;
- }
- }
- } );
- this.s.iLeftWidth = iLeftWidth;
- this.s.iRightWidth = iRightWidth;
- },
- /**
- * Set up the DOM for the fixed column. The way the layout works is to create a 1x3 grid
- * for the left column, the DataTable (for which we just reuse the scrolling element DataTable
- * puts into the DOM) and the right column. In each of he two fixed column elements there is a
- * grouping wrapper element and then a head, body and footer wrapper. In each of these we then
- * place the cloned header, body or footer tables. This effectively gives as 3x3 grid structure.
- * @returns {void}
- * @private
- */
- "_fnGridSetup": function ()
- {
- var that = this;
- var oOverflow = this._fnDTOverflow();
- var block;
- this.dom.body = this.s.dt.nTable;
- this.dom.header = this.s.dt.nTHead.parentNode;
- this.dom.header.parentNode.parentNode.style.position = "relative";
- var nSWrapper =
- $('<div class="DTFC_ScrollWrapper" style="position:relative; clear:both;">'+
- '<div class="DTFC_LeftWrapper" style="position:absolute; top:0; left:0;" aria-hidden="true">'+
- '<div class="DTFC_LeftHeadWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
- '<div class="DTFC_LeftBodyWrapper" style="position:relative; top:0; left:0; overflow:hidden;">'+
- '<div class="DTFC_LeftBodyLiner" style="position:relative; top:0; left:0; overflow-y:scroll;"></div>'+
- '</div>'+
- '<div class="DTFC_LeftFootWrapper" style="position:relative; top:0; left:0; overflow:hidden;"></div>'+
- '</div>'+
- '<div class="DTFC_RightWrapper" style="position:absolute; top:0; right:0;" aria-hidden="true">'+
- '<div class="DTFC_RightHeadWrapper" style="position:relative; top:0; left:0;">'+
- '<div class="DTFC_RightHeadBlocker DTFC_Blocker" style="position:absolute; top:0; bottom:0;"></div>'+
- '</div>'+
- '<div class="DTFC_RightBodyWrapper" style="position:relative; top:0; left:0; overflow:hidden;">'+
- '<div class="DTFC_RightBodyLiner" style="position:relative; top:0; left:0; overflow-y:scroll;"></div>'+
- '</div>'+
- '<div class="DTFC_RightFootWrapper" style="position:relative; top:0; left:0;">'+
- '<div class="DTFC_RightFootBlocker DTFC_Blocker" style="position:absolute; top:0; bottom:0;"></div>'+
- '</div>'+
- '</div>'+
- '</div>')[0];
- var nLeft = nSWrapper.childNodes[0];
- var nRight = nSWrapper.childNodes[1];
- this.dom.grid.dt.parentNode.insertBefore( nSWrapper, this.dom.grid.dt );
- nSWrapper.appendChild( this.dom.grid.dt );
- this.dom.grid.wrapper = nSWrapper;
- if ( this.s.iLeftColumns > 0 )
- {
- this.dom.grid.left.wrapper = nLeft;
- this.dom.grid.left.head = nLeft.childNodes[0];
- this.dom.grid.left.body = nLeft.childNodes[1];
- this.dom.grid.left.liner = $('div.DTFC_LeftBodyLiner', nSWrapper)[0];
- nSWrapper.appendChild( nLeft );
- }
- if ( this.s.iRightColumns > 0 )
- {
- this.dom.grid.right.wrapper = nRight;
- this.dom.grid.right.head = nRight.childNodes[0];
- this.dom.grid.right.body = nRight.childNodes[1];
- this.dom.grid.right.liner = $('div.DTFC_RightBodyLiner', nSWrapper)[0];
- nRight.style.right = oOverflow.bar+"px";
- block = $('div.DTFC_RightHeadBlocker', nSWrapper)[0];
- block.style.width = oOverflow.bar+"px";
- block.style.right = -oOverflow.bar+"px";
- this.dom.grid.right.headBlock = block;
- block = $('div.DTFC_RightFootBlocker', nSWrapper)[0];
- block.style.width = oOverflow.bar+"px";
- block.style.right = -oOverflow.bar+"px";
- this.dom.grid.right.footBlock = block;
- nSWrapper.appendChild( nRight );
- }
- if ( this.s.dt.nTFoot )
- {
- this.dom.footer = this.s.dt.nTFoot.parentNode;
- if ( this.s.iLeftColumns > 0 )
- {
- this.dom.grid.left.foot = nLeft.childNodes[2];
- }
- if ( this.s.iRightColumns > 0 )
- {
- this.dom.grid.right.foot = nRight.childNodes[2];
- }
- }
- // RTL support - swap the position of the left and right columns (#48)
- if ( this.s.rtl ) {
- $('div.DTFC_RightHeadBlocker', nSWrapper).css( {
- left: -oOverflow.bar+'px',
- right: ''
- } );
- }
- },
- /**
- * Style and position the grid used for the FixedColumns layout
- * @returns {void}
- * @private
- */
- "_fnGridLayout": function ()
- {
- var that = this;
- var oGrid = this.dom.grid;
- var iWidth = $(oGrid.wrapper).width();
- var iBodyHeight = this.s.dt.nTable.parentNode.offsetHeight;
- var iFullHeight = this.s.dt.nTable.parentNode.parentNode.offsetHeight;
- var oOverflow = this._fnDTOverflow();
- var iLeftWidth = this.s.iLeftWidth;
- var iRightWidth = this.s.iRightWidth;
- var rtl = $(this.dom.body).css('direction') === 'rtl';
- var wrapper;
- var scrollbarAdjust = function ( node, width ) {
- if ( ! oOverflow.bar ) {
- // If there is no scrollbar (Macs) we need to hide the auto scrollbar
- node.style.width = (width+20)+"px";
- node.style.paddingRight = "20px";
- node.style.boxSizing = "border-box";
- }
- else if ( that._firefoxScrollError() ) {
- // See the above function for why this is required
- if ( $(node).height() > 34 ) {
- node.style.width = (width+oOverflow.bar)+"px";
- }
- }
- else {
- // Otherwise just overflow by the scrollbar
- node.style.width = (width+oOverflow.bar)+"px";
- }
- };
- // When x scrolling - don't paint the fixed columns over the x scrollbar
- if ( oOverflow.x )
- {
- iBodyHeight -= oOverflow.bar;
- }
- oGrid.wrapper.style.height = iFullHeight+"px";
- if ( this.s.iLeftColumns > 0 )
- {
- wrapper = oGrid.left.wrapper;
- wrapper.style.width = iLeftWidth+'px';
- wrapper.style.height = '1px';
- // Swap the position of the left and right columns for rtl (#48)
- // This is always up against the edge, scrollbar on the far side
- if ( rtl ) {
- wrapper.style.left = '';
- wrapper.style.right = 0;
- }
- else {
- wrapper.style.left = 0;
- wrapper.style.right = '';
- }
- oGrid.left.body.style.height = iBodyHeight+"px";
- if ( oGrid.left.foot ) {
- oGrid.left.foot.style.top = (oOverflow.x ? oOverflow.bar : 0)+"px"; // shift footer for scrollbar
- }
- scrollbarAdjust( oGrid.left.liner, iLeftWidth );
- oGrid.left.liner.style.height = iBodyHeight+"px";
- oGrid.left.liner.style.maxHeight = iBodyHeight+"px";
- }
- if ( this.s.iRightColumns > 0 )
- {
- wrapper = oGrid.right.wrapper;
- wrapper.style.width = iRightWidth+'px';
- wrapper.style.height = '1px';
- // Need to take account of the vertical scrollbar
- if ( this.s.rtl ) {
- wrapper.style.left = oOverflow.y ? oOverflow.bar+'px' : 0;
- wrapper.style.right = '';
- }
- else {
- wrapper.style.left = '';
- wrapper.style.right = oOverflow.y ? oOverflow.bar+'px' : 0;
- }
- oGrid.right.body.style.height = iBodyHeight+"px";
- if ( oGrid.right.foot ) {
- oGrid.right.foot.style.top = (oOverflow.x ? oOverflow.bar : 0)+"px";
- }
- scrollbarAdjust( oGrid.right.liner, iRightWidth );
- oGrid.right.liner.style.height = iBodyHeight+"px";
- oGrid.right.liner.style.maxHeight = iBodyHeight+"px";
- oGrid.right.headBlock.style.display = oOverflow.y ? 'block' : 'none';
- oGrid.right.footBlock.style.display = oOverflow.y ? 'block' : 'none';
- }
- },
- /**
- * Get information about the DataTable's scrolling state - specifically if the table is scrolling
- * on either the x or y axis, and also the scrollbar width.
- * @returns {object} Information about the DataTables scrolling state with the properties:
- * 'x', 'y' and 'bar'
- * @private
- */
- "_fnDTOverflow": function ()
- {
- var nTable = this.s.dt.nTable;
- var nTableScrollBody = nTable.parentNode;
- var out = {
- "x": false,
- "y": false,
- "bar": this.s.dt.oScroll.iBarWidth
- };
- if ( nTable.offsetWidth > nTableScrollBody.clientWidth )
- {
- out.x = true;
- }
- if ( nTable.offsetHeight > nTableScrollBody.clientHeight )
- {
- out.y = true;
- }
- return out;
- },
- /**
- * Clone and position the fixed columns
- * @returns {void}
- * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true)
- * @private
- */
- "_fnDraw": function ( bAll )
- {
- this._fnGridLayout();
- this._fnCloneLeft( bAll );
- this._fnCloneRight( bAll );
- /* Draw callback function */
- if ( this.s.fnDrawCallback !== null )
- {
- this.s.fnDrawCallback.call( this, this.dom.clone.left, this.dom.clone.right );
- }
- /* Event triggering */
- $(this).trigger( 'draw.dtfc', {
- "leftClone": this.dom.clone.left,
- "rightClone": this.dom.clone.right
- } );
- },
- /**
- * Clone the right columns
- * @returns {void}
- * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true)
- * @private
- */
- "_fnCloneRight": function ( bAll )
- {
- if ( this.s.iRightColumns <= 0 ) {
- return;
- }
- var that = this,
- i, jq,
- aiColumns = [];
- for ( i=this.s.iTableColumns-this.s.iRightColumns ; i<this.s.iTableColumns ; i++ ) {
- if ( this.s.dt.aoColumns[i].bVisible ) {
- aiColumns.push( i );
- }
- }
- this._fnClone( this.dom.clone.right, this.dom.grid.right, aiColumns, bAll );
- },
- /**
- * Clone the left columns
- * @returns {void}
- * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true)
- * @private
- */
- "_fnCloneLeft": function ( bAll )
- {
- if ( this.s.iLeftColumns <= 0 ) {
- return;
- }
- var that = this,
- i, jq,
- aiColumns = [];
- for ( i=0 ; i<this.s.iLeftColumns ; i++ ) {
- if ( this.s.dt.aoColumns[i].bVisible ) {
- aiColumns.push( i );
- }
- }
- this._fnClone( this.dom.clone.left, this.dom.grid.left, aiColumns, bAll );
- },
- /**
- * Make a copy of the layout object for a header or footer element from DataTables. Note that
- * this method will clone the nodes in the layout object.
- * @returns {Array} Copy of the layout array
- * @param {Object} aoOriginal Layout array from DataTables (aoHeader or aoFooter)
- * @param {Object} aiColumns Columns to copy
- * @param {boolean} events Copy cell events or not
- * @private
- */
- "_fnCopyLayout": function ( aoOriginal, aiColumns, events )
- {
- var aReturn = [];
- var aClones = [];
- var aCloned = [];
- for ( var i=0, iLen=aoOriginal.length ; i<iLen ; i++ )
- {
- var aRow = [];
- aRow.nTr = $(aoOriginal[i].nTr).clone(events, false)[0];
- for ( var j=0, jLen=this.s.iTableColumns ; j<jLen ; j++ )
- {
- if ( $.inArray( j, aiColumns ) === -1 )
- {
- continue;
- }
- var iCloned = $.inArray( aoOriginal[i][j].cell, aCloned );
- if ( iCloned === -1 )
- {
- var nClone = $(aoOriginal[i][j].cell).clone(events, false)[0];
- aClones.push( nClone );
- aCloned.push( aoOriginal[i][j].cell );
- aRow.push( {
- "cell": nClone,
- "unique": aoOriginal[i][j].unique
- } );
- }
- else
- {
- aRow.push( {
- "cell": aClones[ iCloned ],
- "unique": aoOriginal[i][j].unique
- } );
- }
- }
- aReturn.push( aRow );
- }
- return aReturn;
- },
- /**
- * Clone the DataTable nodes and place them in the DOM (sized correctly)
- * @returns {void}
- * @param {Object} oClone Object containing the header, footer and body cloned DOM elements
- * @param {Object} oGrid Grid object containing the display grid elements for the cloned
- * column (left or right)
- * @param {Array} aiColumns Column indexes which should be operated on from the DataTable
- * @param {Boolean} bAll Indicate if the header and footer should be updated as well (true)
- * @private
- */
- "_fnClone": function ( oClone, oGrid, aiColumns, bAll )
- {
- var that = this,
- i, iLen, j, jLen, jq, nTarget, iColumn, nClone, iIndex, aoCloneLayout,
- jqCloneThead, aoFixedHeader,
- dt = this.s.dt;
- /*
- * Header
- */
- if ( bAll )
- {
- $(oClone.header).remove();
- oClone.header = $(this.dom.header).clone(true, false)[0];
- oClone.header.className += " DTFC_Cloned";
- oClone.header.style.width = "100%";
- oGrid.head.appendChild( oClone.header );
- /* Copy the DataTables layout cache for the header for our floating column */
- aoCloneLayout = this._fnCopyLayout( dt.aoHeader, aiColumns, true );
- jqCloneThead = $('>thead', oClone.header);
- jqCloneThead.empty();
- /* Add the created cloned TR elements to the table */
- for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
- {
- jqCloneThead[0].appendChild( aoCloneLayout[i].nTr );
- }
- /* Use the handy _fnDrawHead function in DataTables to do the rowspan/colspan
- * calculations for us
- */
- dt.oApi._fnDrawHead( dt, aoCloneLayout, true );
- }
- else
- {
- /* To ensure that we copy cell classes exactly, regardless of colspan, multiple rows
- * etc, we make a copy of the header from the DataTable again, but don't insert the
- * cloned cells, just copy the classes across. To get the matching layout for the
- * fixed component, we use the DataTables _fnDetectHeader method, allowing 1:1 mapping
- */
- aoCloneLayout = this._fnCopyLayout( dt.aoHeader, aiColumns, false );
- aoFixedHeader=[];
- dt.oApi._fnDetectHeader( aoFixedHeader, $('>thead', oClone.header)[0] );
- for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
- {
- for ( j=0, jLen=aoCloneLayout[i].length ; j<jLen ; j++ )
- {
- aoFixedHeader[i][j].cell.className = aoCloneLayout[i][j].cell.className;
- // If jQuery UI theming is used we need to copy those elements as well
- $('span.DataTables_sort_icon', aoFixedHeader[i][j].cell).each( function () {
- this.className = $('span.DataTables_sort_icon', aoCloneLayout[i][j].cell)[0].className;
- } );
- }
- }
- }
- this._fnEqualiseHeights( 'thead', this.dom.header, oClone.header );
- /*
- * Body
- */
- if ( this.s.sHeightMatch == 'auto' )
- {
- /* Remove any heights which have been applied already and let the browser figure it out */
- $('>tbody>tr', that.dom.body).css('height', 'auto');
- }
- if ( oClone.body !== null )
- {
- $(oClone.body).remove();
- oClone.body = null;
- }
- oClone.body = $(this.dom.body).clone(true)[0];
- oClone.body.className += " DTFC_Cloned";
- oClone.body.style.paddingBottom = dt.oScroll.iBarWidth+"px";
- oClone.body.style.marginBottom = (dt.oScroll.iBarWidth*2)+"px"; /* For IE */
- if ( oClone.body.getAttribute('id') !== null )
- {
- oClone.body.removeAttribute('id');
- }
- $('>thead>tr', oClone.body).empty();
- $('>tfoot', oClone.body).remove();
- var nBody = $('tbody', oClone.body)[0];
- $(nBody).empty();
- if ( dt.aiDisplay.length > 0 )
- {
- /* Copy the DataTables' header elements to force the column width in exactly the
- * same way that DataTables does it - have the header element, apply the width and
- * colapse it down
- */
- var nInnerThead = $('>thead>tr', oClone.body)[0];
- for ( iIndex=0 ; iIndex<aiColumns.length ; iIndex++ )
- {
- iColumn = aiColumns[iIndex];
- nClone = $(dt.aoColumns[iColumn].nTh).clone(true)[0];
- nClone.innerHTML = "";
- var oStyle = nClone.style;
- oStyle.paddingTop = "0";
- oStyle.paddingBottom = "0";
- oStyle.borderTopWidth = "0";
- oStyle.borderBottomWidth = "0";
- oStyle.height = 0;
- oStyle.width = that.s.aiInnerWidths[iColumn]+"px";
- nInnerThead.appendChild( nClone );
- }
- /* Add in the tbody elements, cloning form the master table */
- $('>tbody>tr', that.dom.body).each( function (z) {
- var i = that.s.dt.oFeatures.bServerSide===false ?
- that.s.dt.aiDisplay[ that.s.dt._iDisplayStart+z ] : z;
- var aTds = that.s.dt.aoData[ i ].anCells || $(this).children('td, th');
- var n = this.cloneNode(false);
- n.removeAttribute('id');
- n.setAttribute( 'data-dt-row', i );
- for ( iIndex=0 ; iIndex<aiColumns.length ; iIndex++ )
- {
- iColumn = aiColumns[iIndex];
- if ( aTds.length > 0 )
- {
- nClone = $( aTds[iColumn] ).clone(true, true)[0];
- nClone.removeAttribute( 'id' );
- nClone.setAttribute( 'data-dt-row', i );
- nClone.setAttribute( 'data-dt-column', iColumn );
- n.appendChild( nClone );
- }
- }
- nBody.appendChild( n );
- } );
- }
- else
- {
- $('>tbody>tr', that.dom.body).each( function (z) {
- nClone = this.cloneNode(true);
- nClone.className += ' DTFC_NoData';
- $('td', nClone).html('');
- nBody.appendChild( nClone );
- } );
- }
- oClone.body.style.width = "100%";
- oClone.body.style.margin = "0";
- oClone.body.style.padding = "0";
- // Interop with Scroller - need to use a height forcing element in the
- // scrolling area in the same way that Scroller does in the body scroll.
- if ( dt.oScroller !== undefined )
- {
- var scrollerForcer = dt.oScroller.dom.force;
- if ( ! oGrid.forcer ) {
- oGrid.forcer = scrollerForcer.cloneNode( true );
- oGrid.liner.appendChild( oGrid.forcer );
- }
- else {
- oGrid.forcer.style.height = scrollerForcer.style.height;
- }
- }
- oGrid.liner.appendChild( oClone.body );
- this._fnEqualiseHeights( 'tbody', that.dom.body, oClone.body );
- /*
- * Footer
- */
- if ( dt.nTFoot !== null )
- {
- if ( bAll )
- {
- if ( oClone.footer !== null )
- {
- oClone.footer.parentNode.removeChild( oClone.footer );
- }
- oClone.footer = $(this.dom.footer).clone(true, true)[0];
- oClone.footer.className += " DTFC_Cloned";
- oClone.footer.style.width = "100%";
- oGrid.foot.appendChild( oClone.footer );
- /* Copy the footer just like we do for the header */
- aoCloneLayout = this._fnCopyLayout( dt.aoFooter, aiColumns, true );
- var jqCloneTfoot = $('>tfoot', oClone.footer);
- jqCloneTfoot.empty();
- for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
- {
- jqCloneTfoot[0].appendChild( aoCloneLayout[i].nTr );
- }
- dt.oApi._fnDrawHead( dt, aoCloneLayout, true );
- }
- else
- {
- aoCloneLayout = this._fnCopyLayout( dt.aoFooter, aiColumns, false );
- var aoCurrFooter=[];
- dt.oApi._fnDetectHeader( aoCurrFooter, $('>tfoot', oClone.footer)[0] );
- for ( i=0, iLen=aoCloneLayout.length ; i<iLen ; i++ )
- {
- for ( j=0, jLen=aoCloneLayout[i].length ; j<jLen ; j++ )
- {
- aoCurrFooter[i][j].cell.className = aoCloneLayout[i][j].cell.className;
- }
- }
- }
- this._fnEqualiseHeights( 'tfoot', this.dom.footer, oClone.footer );
- }
- /* Equalise the column widths between the header footer and body - body get's priority */
- var anUnique = dt.oApi._fnGetUniqueThs( dt, $('>thead', oClone.header)[0] );
- $(anUnique).each( function (i) {
- iColumn = aiColumns[i];
- this.style.width = that.s.aiInnerWidths[iColumn]+"px";
- } );
- if ( that.s.dt.nTFoot !== null )
- {
- anUnique = dt.oApi._fnGetUniqueThs( dt, $('>tfoot', oClone.footer)[0] );
- $(anUnique).each( function (i) {
- iColumn = aiColumns[i];
- this.style.width = that.s.aiInnerWidths[iColumn]+"px";
- } );
- }
- },
- /**
- * From a given table node (THEAD etc), get a list of TR direct child elements
- * @param {Node} nIn Table element to search for TR elements (THEAD, TBODY or TFOOT element)
- * @returns {Array} List of TR elements found
- * @private
- */
- "_fnGetTrNodes": function ( nIn )
- {
- var aOut = [];
- for ( var i=0, iLen=nIn.childNodes.length ; i<iLen ; i++ )
- {
- if ( nIn.childNodes[i].nodeName.toUpperCase() == "TR" )
- {
- aOut.push( nIn.childNodes[i] );
- }
- }
- return aOut;
- },
- /**
- * Equalise the heights of the rows in a given table node in a cross browser way
- * @returns {void}
- * @param {String} nodeName Node type - thead, tbody or tfoot
- * @param {Node} original Original node to take the heights from
- * @param {Node} clone Copy the heights to
- * @private
- */
- "_fnEqualiseHeights": function ( nodeName, original, clone )
- {
- if ( this.s.sHeightMatch == 'none' && nodeName !== 'thead' && nodeName !== 'tfoot' )
- {
- return;
- }
- var that = this,
- i, iLen, iHeight, iHeight2, iHeightOriginal, iHeightClone,
- rootOriginal = original.getElementsByTagName(nodeName)[0],
- rootClone = clone.getElementsByTagName(nodeName)[0],
- jqBoxHack = $('>'+nodeName+'>tr:eq(0)', original).children(':first'),
- iBoxHack = jqBoxHack.outerHeight() - jqBoxHack.height(),
- anOriginal = this._fnGetTrNodes( rootOriginal ),
- anClone = this._fnGetTrNodes( rootClone ),
- heights = [];
- for ( i=0, iLen=anClone.length ; i<iLen ; i++ )
- {
- iHeightOriginal = anOriginal[i].offsetHeight;
- iHeightClone = anClone[i].offsetHeight;
- iHeight = iHeightClone > iHeightOriginal ? iHeightClone : iHeightOriginal;
- if ( this.s.sHeightMatch == 'semiauto' )
- {
- anOriginal[i]._DTTC_iHeight = iHeight;
- }
- heights.push( iHeight );
- }
- for ( i=0, iLen=anClone.length ; i<iLen ; i++ )
- {
- anClone[i].style.height = heights[i]+"px";
- anOriginal[i].style.height = heights[i]+"px";
- }
- },
- /**
- * Determine if the UA suffers from Firefox's overflow:scroll scrollbars
- * not being shown bug.
- *
- * Firefox doesn't draw scrollbars, even if it is told to using
- * overflow:scroll, if the div is less than 34px height. See bugs 292284 and
- * 781885. Using UA detection here since this is particularly hard to detect
- * using objects - its a straight up rendering error in Firefox.
- *
- * @return {boolean} True if Firefox error is present, false otherwise
- */
- _firefoxScrollError: function () {
- if ( _firefoxScroll === undefined ) {
- var test = $('<div/>')
- .css( {
- position: 'absolute',
- top: 0,
- left: 0,
- height: 10,
- width: 50,
- overflow: 'scroll'
- } )
- .appendTo( 'body' );
- // Make sure this doesn't apply on Macs with 0 width scrollbars
- _firefoxScroll = (
- test[0].clientWidth === test[0].offsetWidth && this._fnDTOverflow().bar !== 0
- );
- test.remove();
- }
- return _firefoxScroll;
- }
- } );
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Statics
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * FixedColumns default settings for initialisation
- * @name FixedColumns.defaults
- * @namespace
- * @static
- */
- FixedColumns.defaults = /** @lends FixedColumns.defaults */{
- /**
- * Number of left hand columns to fix in position
- * @type int
- * @default 1
- * @static
- * @example
- * var = $('#example').dataTable( {
- * "scrollX": "100%"
- * } );
- * new $.fn.dataTable.fixedColumns( table, {
- * "leftColumns": 2
- * } );
- */
- "iLeftColumns": 1,
- /**
- * Number of right hand columns to fix in position
- * @type int
- * @default 0
- * @static
- * @example
- * var table = $('#example').dataTable( {
- * "scrollX": "100%"
- * } );
- * new $.fn.dataTable.fixedColumns( table, {
- * "rightColumns": 1
- * } );
- */
- "iRightColumns": 0,
- /**
- * Draw callback function which is called when FixedColumns has redrawn the fixed assets
- * @type function(object, object):void
- * @default null
- * @static
- * @example
- * var table = $('#example').dataTable( {
- * "scrollX": "100%"
- * } );
- * new $.fn.dataTable.fixedColumns( table, {
- * "drawCallback": function () {
- * alert( "FixedColumns redraw" );
- * }
- * } );
- */
- "fnDrawCallback": null,
- /**
- * Height matching algorthim to use. This can be "none" which will result in no height
- * matching being applied by FixedColumns (height matching could be forced by CSS in this
- * case), "semiauto" whereby the height calculation will be performed once, and the result
- * cached to be used again (fnRecalculateHeight can be used to force recalculation), or
- * "auto" when height matching is performed on every draw (slowest but must accurate)
- * @type string
- * @default semiauto
- * @static
- * @example
- * var table = $('#example').dataTable( {
- * "scrollX": "100%"
- * } );
- * new $.fn.dataTable.fixedColumns( table, {
- * "heightMatch": "auto"
- * } );
- */
- "sHeightMatch": "semiauto"
- };
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constants
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * FixedColumns version
- * @name FixedColumns.version
- * @type String
- * @default See code
- * @static
- */
- FixedColumns.version = "3.2.6";
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables API integration
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- DataTable.Api.register( 'fixedColumns()', function () {
- return this;
- } );
- DataTable.Api.register( 'fixedColumns().update()', function () {
- return this.iterator( 'table', function ( ctx ) {
- if ( ctx._oFixedColumns ) {
- ctx._oFixedColumns.fnUpdate();
- }
- } );
- } );
- DataTable.Api.register( 'fixedColumns().relayout()', function () {
- return this.iterator( 'table', function ( ctx ) {
- if ( ctx._oFixedColumns ) {
- ctx._oFixedColumns.fnRedrawLayout();
- }
- } );
- } );
- DataTable.Api.register( 'rows().recalcHeight()', function () {
- return this.iterator( 'row', function ( ctx, idx ) {
- if ( ctx._oFixedColumns ) {
- ctx._oFixedColumns.fnRecalculateHeight( this.row(idx).node() );
- }
- } );
- } );
- DataTable.Api.register( 'fixedColumns().rowIndex()', function ( row ) {
- row = $(row);
- return row.parents('.DTFC_Cloned').length ?
- this.rows( { page: 'current' } ).indexes()[ row.index() ] :
- this.row( row ).index();
- } );
- DataTable.Api.register( 'fixedColumns().cellIndex()', function ( cell ) {
- cell = $(cell);
- if ( cell.parents('.DTFC_Cloned').length ) {
- var rowClonedIdx = cell.parent().index();
- var rowIdx = this.rows( { page: 'current' } ).indexes()[ rowClonedIdx ];
- var columnIdx;
- if ( cell.parents('.DTFC_LeftWrapper').length ) {
- columnIdx = cell.index();
- }
- else {
- var columns = this.columns().flatten().length;
- columnIdx = columns - this.context[0]._oFixedColumns.s.iRightColumns + cell.index();
- }
- return {
- row: rowIdx,
- column: this.column.index( 'toData', columnIdx ),
- columnVisible: columnIdx
- };
- }
- else {
- return this.cell( cell ).index();
- }
- } );
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Initialisation
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- // Attach a listener to the document which listens for DataTables initialisation
- // events so we can automatically initialise
- $(document).on( 'init.dt.fixedColumns', function (e, settings) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- var init = settings.oInit.fixedColumns;
- var defaults = DataTable.defaults.fixedColumns;
- if ( init || defaults ) {
- var opts = $.extend( {}, init, defaults );
- if ( init !== false ) {
- new FixedColumns( settings, opts );
- }
- }
- } );
- // Make FixedColumns accessible from the DataTables instance
- $.fn.dataTable.FixedColumns = FixedColumns;
- $.fn.DataTable.FixedColumns = FixedColumns;
- return FixedColumns;
- }));
- /*! Bootstrap 4 styling wrapper for FixedColumns
- * ©2018 SpryMedia Ltd - datatables.net/license
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net-bs4', 'datatables.net-fixedcolumns'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net-bs4')(root, $).$;
- }
- if ( ! $.fn.dataTable.FixedColumns ) {
- require('datatables.net-fixedcolumns')(root, $);
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- return $.fn.dataTable;
- }));
- /*! FixedHeader 3.1.5
- * ©2009-2018 SpryMedia Ltd - datatables.net/license
- */
- /**
- * @summary FixedHeader
- * @description Fix a table's header or footer, so it is always visible while
- * scrolling
- * @version 3.1.5
- * @file dataTables.fixedHeader.js
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
- * @contact www.sprymedia.co.uk/contact
- * @copyright Copyright 2009-2018 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license/mit
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- var _instCounter = 0;
- var FixedHeader = function ( dt, config ) {
- // Sanity check - you just know it will happen
- if ( ! (this instanceof FixedHeader) ) {
- throw "FixedHeader must be initialised with the 'new' keyword.";
- }
- // Allow a boolean true for defaults
- if ( config === true ) {
- config = {};
- }
- dt = new DataTable.Api( dt );
- this.c = $.extend( true, {}, FixedHeader.defaults, config );
- this.s = {
- dt: dt,
- position: {
- theadTop: 0,
- tbodyTop: 0,
- tfootTop: 0,
- tfootBottom: 0,
- width: 0,
- left: 0,
- tfootHeight: 0,
- theadHeight: 0,
- windowHeight: $(window).height(),
- visible: true
- },
- headerMode: null,
- footerMode: null,
- autoWidth: dt.settings()[0].oFeatures.bAutoWidth,
- namespace: '.dtfc'+(_instCounter++),
- scrollLeft: {
- header: -1,
- footer: -1
- },
- enable: true
- };
- this.dom = {
- floatingHeader: null,
- thead: $(dt.table().header()),
- tbody: $(dt.table().body()),
- tfoot: $(dt.table().footer()),
- header: {
- host: null,
- floating: null,
- placeholder: null
- },
- footer: {
- host: null,
- floating: null,
- placeholder: null
- }
- };
- this.dom.header.host = this.dom.thead.parent();
- this.dom.footer.host = this.dom.tfoot.parent();
- var dtSettings = dt.settings()[0];
- if ( dtSettings._fixedHeader ) {
- throw "FixedHeader already initialised on table "+dtSettings.nTable.id;
- }
- dtSettings._fixedHeader = this;
- this._constructor();
- };
- /*
- * Variable: FixedHeader
- * Purpose: Prototype for FixedHeader
- * Scope: global
- */
- $.extend( FixedHeader.prototype, {
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * API methods
- */
-
- /**
- * Enable / disable the fixed elements
- *
- * @param {boolean} enable `true` to enable, `false` to disable
- */
- enable: function ( enable )
- {
- this.s.enable = enable;
- if ( this.c.header ) {
- this._modeChange( 'in-place', 'header', true );
- }
- if ( this.c.footer && this.dom.tfoot.length ) {
- this._modeChange( 'in-place', 'footer', true );
- }
- this.update();
- },
-
- /**
- * Set header offset
- *
- * @param {int} new value for headerOffset
- */
- headerOffset: function ( offset )
- {
- if ( offset !== undefined ) {
- this.c.headerOffset = offset;
- this.update();
- }
- return this.c.headerOffset;
- },
-
- /**
- * Set footer offset
- *
- * @param {int} new value for footerOffset
- */
- footerOffset: function ( offset )
- {
- if ( offset !== undefined ) {
- this.c.footerOffset = offset;
- this.update();
- }
- return this.c.footerOffset;
- },
-
- /**
- * Recalculate the position of the fixed elements and force them into place
- */
- update: function ()
- {
- this._positions();
- this._scroll( true );
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constructor
- */
-
- /**
- * FixedHeader constructor - adding the required event listeners and
- * simple initialisation
- *
- * @private
- */
- _constructor: function ()
- {
- var that = this;
- var dt = this.s.dt;
- $(window)
- .on( 'scroll'+this.s.namespace, function () {
- that._scroll();
- } )
- .on( 'resize'+this.s.namespace, DataTable.util.throttle( function () {
- that.s.position.windowHeight = $(window).height();
- that.update();
- }, 50 ) );
- var autoHeader = $('.fh-fixedHeader');
- if ( ! this.c.headerOffset && autoHeader.length ) {
- this.c.headerOffset = autoHeader.outerHeight();
- }
- var autoFooter = $('.fh-fixedFooter');
- if ( ! this.c.footerOffset && autoFooter.length ) {
- this.c.footerOffset = autoFooter.outerHeight();
- }
- dt.on( 'column-reorder.dt.dtfc column-visibility.dt.dtfc draw.dt.dtfc column-sizing.dt.dtfc responsive-display.dt.dtfc', function () {
- that.update();
- } );
- dt.on( 'destroy.dtfc', function () {
- if ( that.c.header ) {
- that._modeChange( 'in-place', 'header', true );
- }
- if ( that.c.footer && that.dom.tfoot.length ) {
- that._modeChange( 'in-place', 'footer', true );
- }
- dt.off( '.dtfc' );
- $(window).off( that.s.namespace );
- } );
- this._positions();
- this._scroll();
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Private methods
- */
- /**
- * Clone a fixed item to act as a place holder for the original element
- * which is moved into a clone of the table element, and moved around the
- * document to give the fixed effect.
- *
- * @param {string} item 'header' or 'footer'
- * @param {boolean} force Force the clone to happen, or allow automatic
- * decision (reuse existing if available)
- * @private
- */
- _clone: function ( item, force )
- {
- var dt = this.s.dt;
- var itemDom = this.dom[ item ];
- var itemElement = item === 'header' ?
- this.dom.thead :
- this.dom.tfoot;
- if ( ! force && itemDom.floating ) {
- // existing floating element - reuse it
- itemDom.floating.removeClass( 'fixedHeader-floating fixedHeader-locked' );
- }
- else {
- if ( itemDom.floating ) {
- itemDom.placeholder.remove();
- this._unsize( item );
- itemDom.floating.children().detach();
- itemDom.floating.remove();
- }
- itemDom.floating = $( dt.table().node().cloneNode( false ) )
- .css( 'table-layout', 'fixed' )
- .attr( 'aria-hidden', 'true' )
- .removeAttr( 'id' )
- .append( itemElement )
- .appendTo( 'body' );
- // Insert a fake thead/tfoot into the DataTable to stop it jumping around
- itemDom.placeholder = itemElement.clone( false )
- itemDom.placeholder
- .find( '*[id]' )
- .removeAttr( 'id' );
- itemDom.host.prepend( itemDom.placeholder );
- // Clone widths
- this._matchWidths( itemDom.placeholder, itemDom.floating );
- }
- },
- /**
- * Copy widths from the cells in one element to another. This is required
- * for the footer as the footer in the main table takes its sizes from the
- * header columns. That isn't present in the footer so to have it still
- * align correctly, the sizes need to be copied over. It is also required
- * for the header when auto width is not enabled
- *
- * @param {jQuery} from Copy widths from
- * @param {jQuery} to Copy widths to
- * @private
- */
- _matchWidths: function ( from, to ) {
- var get = function ( name ) {
- return $(name, from)
- .map( function () {
- return $(this).width();
- } ).toArray();
- };
- var set = function ( name, toWidths ) {
- $(name, to).each( function ( i ) {
- $(this).css( {
- width: toWidths[i],
- minWidth: toWidths[i]
- } );
- } );
- };
- var thWidths = get( 'th' );
- var tdWidths = get( 'td' );
- set( 'th', thWidths );
- set( 'td', tdWidths );
- },
- /**
- * Remove assigned widths from the cells in an element. This is required
- * when inserting the footer back into the main table so the size is defined
- * by the header columns and also when auto width is disabled in the
- * DataTable.
- *
- * @param {string} item The `header` or `footer`
- * @private
- */
- _unsize: function ( item ) {
- var el = this.dom[ item ].floating;
- if ( el && (item === 'footer' || (item === 'header' && ! this.s.autoWidth)) ) {
- $('th, td', el).css( {
- width: '',
- minWidth: ''
- } );
- }
- else if ( el && item === 'header' ) {
- $('th, td', el).css( 'min-width', '' );
- }
- },
- /**
- * Reposition the floating elements to take account of horizontal page
- * scroll
- *
- * @param {string} item The `header` or `footer`
- * @param {int} scrollLeft Document scrollLeft
- * @private
- */
- _horizontal: function ( item, scrollLeft )
- {
- var itemDom = this.dom[ item ];
- var position = this.s.position;
- var lastScrollLeft = this.s.scrollLeft;
- if ( itemDom.floating && lastScrollLeft[ item ] !== scrollLeft ) {
- itemDom.floating.css( 'left', position.left - scrollLeft );
- lastScrollLeft[ item ] = scrollLeft;
- }
- },
- /**
- * Change from one display mode to another. Each fixed item can be in one
- * of:
- *
- * * `in-place` - In the main DataTable
- * * `in` - Floating over the DataTable
- * * `below` - (Header only) Fixed to the bottom of the table body
- * * `above` - (Footer only) Fixed to the top of the table body
- *
- * @param {string} mode Mode that the item should be shown in
- * @param {string} item 'header' or 'footer'
- * @param {boolean} forceChange Force a redraw of the mode, even if already
- * in that mode.
- * @private
- */
- _modeChange: function ( mode, item, forceChange )
- {
- var dt = this.s.dt;
- var itemDom = this.dom[ item ];
- var position = this.s.position;
- // Record focus. Browser's will cause input elements to loose focus if
- // they are inserted else where in the doc
- var tablePart = this.dom[ item==='footer' ? 'tfoot' : 'thead' ];
- var focus = $.contains( tablePart[0], document.activeElement ) ?
- document.activeElement :
- null;
-
- if ( focus ) {
- focus.blur();
- }
- if ( mode === 'in-place' ) {
- // Insert the header back into the table's real header
- if ( itemDom.placeholder ) {
- itemDom.placeholder.remove();
- itemDom.placeholder = null;
- }
- this._unsize( item );
- if ( item === 'header' ) {
- itemDom.host.prepend( tablePart );
- }
- else {
- itemDom.host.append( tablePart );
- }
- if ( itemDom.floating ) {
- itemDom.floating.remove();
- itemDom.floating = null;
- }
- }
- else if ( mode === 'in' ) {
- // Remove the header from the read header and insert into a fixed
- // positioned floating table clone
- this._clone( item, forceChange );
- itemDom.floating
- .addClass( 'fixedHeader-floating' )
- .css( item === 'header' ? 'top' : 'bottom', this.c[item+'Offset'] )
- .css( 'left', position.left+'px' )
- .css( 'width', position.width+'px' );
- if ( item === 'footer' ) {
- itemDom.floating.css( 'top', '' );
- }
- }
- else if ( mode === 'below' ) { // only used for the header
- // Fix the position of the floating header at base of the table body
- this._clone( item, forceChange );
- itemDom.floating
- .addClass( 'fixedHeader-locked' )
- .css( 'top', position.tfootTop - position.theadHeight )
- .css( 'left', position.left+'px' )
- .css( 'width', position.width+'px' );
- }
- else if ( mode === 'above' ) { // only used for the footer
- // Fix the position of the floating footer at top of the table body
- this._clone( item, forceChange );
- itemDom.floating
- .addClass( 'fixedHeader-locked' )
- .css( 'top', position.tbodyTop )
- .css( 'left', position.left+'px' )
- .css( 'width', position.width+'px' );
- }
- // Restore focus if it was lost
- if ( focus && focus !== document.activeElement ) {
- setTimeout( function () {
- focus.focus();
- }, 10 );
- }
- this.s.scrollLeft.header = -1;
- this.s.scrollLeft.footer = -1;
- this.s[item+'Mode'] = mode;
- },
- /**
- * Cache the positional information that is required for the mode
- * calculations that FixedHeader performs.
- *
- * @private
- */
- _positions: function ()
- {
- var dt = this.s.dt;
- var table = dt.table();
- var position = this.s.position;
- var dom = this.dom;
- var tableNode = $(table.node());
- // Need to use the header and footer that are in the main table,
- // regardless of if they are clones, since they hold the positions we
- // want to measure from
- var thead = tableNode.children('thead');
- var tfoot = tableNode.children('tfoot');
- var tbody = dom.tbody;
- position.visible = tableNode.is(':visible');
- position.width = tableNode.outerWidth();
- position.left = tableNode.offset().left;
- position.theadTop = thead.offset().top;
- position.tbodyTop = tbody.offset().top;
- position.theadHeight = position.tbodyTop - position.theadTop;
- if ( tfoot.length ) {
- position.tfootTop = tfoot.offset().top;
- position.tfootBottom = position.tfootTop + tfoot.outerHeight();
- position.tfootHeight = position.tfootBottom - position.tfootTop;
- }
- else {
- position.tfootTop = position.tbodyTop + tbody.outerHeight();
- position.tfootBottom = position.tfootTop;
- position.tfootHeight = position.tfootTop;
- }
- },
- /**
- * Mode calculation - determine what mode the fixed items should be placed
- * into.
- *
- * @param {boolean} forceChange Force a redraw of the mode, even if already
- * in that mode.
- * @private
- */
- _scroll: function ( forceChange )
- {
- var windowTop = $(document).scrollTop();
- var windowLeft = $(document).scrollLeft();
- var position = this.s.position;
- var headerMode, footerMode;
- if ( ! this.s.enable ) {
- return;
- }
- if ( this.c.header ) {
- if ( ! position.visible || windowTop <= position.theadTop - this.c.headerOffset ) {
- headerMode = 'in-place';
- }
- else if ( windowTop <= position.tfootTop - position.theadHeight - this.c.headerOffset ) {
- headerMode = 'in';
- }
- else {
- headerMode = 'below';
- }
- if ( forceChange || headerMode !== this.s.headerMode ) {
- this._modeChange( headerMode, 'header', forceChange );
- }
- this._horizontal( 'header', windowLeft );
- }
- if ( this.c.footer && this.dom.tfoot.length ) {
- if ( ! position.visible || windowTop + position.windowHeight >= position.tfootBottom + this.c.footerOffset ) {
- footerMode = 'in-place';
- }
- else if ( position.windowHeight + windowTop > position.tbodyTop + position.tfootHeight + this.c.footerOffset ) {
- footerMode = 'in';
- }
- else {
- footerMode = 'above';
- }
- if ( forceChange || footerMode !== this.s.footerMode ) {
- this._modeChange( footerMode, 'footer', forceChange );
- }
- this._horizontal( 'footer', windowLeft );
- }
- }
- } );
- /**
- * Version
- * @type {String}
- * @static
- */
- FixedHeader.version = "3.1.5";
- /**
- * Defaults
- * @type {Object}
- * @static
- */
- FixedHeader.defaults = {
- header: true,
- footer: false,
- headerOffset: 0,
- footerOffset: 0
- };
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables interfaces
- */
- // Attach for constructor access
- $.fn.dataTable.FixedHeader = FixedHeader;
- $.fn.DataTable.FixedHeader = FixedHeader;
- // DataTables creation - check if the FixedHeader option has been defined on the
- // table and if so, initialise
- $(document).on( 'init.dt.dtfh', function (e, settings, json) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- var init = settings.oInit.fixedHeader;
- var defaults = DataTable.defaults.fixedHeader;
- if ( (init || defaults) && ! settings._fixedHeader ) {
- var opts = $.extend( {}, defaults, init );
- if ( init !== false ) {
- new FixedHeader( settings, opts );
- }
- }
- } );
- // DataTables API methods
- DataTable.Api.register( 'fixedHeader()', function () {} );
- DataTable.Api.register( 'fixedHeader.adjust()', function () {
- return this.iterator( 'table', function ( ctx ) {
- var fh = ctx._fixedHeader;
- if ( fh ) {
- fh.update();
- }
- } );
- } );
- DataTable.Api.register( 'fixedHeader.enable()', function ( flag ) {
- return this.iterator( 'table', function ( ctx ) {
- var fh = ctx._fixedHeader;
- flag = ( flag !== undefined ? flag : true );
- if ( fh && flag !== fh.s.enable ) {
- fh.enable( flag );
- }
- } );
- } );
- DataTable.Api.register( 'fixedHeader.disable()', function ( ) {
- return this.iterator( 'table', function ( ctx ) {
- var fh = ctx._fixedHeader;
- if ( fh && fh.s.enable ) {
- fh.enable( false );
- }
- } );
- } );
- $.each( ['header', 'footer'], function ( i, el ) {
- DataTable.Api.register( 'fixedHeader.'+el+'Offset()', function ( offset ) {
- var ctx = this.context;
- if ( offset === undefined ) {
- return ctx.length && ctx[0]._fixedHeader ?
- ctx[0]._fixedHeader[el +'Offset']() :
- undefined;
- }
- return this.iterator( 'table', function ( ctx ) {
- var fh = ctx._fixedHeader;
- if ( fh ) {
- fh[ el +'Offset' ]( offset );
- }
- } );
- } );
- } );
- return FixedHeader;
- }));
- /*! Bootstrap 4 styling wrapper for FixedHeader
- * ©2018 SpryMedia Ltd - datatables.net/license
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net-bs4', 'datatables.net-fixedheader'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net-bs4')(root, $).$;
- }
- if ( ! $.fn.dataTable.FixedHeader ) {
- require('datatables.net-fixedheader')(root, $);
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- return $.fn.dataTable;
- }));
- /*! KeyTable 2.5.0
- * ©2009-2018 SpryMedia Ltd - datatables.net/license
- */
- /**
- * @summary KeyTable
- * @description Spreadsheet like keyboard navigation for DataTables
- * @version 2.5.0
- * @file dataTables.keyTable.js
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
- * @contact www.sprymedia.co.uk/contact
- * @copyright Copyright 2009-2018 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license/mit
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- var KeyTable = function ( dt, opts ) {
- // Sanity check that we are using DataTables 1.10 or newer
- if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) {
- throw 'KeyTable requires DataTables 1.10.8 or newer';
- }
- // User and defaults configuration object
- this.c = $.extend( true, {},
- DataTable.defaults.keyTable,
- KeyTable.defaults,
- opts
- );
- // Internal settings
- this.s = {
- /** @type {DataTable.Api} DataTables' API instance */
- dt: new DataTable.Api( dt ),
- enable: true,
- /** @type {bool} Flag for if a draw is triggered by focus */
- focusDraw: false,
- /** @type {bool} Flag to indicate when waiting for a draw to happen.
- * Will ignore key presses at this point
- */
- waitingForDraw: false,
- /** @type {object} Information about the last cell that was focused */
- lastFocus: null
- };
- // DOM items
- this.dom = {
- };
- // Check if row reorder has already been initialised on this table
- var settings = this.s.dt.settings()[0];
- var exisiting = settings.keytable;
- if ( exisiting ) {
- return exisiting;
- }
- settings.keytable = this;
- this._constructor();
- };
- $.extend( KeyTable.prototype, {
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * API methods for DataTables API interface
- */
- /**
- * Blur the table's cell focus
- */
- blur: function ()
- {
- this._blur();
- },
- /**
- * Enable cell focus for the table
- *
- * @param {string} state Can be `true`, `false` or `-string navigation-only`
- */
- enable: function ( state )
- {
- this.s.enable = state;
- },
- /**
- * Focus on a cell
- * @param {integer} row Row index
- * @param {integer} column Column index
- */
- focus: function ( row, column )
- {
- this._focus( this.s.dt.cell( row, column ) );
- },
- /**
- * Is the cell focused
- * @param {object} cell Cell index to check
- * @returns {boolean} true if focused, false otherwise
- */
- focused: function ( cell )
- {
- var lastFocus = this.s.lastFocus;
- if ( ! lastFocus ) {
- return false;
- }
- var lastIdx = this.s.lastFocus.cell.index();
- return cell.row === lastIdx.row && cell.column === lastIdx.column;
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constructor
- */
- /**
- * Initialise the KeyTable instance
- *
- * @private
- */
- _constructor: function ()
- {
- this._tabInput();
- var that = this;
- var dt = this.s.dt;
- var table = $( dt.table().node() );
- // Need to be able to calculate the cell positions relative to the table
- if ( table.css('position') === 'static' ) {
- table.css( 'position', 'relative' );
- }
- // Click to focus
- $( dt.table().body() ).on( 'click.keyTable', 'th, td', function (e) {
- if ( that.s.enable === false ) {
- return;
- }
- var cell = dt.cell( this );
- if ( ! cell.any() ) {
- return;
- }
- that._focus( cell, null, false, e );
- } );
- // Key events
- $( document ).on( 'keydown.keyTable', function (e) {
- that._key( e );
- } );
- // Click blur
- if ( this.c.blurable ) {
- $( document ).on( 'mousedown.keyTable', function ( e ) {
- // Click on the search input will blur focus
- if ( $(e.target).parents( '.dataTables_filter' ).length ) {
- that._blur();
- }
- // If the click was inside the DataTables container, don't blur
- if ( $(e.target).parents().filter( dt.table().container() ).length ) {
- return;
- }
- // Don't blur in Editor form
- if ( $(e.target).parents('div.DTE').length ) {
- return;
- }
- // Or an Editor date input
- if ( $(e.target).parents('div.editor-datetime').length ) {
- return;
- }
- //If the click was inside the fixed columns container, don't blur
- if ( $(e.target).parents().filter('.DTFC_Cloned').length ) {
- return;
- }
- that._blur();
- } );
- }
- if ( this.c.editor ) {
- var editor = this.c.editor;
- // Need to disable KeyTable when the main editor is shown
- editor.on( 'open.keyTableMain', function (e, mode, action) {
- if ( mode !== 'inline' && that.s.enable ) {
- that.enable( false );
- editor.one( 'close.keyTable', function () {
- that.enable( true );
- } );
- }
- } );
- if ( this.c.editOnFocus ) {
- dt.on( 'key-focus.keyTable key-refocus.keyTable', function ( e, dt, cell, orig ) {
- that._editor( null, orig, true );
- } );
- }
- // Activate Editor when a key is pressed (will be ignored, if
- // already active).
- dt.on( 'key.keyTable', function ( e, dt, key, cell, orig ) {
- that._editor( key, orig, false );
- } );
- // Active editing on double click - it will already have focus from
- // the click event handler above
- $( dt.table().body() ).on( 'dblclick.keyTable', 'th, td', function (e) {
- if ( that.s.enable === false ) {
- return;
- }
- var cell = dt.cell( this );
- if ( ! cell.any() ) {
- return;
- }
- that._editor( null, e, true );
- } );
- }
- // Stave saving
- if ( dt.settings()[0].oFeatures.bStateSave ) {
- dt.on( 'stateSaveParams.keyTable', function (e, s, d) {
- d.keyTable = that.s.lastFocus ?
- that.s.lastFocus.cell.index() :
- null;
- } );
- }
- // Redraw - retain focus on the current cell
- dt.on( 'draw.keyTable', function (e) {
- if ( that.s.focusDraw ) {
- return;
- }
- var lastFocus = that.s.lastFocus;
- if ( lastFocus && lastFocus.node && $(lastFocus.node).closest('body') === document.body ) {
- var relative = that.s.lastFocus.relative;
- var info = dt.page.info();
- var row = relative.row + info.start;
- if ( info.recordsDisplay === 0 ) {
- return;
- }
- // Reverse if needed
- if ( row >= info.recordsDisplay ) {
- row = info.recordsDisplay - 1;
- }
- that._focus( row, relative.column, true, e );
- }
- } );
- // Clipboard support
- if ( this.c.clipboard ) {
- this._clipboard();
- }
- dt.on( 'destroy.keyTable', function () {
- dt.off( '.keyTable' );
- $( dt.table().body() ).off( 'click.keyTable', 'th, td' );
- $( document )
- .off( 'keydown.keyTable' )
- .off( 'click.keyTable' )
- .off( 'copy.keyTable' )
- .off( 'paste.keyTable' );
- } );
- // Initial focus comes from state or options
- var state = dt.state.loaded();
- if ( state && state.keyTable ) {
- // Wait until init is done
- dt.one( 'init', function () {
- var cell = dt.cell( state.keyTable );
- // Ensure that the saved cell still exists
- if ( cell.any() ) {
- cell.focus();
- }
- } );
- }
- else if ( this.c.focus ) {
- dt.cell( this.c.focus ).focus();
- }
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Private methods
- */
- /**
- * Blur the control
- *
- * @private
- */
- _blur: function ()
- {
- if ( ! this.s.enable || ! this.s.lastFocus ) {
- return;
- }
- var cell = this.s.lastFocus.cell;
- $( cell.node() ).removeClass( this.c.className );
- this.s.lastFocus = null;
- this._updateFixedColumns(cell.index().column);
- this._emitEvent( 'key-blur', [ this.s.dt, cell ] );
- },
- /**
- * Clipboard interaction handlers
- *
- * @private
- */
- _clipboard: function () {
- var dt = this.s.dt;
- var that = this;
- // IE8 doesn't support getting selected text
- if ( ! window.getSelection ) {
- return;
- }
- $(document).on( 'copy.keyTable', function (ejq) {
- var e = ejq.originalEvent;
- var selection = window.getSelection().toString();
- var focused = that.s.lastFocus;
- // Only copy cell text to clipboard if there is no other selection
- // and there is a focused cell
- if ( ! selection && focused ) {
- e.clipboardData.setData(
- 'text/plain',
- focused.cell.render( that.c.clipboardOrthogonal )
- );
- e.preventDefault();
- }
- } );
- $(document).on( 'paste.keyTable', function (ejq) {
- var e = ejq.originalEvent;
- var focused = that.s.lastFocus;
- var activeEl = document.activeElement;
- var editor = that.c.editor;
- var pastedText;
- if ( focused && (! activeEl || activeEl.nodeName.toLowerCase() === 'body') ) {
- e.preventDefault();
- if ( window.clipboardData && window.clipboardData.getData ) {
- // IE
- pastedText = window.clipboardData.getData('Text');
- }
- else if ( e.clipboardData && e.clipboardData.getData ) {
- // Everything else
- pastedText = e.clipboardData.getData('text/plain');
- }
- if ( editor ) {
- // Got Editor - need to activate inline editing,
- // set the value and submit
- editor
- .inline( focused.cell.index() )
- .set( editor.displayed()[0], pastedText )
- .submit();
- }
- else {
- // No editor, so just dump the data in
- focused.cell.data( pastedText );
- dt.draw(false);
- }
- }
- } );
- },
- /**
- * Get an array of the column indexes that KeyTable can operate on. This
- * is a merge of the user supplied columns and the visible columns.
- *
- * @private
- */
- _columns: function ()
- {
- var dt = this.s.dt;
- var user = dt.columns( this.c.columns ).indexes();
- var out = [];
- dt.columns( ':visible' ).every( function (i) {
- if ( user.indexOf( i ) !== -1 ) {
- out.push( i );
- }
- } );
- return out;
- },
- /**
- * Perform excel like navigation for Editor by triggering an edit on key
- * press
- *
- * @param {integer} key Key code for the pressed key
- * @param {object} orig Original event
- * @private
- */
- _editor: function ( key, orig, hardEdit )
- {
- var that = this;
- var dt = this.s.dt;
- var editor = this.c.editor;
- var editCell = this.s.lastFocus.cell;
- // Do nothing if there is already an inline edit in this cell
- if ( $('div.DTE', editCell.node()).length ) {
- return;
- }
- // Don't activate Editor on control key presses
- if ( key !== null && (
- (key >= 0x00 && key <= 0x09) ||
- key === 0x0b ||
- key === 0x0c ||
- (key >= 0x0e && key <= 0x1f) ||
- (key >= 0x70 && key <= 0x7b) ||
- (key >= 0x7f && key <= 0x9f)
- ) ) {
- return;
- }
- orig.stopPropagation();
- // Return key should do nothing - for textareas it would empty the
- // contents
- if ( key === 13 ) {
- orig.preventDefault();
- }
- var editInline = function () {
- editor
- .one( 'open.keyTable', function () {
- // Remove cancel open
- editor.off( 'cancelOpen.keyTable' );
- // Excel style - select all text
- if ( ! hardEdit ) {
- $('div.DTE_Field_InputControl input, div.DTE_Field_InputControl textarea').select();
- }
- // Reduce the keys the Keys listens for
- dt.keys.enable( hardEdit ? 'tab-only' : 'navigation-only' );
- // On blur of the navigation submit
- dt.on( 'key-blur.editor', function () {
- if ( editor.displayed() ) {
- editor.submit();
- }
- } );
- // Highlight the cell a different colour on full edit
- if ( hardEdit ) {
- $( dt.table().container() ).addClass('dtk-focus-alt');
- }
- editor.on( 'submitUnsuccessful.keyTable', function () {
- that._focus( editCell, null, false );
- } );
- // Restore full key navigation on close
- editor.one( 'close', function () {
- dt.keys.enable( true );
- dt.off( 'key-blur.editor' );
- editor.off( '.keyTable' );
- $( dt.table().container() ).removeClass('dtk-focus-alt');
- } );
- } )
- .one( 'cancelOpen.keyTable', function () {
- // `preOpen` can cancel the display of the form, so it
- // might be that the open event handler isn't needed
- editor.off( '.keyTable' );
- } )
- .inline( editCell.index() );
- };
- // Editor 1.7 listens for `return` on keyup, so if return is the trigger
- // key, we need to wait for `keyup` otherwise Editor would just submit
- // the content triggered by this keypress.
- if ( key === 13 ) {
- hardEdit = true;
- $(document).one( 'keyup', function () { // immediately removed
- editInline();
- } );
- }
- else {
- editInline();
- }
- },
- /**
- * Emit an event on the DataTable for listeners
- *
- * @param {string} name Event name
- * @param {array} args Event arguments
- * @private
- */
- _emitEvent: function ( name, args )
- {
- this.s.dt.iterator( 'table', function ( ctx, i ) {
- $(ctx.nTable).triggerHandler( name, args );
- } );
- },
- /**
- * Focus on a particular cell, shifting the table's paging if required
- *
- * @param {DataTables.Api|integer} row Can be given as an API instance that
- * contains the cell to focus or as an integer. As the latter it is the
- * visible row index (from the whole data set) - NOT the data index
- * @param {integer} [column] Not required if a cell is given as the first
- * parameter. Otherwise this is the column data index for the cell to
- * focus on
- * @param {boolean} [shift=true] Should the viewport be moved to show cell
- * @private
- */
- _focus: function ( row, column, shift, originalEvent )
- {
- var that = this;
- var dt = this.s.dt;
- var pageInfo = dt.page.info();
- var lastFocus = this.s.lastFocus;
- if ( ! originalEvent) {
- originalEvent = null;
- }
- if ( ! this.s.enable ) {
- return;
- }
- if ( typeof row !== 'number' ) {
- // Its an API instance - check that there is actually a row
- if ( ! row.any() ) {
- return;
- }
- // Convert the cell to a row and column
- var index = row.index();
- column = index.column;
- row = dt
- .rows( { filter: 'applied', order: 'applied' } )
- .indexes()
- .indexOf( index.row );
-
- // Don't focus rows that were filtered out.
- if ( row < 0 ) {
- return;
- }
- // For server-side processing normalise the row by adding the start
- // point, since `rows().indexes()` includes only rows that are
- // available at the client-side
- if ( pageInfo.serverSide ) {
- row += pageInfo.start;
- }
- }
- // Is the row on the current page? If not, we need to redraw to show the
- // page
- if ( pageInfo.length !== -1 && (row < pageInfo.start || row >= pageInfo.start+pageInfo.length) ) {
- this.s.focusDraw = true;
- this.s.waitingForDraw = true;
- dt
- .one( 'draw', function () {
- that.s.focusDraw = false;
- that.s.waitingForDraw = false;
- that._focus( row, column, undefined, originalEvent );
- } )
- .page( Math.floor( row / pageInfo.length ) )
- .draw( false );
- return;
- }
- // In the available columns?
- if ( $.inArray( column, this._columns() ) === -1 ) {
- return;
- }
- // De-normalise the server-side processing row, so we select the row
- // in its displayed position
- if ( pageInfo.serverSide ) {
- row -= pageInfo.start;
- }
- // Get the cell from the current position - ignoring any cells which might
- // not have been rendered (therefore can't use `:eq()` selector).
- var cells = dt.cells( null, column, {search: 'applied', order: 'applied'} ).flatten();
- var cell = dt.cell( cells[ row ] );
- if ( lastFocus ) {
- // Don't trigger a refocus on the same cell
- if ( lastFocus.node === cell.node() ) {
- this._emitEvent( 'key-refocus', [ this.s.dt, cell, originalEvent || null ] );
- return;
- }
- // Otherwise blur the old focus
- this._blur();
- }
- var node = $( cell.node() );
- node.addClass( this.c.className );
- this._updateFixedColumns(column);
- // Shift viewpoint and page to make cell visible
- if ( shift === undefined || shift === true ) {
- this._scroll( $(window), $(document.body), node, 'offset' );
- var bodyParent = dt.table().body().parentNode;
- if ( bodyParent !== dt.table().header().parentNode ) {
- var parent = $(bodyParent.parentNode);
- this._scroll( parent, parent, node, 'position' );
- }
- }
- // Event and finish
- this.s.lastFocus = {
- cell: cell,
- node: cell.node(),
- relative: {
- row: dt.rows( { page: 'current' } ).indexes().indexOf( cell.index().row ),
- column: cell.index().column
- }
- };
- this._emitEvent( 'key-focus', [ this.s.dt, cell, originalEvent || null ] );
- dt.state.save();
- },
- /**
- * Handle key press
- *
- * @param {object} e Event
- * @private
- */
- _key: function ( e )
- {
- // If we are waiting for a draw to happen from another key event, then
- // do nothing for this new key press.
- if ( this.s.waitingForDraw ) {
- e.preventDefault();
- return;
- }
- var enable = this.s.enable;
- var navEnable = enable === true || enable === 'navigation-only';
- if ( ! enable ) {
- return;
- }
- if ( (e.keyCode === 0 || e.ctrlKey || e.metaKey || e.altKey) && !(e.ctrlKey && e.altKey) ) {
- return;
- }
- // If not focused, then there is no key action to take
- var lastFocus = this.s.lastFocus;
- if ( ! lastFocus ) {
- return;
- }
- var that = this;
- var dt = this.s.dt;
- var scrolling = this.s.dt.settings()[0].oScroll.sY ? true : false;
- // If we are not listening for this key, do nothing
- if ( this.c.keys && $.inArray( e.keyCode, this.c.keys ) === -1 ) {
- return;
- }
- switch( e.keyCode ) {
- case 9: // tab
- // `enable` can be tab-only
- this._shift( e, e.shiftKey ? 'left' : 'right', true );
- break;
- case 27: // esc
- if ( this.s.blurable && enable === true ) {
- this._blur();
- }
- break;
- case 33: // page up (previous page)
- case 34: // page down (next page)
- if ( navEnable && !scrolling ) {
- e.preventDefault();
- dt
- .page( e.keyCode === 33 ? 'previous' : 'next' )
- .draw( false );
- }
- break;
- case 35: // end (end of current page)
- case 36: // home (start of current page)
- if ( navEnable ) {
- e.preventDefault();
- var indexes = dt.cells( {page: 'current'} ).indexes();
- var colIndexes = this._columns();
- this._focus( dt.cell(
- indexes[ e.keyCode === 35 ? indexes.length-1 : colIndexes[0] ]
- ), null, true, e );
- }
- break;
- case 37: // left arrow
- if ( navEnable ) {
- this._shift( e, 'left' );
- }
- break;
- case 38: // up arrow
- if ( navEnable ) {
- this._shift( e, 'up' );
- }
- break;
- case 39: // right arrow
- if ( navEnable ) {
- this._shift( e, 'right' );
- }
- break;
- case 40: // down arrow
- if ( navEnable ) {
- this._shift( e, 'down' );
- }
- break;
- default:
- // Everything else - pass through only when fully enabled
- if ( enable === true ) {
- this._emitEvent( 'key', [ dt, e.keyCode, this.s.lastFocus.cell, e ] );
- }
- break;
- }
- },
- /**
- * Scroll a container to make a cell visible in it. This can be used for
- * both DataTables scrolling and native window scrolling.
- *
- * @param {jQuery} container Scrolling container
- * @param {jQuery} scroller Item being scrolled
- * @param {jQuery} cell Cell in the scroller
- * @param {string} posOff `position` or `offset` - which to use for the
- * calculation. `offset` for the document, otherwise `position`
- * @private
- */
- _scroll: function ( container, scroller, cell, posOff )
- {
- var offset = cell[posOff]();
- var height = cell.outerHeight();
- var width = cell.outerWidth();
- var scrollTop = scroller.scrollTop();
- var scrollLeft = scroller.scrollLeft();
- var containerHeight = container.height();
- var containerWidth = container.width();
- // If Scroller is being used, the table can be `position: absolute` and that
- // needs to be taken account of in the offset. If no Scroller, this will be 0
- if ( posOff === 'position' ) {
- offset.top += parseInt( cell.closest('table').css('top'), 10 );
- }
- // Top correction
- if ( offset.top < scrollTop ) {
- scroller.scrollTop( offset.top );
- }
- // Left correction
- if ( offset.left < scrollLeft ) {
- scroller.scrollLeft( offset.left );
- }
- // Bottom correction
- if ( offset.top + height > scrollTop + containerHeight && height < containerHeight ) {
- scroller.scrollTop( offset.top + height - containerHeight );
- }
- // Right correction
- if ( offset.left + width > scrollLeft + containerWidth && width < containerWidth ) {
- scroller.scrollLeft( offset.left + width - containerWidth );
- }
- },
- /**
- * Calculate a single offset movement in the table - up, down, left and
- * right and then perform the focus if possible
- *
- * @param {object} e Event object
- * @param {string} direction Movement direction
- * @param {boolean} keyBlurable `true` if the key press can result in the
- * table being blurred. This is so arrow keys won't blur the table, but
- * tab will.
- * @private
- */
- _shift: function ( e, direction, keyBlurable )
- {
- var that = this;
- var dt = this.s.dt;
- var pageInfo = dt.page.info();
- var rows = pageInfo.recordsDisplay;
- var currentCell = this.s.lastFocus.cell;
- var columns = this._columns();
- if ( ! currentCell ) {
- return;
- }
- var currRow = dt
- .rows( { filter: 'applied', order: 'applied' } )
- .indexes()
- .indexOf( currentCell.index().row );
- // When server-side processing, `rows().indexes()` only gives the rows
- // that are available at the client-side, so we need to normalise the
- // row's current position by the display start point
- if ( pageInfo.serverSide ) {
- currRow += pageInfo.start;
- }
- var currCol = dt
- .columns( columns )
- .indexes()
- .indexOf( currentCell.index().column );
- var
- row = currRow,
- column = columns[ currCol ]; // row is the display, column is an index
- if ( direction === 'right' ) {
- if ( currCol >= columns.length - 1 ) {
- row++;
- column = columns[0];
- }
- else {
- column = columns[ currCol+1 ];
- }
- }
- else if ( direction === 'left' ) {
- if ( currCol === 0 ) {
- row--;
- column = columns[ columns.length - 1 ];
- }
- else {
- column = columns[ currCol-1 ];
- }
- }
- else if ( direction === 'up' ) {
- row--;
- }
- else if ( direction === 'down' ) {
- row++;
- }
- if ( row >= 0 && row < rows && $.inArray( column, columns ) !== -1
- ) {
- e.preventDefault();
- this._focus( row, column, true, e );
- }
- else if ( ! keyBlurable || ! this.c.blurable ) {
- // No new focus, but if the table isn't blurable, then don't loose
- // focus
- e.preventDefault();
- }
- else {
- this._blur();
- }
- },
- /**
- * Create a hidden input element that can receive focus on behalf of the
- * table
- *
- * @private
- */
- _tabInput: function ()
- {
- var that = this;
- var dt = this.s.dt;
- var tabIndex = this.c.tabIndex !== null ?
- this.c.tabIndex :
- dt.settings()[0].iTabIndex;
- if ( tabIndex == -1 ) {
- return;
- }
- var div = $('<div><input type="text" tabindex="'+tabIndex+'"/></div>')
- .css( {
- position: 'absolute',
- height: 1,
- width: 0,
- overflow: 'hidden'
- } )
- .insertBefore( dt.table().node() );
- div.children().on( 'focus', function (e) {
- if ( dt.cell(':eq(0)', {page: 'current'}).any() ) {
- that._focus( dt.cell(':eq(0)', '0:visible', {page: 'current'}), null, true, e );
- }
- } );
- },
- /**
- * Update fixed columns if they are enabled and if the cell we are
- * focusing is inside a fixed column
- * @param {integer} column Index of the column being changed
- * @private
- */
- _updateFixedColumns: function( column )
- {
- var dt = this.s.dt;
- var settings = dt.settings()[0];
- if ( settings._oFixedColumns ) {
- var leftCols = settings._oFixedColumns.s.iLeftColumns;
- var rightCols = settings.aoColumns.length - settings._oFixedColumns.s.iRightColumns;
- if (column < leftCols || column >= rightCols) {
- dt.fixedColumns().update();
- }
- }
- }
- } );
- /**
- * KeyTable default settings for initialisation
- *
- * @namespace
- * @name KeyTable.defaults
- * @static
- */
- KeyTable.defaults = {
- /**
- * Can focus be removed from the table
- * @type {Boolean}
- */
- blurable: true,
- /**
- * Class to give to the focused cell
- * @type {String}
- */
- className: 'focus',
- /**
- * Enable or disable clipboard support
- * @type {Boolean}
- */
- clipboard: true,
- /**
- * Orthogonal data that should be copied to clipboard
- * @type {string}
- */
- clipboardOrthogonal: 'display',
- /**
- * Columns that can be focused. This is automatically merged with the
- * visible columns as only visible columns can gain focus.
- * @type {String}
- */
- columns: '', // all
- /**
- * Editor instance to automatically perform Excel like navigation
- * @type {Editor}
- */
- editor: null,
- /**
- * Trigger editing immediately on focus
- * @type {boolean}
- */
- editOnFocus: false,
- /**
- * Select a cell to automatically select on start up. `null` for no
- * automatic selection
- * @type {cell-selector}
- */
- focus: null,
- /**
- * Array of keys to listen for
- * @type {null|array}
- */
- keys: null,
- /**
- * Tab index for where the table should sit in the document's tab flow
- * @type {integer|null}
- */
- tabIndex: null
- };
- KeyTable.version = "2.5.0";
- $.fn.dataTable.KeyTable = KeyTable;
- $.fn.DataTable.KeyTable = KeyTable;
- DataTable.Api.register( 'cell.blur()', function () {
- return this.iterator( 'table', function (ctx) {
- if ( ctx.keytable ) {
- ctx.keytable.blur();
- }
- } );
- } );
- DataTable.Api.register( 'cell().focus()', function () {
- return this.iterator( 'cell', function (ctx, row, column) {
- if ( ctx.keytable ) {
- ctx.keytable.focus( row, column );
- }
- } );
- } );
- DataTable.Api.register( 'keys.disable()', function () {
- return this.iterator( 'table', function (ctx) {
- if ( ctx.keytable ) {
- ctx.keytable.enable( false );
- }
- } );
- } );
- DataTable.Api.register( 'keys.enable()', function ( opts ) {
- return this.iterator( 'table', function (ctx) {
- if ( ctx.keytable ) {
- ctx.keytable.enable( opts === undefined ? true : opts );
- }
- } );
- } );
- // Cell selector
- DataTable.ext.selector.cell.push( function ( settings, opts, cells ) {
- var focused = opts.focused;
- var kt = settings.keytable;
- var out = [];
- if ( ! kt || focused === undefined ) {
- return cells;
- }
- for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
- if ( (focused === true && kt.focused( cells[i] ) ) ||
- (focused === false && ! kt.focused( cells[i] ) )
- ) {
- out.push( cells[i] );
- }
- }
- return out;
- } );
- // Attach a listener to the document which listens for DataTables initialisation
- // events so we can automatically initialise
- $(document).on( 'preInit.dt.dtk', function (e, settings, json) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- var init = settings.oInit.keys;
- var defaults = DataTable.defaults.keys;
- if ( init || defaults ) {
- var opts = $.extend( {}, defaults, init );
- if ( init !== false ) {
- new KeyTable( settings, opts );
- }
- }
- } );
- return KeyTable;
- }));
- /*! Bootstrap 4 styling wrapper for KeyTable
- * ©2018 SpryMedia Ltd - datatables.net/license
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net-bs4', 'datatables.net-keytable'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net-bs4')(root, $).$;
- }
- if ( ! $.fn.dataTable.KeyTable ) {
- require('datatables.net-keytable')(root, $);
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- return $.fn.dataTable;
- }));
- /*! Responsive 2.2.3
- * 2014-2018 SpryMedia Ltd - datatables.net/license
- */
- /**
- * @summary Responsive
- * @description Responsive tables plug-in for DataTables
- * @version 2.2.3
- * @file dataTables.responsive.js
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
- * @contact www.sprymedia.co.uk/contact
- * @copyright Copyright 2014-2018 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license/mit
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- /**
- * Responsive is a plug-in for the DataTables library that makes use of
- * DataTables' ability to change the visibility of columns, changing the
- * visibility of columns so the displayed columns fit into the table container.
- * The end result is that complex tables will be dynamically adjusted to fit
- * into the viewport, be it on a desktop, tablet or mobile browser.
- *
- * Responsive for DataTables has two modes of operation, which can used
- * individually or combined:
- *
- * * Class name based control - columns assigned class names that match the
- * breakpoint logic can be shown / hidden as required for each breakpoint.
- * * Automatic control - columns are automatically hidden when there is no
- * room left to display them. Columns removed from the right.
- *
- * In additional to column visibility control, Responsive also has built into
- * options to use DataTables' child row display to show / hide the information
- * from the table that has been hidden. There are also two modes of operation
- * for this child row display:
- *
- * * Inline - when the control element that the user can use to show / hide
- * child rows is displayed inside the first column of the table.
- * * Column - where a whole column is dedicated to be the show / hide control.
- *
- * Initialisation of Responsive is performed by:
- *
- * * Adding the class `responsive` or `dt-responsive` to the table. In this case
- * Responsive will automatically be initialised with the default configuration
- * options when the DataTable is created.
- * * Using the `responsive` option in the DataTables configuration options. This
- * can also be used to specify the configuration options, or simply set to
- * `true` to use the defaults.
- *
- * @class
- * @param {object} settings DataTables settings object for the host table
- * @param {object} [opts] Configuration options
- * @requires jQuery 1.7+
- * @requires DataTables 1.10.3+
- *
- * @example
- * $('#example').DataTable( {
- * responsive: true
- * } );
- * } );
- */
- var Responsive = function ( settings, opts ) {
- // Sanity check that we are using DataTables 1.10 or newer
- if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.10' ) ) {
- throw 'DataTables Responsive requires DataTables 1.10.10 or newer';
- }
- this.s = {
- dt: new DataTable.Api( settings ),
- columns: [],
- current: []
- };
- // Check if responsive has already been initialised on this table
- if ( this.s.dt.settings()[0].responsive ) {
- return;
- }
- // details is an object, but for simplicity the user can give it as a string
- // or a boolean
- if ( opts && typeof opts.details === 'string' ) {
- opts.details = { type: opts.details };
- }
- else if ( opts && opts.details === false ) {
- opts.details = { type: false };
- }
- else if ( opts && opts.details === true ) {
- opts.details = { type: 'inline' };
- }
- this.c = $.extend( true, {}, Responsive.defaults, DataTable.defaults.responsive, opts );
- settings.responsive = this;
- this._constructor();
- };
- $.extend( Responsive.prototype, {
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constructor
- */
- /**
- * Initialise the Responsive instance
- *
- * @private
- */
- _constructor: function ()
- {
- var that = this;
- var dt = this.s.dt;
- var dtPrivateSettings = dt.settings()[0];
- var oldWindowWidth = $(window).width();
- dt.settings()[0]._responsive = this;
- // Use DataTables' throttle function to avoid processor thrashing on
- // resize
- $(window).on( 'resize.dtr orientationchange.dtr', DataTable.util.throttle( function () {
- // iOS has a bug whereby resize can fire when only scrolling
- // See: http://stackoverflow.com/questions/8898412
- var width = $(window).width();
- if ( width !== oldWindowWidth ) {
- that._resize();
- oldWindowWidth = width;
- }
- } ) );
- // DataTables doesn't currently trigger an event when a row is added, so
- // we need to hook into its private API to enforce the hidden rows when
- // new data is added
- dtPrivateSettings.oApi._fnCallbackReg( dtPrivateSettings, 'aoRowCreatedCallback', function (tr, data, idx) {
- if ( $.inArray( false, that.s.current ) !== -1 ) {
- $('>td, >th', tr).each( function ( i ) {
- var idx = dt.column.index( 'toData', i );
- if ( that.s.current[idx] === false ) {
- $(this).css('display', 'none');
- }
- } );
- }
- } );
- // Destroy event handler
- dt.on( 'destroy.dtr', function () {
- dt.off( '.dtr' );
- $( dt.table().body() ).off( '.dtr' );
- $(window).off( 'resize.dtr orientationchange.dtr' );
- // Restore the columns that we've hidden
- $.each( that.s.current, function ( i, val ) {
- if ( val === false ) {
- that._setColumnVis( i, true );
- }
- } );
- } );
- // Reorder the breakpoints array here in case they have been added out
- // of order
- this.c.breakpoints.sort( function (a, b) {
- return a.width < b.width ? 1 :
- a.width > b.width ? -1 : 0;
- } );
- this._classLogic();
- this._resizeAuto();
- // Details handler
- var details = this.c.details;
- if ( details.type !== false ) {
- that._detailsInit();
- // DataTables will trigger this event on every column it shows and
- // hides individually
- dt.on( 'column-visibility.dtr', function () {
- // Use a small debounce to allow multiple columns to be set together
- if ( that._timer ) {
- clearTimeout( that._timer );
- }
- that._timer = setTimeout( function () {
- that._timer = null;
- that._classLogic();
- that._resizeAuto();
- that._resize();
- that._redrawChildren();
- }, 100 );
- } );
- // Redraw the details box on each draw which will happen if the data
- // has changed. This is used until DataTables implements a native
- // `updated` event for rows
- dt.on( 'draw.dtr', function () {
- that._redrawChildren();
- } );
- $(dt.table().node()).addClass( 'dtr-'+details.type );
- }
- dt.on( 'column-reorder.dtr', function (e, settings, details) {
- that._classLogic();
- that._resizeAuto();
- that._resize();
- } );
- // Change in column sizes means we need to calc
- dt.on( 'column-sizing.dtr', function () {
- that._resizeAuto();
- that._resize();
- });
- // On Ajax reload we want to reopen any child rows which are displayed
- // by responsive
- dt.on( 'preXhr.dtr', function () {
- var rowIds = [];
- dt.rows().every( function () {
- if ( this.child.isShown() ) {
- rowIds.push( this.id(true) );
- }
- } );
- dt.one( 'draw.dtr', function () {
- that._resizeAuto();
- that._resize();
- dt.rows( rowIds ).every( function () {
- that._detailsDisplay( this, false );
- } );
- } );
- });
- dt.on( 'init.dtr', function (e, settings, details) {
- that._resizeAuto();
- that._resize();
- // If columns were hidden, then DataTables needs to adjust the
- // column sizing
- if ( $.inArray( false, that.s.current ) ) {
- dt.columns.adjust();
- }
- } );
- // First pass - draw the table for the current viewport size
- this._resize();
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Private methods
- */
- /**
- * Calculate the visibility for the columns in a table for a given
- * breakpoint. The result is pre-determined based on the class logic if
- * class names are used to control all columns, but the width of the table
- * is also used if there are columns which are to be automatically shown
- * and hidden.
- *
- * @param {string} breakpoint Breakpoint name to use for the calculation
- * @return {array} Array of boolean values initiating the visibility of each
- * column.
- * @private
- */
- _columnsVisiblity: function ( breakpoint )
- {
- var dt = this.s.dt;
- var columns = this.s.columns;
- var i, ien;
- // Create an array that defines the column ordering based first on the
- // column's priority, and secondly the column index. This allows the
- // columns to be removed from the right if the priority matches
- var order = columns
- .map( function ( col, idx ) {
- return {
- columnIdx: idx,
- priority: col.priority
- };
- } )
- .sort( function ( a, b ) {
- if ( a.priority !== b.priority ) {
- return a.priority - b.priority;
- }
- return a.columnIdx - b.columnIdx;
- } );
- // Class logic - determine which columns are in this breakpoint based
- // on the classes. If no class control (i.e. `auto`) then `-` is used
- // to indicate this to the rest of the function
- var display = $.map( columns, function ( col, i ) {
- if ( dt.column(i).visible() === false ) {
- return 'not-visible';
- }
- return col.auto && col.minWidth === null ?
- false :
- col.auto === true ?
- '-' :
- $.inArray( breakpoint, col.includeIn ) !== -1;
- } );
- // Auto column control - first pass: how much width is taken by the
- // ones that must be included from the non-auto columns
- var requiredWidth = 0;
- for ( i=0, ien=display.length ; i<ien ; i++ ) {
- if ( display[i] === true ) {
- requiredWidth += columns[i].minWidth;
- }
- }
- // Second pass, use up any remaining width for other columns. For
- // scrolling tables we need to subtract the width of the scrollbar. It
- // may not be requires which makes this sub-optimal, but it would
- // require another full redraw to make complete use of those extra few
- // pixels
- var scrolling = dt.settings()[0].oScroll;
- var bar = scrolling.sY || scrolling.sX ? scrolling.iBarWidth : 0;
- var widthAvailable = dt.table().container().offsetWidth - bar;
- var usedWidth = widthAvailable - requiredWidth;
- // Control column needs to always be included. This makes it sub-
- // optimal in terms of using the available with, but to stop layout
- // thrashing or overflow. Also we need to account for the control column
- // width first so we know how much width is available for the other
- // columns, since the control column might not be the first one shown
- for ( i=0, ien=display.length ; i<ien ; i++ ) {
- if ( columns[i].control ) {
- usedWidth -= columns[i].minWidth;
- }
- }
- // Allow columns to be shown (counting by priority and then right to
- // left) until we run out of room
- var empty = false;
- for ( i=0, ien=order.length ; i<ien ; i++ ) {
- var colIdx = order[i].columnIdx;
- if ( display[colIdx] === '-' && ! columns[colIdx].control && columns[colIdx].minWidth ) {
- // Once we've found a column that won't fit we don't let any
- // others display either, or columns might disappear in the
- // middle of the table
- if ( empty || usedWidth - columns[colIdx].minWidth < 0 ) {
- empty = true;
- display[colIdx] = false;
- }
- else {
- display[colIdx] = true;
- }
- usedWidth -= columns[colIdx].minWidth;
- }
- }
- // Determine if the 'control' column should be shown (if there is one).
- // This is the case when there is a hidden column (that is not the
- // control column). The two loops look inefficient here, but they are
- // trivial and will fly through. We need to know the outcome from the
- // first , before the action in the second can be taken
- var showControl = false;
- for ( i=0, ien=columns.length ; i<ien ; i++ ) {
- if ( ! columns[i].control && ! columns[i].never && display[i] === false ) {
- showControl = true;
- break;
- }
- }
- for ( i=0, ien=columns.length ; i<ien ; i++ ) {
- if ( columns[i].control ) {
- display[i] = showControl;
- }
- // Replace not visible string with false from the control column detection above
- if ( display[i] === 'not-visible' ) {
- display[i] = false;
- }
- }
- // Finally we need to make sure that there is at least one column that
- // is visible
- if ( $.inArray( true, display ) === -1 ) {
- display[0] = true;
- }
- return display;
- },
- /**
- * Create the internal `columns` array with information about the columns
- * for the table. This includes determining which breakpoints the column
- * will appear in, based upon class names in the column, which makes up the
- * vast majority of this method.
- *
- * @private
- */
- _classLogic: function ()
- {
- var that = this;
- var calc = {};
- var breakpoints = this.c.breakpoints;
- var dt = this.s.dt;
- var columns = dt.columns().eq(0).map( function (i) {
- var column = this.column(i);
- var className = column.header().className;
- var priority = dt.settings()[0].aoColumns[i].responsivePriority;
- if ( priority === undefined ) {
- var dataPriority = $(column.header()).data('priority');
- priority = dataPriority !== undefined ?
- dataPriority * 1 :
- 10000;
- }
- return {
- className: className,
- includeIn: [],
- auto: false,
- control: false,
- never: className.match(/\bnever\b/) ? true : false,
- priority: priority
- };
- } );
- // Simply add a breakpoint to `includeIn` array, ensuring that there are
- // no duplicates
- var add = function ( colIdx, name ) {
- var includeIn = columns[ colIdx ].includeIn;
- if ( $.inArray( name, includeIn ) === -1 ) {
- includeIn.push( name );
- }
- };
- var column = function ( colIdx, name, operator, matched ) {
- var size, i, ien;
- if ( ! operator ) {
- columns[ colIdx ].includeIn.push( name );
- }
- else if ( operator === 'max-' ) {
- // Add this breakpoint and all smaller
- size = that._find( name ).width;
- for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
- if ( breakpoints[i].width <= size ) {
- add( colIdx, breakpoints[i].name );
- }
- }
- }
- else if ( operator === 'min-' ) {
- // Add this breakpoint and all larger
- size = that._find( name ).width;
- for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
- if ( breakpoints[i].width >= size ) {
- add( colIdx, breakpoints[i].name );
- }
- }
- }
- else if ( operator === 'not-' ) {
- // Add all but this breakpoint
- for ( i=0, ien=breakpoints.length ; i<ien ; i++ ) {
- if ( breakpoints[i].name.indexOf( matched ) === -1 ) {
- add( colIdx, breakpoints[i].name );
- }
- }
- }
- };
- // Loop over each column and determine if it has a responsive control
- // class
- columns.each( function ( col, i ) {
- var classNames = col.className.split(' ');
- var hasClass = false;
- // Split the class name up so multiple rules can be applied if needed
- for ( var k=0, ken=classNames.length ; k<ken ; k++ ) {
- var className = $.trim( classNames[k] );
- if ( className === 'all' ) {
- // Include in all
- hasClass = true;
- col.includeIn = $.map( breakpoints, function (a) {
- return a.name;
- } );
- return;
- }
- else if ( className === 'none' || col.never ) {
- // Include in none (default) and no auto
- hasClass = true;
- return;
- }
- else if ( className === 'control' ) {
- // Special column that is only visible, when one of the other
- // columns is hidden. This is used for the details control
- hasClass = true;
- col.control = true;
- return;
- }
- $.each( breakpoints, function ( j, breakpoint ) {
- // Does this column have a class that matches this breakpoint?
- var brokenPoint = breakpoint.name.split('-');
- var re = new RegExp( '(min\\-|max\\-|not\\-)?('+brokenPoint[0]+')(\\-[_a-zA-Z0-9])?' );
- var match = className.match( re );
- if ( match ) {
- hasClass = true;
- if ( match[2] === brokenPoint[0] && match[3] === '-'+brokenPoint[1] ) {
- // Class name matches breakpoint name fully
- column( i, breakpoint.name, match[1], match[2]+match[3] );
- }
- else if ( match[2] === brokenPoint[0] && ! match[3] ) {
- // Class name matched primary breakpoint name with no qualifier
- column( i, breakpoint.name, match[1], match[2] );
- }
- }
- } );
- }
- // If there was no control class, then automatic sizing is used
- if ( ! hasClass ) {
- col.auto = true;
- }
- } );
- this.s.columns = columns;
- },
- /**
- * Show the details for the child row
- *
- * @param {DataTables.Api} row API instance for the row
- * @param {boolean} update Update flag
- * @private
- */
- _detailsDisplay: function ( row, update )
- {
- var that = this;
- var dt = this.s.dt;
- var details = this.c.details;
- if ( details && details.type !== false ) {
- var res = details.display( row, update, function () {
- return details.renderer(
- dt, row[0], that._detailsObj(row[0])
- );
- } );
- if ( res === true || res === false ) {
- $(dt.table().node()).triggerHandler( 'responsive-display.dt', [dt, row, res, update] );
- }
- }
- },
- /**
- * Initialisation for the details handler
- *
- * @private
- */
- _detailsInit: function ()
- {
- var that = this;
- var dt = this.s.dt;
- var details = this.c.details;
- // The inline type always uses the first child as the target
- if ( details.type === 'inline' ) {
- details.target = 'td:first-child, th:first-child';
- }
- // Keyboard accessibility
- dt.on( 'draw.dtr', function () {
- that._tabIndexes();
- } );
- that._tabIndexes(); // Initial draw has already happened
- $( dt.table().body() ).on( 'keyup.dtr', 'td, th', function (e) {
- if ( e.keyCode === 13 && $(this).data('dtr-keyboard') ) {
- $(this).click();
- }
- } );
- // type.target can be a string jQuery selector or a column index
- var target = details.target;
- var selector = typeof target === 'string' ? target : 'td, th';
- // Click handler to show / hide the details rows when they are available
- $( dt.table().body() )
- .on( 'click.dtr mousedown.dtr mouseup.dtr', selector, function (e) {
- // If the table is not collapsed (i.e. there is no hidden columns)
- // then take no action
- if ( ! $(dt.table().node()).hasClass('collapsed' ) ) {
- return;
- }
- // Check that the row is actually a DataTable's controlled node
- if ( $.inArray( $(this).closest('tr').get(0), dt.rows().nodes().toArray() ) === -1 ) {
- return;
- }
- // For column index, we determine if we should act or not in the
- // handler - otherwise it is already okay
- if ( typeof target === 'number' ) {
- var targetIdx = target < 0 ?
- dt.columns().eq(0).length + target :
- target;
- if ( dt.cell( this ).index().column !== targetIdx ) {
- return;
- }
- }
- // $().closest() includes itself in its check
- var row = dt.row( $(this).closest('tr') );
- // Check event type to do an action
- if ( e.type === 'click' ) {
- // The renderer is given as a function so the caller can execute it
- // only when they need (i.e. if hiding there is no point is running
- // the renderer)
- that._detailsDisplay( row, false );
- }
- else if ( e.type === 'mousedown' ) {
- // For mouse users, prevent the focus ring from showing
- $(this).css('outline', 'none');
- }
- else if ( e.type === 'mouseup' ) {
- // And then re-allow at the end of the click
- $(this).blur().css('outline', '');
- }
- } );
- },
- /**
- * Get the details to pass to a renderer for a row
- * @param {int} rowIdx Row index
- * @private
- */
- _detailsObj: function ( rowIdx )
- {
- var that = this;
- var dt = this.s.dt;
- return $.map( this.s.columns, function( col, i ) {
- // Never and control columns should not be passed to the renderer
- if ( col.never || col.control ) {
- return;
- }
- return {
- title: dt.settings()[0].aoColumns[ i ].sTitle,
- data: dt.cell( rowIdx, i ).render( that.c.orthogonal ),
- hidden: dt.column( i ).visible() && !that.s.current[ i ],
- columnIndex: i,
- rowIndex: rowIdx
- };
- } );
- },
- /**
- * Find a breakpoint object from a name
- *
- * @param {string} name Breakpoint name to find
- * @return {object} Breakpoint description object
- * @private
- */
- _find: function ( name )
- {
- var breakpoints = this.c.breakpoints;
- for ( var i=0, ien=breakpoints.length ; i<ien ; i++ ) {
- if ( breakpoints[i].name === name ) {
- return breakpoints[i];
- }
- }
- },
- /**
- * Re-create the contents of the child rows as the display has changed in
- * some way.
- *
- * @private
- */
- _redrawChildren: function ()
- {
- var that = this;
- var dt = this.s.dt;
- dt.rows( {page: 'current'} ).iterator( 'row', function ( settings, idx ) {
- var row = dt.row( idx );
- that._detailsDisplay( dt.row( idx ), true );
- } );
- },
- /**
- * Alter the table display for a resized viewport. This involves first
- * determining what breakpoint the window currently is in, getting the
- * column visibilities to apply and then setting them.
- *
- * @private
- */
- _resize: function ()
- {
- var that = this;
- var dt = this.s.dt;
- var width = $(window).width();
- var breakpoints = this.c.breakpoints;
- var breakpoint = breakpoints[0].name;
- var columns = this.s.columns;
- var i, ien;
- var oldVis = this.s.current.slice();
- // Determine what breakpoint we are currently at
- for ( i=breakpoints.length-1 ; i>=0 ; i-- ) {
- if ( width <= breakpoints[i].width ) {
- breakpoint = breakpoints[i].name;
- break;
- }
- }
-
- // Show the columns for that break point
- var columnsVis = this._columnsVisiblity( breakpoint );
- this.s.current = columnsVis;
- // Set the class before the column visibility is changed so event
- // listeners know what the state is. Need to determine if there are
- // any columns that are not visible but can be shown
- var collapsedClass = false;
- for ( i=0, ien=columns.length ; i<ien ; i++ ) {
- if ( columnsVis[i] === false && ! columns[i].never && ! columns[i].control && ! dt.column(i).visible() === false ) {
- collapsedClass = true;
- break;
- }
- }
- $( dt.table().node() ).toggleClass( 'collapsed', collapsedClass );
- var changed = false;
- var visible = 0;
- dt.columns().eq(0).each( function ( colIdx, i ) {
- if ( columnsVis[i] === true ) {
- visible++;
- }
- if ( columnsVis[i] !== oldVis[i] ) {
- changed = true;
- that._setColumnVis( colIdx, columnsVis[i] );
- }
- } );
- if ( changed ) {
- this._redrawChildren();
- // Inform listeners of the change
- $(dt.table().node()).trigger( 'responsive-resize.dt', [dt, this.s.current] );
- // If no records, update the "No records" display element
- if ( dt.page.info().recordsDisplay === 0 ) {
- $('td', dt.table().body()).eq(0).attr('colspan', visible);
- }
- }
- },
- /**
- * Determine the width of each column in the table so the auto column hiding
- * has that information to work with. This method is never going to be 100%
- * perfect since column widths can change slightly per page, but without
- * seriously compromising performance this is quite effective.
- *
- * @private
- */
- _resizeAuto: function ()
- {
- var dt = this.s.dt;
- var columns = this.s.columns;
- // Are we allowed to do auto sizing?
- if ( ! this.c.auto ) {
- return;
- }
- // Are there any columns that actually need auto-sizing, or do they all
- // have classes defined
- if ( $.inArray( true, $.map( columns, function (c) { return c.auto; } ) ) === -1 ) {
- return;
- }
- // Need to restore all children. They will be reinstated by a re-render
- if ( ! $.isEmptyObject( _childNodeStore ) ) {
- $.each( _childNodeStore, function ( key ) {
- var idx = key.split('-');
- _childNodesRestore( dt, idx[0]*1, idx[1]*1 );
- } );
- }
- // Clone the table with the current data in it
- var tableWidth = dt.table().node().offsetWidth;
- var columnWidths = dt.columns;
- var clonedTable = dt.table().node().cloneNode( false );
- var clonedHeader = $( dt.table().header().cloneNode( false ) ).appendTo( clonedTable );
- var clonedBody = $( dt.table().body() ).clone( false, false ).empty().appendTo( clonedTable ); // use jQuery because of IE8
- // Header
- var headerCells = dt.columns()
- .header()
- .filter( function (idx) {
- return dt.column(idx).visible();
- } )
- .to$()
- .clone( false )
- .css( 'display', 'table-cell' )
- .css( 'min-width', 0 );
- // Body rows - we don't need to take account of DataTables' column
- // visibility since we implement our own here (hence the `display` set)
- $(clonedBody)
- .append( $(dt.rows( { page: 'current' } ).nodes()).clone( false ) )
- .find( 'th, td' ).css( 'display', '' );
- // Footer
- var footer = dt.table().footer();
- if ( footer ) {
- var clonedFooter = $( footer.cloneNode( false ) ).appendTo( clonedTable );
- var footerCells = dt.columns()
- .footer()
- .filter( function (idx) {
- return dt.column(idx).visible();
- } )
- .to$()
- .clone( false )
- .css( 'display', 'table-cell' );
- $('<tr/>')
- .append( footerCells )
- .appendTo( clonedFooter );
- }
- $('<tr/>')
- .append( headerCells )
- .appendTo( clonedHeader );
- // In the inline case extra padding is applied to the first column to
- // give space for the show / hide icon. We need to use this in the
- // calculation
- if ( this.c.details.type === 'inline' ) {
- $(clonedTable).addClass( 'dtr-inline collapsed' );
- }
-
- // It is unsafe to insert elements with the same name into the DOM
- // multiple times. For example, cloning and inserting a checked radio
- // clears the chcecked state of the original radio.
- $( clonedTable ).find( '[name]' ).removeAttr( 'name' );
- // A position absolute table would take the table out of the flow of
- // our container element, bypassing the height and width (Scroller)
- $( clonedTable ).css( 'position', 'relative' )
-
- var inserted = $('<div/>')
- .css( {
- width: 1,
- height: 1,
- overflow: 'hidden',
- clear: 'both'
- } )
- .append( clonedTable );
- inserted.insertBefore( dt.table().node() );
- // The cloned header now contains the smallest that each column can be
- headerCells.each( function (i) {
- var idx = dt.column.index( 'fromVisible', i );
- columns[ idx ].minWidth = this.offsetWidth || 0;
- } );
- inserted.remove();
- },
- /**
- * Set a column's visibility.
- *
- * We don't use DataTables' column visibility controls in order to ensure
- * that column visibility can Responsive can no-exist. Since only IE8+ is
- * supported (and all evergreen browsers of course) the control of the
- * display attribute works well.
- *
- * @param {integer} col Column index
- * @param {boolean} showHide Show or hide (true or false)
- * @private
- */
- _setColumnVis: function ( col, showHide )
- {
- var dt = this.s.dt;
- var display = showHide ? '' : 'none'; // empty string will remove the attr
- $( dt.column( col ).header() ).css( 'display', display );
- $( dt.column( col ).footer() ).css( 'display', display );
- dt.column( col ).nodes().to$().css( 'display', display );
- // If the are child nodes stored, we might need to reinsert them
- if ( ! $.isEmptyObject( _childNodeStore ) ) {
- dt.cells( null, col ).indexes().each( function (idx) {
- _childNodesRestore( dt, idx.row, idx.column );
- } );
- }
- },
- /**
- * Update the cell tab indexes for keyboard accessibility. This is called on
- * every table draw - that is potentially inefficient, but also the least
- * complex option given that column visibility can change on the fly. Its a
- * shame user-focus was removed from CSS 3 UI, as it would have solved this
- * issue with a single CSS statement.
- *
- * @private
- */
- _tabIndexes: function ()
- {
- var dt = this.s.dt;
- var cells = dt.cells( { page: 'current' } ).nodes().to$();
- var ctx = dt.settings()[0];
- var target = this.c.details.target;
- cells.filter( '[data-dtr-keyboard]' ).removeData( '[data-dtr-keyboard]' );
- if ( typeof target === 'number' ) {
- dt.cells( null, target, { page: 'current' } ).nodes().to$()
- .attr( 'tabIndex', ctx.iTabIndex )
- .data( 'dtr-keyboard', 1 );
- }
- else {
- // This is a bit of a hack - we need to limit the selected nodes to just
- // those of this table
- if ( target === 'td:first-child, th:first-child' ) {
- target = '>td:first-child, >th:first-child';
- }
- $( target, dt.rows( { page: 'current' } ).nodes() )
- .attr( 'tabIndex', ctx.iTabIndex )
- .data( 'dtr-keyboard', 1 );
- }
- }
- } );
- /**
- * List of default breakpoints. Each item in the array is an object with two
- * properties:
- *
- * * `name` - the breakpoint name.
- * * `width` - the breakpoint width
- *
- * @name Responsive.breakpoints
- * @static
- */
- Responsive.breakpoints = [
- { name: 'desktop', width: Infinity },
- { name: 'tablet-l', width: 1024 },
- { name: 'tablet-p', width: 768 },
- { name: 'mobile-l', width: 480 },
- { name: 'mobile-p', width: 320 }
- ];
- /**
- * Display methods - functions which define how the hidden data should be shown
- * in the table.
- *
- * @namespace
- * @name Responsive.defaults
- * @static
- */
- Responsive.display = {
- childRow: function ( row, update, render ) {
- if ( update ) {
- if ( $(row.node()).hasClass('parent') ) {
- row.child( render(), 'child' ).show();
- return true;
- }
- }
- else {
- if ( ! row.child.isShown() ) {
- row.child( render(), 'child' ).show();
- $( row.node() ).addClass( 'parent' );
- return true;
- }
- else {
- row.child( false );
- $( row.node() ).removeClass( 'parent' );
- return false;
- }
- }
- },
- childRowImmediate: function ( row, update, render ) {
- if ( (! update && row.child.isShown()) || ! row.responsive.hasHidden() ) {
- // User interaction and the row is show, or nothing to show
- row.child( false );
- $( row.node() ).removeClass( 'parent' );
- return false;
- }
- else {
- // Display
- row.child( render(), 'child' ).show();
- $( row.node() ).addClass( 'parent' );
- return true;
- }
- },
- // This is a wrapper so the modal options for Bootstrap and jQuery UI can
- // have options passed into them. This specific one doesn't need to be a
- // function but it is for consistency in the `modal` name
- modal: function ( options ) {
- return function ( row, update, render ) {
- if ( ! update ) {
- // Show a modal
- var close = function () {
- modal.remove(); // will tidy events for us
- $(document).off( 'keypress.dtr' );
- };
- var modal = $('<div class="dtr-modal"/>')
- .append( $('<div class="dtr-modal-display"/>')
- .append( $('<div class="dtr-modal-content"/>')
- .append( render() )
- )
- .append( $('<div class="dtr-modal-close">×</div>' )
- .click( function () {
- close();
- } )
- )
- )
- .append( $('<div class="dtr-modal-background"/>')
- .click( function () {
- close();
- } )
- )
- .appendTo( 'body' );
- $(document).on( 'keyup.dtr', function (e) {
- if ( e.keyCode === 27 ) {
- e.stopPropagation();
- close();
- }
- } );
- }
- else {
- $('div.dtr-modal-content')
- .empty()
- .append( render() );
- }
- if ( options && options.header ) {
- $('div.dtr-modal-content').prepend(
- '<h2>'+options.header( row )+'</h2>'
- );
- }
- };
- }
- };
- var _childNodeStore = {};
- function _childNodes( dt, row, col ) {
- var name = row+'-'+col;
- if ( _childNodeStore[ name ] ) {
- return _childNodeStore[ name ];
- }
- // https://jsperf.com/childnodes-array-slice-vs-loop
- var nodes = [];
- var children = dt.cell( row, col ).node().childNodes;
- for ( var i=0, ien=children.length ; i<ien ; i++ ) {
- nodes.push( children[i] );
- }
- _childNodeStore[ name ] = nodes;
- return nodes;
- }
- function _childNodesRestore( dt, row, col ) {
- var name = row+'-'+col;
- if ( ! _childNodeStore[ name ] ) {
- return;
- }
- var node = dt.cell( row, col ).node();
- var store = _childNodeStore[ name ];
- var parent = store[0].parentNode;
- var parentChildren = parent.childNodes;
- var a = [];
- for ( var i=0, ien=parentChildren.length ; i<ien ; i++ ) {
- a.push( parentChildren[i] );
- }
- for ( var j=0, jen=a.length ; j<jen ; j++ ) {
- node.appendChild( a[j] );
- }
- _childNodeStore[ name ] = undefined;
- }
- /**
- * Display methods - functions which define how the hidden data should be shown
- * in the table.
- *
- * @namespace
- * @name Responsive.defaults
- * @static
- */
- Responsive.renderer = {
- listHiddenNodes: function () {
- return function ( api, rowIdx, columns ) {
- var ul = $('<ul data-dtr-index="'+rowIdx+'" class="dtr-details"/>');
- var found = false;
- var data = $.each( columns, function ( i, col ) {
- if ( col.hidden ) {
- $(
- '<li data-dtr-index="'+col.columnIndex+'" data-dt-row="'+col.rowIndex+'" data-dt-column="'+col.columnIndex+'">'+
- '<span class="dtr-title">'+
- col.title+
- '</span> '+
- '</li>'
- )
- .append( $('<span class="dtr-data"/>').append( _childNodes( api, col.rowIndex, col.columnIndex ) ) )// api.cell( col.rowIndex, col.columnIndex ).node().childNodes ) )
- .appendTo( ul );
- found = true;
- }
- } );
- return found ?
- ul :
- false;
- };
- },
- listHidden: function () {
- return function ( api, rowIdx, columns ) {
- var data = $.map( columns, function ( col ) {
- return col.hidden ?
- '<li data-dtr-index="'+col.columnIndex+'" data-dt-row="'+col.rowIndex+'" data-dt-column="'+col.columnIndex+'">'+
- '<span class="dtr-title">'+
- col.title+
- '</span> '+
- '<span class="dtr-data">'+
- col.data+
- '</span>'+
- '</li>' :
- '';
- } ).join('');
- return data ?
- $('<ul data-dtr-index="'+rowIdx+'" class="dtr-details"/>').append( data ) :
- false;
- }
- },
- tableAll: function ( options ) {
- options = $.extend( {
- tableClass: ''
- }, options );
- return function ( api, rowIdx, columns ) {
- var data = $.map( columns, function ( col ) {
- return '<tr data-dt-row="'+col.rowIndex+'" data-dt-column="'+col.columnIndex+'">'+
- '<td>'+col.title+':'+'</td> '+
- '<td>'+col.data+'</td>'+
- '</tr>';
- } ).join('');
- return $('<table class="'+options.tableClass+' dtr-details" width="100%"/>').append( data );
- }
- }
- };
- /**
- * Responsive default settings for initialisation
- *
- * @namespace
- * @name Responsive.defaults
- * @static
- */
- Responsive.defaults = {
- /**
- * List of breakpoints for the instance. Note that this means that each
- * instance can have its own breakpoints. Additionally, the breakpoints
- * cannot be changed once an instance has been creased.
- *
- * @type {Array}
- * @default Takes the value of `Responsive.breakpoints`
- */
- breakpoints: Responsive.breakpoints,
- /**
- * Enable / disable auto hiding calculations. It can help to increase
- * performance slightly if you disable this option, but all columns would
- * need to have breakpoint classes assigned to them
- *
- * @type {Boolean}
- * @default `true`
- */
- auto: true,
- /**
- * Details control. If given as a string value, the `type` property of the
- * default object is set to that value, and the defaults used for the rest
- * of the object - this is for ease of implementation.
- *
- * The object consists of the following properties:
- *
- * * `display` - A function that is used to show and hide the hidden details
- * * `renderer` - function that is called for display of the child row data.
- * The default function will show the data from the hidden columns
- * * `target` - Used as the selector for what objects to attach the child
- * open / close to
- * * `type` - `false` to disable the details display, `inline` or `column`
- * for the two control types
- *
- * @type {Object|string}
- */
- details: {
- display: Responsive.display.childRow,
- renderer: Responsive.renderer.listHidden(),
- target: 0,
- type: 'inline'
- },
- /**
- * Orthogonal data request option. This is used to define the data type
- * requested when Responsive gets the data to show in the child row.
- *
- * @type {String}
- */
- orthogonal: 'display'
- };
- /*
- * API
- */
- var Api = $.fn.dataTable.Api;
- // Doesn't do anything - work around for a bug in DT... Not documented
- Api.register( 'responsive()', function () {
- return this;
- } );
- Api.register( 'responsive.index()', function ( li ) {
- li = $(li);
- return {
- column: li.data('dtr-index'),
- row: li.parent().data('dtr-index')
- };
- } );
- Api.register( 'responsive.rebuild()', function () {
- return this.iterator( 'table', function ( ctx ) {
- if ( ctx._responsive ) {
- ctx._responsive._classLogic();
- }
- } );
- } );
- Api.register( 'responsive.recalc()', function () {
- return this.iterator( 'table', function ( ctx ) {
- if ( ctx._responsive ) {
- ctx._responsive._resizeAuto();
- ctx._responsive._resize();
- }
- } );
- } );
- Api.register( 'responsive.hasHidden()', function () {
- var ctx = this.context[0];
- return ctx._responsive ?
- $.inArray( false, ctx._responsive.s.current ) !== -1 :
- false;
- } );
- Api.registerPlural( 'columns().responsiveHidden()', 'column().responsiveHidden()', function () {
- return this.iterator( 'column', function ( settings, column ) {
- return settings._responsive ?
- settings._responsive.s.current[ column ] :
- false;
- }, 1 );
- } );
- /**
- * Version information
- *
- * @name Responsive.version
- * @static
- */
- Responsive.version = '2.2.3';
- $.fn.dataTable.Responsive = Responsive;
- $.fn.DataTable.Responsive = Responsive;
- // Attach a listener to the document which listens for DataTables initialisation
- // events so we can automatically initialise
- $(document).on( 'preInit.dt.dtr', function (e, settings, json) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- if ( $(settings.nTable).hasClass( 'responsive' ) ||
- $(settings.nTable).hasClass( 'dt-responsive' ) ||
- settings.oInit.responsive ||
- DataTable.defaults.responsive
- ) {
- var init = settings.oInit.responsive;
- if ( init !== false ) {
- new Responsive( settings, $.isPlainObject( init ) ? init : {} );
- }
- }
- } );
- return Responsive;
- }));
- /*! Bootstrap 4 integration for DataTables' Responsive
- * ©2016 SpryMedia Ltd - datatables.net/license
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net-bs4', 'datatables.net-responsive'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net-bs4')(root, $).$;
- }
- if ( ! $.fn.dataTable.Responsive ) {
- require('datatables.net-responsive')(root, $);
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- var _display = DataTable.Responsive.display;
- var _original = _display.modal;
- var _modal = $(
- '<div class="modal fade dtr-bs-modal" role="dialog">'+
- '<div class="modal-dialog" role="document">'+
- '<div class="modal-content">'+
- '<div class="modal-header">'+
- '<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>'+
- '</div>'+
- '<div class="modal-body"/>'+
- '</div>'+
- '</div>'+
- '</div>'
- );
- _display.modal = function ( options ) {
- return function ( row, update, render ) {
- if ( ! $.fn.modal ) {
- _original( row, update, render );
- }
- else {
- if ( ! update ) {
- if ( options && options.header ) {
- var header = _modal.find('div.modal-header');
- var button = header.find('button').detach();
-
- header
- .empty()
- .append( '<h4 class="modal-title">'+options.header( row )+'</h4>' )
- .append( button );
- }
- _modal.find( 'div.modal-body' )
- .empty()
- .append( render() );
- _modal
- .appendTo( 'body' )
- .modal();
- }
- }
- };
- };
- return DataTable.Responsive;
- }));
- /*! RowGroup 1.1.0
- * ©2017-2018 SpryMedia Ltd - datatables.net/license
- */
- /**
- * @summary RowGroup
- * @description RowGrouping for DataTables
- * @version 1.1.0
- * @file dataTables.rowGroup.js
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
- * @contact datatables.net
- * @copyright Copyright 2017-2018 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license/mit
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- var RowGroup = function ( dt, opts ) {
- // Sanity check that we are using DataTables 1.10 or newer
- if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) {
- throw 'RowGroup requires DataTables 1.10.8 or newer';
- }
- // User and defaults configuration object
- this.c = $.extend( true, {},
- DataTable.defaults.rowGroup,
- RowGroup.defaults,
- opts
- );
- // Internal settings
- this.s = {
- dt: new DataTable.Api( dt )
- };
- // DOM items
- this.dom = {
- };
- // Check if row grouping has already been initialised on this table
- var settings = this.s.dt.settings()[0];
- var existing = settings.rowGroup;
- if ( existing ) {
- return existing;
- }
- settings.rowGroup = this;
- this._constructor();
- };
- $.extend( RowGroup.prototype, {
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * API methods for DataTables API interface
- */
- /**
- * Get/set the grouping data source - need to call draw after this is
- * executed as a setter
- * @returns string~RowGroup
- */
- dataSrc: function ( val )
- {
- if ( val === undefined ) {
- return this.c.dataSrc;
- }
- var dt = this.s.dt;
- this.c.dataSrc = val;
- $(dt.table().node()).triggerHandler( 'rowgroup-datasrc.dt', [ dt, val ] );
- return this;
- },
- /**
- * Disable - need to call draw after this is executed
- * @returns RowGroup
- */
- disable: function ()
- {
- this.c.enable = false;
- return this;
- },
- /**
- * Enable - need to call draw after this is executed
- * @returns RowGroup
- */
- enable: function ( flag )
- {
- if ( flag === false ) {
- return this.disable();
- }
- this.c.enable = true;
- return this;
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constructor
- */
- _constructor: function ()
- {
- var that = this;
- var dt = this.s.dt;
- dt.on( 'draw.dtrg', function () {
- if ( that.c.enable ) {
- that._draw();
- }
- } );
- dt.on( 'column-visibility.dt.dtrg responsive-resize.dt.dtrg', function () {
- that._adjustColspan();
- } );
- dt.on( 'destroy', function () {
- dt.off( '.dtrg' );
- } );
- dt.on('responsive-resize.dt', function () {
- that._adjustColspan();
- })
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Private methods
- */
- /**
- * Adjust column span when column visibility changes
- * @private
- */
- _adjustColspan: function ()
- {
- $( 'tr.'+this.c.className, this.s.dt.table().body() ).find('td')
- .attr( 'colspan', this._colspan() );
- },
- /**
- * Get the number of columns that a grouping row should span
- * @private
- */
- _colspan: function ()
- {
- return this.s.dt.columns().visible().reduce( function (a, b) {
- return a + b;
- }, 0 );
- },
- /**
- * Update function that is called whenever we need to draw the grouping rows.
- * This is basically a bootstrap for the self iterative _group and _groupDisplay
- * methods
- * @private
- */
- _draw: function ()
- {
- var dt = this.s.dt;
- var groupedRows = this._group( 0, dt.rows( { page: 'current' } ).indexes() );
- this._groupDisplay( 0, groupedRows );
- },
- /**
- * Get the grouping information from a data set (index) of rows
- * @param {number} level Nesting level
- * @param {DataTables.Api} rows API of the rows to consider for this group
- * @returns {object[]} Nested grouping information - it is structured like this:
- * {
- * dataPoint: 'Edinburgh',
- * rows: [ 1,2,3,4,5,6,7 ],
- * children: [ {
- * dataPoint: 'developer'
- * rows: [ 1, 2, 3 ]
- * },
- * {
- * dataPoint: 'support',
- * rows: [ 4, 5, 6, 7 ]
- * } ]
- * }
- * @private
- */
- _group: function ( level, rows ) {
- var fns = $.isArray( this.c.dataSrc ) ? this.c.dataSrc : [ this.c.dataSrc ];
- var fn = DataTable.ext.oApi._fnGetObjectDataFn( fns[ level ] );
- var dt = this.s.dt;
- var group, last;
- var data = [];
- for ( var i=0, ien=rows.length ; i<ien ; i++ ) {
- var rowIndex = rows[i];
- var rowData = dt.row( rowIndex ).data();
- var group = fn( rowData );
- if ( group === null || group === undefined ) {
- group = that.c.emptyDataGroup;
- }
-
- if ( last === undefined || group !== last ) {
- data.push( {
- dataPoint: group,
- rows: []
- } );
- last = group;
- }
- data[ data.length-1 ].rows.push( rowIndex );
- }
- if ( fns[ level+1 ] !== undefined ) {
- for ( var i=0, ien=data.length ; i<ien ; i++ ) {
- data[i].children = this._group( level+1, data[i].rows );
- }
- }
- return data;
- },
- /**
- * Row group display - insert the rows into the document
- * @param {number} level Nesting level
- * @param {object[]} groups Takes the nested array from `_group`
- * @private
- */
- _groupDisplay: function ( level, groups )
- {
- var dt = this.s.dt;
- var display;
-
- for ( var i=0, ien=groups.length ; i<ien ; i++ ) {
- var group = groups[i];
- var groupName = group.dataPoint;
- var row;
- var rows = group.rows;
- if ( this.c.startRender ) {
- display = this.c.startRender.call( this, dt.rows(rows), groupName, level );
- row = this._rowWrap( display, this.c.startClassName, level );
- if ( row ) {
- row.insertBefore( dt.row( rows[0] ).node() );
- }
- }
- if ( this.c.endRender ) {
- display = this.c.endRender.call( this, dt.rows(rows), groupName, level );
- row = this._rowWrap( display, this.c.endClassName, level );
- if ( row ) {
- row.insertAfter( dt.row( rows[ rows.length-1 ] ).node() );
- }
- }
- if ( group.children ) {
- this._groupDisplay( level+1, group.children );
- }
- }
- },
- /**
- * Take a rendered value from an end user and make it suitable for display
- * as a row, by wrapping it in a row, or detecting that it is a row.
- * @param {node|jQuery|string} display Display value
- * @param {string} className Class to add to the row
- * @param {array} group
- * @param {number} group level
- * @private
- */
- _rowWrap: function ( display, className, level )
- {
- var row;
-
- if ( display === null || display === '' ) {
- display = this.c.emptyDataGroup;
- }
- if ( display === undefined ) {
- return null;
- }
-
- if ( typeof display === 'object' && display.nodeName && display.nodeName.toLowerCase() === 'tr') {
- row = $(display);
- }
- else if (display instanceof $ && display.length && display[0].nodeName.toLowerCase() === 'tr') {
- row = display;
- }
- else {
- row = $('<tr/>')
- .append(
- $('<td/>')
- .attr( 'colspan', this._colspan() )
- .append( display )
- );
- }
- return row
- .addClass( this.c.className )
- .addClass( className )
- .addClass( 'dtrg-level-'+level );
- }
- } );
- /**
- * RowGroup default settings for initialisation
- *
- * @namespace
- * @name RowGroup.defaults
- * @static
- */
- RowGroup.defaults = {
- /**
- * Class to apply to grouping rows - applied to both the start and
- * end grouping rows.
- * @type string
- */
- className: 'dtrg-group',
- /**
- * Data property from which to read the grouping information
- * @type string|integer|array
- */
- dataSrc: 0,
- /**
- * Text to show if no data is found for a group
- * @type string
- */
- emptyDataGroup: 'No group',
- /**
- * Initial enablement state
- * @boolean
- */
- enable: true,
- /**
- * Class name to give to the end grouping row
- * @type string
- */
- endClassName: 'dtrg-end',
- /**
- * End grouping label function
- * @function
- */
- endRender: null,
- /**
- * Class name to give to the start grouping row
- * @type string
- */
- startClassName: 'dtrg-start',
- /**
- * Start grouping label function
- * @function
- */
- startRender: function ( rows, group ) {
- return group;
- }
- };
- RowGroup.version = "1.1.0";
- $.fn.dataTable.RowGroup = RowGroup;
- $.fn.DataTable.RowGroup = RowGroup;
- DataTable.Api.register( 'rowGroup()', function () {
- return this;
- } );
- DataTable.Api.register( 'rowGroup().disable()', function () {
- return this.iterator( 'table', function (ctx) {
- if ( ctx.rowGroup ) {
- ctx.rowGroup.enable( false );
- }
- } );
- } );
- DataTable.Api.register( 'rowGroup().enable()', function ( opts ) {
- return this.iterator( 'table', function (ctx) {
- if ( ctx.rowGroup ) {
- ctx.rowGroup.enable( opts === undefined ? true : opts );
- }
- } );
- } );
- DataTable.Api.register( 'rowGroup().dataSrc()', function ( val ) {
- if ( val === undefined ) {
- return this.context[0].rowGroup.dataSrc();
- }
- return this.iterator( 'table', function (ctx) {
- if ( ctx.rowGroup ) {
- ctx.rowGroup.dataSrc( val );
- }
- } );
- } );
- // Attach a listener to the document which listens for DataTables initialisation
- // events so we can automatically initialise
- $(document).on( 'preInit.dt.dtrg', function (e, settings, json) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- var init = settings.oInit.rowGroup;
- var defaults = DataTable.defaults.rowGroup;
- if ( init || defaults ) {
- var opts = $.extend( {}, defaults, init );
- if ( init !== false ) {
- new RowGroup( settings, opts );
- }
- }
- } );
- return RowGroup;
- }));
- /*! Bootstrap 4 styling wrapper for RowGroup
- * ©2018 SpryMedia Ltd - datatables.net/license
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net-bs4', 'datatables.net-rowgroup'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net-bs4')(root, $).$;
- }
- if ( ! $.fn.dataTable.RowGroup ) {
- require('datatables.net-rowgroup')(root, $);
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- return $.fn.dataTable;
- }));
- /*! RowReorder 1.2.5
- * 2015-2018 SpryMedia Ltd - datatables.net/license
- */
- /**
- * @summary RowReorder
- * @description Row reordering extension for DataTables
- * @version 1.2.5
- * @file dataTables.rowReorder.js
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
- * @contact www.sprymedia.co.uk/contact
- * @copyright Copyright 2015-2018 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license/mit
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- /**
- * RowReorder provides the ability in DataTables to click and drag rows to
- * reorder them. When a row is dropped the data for the rows effected will be
- * updated to reflect the change. Normally this data point should also be the
- * column being sorted upon in the DataTable but this does not need to be the
- * case. RowReorder implements a "data swap" method - so the rows being
- * reordered take the value of the data point from the row that used to occupy
- * the row's new position.
- *
- * Initialisation is done by either:
- *
- * * `rowReorder` parameter in the DataTable initialisation object
- * * `new $.fn.dataTable.RowReorder( table, opts )` after DataTables
- * initialisation.
- *
- * @class
- * @param {object} settings DataTables settings object for the host table
- * @param {object} [opts] Configuration options
- * @requires jQuery 1.7+
- * @requires DataTables 1.10.7+
- */
- var RowReorder = function ( dt, opts ) {
- // Sanity check that we are using DataTables 1.10 or newer
- if ( ! DataTable.versionCheck || ! DataTable.versionCheck( '1.10.8' ) ) {
- throw 'DataTables RowReorder requires DataTables 1.10.8 or newer';
- }
- // User and defaults configuration object
- this.c = $.extend( true, {},
- DataTable.defaults.rowReorder,
- RowReorder.defaults,
- opts
- );
- // Internal settings
- this.s = {
- /** @type {integer} Scroll body top cache */
- bodyTop: null,
- /** @type {DataTable.Api} DataTables' API instance */
- dt: new DataTable.Api( dt ),
- /** @type {function} Data fetch function */
- getDataFn: DataTable.ext.oApi._fnGetObjectDataFn( this.c.dataSrc ),
- /** @type {array} Pixel positions for row insertion calculation */
- middles: null,
- /** @type {Object} Cached dimension information for use in the mouse move event handler */
- scroll: {},
- /** @type {integer} Interval object used for smooth scrolling */
- scrollInterval: null,
- /** @type {function} Data set function */
- setDataFn: DataTable.ext.oApi._fnSetObjectDataFn( this.c.dataSrc ),
- /** @type {Object} Mouse down information */
- start: {
- top: 0,
- left: 0,
- offsetTop: 0,
- offsetLeft: 0,
- nodes: []
- },
- /** @type {integer} Window height cached value */
- windowHeight: 0,
- /** @type {integer} Document outer height cached value */
- documentOuterHeight: 0,
- /** @type {integer} DOM clone outer height cached value */
- domCloneOuterHeight: 0
- };
- // DOM items
- this.dom = {
- /** @type {jQuery} Cloned row being moved around */
- clone: null,
- /** @type {jQuery} DataTables scrolling container */
- dtScroll: $('div.dataTables_scrollBody', this.s.dt.table().container())
- };
- // Check if row reorder has already been initialised on this table
- var settings = this.s.dt.settings()[0];
- var exisiting = settings.rowreorder;
- if ( exisiting ) {
- return exisiting;
- }
- settings.rowreorder = this;
- this._constructor();
- };
- $.extend( RowReorder.prototype, {
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constructor
- */
- /**
- * Initialise the RowReorder instance
- *
- * @private
- */
- _constructor: function ()
- {
- var that = this;
- var dt = this.s.dt;
- var table = $( dt.table().node() );
- // Need to be able to calculate the row positions relative to the table
- if ( table.css('position') === 'static' ) {
- table.css( 'position', 'relative' );
- }
- // listen for mouse down on the target column - we have to implement
- // this rather than using HTML5 drag and drop as drag and drop doesn't
- // appear to work on table rows at this time. Also mobile browsers are
- // not supported.
- // Use `table().container()` rather than just the table node for IE8 -
- // otherwise it only works once...
- $(dt.table().container()).on( 'mousedown.rowReorder touchstart.rowReorder', this.c.selector, function (e) {
- if ( ! that.c.enable ) {
- return;
- }
- // Ignore excluded children of the selector
- if ( $(e.target).is(that.c.excludedChildren) ) {
- return true;
- }
- var tr = $(this).closest('tr');
- var row = dt.row( tr );
- // Double check that it is a DataTable row
- if ( row.any() ) {
- that._emitEvent( 'pre-row-reorder', {
- node: row.node(),
- index: row.index()
- } );
- that._mouseDown( e, tr );
- return false;
- }
- } );
- dt.on( 'destroy.rowReorder', function () {
- $(dt.table().container()).off( '.rowReorder' );
- dt.off( '.rowReorder' );
- } );
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Private methods
- */
-
- /**
- * Cache the measurements that RowReorder needs in the mouse move handler
- * to attempt to speed things up, rather than reading from the DOM.
- *
- * @private
- */
- _cachePositions: function ()
- {
- var dt = this.s.dt;
- // Frustratingly, if we add `position:relative` to the tbody, the
- // position is still relatively to the parent. So we need to adjust
- // for that
- var headerHeight = $( dt.table().node() ).find('thead').outerHeight();
- // Need to pass the nodes through jQuery to get them in document order,
- // not what DataTables thinks it is, since we have been altering the
- // order
- var nodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );
- var tops = $.map( nodes, function ( node, i ) {
- return $(node).position().top - headerHeight;
- } );
- var middles = $.map( tops, function ( top, i ) {
- return tops.length < i-1 ?
- (top + tops[i+1]) / 2 :
- (top + top + $( dt.row( ':last-child' ).node() ).outerHeight() ) / 2;
- } );
- this.s.middles = middles;
- this.s.bodyTop = $( dt.table().body() ).offset().top;
- this.s.windowHeight = $(window).height();
- this.s.documentOuterHeight = $(document).outerHeight();
- },
- /**
- * Clone a row so it can be floated around the screen
- *
- * @param {jQuery} target Node to be cloned
- * @private
- */
- _clone: function ( target )
- {
- var dt = this.s.dt;
- var clone = $( dt.table().node().cloneNode(false) )
- .addClass( 'dt-rowReorder-float' )
- .append('<tbody/>')
- .append( target.clone( false ) );
- // Match the table and column widths - read all sizes before setting
- // to reduce reflows
- var tableWidth = target.outerWidth();
- var tableHeight = target.outerHeight();
- var sizes = target.children().map( function () {
- return $(this).width();
- } );
- clone
- .width( tableWidth )
- .height( tableHeight )
- .find('tr').children().each( function (i) {
- this.style.width = sizes[i]+'px';
- } );
- // Insert into the document to have it floating around
- clone.appendTo( 'body' );
- this.dom.clone = clone;
- this.s.domCloneOuterHeight = clone.outerHeight();
- },
- /**
- * Update the cloned item's position in the document
- *
- * @param {object} e Event giving the mouse's position
- * @private
- */
- _clonePosition: function ( e )
- {
- var start = this.s.start;
- var topDiff = this._eventToPage( e, 'Y' ) - start.top;
- var leftDiff = this._eventToPage( e, 'X' ) - start.left;
- var snap = this.c.snapX;
- var left;
- var top = topDiff + start.offsetTop;
- if ( snap === true ) {
- left = start.offsetLeft;
- }
- else if ( typeof snap === 'number' ) {
- left = start.offsetLeft + snap;
- }
- else {
- left = leftDiff + start.offsetLeft;
- }
- if(top < 0) {
- top = 0
- }
- else if(top + this.s.domCloneOuterHeight > this.s.documentOuterHeight) {
- top = this.s.documentOuterHeight - this.s.domCloneOuterHeight;
- }
- this.dom.clone.css( {
- top: top,
- left: left
- } );
- },
- /**
- * Emit an event on the DataTable for listeners
- *
- * @param {string} name Event name
- * @param {array} args Event arguments
- * @private
- */
- _emitEvent: function ( name, args )
- {
- this.s.dt.iterator( 'table', function ( ctx, i ) {
- $(ctx.nTable).triggerHandler( name+'.dt', args );
- } );
- },
- /**
- * Get pageX/Y position from an event, regardless of if it is a mouse or
- * touch event.
- *
- * @param {object} e Event
- * @param {string} pos X or Y (must be a capital)
- * @private
- */
- _eventToPage: function ( e, pos )
- {
- if ( e.type.indexOf( 'touch' ) !== -1 ) {
- return e.originalEvent.touches[0][ 'page'+pos ];
- }
- return e[ 'page'+pos ];
- },
- /**
- * Mouse down event handler. Read initial positions and add event handlers
- * for the move.
- *
- * @param {object} e Mouse event
- * @param {jQuery} target TR element that is to be moved
- * @private
- */
- _mouseDown: function ( e, target )
- {
- var that = this;
- var dt = this.s.dt;
- var start = this.s.start;
- var offset = target.offset();
- start.top = this._eventToPage( e, 'Y' );
- start.left = this._eventToPage( e, 'X' );
- start.offsetTop = offset.top;
- start.offsetLeft = offset.left;
- start.nodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );
- this._cachePositions();
- this._clone( target );
- this._clonePosition( e );
- this.dom.target = target;
- target.addClass( 'dt-rowReorder-moving' );
- $( document )
- .on( 'mouseup.rowReorder touchend.rowReorder', function (e) {
- that._mouseUp(e);
- } )
- .on( 'mousemove.rowReorder touchmove.rowReorder', function (e) {
- that._mouseMove(e);
- } );
- // Check if window is x-scrolling - if not, disable it for the duration
- // of the drag
- if ( $(window).width() === $(document).width() ) {
- $(document.body).addClass( 'dt-rowReorder-noOverflow' );
- }
- // Cache scrolling information so mouse move doesn't need to read.
- // This assumes that the window and DT scroller will not change size
- // during an row drag, which I think is a fair assumption
- var scrollWrapper = this.dom.dtScroll;
- this.s.scroll = {
- windowHeight: $(window).height(),
- windowWidth: $(window).width(),
- dtTop: scrollWrapper.length ? scrollWrapper.offset().top : null,
- dtLeft: scrollWrapper.length ? scrollWrapper.offset().left : null,
- dtHeight: scrollWrapper.length ? scrollWrapper.outerHeight() : null,
- dtWidth: scrollWrapper.length ? scrollWrapper.outerWidth() : null
- };
- },
- /**
- * Mouse move event handler - move the cloned row and shuffle the table's
- * rows if required.
- *
- * @param {object} e Mouse event
- * @private
- */
- _mouseMove: function ( e )
- {
- this._clonePosition( e );
- // Transform the mouse position into a position in the table's body
- var bodyY = this._eventToPage( e, 'Y' ) - this.s.bodyTop;
- var middles = this.s.middles;
- var insertPoint = null;
- var dt = this.s.dt;
- var body = dt.table().body();
- // Determine where the row should be inserted based on the mouse
- // position
- for ( var i=0, ien=middles.length ; i<ien ; i++ ) {
- if ( bodyY < middles[i] ) {
- insertPoint = i;
- break;
- }
- }
- if ( insertPoint === null ) {
- insertPoint = middles.length;
- }
- // Perform the DOM shuffle if it has changed from last time
- if ( this.s.lastInsert === null || this.s.lastInsert !== insertPoint ) {
- if ( insertPoint === 0 ) {
- this.dom.target.prependTo( body );
- }
- else {
- var nodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );
- if ( insertPoint > this.s.lastInsert ) {
- this.dom.target.insertAfter( nodes[ insertPoint-1 ] );
- }
- else {
- this.dom.target.insertBefore( nodes[ insertPoint ] );
- }
- }
- this._cachePositions();
- this.s.lastInsert = insertPoint;
- }
- this._shiftScroll( e );
- },
- /**
- * Mouse up event handler - release the event handlers and perform the
- * table updates
- *
- * @param {object} e Mouse event
- * @private
- */
- _mouseUp: function ( e )
- {
- var that = this;
- var dt = this.s.dt;
- var i, ien;
- var dataSrc = this.c.dataSrc;
- this.dom.clone.remove();
- this.dom.clone = null;
- this.dom.target.removeClass( 'dt-rowReorder-moving' );
- //this.dom.target = null;
- $(document).off( '.rowReorder' );
- $(document.body).removeClass( 'dt-rowReorder-noOverflow' );
- clearInterval( this.s.scrollInterval );
- this.s.scrollInterval = null;
- // Calculate the difference
- var startNodes = this.s.start.nodes;
- var endNodes = $.unique( dt.rows( { page: 'current' } ).nodes().toArray() );
- var idDiff = {};
- var fullDiff = [];
- var diffNodes = [];
- var getDataFn = this.s.getDataFn;
- var setDataFn = this.s.setDataFn;
- for ( i=0, ien=startNodes.length ; i<ien ; i++ ) {
- if ( startNodes[i] !== endNodes[i] ) {
- var id = dt.row( endNodes[i] ).id();
- var endRowData = dt.row( endNodes[i] ).data();
- var startRowData = dt.row( startNodes[i] ).data();
- if ( id ) {
- idDiff[ id ] = getDataFn( startRowData );
- }
- fullDiff.push( {
- node: endNodes[i],
- oldData: getDataFn( endRowData ),
- newData: getDataFn( startRowData ),
- newPosition: i,
- oldPosition: $.inArray( endNodes[i], startNodes )
- } );
- diffNodes.push( endNodes[i] );
- }
- }
-
- // Create event args
- var eventArgs = [ fullDiff, {
- dataSrc: dataSrc,
- nodes: diffNodes,
- values: idDiff,
- triggerRow: dt.row( this.dom.target )
- } ];
-
- // Emit event
- this._emitEvent( 'row-reorder', eventArgs );
- var update = function () {
- if ( that.c.update ) {
- for ( i=0, ien=fullDiff.length ; i<ien ; i++ ) {
- var row = dt.row( fullDiff[i].node );
- var rowData = row.data();
- setDataFn( rowData, fullDiff[i].newData );
- // Invalidate the cell that has the same data source as the dataSrc
- dt.columns().every( function () {
- if ( this.dataSrc() === dataSrc ) {
- dt.cell( fullDiff[i].node, this.index() ).invalidate( 'data' );
- }
- } );
- }
- // Trigger row reordered event
- that._emitEvent( 'row-reordered', eventArgs );
- dt.draw( false );
- }
- };
- // Editor interface
- if ( this.c.editor ) {
- // Disable user interaction while Editor is submitting
- this.c.enable = false;
- this.c.editor
- .edit(
- diffNodes,
- false,
- $.extend( {submit: 'changed'}, this.c.formOptions )
- )
- .multiSet( dataSrc, idDiff )
- .one( 'preSubmitCancelled.rowReorder', function () {
- that.c.enable = true;
- that.c.editor.off( '.rowReorder' );
- dt.draw( false );
- } )
- .one( 'submitUnsuccessful.rowReorder', function () {
- dt.draw( false );
- } )
- .one( 'submitSuccess.rowReorder', function () {
- update();
- } )
- .one( 'submitComplete', function () {
- that.c.enable = true;
- that.c.editor.off( '.rowReorder' );
- } )
- .submit();
- }
- else {
- update();
- }
- },
- /**
- * Move the window and DataTables scrolling during a drag to scroll new
- * content into view.
- *
- * This matches the `_shiftScroll` method used in AutoFill, but only
- * horizontal scrolling is considered here.
- *
- * @param {object} e Mouse move event object
- * @private
- */
- _shiftScroll: function ( e )
- {
- var that = this;
- var dt = this.s.dt;
- var scroll = this.s.scroll;
- var runInterval = false;
- var scrollSpeed = 5;
- var buffer = 65;
- var
- windowY = e.pageY - document.body.scrollTop,
- windowVert,
- dtVert;
- // Window calculations - based on the mouse position in the window,
- // regardless of scrolling
- if ( windowY < buffer ) {
- windowVert = scrollSpeed * -1;
- }
- else if ( windowY > scroll.windowHeight - buffer ) {
- windowVert = scrollSpeed;
- }
- // DataTables scrolling calculations - based on the table's position in
- // the document and the mouse position on the page
- if ( scroll.dtTop !== null && e.pageY < scroll.dtTop + buffer ) {
- dtVert = scrollSpeed * -1;
- }
- else if ( scroll.dtTop !== null && e.pageY > scroll.dtTop + scroll.dtHeight - buffer ) {
- dtVert = scrollSpeed;
- }
- // This is where it gets interesting. We want to continue scrolling
- // without requiring a mouse move, so we need an interval to be
- // triggered. The interval should continue until it is no longer needed,
- // but it must also use the latest scroll commands (for example consider
- // that the mouse might move from scrolling up to scrolling left, all
- // with the same interval running. We use the `scroll` object to "pass"
- // this information to the interval. Can't use local variables as they
- // wouldn't be the ones that are used by an already existing interval!
- if ( windowVert || dtVert ) {
- scroll.windowVert = windowVert;
- scroll.dtVert = dtVert;
- runInterval = true;
- }
- else if ( this.s.scrollInterval ) {
- // Don't need to scroll - remove any existing timer
- clearInterval( this.s.scrollInterval );
- this.s.scrollInterval = null;
- }
- // If we need to run the interval to scroll and there is no existing
- // interval (if there is an existing one, it will continue to run)
- if ( ! this.s.scrollInterval && runInterval ) {
- this.s.scrollInterval = setInterval( function () {
- // Don't need to worry about setting scroll <0 or beyond the
- // scroll bound as the browser will just reject that.
- if ( scroll.windowVert ) {
- document.body.scrollTop += scroll.windowVert;
- }
- // DataTables scrolling
- if ( scroll.dtVert ) {
- var scroller = that.dom.dtScroll[0];
- if ( scroll.dtVert ) {
- scroller.scrollTop += scroll.dtVert;
- }
- }
- }, 20 );
- }
- }
- } );
- /**
- * RowReorder default settings for initialisation
- *
- * @namespace
- * @name RowReorder.defaults
- * @static
- */
- RowReorder.defaults = {
- /**
- * Data point in the host row's data source object for where to get and set
- * the data to reorder. This will normally also be the sorting column.
- *
- * @type {Number}
- */
- dataSrc: 0,
- /**
- * Editor instance that will be used to perform the update
- *
- * @type {DataTable.Editor}
- */
- editor: null,
- /**
- * Enable / disable RowReorder's user interaction
- * @type {Boolean}
- */
- enable: true,
- /**
- * Form options to pass to Editor when submitting a change in the row order.
- * See the Editor `from-options` object for details of the options
- * available.
- * @type {Object}
- */
- formOptions: {},
- /**
- * Drag handle selector. This defines the element that when dragged will
- * reorder a row.
- *
- * @type {String}
- */
- selector: 'td:first-child',
- /**
- * Optionally lock the dragged row's x-position. This can be `true` to
- * fix the position match the host table's, `false` to allow free movement
- * of the row, or a number to define an offset from the host table.
- *
- * @type {Boolean|number}
- */
- snapX: false,
- /**
- * Update the table's data on drop
- *
- * @type {Boolean}
- */
- update: true,
- /**
- * Selector for children of the drag handle selector that mouseDown events
- * will be passed through to and drag will not activate
- *
- * @type {String}
- */
- excludedChildren: 'a'
- };
- /*
- * API
- */
- var Api = $.fn.dataTable.Api;
- // Doesn't do anything - work around for a bug in DT... Not documented
- Api.register( 'rowReorder()', function () {
- return this;
- } );
- Api.register( 'rowReorder.enable()', function ( toggle ) {
- if ( toggle === undefined ) {
- toggle = true;
- }
- return this.iterator( 'table', function ( ctx ) {
- if ( ctx.rowreorder ) {
- ctx.rowreorder.c.enable = toggle;
- }
- } );
- } );
- Api.register( 'rowReorder.disable()', function () {
- return this.iterator( 'table', function ( ctx ) {
- if ( ctx.rowreorder ) {
- ctx.rowreorder.c.enable = false;
- }
- } );
- } );
- /**
- * Version information
- *
- * @name RowReorder.version
- * @static
- */
- RowReorder.version = '1.2.5';
- $.fn.dataTable.RowReorder = RowReorder;
- $.fn.DataTable.RowReorder = RowReorder;
- // Attach a listener to the document which listens for DataTables initialisation
- // events so we can automatically initialise
- $(document).on( 'init.dt.dtr', function (e, settings, json) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- var init = settings.oInit.rowReorder;
- var defaults = DataTable.defaults.rowReorder;
- if ( init || defaults ) {
- var opts = $.extend( {}, init, defaults );
- if ( init !== false ) {
- new RowReorder( settings, opts );
- }
- }
- } );
- return RowReorder;
- }));
- /*! Bootstrap 4 styling wrapper for RowReorder
- * ©2018 SpryMedia Ltd - datatables.net/license
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net-bs4', 'datatables.net-rowreorder'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net-bs4')(root, $).$;
- }
- if ( ! $.fn.dataTable.RowReorder ) {
- require('datatables.net-rowreorder')(root, $);
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- return $.fn.dataTable;
- }));
- /*! Scroller 2.0.0
- * ©2011-2018 SpryMedia Ltd - datatables.net/license
- */
- /**
- * @summary Scroller
- * @description Virtual rendering for DataTables
- * @version 2.0.0
- * @file dataTables.scroller.js
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
- * @contact www.sprymedia.co.uk/contact
- * @copyright Copyright 2011-2018 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license/mit
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- /**
- * Scroller is a virtual rendering plug-in for DataTables which allows large
- * datasets to be drawn on screen every quickly. What the virtual rendering means
- * is that only the visible portion of the table (and a bit to either side to make
- * the scrolling smooth) is drawn, while the scrolling container gives the
- * visual impression that the whole table is visible. This is done by making use
- * of the pagination abilities of DataTables and moving the table around in the
- * scrolling container DataTables adds to the page. The scrolling container is
- * forced to the height it would be for the full table display using an extra
- * element.
- *
- * Note that rows in the table MUST all be the same height. Information in a cell
- * which expands on to multiple lines will cause some odd behaviour in the scrolling.
- *
- * Scroller is initialised by simply including the letter 'S' in the sDom for the
- * table you want to have this feature enabled on. Note that the 'S' must come
- * AFTER the 't' parameter in `dom`.
- *
- * Key features include:
- * <ul class="limit_length">
- * <li>Speed! The aim of Scroller for DataTables is to make rendering large data sets fast</li>
- * <li>Full compatibility with deferred rendering in DataTables for maximum speed</li>
- * <li>Display millions of rows</li>
- * <li>Integration with state saving in DataTables (scrolling position is saved)</li>
- * <li>Easy to use</li>
- * </ul>
- *
- * @class
- * @constructor
- * @global
- * @param {object} dt DataTables settings object or API instance
- * @param {object} [opts={}] Configuration object for FixedColumns. Options
- * are defined by {@link Scroller.defaults}
- *
- * @requires jQuery 1.7+
- * @requires DataTables 1.10.0+
- *
- * @example
- * $(document).ready(function() {
- * $('#example').DataTable( {
- * "scrollY": "200px",
- * "ajax": "media/dataset/large.txt",
- * "scroller": true,
- * "deferRender": true
- * } );
- * } );
- */
- var Scroller = function ( dt, opts ) {
- /* Sanity check - you just know it will happen */
- if ( ! (this instanceof Scroller) ) {
- alert( "Scroller warning: Scroller must be initialised with the 'new' keyword." );
- return;
- }
- if ( opts === undefined ) {
- opts = {};
- }
- var dtApi = $.fn.dataTable.Api( dt );
- /**
- * Settings object which contains customisable information for the Scroller instance
- * @namespace
- * @private
- * @extends Scroller.defaults
- */
- this.s = {
- /**
- * DataTables settings object
- * @type object
- * @default Passed in as first parameter to constructor
- */
- dt: dtApi.settings()[0],
- /**
- * DataTables API instance
- * @type DataTable.Api
- */
- dtApi: dtApi,
- /**
- * Pixel location of the top of the drawn table in the viewport
- * @type int
- * @default 0
- */
- tableTop: 0,
- /**
- * Pixel location of the bottom of the drawn table in the viewport
- * @type int
- * @default 0
- */
- tableBottom: 0,
- /**
- * Pixel location of the boundary for when the next data set should be loaded and drawn
- * when scrolling up the way.
- * @type int
- * @default 0
- * @private
- */
- redrawTop: 0,
- /**
- * Pixel location of the boundary for when the next data set should be loaded and drawn
- * when scrolling down the way. Note that this is actually calculated as the offset from
- * the top.
- * @type int
- * @default 0
- * @private
- */
- redrawBottom: 0,
- /**
- * Auto row height or not indicator
- * @type bool
- * @default 0
- */
- autoHeight: true,
- /**
- * Number of rows calculated as visible in the visible viewport
- * @type int
- * @default 0
- */
- viewportRows: 0,
- /**
- * setTimeout reference for state saving, used when state saving is enabled in the DataTable
- * and when the user scrolls the viewport in order to stop the cookie set taking too much
- * CPU!
- * @type int
- * @default 0
- */
- stateTO: null,
- /**
- * setTimeout reference for the redraw, used when server-side processing is enabled in the
- * DataTables in order to prevent DoSing the server
- * @type int
- * @default null
- */
- drawTO: null,
- heights: {
- jump: null,
- page: null,
- virtual: null,
- scroll: null,
- /**
- * Height of rows in the table
- * @type int
- * @default 0
- */
- row: null,
- /**
- * Pixel height of the viewport
- * @type int
- * @default 0
- */
- viewport: null,
- labelFactor: 1
- },
- topRowFloat: 0,
- scrollDrawDiff: null,
- loaderVisible: false,
- forceReposition: false,
- baseRowTop: 0,
- baseScrollTop: 0,
- mousedown: false,
- lastScrollTop: 0
- };
- // @todo The defaults should extend a `c` property and the internal settings
- // only held in the `s` property. At the moment they are mixed
- this.s = $.extend( this.s, Scroller.oDefaults, opts );
- // Workaround for row height being read from height object (see above comment)
- this.s.heights.row = this.s.rowHeight;
- /**
- * DOM elements used by the class instance
- * @private
- * @namespace
- *
- */
- this.dom = {
- "force": document.createElement('div'),
- "label": $('<div class="dts_label">0</div>'),
- "scroller": null,
- "table": null,
- "loader": null
- };
- // Attach the instance to the DataTables instance so it can be accessed in
- // future. Don't initialise Scroller twice on the same table
- if ( this.s.dt.oScroller ) {
- return;
- }
- this.s.dt.oScroller = this;
- /* Let's do it */
- this.construct();
- };
- $.extend( Scroller.prototype, {
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Public methods - to be exposed via the DataTables API
- */
- /**
- * Calculate and store information about how many rows are to be displayed
- * in the scrolling viewport, based on current dimensions in the browser's
- * rendering. This can be particularly useful if the table is initially
- * drawn in a hidden element - for example in a tab.
- * @param {bool} [redraw=true] Redraw the table automatically after the recalculation, with
- * the new dimensions forming the basis for the draw.
- * @returns {void}
- */
- measure: function ( redraw )
- {
- if ( this.s.autoHeight )
- {
- this._calcRowHeight();
- }
- var heights = this.s.heights;
- if ( heights.row ) {
- heights.viewport = $.contains(document, this.dom.scroller) ?
- this.dom.scroller.clientHeight :
- this._parseHeight($(this.dom.scroller).css('height'));
- // If collapsed (no height) use the max-height parameter
- if ( ! heights.viewport ) {
- heights.viewport = this._parseHeight($(this.dom.scroller).css('max-height'));
- }
- this.s.viewportRows = parseInt( heights.viewport / heights.row, 10 )+1;
- this.s.dt._iDisplayLength = this.s.viewportRows * this.s.displayBuffer;
- }
- var label = this.dom.label.outerHeight();
- heights.labelFactor = (heights.viewport-label) / heights.scroll;
- if ( redraw === undefined || redraw )
- {
- this.s.dt.oInstance.fnDraw( false );
- }
- },
- /**
- * Get information about current displayed record range. This corresponds to
- * the information usually displayed in the "Info" block of the table.
- *
- * @returns {object} info as an object:
- * {
- * start: {int}, // the 0-indexed record at the top of the viewport
- * end: {int}, // the 0-indexed record at the bottom of the viewport
- * }
- */
- pageInfo: function()
- {
- var
- dt = this.s.dt,
- iScrollTop = this.dom.scroller.scrollTop,
- iTotal = dt.fnRecordsDisplay(),
- iPossibleEnd = Math.ceil(this.pixelsToRow(iScrollTop + this.s.heights.viewport, false, this.s.ani));
- return {
- start: Math.floor(this.pixelsToRow(iScrollTop, false, this.s.ani)),
- end: iTotal < iPossibleEnd ? iTotal-1 : iPossibleEnd-1
- };
- },
- /**
- * Calculate the row number that will be found at the given pixel position
- * (y-scroll).
- *
- * Please note that when the height of the full table exceeds 1 million
- * pixels, Scroller switches into a non-linear mode for the scrollbar to fit
- * all of the records into a finite area, but this function returns a linear
- * value (relative to the last non-linear positioning).
- * @param {int} pixels Offset from top to calculate the row number of
- * @param {int} [intParse=true] If an integer value should be returned
- * @param {int} [virtual=false] Perform the calculations in the virtual domain
- * @returns {int} Row index
- */
- pixelsToRow: function ( pixels, intParse, virtual )
- {
- var diff = pixels - this.s.baseScrollTop;
- var row = virtual ?
- (this._domain( 'physicalToVirtual', this.s.baseScrollTop ) + diff) / this.s.heights.row :
- ( diff / this.s.heights.row ) + this.s.baseRowTop;
- return intParse || intParse === undefined ?
- parseInt( row, 10 ) :
- row;
- },
- /**
- * Calculate the pixel position from the top of the scrolling container for
- * a given row
- * @param {int} iRow Row number to calculate the position of
- * @returns {int} Pixels
- */
- rowToPixels: function ( rowIdx, intParse, virtual )
- {
- var pixels;
- var diff = rowIdx - this.s.baseRowTop;
- if ( virtual ) {
- pixels = this._domain( 'virtualToPhysical', this.s.baseScrollTop );
- pixels += diff * this.s.heights.row;
- }
- else {
- pixels = this.s.baseScrollTop;
- pixels += diff * this.s.heights.row;
- }
- return intParse || intParse === undefined ?
- parseInt( pixels, 10 ) :
- pixels;
- },
- /**
- * Calculate the row number that will be found at the given pixel position (y-scroll)
- * @param {int} row Row index to scroll to
- * @param {bool} [animate=true] Animate the transition or not
- * @returns {void}
- */
- scrollToRow: function ( row, animate )
- {
- var that = this;
- var ani = false;
- var px = this.rowToPixels( row );
- // We need to know if the table will redraw or not before doing the
- // scroll. If it will not redraw, then we need to use the currently
- // displayed table, and scroll with the physical pixels. Otherwise, we
- // need to calculate the table's new position from the virtual
- // transform.
- var preRows = ((this.s.displayBuffer-1)/2) * this.s.viewportRows;
- var drawRow = row - preRows;
- if ( drawRow < 0 ) {
- drawRow = 0;
- }
- if ( (px > this.s.redrawBottom || px < this.s.redrawTop) && this.s.dt._iDisplayStart !== drawRow ) {
- ani = true;
- px = this._domain( 'virtualToPhysical', row * this.s.heights.row );
- // If we need records outside the current draw region, but the new
- // scrolling position is inside that (due to the non-linear nature
- // for larger numbers of records), we need to force position update.
- if ( this.s.redrawTop < px && px < this.s.redrawBottom ) {
- this.s.forceReposition = true;
- animate = false;
- }
- }
- if ( typeof animate == 'undefined' || animate )
- {
- this.s.ani = ani;
- $(this.dom.scroller).animate( {
- "scrollTop": px
- }, function () {
- // This needs to happen after the animation has completed and
- // the final scroll event fired
- setTimeout( function () {
- that.s.ani = false;
- }, 25 );
- } );
- }
- else
- {
- $(this.dom.scroller).scrollTop( px );
- }
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constructor
- */
- /**
- * Initialisation for Scroller
- * @returns {void}
- * @private
- */
- construct: function ()
- {
- var that = this;
- var dt = this.s.dtApi;
- /* Sanity check */
- if ( !this.s.dt.oFeatures.bPaginate ) {
- this.s.dt.oApi._fnLog( this.s.dt, 0, 'Pagination must be enabled for Scroller' );
- return;
- }
- /* Insert a div element that we can use to force the DT scrolling container to
- * the height that would be required if the whole table was being displayed
- */
- this.dom.force.style.position = "relative";
- this.dom.force.style.top = "0px";
- this.dom.force.style.left = "0px";
- this.dom.force.style.width = "1px";
- this.dom.scroller = $('div.'+this.s.dt.oClasses.sScrollBody, this.s.dt.nTableWrapper)[0];
- this.dom.scroller.appendChild( this.dom.force );
- this.dom.scroller.style.position = "relative";
- this.dom.table = $('>table', this.dom.scroller)[0];
- this.dom.table.style.position = "absolute";
- this.dom.table.style.top = "0px";
- this.dom.table.style.left = "0px";
- // Add class to 'announce' that we are a Scroller table
- $(dt.table().container()).addClass('dts DTS');
- // Add a 'loading' indicator
- if ( this.s.loadingIndicator )
- {
- this.dom.loader = $('<div class="dataTables_processing dts_loading">'+this.s.dt.oLanguage.sLoadingRecords+'</div>')
- .css('display', 'none');
- $(this.dom.scroller.parentNode)
- .css('position', 'relative')
- .append( this.dom.loader );
- }
- this.dom.label.appendTo(this.dom.scroller);
- /* Initial size calculations */
- if ( this.s.heights.row && this.s.heights.row != 'auto' )
- {
- this.s.autoHeight = false;
- }
- this.measure( false );
- // Scrolling callback to see if a page change is needed - use a throttled
- // function for the save save callback so we aren't hitting it on every
- // scroll
- this.s.ingnoreScroll = true;
- this.s.stateSaveThrottle = this.s.dt.oApi._fnThrottle( function () {
- that.s.dtApi.state.save();
- }, 500 );
- $(this.dom.scroller).on( 'scroll.dt-scroller', function (e) {
- that._scroll.call( that );
- } );
- // In iOS we catch the touchstart event in case the user tries to scroll
- // while the display is already scrolling
- $(this.dom.scroller).on('touchstart.dt-scroller', function () {
- that._scroll.call( that );
- } );
- $(this.dom.scroller)
- .on('mousedown.dt-scroller', function () {
- that.s.mousedown = true;
- })
- .on('mouseup.dt-scroller', function () {
- that.s.mouseup = false;
- that.dom.label.css('display', 'none');
- });
- // On resize, update the information element, since the number of rows shown might change
- $(window).on( 'resize.dt-scroller', function () {
- that.measure( false );
- that._info();
- } );
- // Add a state saving parameter to the DT state saving so we can restore the exact
- // position of the scrolling. Slightly surprisingly the scroll position isn't actually
- // stored, but rather tha base units which are needed to calculate it. This allows for
- // virtual scrolling as well.
- var initialStateSave = true;
- var loadedState = dt.state.loaded();
- dt.on( 'stateSaveParams.scroller', function ( e, settings, data ) {
- // Need to used the saved position on init
- data.scroller = {
- topRow: initialStateSave && loadedState && loadedState.scroller ?
- loadedState.scroller.topRow :
- that.s.topRowFloat,
- baseScrollTop: that.s.baseScrollTop,
- baseRowTop: that.s.baseRowTop
- };
- initialStateSave = false;
- } );
- if ( loadedState && loadedState.scroller ) {
- this.s.topRowFloat = loadedState.scroller.topRow;
- this.s.baseScrollTop = loadedState.scroller.baseScrollTop;
- this.s.baseRowTop = loadedState.scroller.baseRowTop;
- }
- dt.on( 'init.scroller', function () {
- that.measure( false );
- that._draw();
- // Update the scroller when the DataTable is redrawn
- dt.on( 'draw.scroller', function () {
- that._draw();
- });
- } );
- // Set height before the draw happens, allowing everything else to update
- // on draw complete without worry for roder.
- dt.on( 'preDraw.dt.scroller', function () {
- that._scrollForce();
- } );
- // Destructor
- dt.on( 'destroy.scroller', function () {
- $(window).off( 'resize.dt-scroller' );
- $(that.dom.scroller).off('.dt-scroller');
- $(that.s.dt.nTable).off( '.scroller' );
- $(that.s.dt.nTableWrapper).removeClass('DTS');
- $('div.DTS_Loading', that.dom.scroller.parentNode).remove();
- that.dom.table.style.position = "";
- that.dom.table.style.top = "";
- that.dom.table.style.left = "";
- } );
- },
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Private methods
- */
- /**
- * Automatic calculation of table row height. This is just a little tricky here as using
- * initialisation DataTables has tale the table out of the document, so we need to create
- * a new table and insert it into the document, calculate the row height and then whip the
- * table out.
- * @returns {void}
- * @private
- */
- _calcRowHeight: function ()
- {
- var dt = this.s.dt;
- var origTable = dt.nTable;
- var nTable = origTable.cloneNode( false );
- var tbody = $('<tbody/>').appendTo( nTable );
- var container = $(
- '<div class="'+dt.oClasses.sWrapper+' DTS">'+
- '<div class="'+dt.oClasses.sScrollWrapper+'">'+
- '<div class="'+dt.oClasses.sScrollBody+'"></div>'+
- '</div>'+
- '</div>'
- );
- // Want 3 rows in the sizing table so :first-child and :last-child
- // CSS styles don't come into play - take the size of the middle row
- $('tbody tr:lt(4)', origTable).clone().appendTo( tbody );
- var rowsCount = $('tr', tbody).length;
- if ( rowsCount === 1 ) {
- tbody.prepend('<tr><td> </td></tr>');
- tbody.append('<tr><td> </td></tr>');
- }
- else {
- for (; rowsCount < 3; rowsCount++) {
- tbody.append('<tr><td> </td></tr>');
- }
- }
-
- $('div.'+dt.oClasses.sScrollBody, container).append( nTable );
- // If initialised using `dom`, use the holding element as the insert point
- var insertEl = this.s.dt.nHolding || origTable.parentNode;
- if ( ! $(insertEl).is(':visible') ) {
- insertEl = 'body';
- }
- container.appendTo( insertEl );
- this.s.heights.row = $('tr', tbody).eq(1).outerHeight();
- container.remove();
- },
- /**
- * Draw callback function which is fired when the DataTable is redrawn. The main function of
- * this method is to position the drawn table correctly the scrolling container for the rows
- * that is displays as a result of the scrolling position.
- * @returns {void}
- * @private
- */
- _draw: function ()
- {
- var
- that = this,
- heights = this.s.heights,
- iScrollTop = this.dom.scroller.scrollTop,
- iActualScrollTop = iScrollTop,
- iScrollBottom = iScrollTop + heights.viewport,
- iTableHeight = $(this.s.dt.nTable).height(),
- displayStart = this.s.dt._iDisplayStart,
- displayLen = this.s.dt._iDisplayLength,
- displayEnd = this.s.dt.fnRecordsDisplay();
- // Disable the scroll event listener while we are updating the DOM
- this.s.skip = true;
- // If paging is reset
- if ( (this.s.dt.bSorted || this.s.dt.bFiltered) && displayStart === 0 && !this.s.dt._drawHold ) {
- this.s.topRowFloat = 0;
- }
- iScrollTop = this.scrollType === 'jump' ?
- this._domain( 'physicalToVirtual', this.s.topRowFloat * heights.row ) :
- iScrollTop;
- // This doesn't work when scrolling with the mouse wheel
- $(that.dom.scroller).scrollTop(iScrollTop);
- // Store positional information so positional calculations can be based
- // upon the current table draw position
- this.s.baseScrollTop = iScrollTop;
- this.s.baseRowTop = this.s.topRowFloat;
- // Position the table in the virtual scroller
- var tableTop = iScrollTop - ((this.s.topRowFloat - displayStart) * heights.row);
- if ( displayStart === 0 ) {
- tableTop = 0;
- }
- else if ( displayStart + displayLen >= displayEnd ) {
- tableTop = heights.scroll - iTableHeight;
- }
- this.dom.table.style.top = tableTop+'px';
- /* Cache some information for the scroller */
- this.s.tableTop = tableTop;
- this.s.tableBottom = iTableHeight + this.s.tableTop;
- // Calculate the boundaries for where a redraw will be triggered by the
- // scroll event listener
- var boundaryPx = (iScrollTop - this.s.tableTop) * this.s.boundaryScale;
- this.s.redrawTop = iScrollTop - boundaryPx;
- this.s.redrawBottom = iScrollTop + boundaryPx > heights.scroll - heights.viewport - heights.row ?
- heights.scroll - heights.viewport - heights.row :
- iScrollTop + boundaryPx;
- this.s.skip = false;
- // Restore the scrolling position that was saved by DataTable's state
- // saving Note that this is done on the second draw when data is Ajax
- // sourced, and the first draw when DOM soured
- if ( this.s.dt.oFeatures.bStateSave && this.s.dt.oLoadedState !== null &&
- typeof this.s.dt.oLoadedState.iScroller != 'undefined' )
- {
- // A quirk of DataTables is that the draw callback will occur on an
- // empty set if Ajax sourced, but not if server-side processing.
- var ajaxSourced = (this.s.dt.sAjaxSource || that.s.dt.ajax) && ! this.s.dt.oFeatures.bServerSide ?
- true :
- false;
- if ( ( ajaxSourced && this.s.dt.iDraw == 2) ||
- (!ajaxSourced && this.s.dt.iDraw == 1) )
- {
- setTimeout( function () {
- $(that.dom.scroller).scrollTop( that.s.dt.oLoadedState.iScroller );
- that.s.redrawTop = that.s.dt.oLoadedState.iScroller - (heights.viewport/2);
- // In order to prevent layout thrashing we need another
- // small delay
- setTimeout( function () {
- that.s.ingnoreScroll = false;
- }, 0 );
- }, 0 );
- }
- }
- else {
- that.s.ingnoreScroll = false;
- }
- // Because of the order of the DT callbacks, the info update will
- // take precedence over the one we want here. So a 'thread' break is
- // needed. Only add the thread break if bInfo is set
- if ( this.s.dt.oFeatures.bInfo ) {
- setTimeout( function () {
- that._info.call( that );
- }, 0 );
- }
- // Hide the loading indicator
- if ( this.dom.loader && this.s.loaderVisible ) {
- this.dom.loader.css( 'display', 'none' );
- this.s.loaderVisible = false;
- }
- },
- /**
- * Convert from one domain to another. The physical domain is the actual
- * pixel count on the screen, while the virtual is if we had browsers which
- * had scrolling containers of infinite height (i.e. the absolute value)
- *
- * @param {string} dir Domain transform direction, `virtualToPhysical` or
- * `physicalToVirtual`
- * @returns {number} Calculated transform
- * @private
- */
- _domain: function ( dir, val )
- {
- var heights = this.s.heights;
- var diff;
- var magic = 10000; // the point at which the non-linear calculations start to happen
- // If the virtual and physical height match, then we use a linear
- // transform between the two, allowing the scrollbar to be linear
- if ( heights.virtual === heights.scroll ) {
- return val;
- }
- // In the first 10k pixels and the last 10k pixels, we want the scrolling
- // to be linear. After that it can be non-linear. It would be unusual for
- // anyone to mouse wheel through that much.
- if ( val < magic ) {
- return val;
- }
- else if ( dir === 'virtualToPhysical' && val > heights.virtual - magic ) {
- diff = heights.virtual - val;
- return heights.scroll - diff;
- }
- else if ( dir === 'physicalToVirtual' && val > heights.scroll - magic ) {
- diff = heights.scroll - val;
- return heights.virtual - diff;
- }
- // Otherwise, we want a non-linear scrollbar to take account of the
- // redrawing regions at the start and end of the table, otherwise these
- // can stutter badly - on large tables 30px (for example) scroll might
- // be hundreds of rows, so the table would be redrawing every few px at
- // the start and end. Use a simple linear eq. to stop this, effectively
- // causing a kink in the scrolling ratio. It does mean the scrollbar is
- // non-linear, but with such massive data sets, the scrollbar is going
- // to be a best guess anyway
- var xMax = dir === 'virtualToPhysical' ?
- heights.virtual :
- heights.scroll;
- var yMax = dir === 'virtualToPhysical' ?
- heights.scroll :
- heights.virtual;
- var m = (yMax - magic) / (xMax - magic);
- var c = magic - (m*magic);
- return (m*val) + c;
- },
- /**
- * Update any information elements that are controlled by the DataTable based on the scrolling
- * viewport and what rows are visible in it. This function basically acts in the same way as
- * _fnUpdateInfo in DataTables, and effectively replaces that function.
- * @returns {void}
- * @private
- */
- _info: function ()
- {
- if ( !this.s.dt.oFeatures.bInfo )
- {
- return;
- }
- var
- dt = this.s.dt,
- language = dt.oLanguage,
- iScrollTop = this.dom.scroller.scrollTop,
- iStart = Math.floor( this.pixelsToRow(iScrollTop, false, this.s.ani)+1 ),
- iMax = dt.fnRecordsTotal(),
- iTotal = dt.fnRecordsDisplay(),
- iPossibleEnd = Math.ceil( this.pixelsToRow(iScrollTop+this.s.heights.viewport, false, this.s.ani) ),
- iEnd = iTotal < iPossibleEnd ? iTotal : iPossibleEnd,
- sStart = dt.fnFormatNumber( iStart ),
- sEnd = dt.fnFormatNumber( iEnd ),
- sMax = dt.fnFormatNumber( iMax ),
- sTotal = dt.fnFormatNumber( iTotal ),
- sOut;
- if ( dt.fnRecordsDisplay() === 0 &&
- dt.fnRecordsDisplay() == dt.fnRecordsTotal() )
- {
- /* Empty record set */
- sOut = language.sInfoEmpty+ language.sInfoPostFix;
- }
- else if ( dt.fnRecordsDisplay() === 0 )
- {
- /* Empty record set after filtering */
- sOut = language.sInfoEmpty +' '+
- language.sInfoFiltered.replace('_MAX_', sMax)+
- language.sInfoPostFix;
- }
- else if ( dt.fnRecordsDisplay() == dt.fnRecordsTotal() )
- {
- /* Normal record set */
- sOut = language.sInfo.
- replace('_START_', sStart).
- replace('_END_', sEnd).
- replace('_MAX_', sMax).
- replace('_TOTAL_', sTotal)+
- language.sInfoPostFix;
- }
- else
- {
- /* Record set after filtering */
- sOut = language.sInfo.
- replace('_START_', sStart).
- replace('_END_', sEnd).
- replace('_MAX_', sMax).
- replace('_TOTAL_', sTotal) +' '+
- language.sInfoFiltered.replace(
- '_MAX_',
- dt.fnFormatNumber(dt.fnRecordsTotal())
- )+
- language.sInfoPostFix;
- }
- var callback = language.fnInfoCallback;
- if ( callback ) {
- sOut = callback.call( dt.oInstance,
- dt, iStart, iEnd, iMax, iTotal, sOut
- );
- }
- var n = dt.aanFeatures.i;
- if ( typeof n != 'undefined' )
- {
- for ( var i=0, iLen=n.length ; i<iLen ; i++ )
- {
- $(n[i]).html( sOut );
- }
- }
- // DT doesn't actually (yet) trigger this event, but it will in future
- $(dt.nTable).triggerHandler( 'info.dt' );
- },
- /**
- * Parse CSS height property string as number
- *
- * An attempt is made to parse the string as a number. Currently supported units are 'px',
- * 'vh', and 'rem'. 'em' is partially supported; it works as long as the parent element's
- * font size matches the body element. Zero is returned for unrecognized strings.
- * @param {string} cssHeight CSS height property string
- * @returns {number} height
- * @private
- */
- _parseHeight: function(cssHeight) {
- var height;
- var matches = /^([+-]?(?:\d+(?:\.\d+)?|\.\d+))(px|em|rem|vh)$/.exec(cssHeight);
- if (matches === null) {
- return 0;
- }
- var value = parseFloat(matches[1]);
- var unit = matches[2];
- if ( unit === 'px' ) {
- height = value;
- }
- else if ( unit === 'vh' ) {
- height = ( value / 100 ) * $(window).height();
- }
- else if ( unit === 'rem' ) {
- height = value * parseFloat($(':root').css('font-size'));
- }
- else if ( unit === 'em' ) {
- height = value * parseFloat($('body').css('font-size'));
- }
- return height ?
- height :
- 0;
- },
- /**
- * Scrolling function - fired whenever the scrolling position is changed.
- * This method needs to use the stored values to see if the table should be
- * redrawn as we are moving towards the end of the information that is
- * currently drawn or not. If needed, then it will redraw the table based on
- * the new position.
- * @returns {void}
- * @private
- */
- _scroll: function ()
- {
- var
- that = this,
- heights = this.s.heights,
- iScrollTop = this.dom.scroller.scrollTop,
- iTopRow;
- if ( this.s.skip ) {
- return;
- }
- if ( this.s.ingnoreScroll ) {
- return;
- }
- if ( iScrollTop === this.s.lastScrollTop ) {
- return;
- }
- /* If the table has been sorted or filtered, then we use the redraw that
- * DataTables as done, rather than performing our own
- */
- if ( this.s.dt.bFiltered || this.s.dt.bSorted ) {
- this.s.lastScrollTop = 0;
- return;
- }
- /* Update the table's information display for what is now in the viewport */
- this._info();
- /* We don't want to state save on every scroll event - that's heavy
- * handed, so use a timeout to update the state saving only when the
- * scrolling has finished
- */
- clearTimeout( this.s.stateTO );
- this.s.stateTO = setTimeout( function () {
- that.s.dtApi.state.save();
- }, 250 );
- this.s.scrollType = Math.abs(iScrollTop - this.s.lastScrollTop) > heights.viewport ?
- 'jump' :
- 'cont';
- this.s.topRowFloat = this.s.scrollType === 'cont' ?
- this.pixelsToRow( iScrollTop, false, false ) :
- this._domain( 'physicalToVirtual', iScrollTop ) / heights.row;
- if ( this.s.topRowFloat < 0 ) {
- this.s.topRowFloat = 0;
- }
- /* Check if the scroll point is outside the trigger boundary which would required
- * a DataTables redraw
- */
- if ( this.s.forceReposition || iScrollTop < this.s.redrawTop || iScrollTop > this.s.redrawBottom ) {
- var preRows = Math.ceil( ((this.s.displayBuffer-1)/2) * this.s.viewportRows );
- iTopRow = parseInt(this.s.topRowFloat, 10) - preRows;
- this.s.forceReposition = false;
- if ( iTopRow <= 0 ) {
- /* At the start of the table */
- iTopRow = 0;
- }
- else if ( iTopRow + this.s.dt._iDisplayLength > this.s.dt.fnRecordsDisplay() ) {
- /* At the end of the table */
- iTopRow = this.s.dt.fnRecordsDisplay() - this.s.dt._iDisplayLength;
- if ( iTopRow < 0 ) {
- iTopRow = 0;
- }
- }
- else if ( iTopRow % 2 !== 0 ) {
- // For the row-striping classes (odd/even) we want only to start
- // on evens otherwise the stripes will change between draws and
- // look rubbish
- iTopRow++;
- }
- if ( iTopRow != this.s.dt._iDisplayStart ) {
- /* Cache the new table position for quick lookups */
- this.s.tableTop = $(this.s.dt.nTable).offset().top;
- this.s.tableBottom = $(this.s.dt.nTable).height() + this.s.tableTop;
- var draw = function () {
- if ( that.s.scrollDrawReq === null ) {
- that.s.scrollDrawReq = iScrollTop;
- }
- that.s.dt._iDisplayStart = iTopRow;
- that.s.dt.oApi._fnDraw( that.s.dt );
- };
- /* Do the DataTables redraw based on the calculated start point - note that when
- * using server-side processing we introduce a small delay to not DoS the server...
- */
- if ( this.s.dt.oFeatures.bServerSide ) {
- clearTimeout( this.s.drawTO );
- this.s.drawTO = setTimeout( draw, this.s.serverWait );
- }
- else {
- draw();
- }
- if ( this.dom.loader && ! this.s.loaderVisible ) {
- this.dom.loader.css( 'display', 'block' );
- this.s.loaderVisible = true;
- }
- }
- }
- else {
- this.s.topRowFloat = this.pixelsToRow( iScrollTop, false, true );
- }
- this.s.lastScrollTop = iScrollTop;
- this.s.stateSaveThrottle();
- if ( this.s.scrollType === 'jump' && this.s.mousedown ) {
- this.dom.label
- .html( this.s.dt.fnFormatNumber( parseInt( this.s.topRowFloat, 10 )+1 ) )
- .css( 'top', iScrollTop + (iScrollTop * heights.labelFactor ) )
- .css( 'display', 'block' );
- }
- },
- /**
- * Force the scrolling container to have height beyond that of just the
- * table that has been drawn so the user can scroll the whole data set.
- *
- * Note that if the calculated required scrolling height exceeds a maximum
- * value (1 million pixels - hard-coded) the forcing element will be set
- * only to that maximum value and virtual / physical domain transforms will
- * be used to allow Scroller to display tables of any number of records.
- * @returns {void}
- * @private
- */
- _scrollForce: function ()
- {
- var heights = this.s.heights;
- var max = 1000000;
- heights.virtual = heights.row * this.s.dt.fnRecordsDisplay();
- heights.scroll = heights.virtual;
- if ( heights.scroll > max ) {
- heights.scroll = max;
- }
- // Minimum height so there is always a row visible (the 'no rows found'
- // if reduced to zero filtering)
- this.dom.force.style.height = heights.scroll > this.s.heights.row ?
- heights.scroll+'px' :
- this.s.heights.row+'px';
- }
- } );
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Statics
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * Scroller default settings for initialisation
- * @namespace
- * @name Scroller.defaults
- * @static
- */
- Scroller.defaults = {
- /**
- * Scroller uses the boundary scaling factor to decide when to redraw the table - which it
- * typically does before you reach the end of the currently loaded data set (in order to
- * allow the data to look continuous to a user scrolling through the data). If given as 0
- * then the table will be redrawn whenever the viewport is scrolled, while 1 would not
- * redraw the table until the currently loaded data has all been shown. You will want
- * something in the middle - the default factor of 0.5 is usually suitable.
- * @type float
- * @default 0.5
- * @static
- */
- boundaryScale: 0.5,
- /**
- * The display buffer is what Scroller uses to calculate how many rows it should pre-fetch
- * for scrolling. Scroller automatically adjusts DataTables' display length to pre-fetch
- * rows that will be shown in "near scrolling" (i.e. just beyond the current display area).
- * The value is based upon the number of rows that can be displayed in the viewport (i.e.
- * a value of 1), and will apply the display range to records before before and after the
- * current viewport - i.e. a factor of 3 will allow Scroller to pre-fetch 1 viewport's worth
- * of rows before the current viewport, the current viewport's rows and 1 viewport's worth
- * of rows after the current viewport. Adjusting this value can be useful for ensuring
- * smooth scrolling based on your data set.
- * @type int
- * @default 7
- * @static
- */
- displayBuffer: 9,
- /**
- * Show (or not) the loading element in the background of the table. Note that you should
- * include the dataTables.scroller.css file for this to be displayed correctly.
- * @type boolean
- * @default false
- * @static
- */
- loadingIndicator: false,
- /**
- * Scroller will attempt to automatically calculate the height of rows for it's internal
- * calculations. However the height that is used can be overridden using this parameter.
- * @type int|string
- * @default auto
- * @static
- */
- rowHeight: "auto",
- /**
- * When using server-side processing, Scroller will wait a small amount of time to allow
- * the scrolling to finish before requesting more data from the server. This prevents
- * you from DoSing your own server! The wait time can be configured by this parameter.
- * @type int
- * @default 200
- * @static
- */
- serverWait: 200
- };
- Scroller.oDefaults = Scroller.defaults;
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constants
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- /**
- * Scroller version
- * @type String
- * @default See code
- * @name Scroller.version
- * @static
- */
- Scroller.version = "2.0.0";
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Initialisation
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
- // Attach a listener to the document which listens for DataTables initialisation
- // events so we can automatically initialise
- $(document).on( 'preInit.dt.dtscroller', function (e, settings) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- var init = settings.oInit.scroller;
- var defaults = DataTable.defaults.scroller;
- if ( init || defaults ) {
- var opts = $.extend( {}, init, defaults );
- if ( init !== false ) {
- new Scroller( settings, opts );
- }
- }
- } );
- // Attach Scroller to DataTables so it can be accessed as an 'extra'
- $.fn.dataTable.Scroller = Scroller;
- $.fn.DataTable.Scroller = Scroller;
- // DataTables 1.10 API method aliases
- var Api = $.fn.dataTable.Api;
- Api.register( 'scroller()', function () {
- return this;
- } );
- // Undocumented and deprecated - is it actually useful at all?
- Api.register( 'scroller().rowToPixels()', function ( rowIdx, intParse, virtual ) {
- var ctx = this.context;
- if ( ctx.length && ctx[0].oScroller ) {
- return ctx[0].oScroller.rowToPixels( rowIdx, intParse, virtual );
- }
- // undefined
- } );
- // Undocumented and deprecated - is it actually useful at all?
- Api.register( 'scroller().pixelsToRow()', function ( pixels, intParse, virtual ) {
- var ctx = this.context;
- if ( ctx.length && ctx[0].oScroller ) {
- return ctx[0].oScroller.pixelsToRow( pixels, intParse, virtual );
- }
- // undefined
- } );
- // `scroller().scrollToRow()` is undocumented and deprecated. Use `scroller.toPosition()
- Api.register( ['scroller().scrollToRow()', 'scroller.toPosition()'], function ( idx, ani ) {
- this.iterator( 'table', function ( ctx ) {
- if ( ctx.oScroller ) {
- ctx.oScroller.scrollToRow( idx, ani );
- }
- } );
- return this;
- } );
- Api.register( 'row().scrollTo()', function ( ani ) {
- var that = this;
- this.iterator( 'row', function ( ctx, rowIdx ) {
- if ( ctx.oScroller ) {
- var displayIdx = that
- .rows( { order: 'applied', search: 'applied' } )
- .indexes()
- .indexOf( rowIdx );
- ctx.oScroller.scrollToRow( displayIdx, ani );
- }
- } );
- return this;
- } );
- Api.register( 'scroller.measure()', function ( redraw ) {
- this.iterator( 'table', function ( ctx ) {
- if ( ctx.oScroller ) {
- ctx.oScroller.measure( redraw );
- }
- } );
- return this;
- } );
- Api.register( 'scroller.page()', function() {
- var ctx = this.context;
- if ( ctx.length && ctx[0].oScroller ) {
- return ctx[0].oScroller.pageInfo();
- }
- // undefined
- } );
- return Scroller;
- }));
- /*! Bootstrap 4 styling wrapper for Scroller
- * ©2018 SpryMedia Ltd - datatables.net/license
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net-bs4', 'datatables.net-scroller'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net-bs4')(root, $).$;
- }
- if ( ! $.fn.dataTable.Scroller ) {
- require('datatables.net-scroller')(root, $);
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- return $.fn.dataTable;
- }));
- /*! Select for DataTables 1.3.0
- * 2015-2018 SpryMedia Ltd - datatables.net/license/mit
- */
- /**
- * @summary Select for DataTables
- * @description A collection of API methods, events and buttons for DataTables
- * that provides selection options of the items in a DataTable
- * @version 1.3.0
- * @file dataTables.select.js
- * @author SpryMedia Ltd (www.sprymedia.co.uk)
- * @contact datatables.net/forums
- * @copyright Copyright 2015-2018 SpryMedia Ltd.
- *
- * This source file is free software, available under the following license:
- * MIT license - http://datatables.net/license/mit
- *
- * This source file is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the license files for details.
- *
- * For details please refer to: http://www.datatables.net/extensions/select
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- // Version information for debugger
- DataTable.select = {};
- DataTable.select.version = '1.3.0';
- DataTable.select.init = function ( dt ) {
- var ctx = dt.settings()[0];
- var init = ctx.oInit.select;
- var defaults = DataTable.defaults.select;
- var opts = init === undefined ?
- defaults :
- init;
- // Set defaults
- var items = 'row';
- var style = 'api';
- var blurable = false;
- var info = true;
- var selector = 'td, th';
- var className = 'selected';
- var setStyle = false;
- ctx._select = {};
- // Initialisation customisations
- if ( opts === true ) {
- style = 'os';
- setStyle = true;
- }
- else if ( typeof opts === 'string' ) {
- style = opts;
- setStyle = true;
- }
- else if ( $.isPlainObject( opts ) ) {
- if ( opts.blurable !== undefined ) {
- blurable = opts.blurable;
- }
- if ( opts.info !== undefined ) {
- info = opts.info;
- }
- if ( opts.items !== undefined ) {
- items = opts.items;
- }
- if ( opts.style !== undefined ) {
- style = opts.style;
- setStyle = true;
- }
- else {
- style = 'os';
- setStyle = true;
- }
- if ( opts.selector !== undefined ) {
- selector = opts.selector;
- }
- if ( opts.className !== undefined ) {
- className = opts.className;
- }
- }
- dt.select.selector( selector );
- dt.select.items( items );
- dt.select.style( style );
- dt.select.blurable( blurable );
- dt.select.info( info );
- ctx._select.className = className;
- // Sort table based on selected rows. Requires Select Datatables extension
- $.fn.dataTable.ext.order['select-checkbox'] = function ( settings, col ) {
- return this.api().column( col, {order: 'index'} ).nodes().map( function ( td ) {
- if ( settings._select.items === 'row' ) {
- return $( td ).parent().hasClass( settings._select.className );
- } else if ( settings._select.items === 'cell' ) {
- return $( td ).hasClass( settings._select.className );
- }
- return false;
- });
- };
- // If the init options haven't enabled select, but there is a selectable
- // class name, then enable
- if ( ! setStyle && $( dt.table().node() ).hasClass( 'selectable' ) ) {
- dt.select.style( 'os' );
- }
- };
- /*
- Select is a collection of API methods, event handlers, event emitters and
- buttons (for the `Buttons` extension) for DataTables. It provides the following
- features, with an overview of how they are implemented:
- ## Selection of rows, columns and cells. Whether an item is selected or not is
- stored in:
- * rows: a `_select_selected` property which contains a boolean value of the
- DataTables' `aoData` object for each row
- * columns: a `_select_selected` property which contains a boolean value of the
- DataTables' `aoColumns` object for each column
- * cells: a `_selected_cells` property which contains an array of boolean values
- of the `aoData` object for each row. The array is the same length as the
- columns array, with each element of it representing a cell.
- This method of using boolean flags allows Select to operate when nodes have not
- been created for rows / cells (DataTables' defer rendering feature).
- ## API methods
- A range of API methods are available for triggering selection and de-selection
- of rows. Methods are also available to configure the selection events that can
- be triggered by an end user (such as which items are to be selected). To a large
- extent, these of API methods *is* Select. It is basically a collection of helper
- functions that can be used to select items in a DataTable.
- Configuration of select is held in the object `_select` which is attached to the
- DataTables settings object on initialisation. Select being available on a table
- is not optional when Select is loaded, but its default is for selection only to
- be available via the API - so the end user wouldn't be able to select rows
- without additional configuration.
- The `_select` object contains the following properties:
- ```
- {
- items:string - Can be `rows`, `columns` or `cells`. Defines what item
- will be selected if the user is allowed to activate row
- selection using the mouse.
- style:string - Can be `none`, `single`, `multi` or `os`. Defines the
- interaction style when selecting items
- blurable:boolean - If row selection can be cleared by clicking outside of
- the table
- info:boolean - If the selection summary should be shown in the table
- information elements
- }
- ```
- In addition to the API methods, Select also extends the DataTables selector
- options for rows, columns and cells adding a `selected` option to the selector
- options object, allowing the developer to select only selected items or
- unselected items.
- ## Mouse selection of items
- Clicking on items can be used to select items. This is done by a simple event
- handler that will select the items using the API methods.
- */
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Local functions
- */
- /**
- * Add one or more cells to the selection when shift clicking in OS selection
- * style cell selection.
- *
- * Cell range is more complicated than row and column as we want to select
- * in the visible grid rather than by index in sequence. For example, if you
- * click first in cell 1-1 and then shift click in 2-2 - cells 1-2 and 2-1
- * should also be selected (and not 1-3, 1-4. etc)
- *
- * @param {DataTable.Api} dt DataTable
- * @param {object} idx Cell index to select to
- * @param {object} last Cell index to select from
- * @private
- */
- function cellRange( dt, idx, last )
- {
- var indexes;
- var columnIndexes;
- var rowIndexes;
- var selectColumns = function ( start, end ) {
- if ( start > end ) {
- var tmp = end;
- end = start;
- start = tmp;
- }
-
- var record = false;
- return dt.columns( ':visible' ).indexes().filter( function (i) {
- if ( i === start ) {
- record = true;
- }
-
- if ( i === end ) { // not else if, as start might === end
- record = false;
- return true;
- }
- return record;
- } );
- };
- var selectRows = function ( start, end ) {
- var indexes = dt.rows( { search: 'applied' } ).indexes();
- // Which comes first - might need to swap
- if ( indexes.indexOf( start ) > indexes.indexOf( end ) ) {
- var tmp = end;
- end = start;
- start = tmp;
- }
- var record = false;
- return indexes.filter( function (i) {
- if ( i === start ) {
- record = true;
- }
-
- if ( i === end ) {
- record = false;
- return true;
- }
- return record;
- } );
- };
- if ( ! dt.cells( { selected: true } ).any() && ! last ) {
- // select from the top left cell to this one
- columnIndexes = selectColumns( 0, idx.column );
- rowIndexes = selectRows( 0 , idx.row );
- }
- else {
- // Get column indexes between old and new
- columnIndexes = selectColumns( last.column, idx.column );
- rowIndexes = selectRows( last.row , idx.row );
- }
- indexes = dt.cells( rowIndexes, columnIndexes ).flatten();
- if ( ! dt.cells( idx, { selected: true } ).any() ) {
- // Select range
- dt.cells( indexes ).select();
- }
- else {
- // Deselect range
- dt.cells( indexes ).deselect();
- }
- }
- /**
- * Disable mouse selection by removing the selectors
- *
- * @param {DataTable.Api} dt DataTable to remove events from
- * @private
- */
- function disableMouseSelection( dt )
- {
- var ctx = dt.settings()[0];
- var selector = ctx._select.selector;
- $( dt.table().container() )
- .off( 'mousedown.dtSelect', selector )
- .off( 'mouseup.dtSelect', selector )
- .off( 'click.dtSelect', selector );
- $('body').off( 'click.dtSelect' + dt.table().node().id );
- }
- /**
- * Attach mouse listeners to the table to allow mouse selection of items
- *
- * @param {DataTable.Api} dt DataTable to remove events from
- * @private
- */
- function enableMouseSelection ( dt )
- {
- var container = $( dt.table().container() );
- var ctx = dt.settings()[0];
- var selector = ctx._select.selector;
- var matchSelection;
- container
- .on( 'mousedown.dtSelect', selector, function(e) {
- // Disallow text selection for shift clicking on the table so multi
- // element selection doesn't look terrible!
- if ( e.shiftKey || e.metaKey || e.ctrlKey ) {
- container
- .css( '-moz-user-select', 'none' )
- .one('selectstart.dtSelect', selector, function () {
- return false;
- } );
- }
- if ( window.getSelection ) {
- matchSelection = window.getSelection();
- }
- } )
- .on( 'mouseup.dtSelect', selector, function() {
- // Allow text selection to occur again, Mozilla style (tested in FF
- // 35.0.1 - still required)
- container.css( '-moz-user-select', '' );
- } )
- .on( 'click.dtSelect', selector, function ( e ) {
- var items = dt.select.items();
- var idx;
- // If text was selected (click and drag), then we shouldn't change
- // the row's selected state
- if ( matchSelection ) {
- var selection = window.getSelection();
- // If the element that contains the selection is not in the table, we can ignore it
- // This can happen if the developer selects text from the click event
- if ( ! selection.anchorNode || $(selection.anchorNode).closest('table')[0] === dt.table().node() ) {
- if ( selection !== matchSelection ) {
- return;
- }
- }
- }
- var ctx = dt.settings()[0];
- var wrapperClass = $.trim(dt.settings()[0].oClasses.sWrapper).replace(/ +/g, '.');
- // Ignore clicks inside a sub-table
- if ( $(e.target).closest('div.'+wrapperClass)[0] != dt.table().container() ) {
- return;
- }
- var cell = dt.cell( $(e.target).closest('td, th') );
- // Check the cell actually belongs to the host DataTable (so child
- // rows, etc, are ignored)
- if ( ! cell.any() ) {
- return;
- }
- var event = $.Event('user-select.dt');
- eventTrigger( dt, event, [ items, cell, e ] );
- if ( event.isDefaultPrevented() ) {
- return;
- }
- var cellIndex = cell.index();
- if ( items === 'row' ) {
- idx = cellIndex.row;
- typeSelect( e, dt, ctx, 'row', idx );
- }
- else if ( items === 'column' ) {
- idx = cell.index().column;
- typeSelect( e, dt, ctx, 'column', idx );
- }
- else if ( items === 'cell' ) {
- idx = cell.index();
- typeSelect( e, dt, ctx, 'cell', idx );
- }
- ctx._select_lastCell = cellIndex;
- } );
- // Blurable
- $('body').on( 'click.dtSelect' + dt.table().node().id, function ( e ) {
- if ( ctx._select.blurable ) {
- // If the click was inside the DataTables container, don't blur
- if ( $(e.target).parents().filter( dt.table().container() ).length ) {
- return;
- }
- // Ignore elements which have been removed from the DOM (i.e. paging
- // buttons)
- if ( $(e.target).parents('html').length === 0 ) {
- return;
- }
- // Don't blur in Editor form
- if ( $(e.target).parents('div.DTE').length ) {
- return;
- }
- clear( ctx, true );
- }
- } );
- }
- /**
- * Trigger an event on a DataTable
- *
- * @param {DataTable.Api} api DataTable to trigger events on
- * @param {boolean} selected true if selected, false if deselected
- * @param {string} type Item type acting on
- * @param {boolean} any Require that there are values before
- * triggering
- * @private
- */
- function eventTrigger ( api, type, args, any )
- {
- if ( any && ! api.flatten().length ) {
- return;
- }
- if ( typeof type === 'string' ) {
- type = type +'.dt';
- }
- args.unshift( api );
- $(api.table().node()).trigger( type, args );
- }
- /**
- * Update the information element of the DataTable showing information about the
- * items selected. This is done by adding tags to the existing text
- *
- * @param {DataTable.Api} api DataTable to update
- * @private
- */
- function info ( api )
- {
- var ctx = api.settings()[0];
- if ( ! ctx._select.info || ! ctx.aanFeatures.i ) {
- return;
- }
- if ( api.select.style() === 'api' ) {
- return;
- }
- var rows = api.rows( { selected: true } ).flatten().length;
- var columns = api.columns( { selected: true } ).flatten().length;
- var cells = api.cells( { selected: true } ).flatten().length;
- var add = function ( el, name, num ) {
- el.append( $('<span class="select-item"/>').append( api.i18n(
- 'select.'+name+'s',
- { _: '%d '+name+'s selected', 0: '', 1: '1 '+name+' selected' },
- num
- ) ) );
- };
- // Internal knowledge of DataTables to loop over all information elements
- $.each( ctx.aanFeatures.i, function ( i, el ) {
- el = $(el);
- var output = $('<span class="select-info"/>');
- add( output, 'row', rows );
- add( output, 'column', columns );
- add( output, 'cell', cells );
- var exisiting = el.children('span.select-info');
- if ( exisiting.length ) {
- exisiting.remove();
- }
- if ( output.text() !== '' ) {
- el.append( output );
- }
- } );
- }
- /**
- * Initialisation of a new table. Attach event handlers and callbacks to allow
- * Select to operate correctly.
- *
- * This will occur _after_ the initial DataTables initialisation, although
- * before Ajax data is rendered, if there is ajax data
- *
- * @param {DataTable.settings} ctx Settings object to operate on
- * @private
- */
- function init ( ctx ) {
- var api = new DataTable.Api( ctx );
- // Row callback so that classes can be added to rows and cells if the item
- // was selected before the element was created. This will happen with the
- // `deferRender` option enabled.
- //
- // This method of attaching to `aoRowCreatedCallback` is a hack until
- // DataTables has proper events for row manipulation If you are reviewing
- // this code to create your own plug-ins, please do not do this!
- ctx.aoRowCreatedCallback.push( {
- fn: function ( row, data, index ) {
- var i, ien;
- var d = ctx.aoData[ index ];
- // Row
- if ( d._select_selected ) {
- $( row ).addClass( ctx._select.className );
- }
- // Cells and columns - if separated out, we would need to do two
- // loops, so it makes sense to combine them into a single one
- for ( i=0, ien=ctx.aoColumns.length ; i<ien ; i++ ) {
- if ( ctx.aoColumns[i]._select_selected || (d._selected_cells && d._selected_cells[i]) ) {
- $(d.anCells[i]).addClass( ctx._select.className );
- }
- }
- },
- sName: 'select-deferRender'
- } );
- // On Ajax reload we want to reselect all rows which are currently selected,
- // if there is an rowId (i.e. a unique value to identify each row with)
- api.on( 'preXhr.dt.dtSelect', function () {
- // note that column selection doesn't need to be cached and then
- // reselected, as they are already selected
- var rows = api.rows( { selected: true } ).ids( true ).filter( function ( d ) {
- return d !== undefined;
- } );
- var cells = api.cells( { selected: true } ).eq(0).map( function ( cellIdx ) {
- var id = api.row( cellIdx.row ).id( true );
- return id ?
- { row: id, column: cellIdx.column } :
- undefined;
- } ).filter( function ( d ) {
- return d !== undefined;
- } );
- // On the next draw, reselect the currently selected items
- api.one( 'draw.dt.dtSelect', function () {
- api.rows( rows ).select();
- // `cells` is not a cell index selector, so it needs a loop
- if ( cells.any() ) {
- cells.each( function ( id ) {
- api.cells( id.row, id.column ).select();
- } );
- }
- } );
- } );
- // Update the table information element with selected item summary
- api.on( 'draw.dtSelect.dt select.dtSelect.dt deselect.dtSelect.dt info.dt', function () {
- info( api );
- } );
- // Clean up and release
- api.on( 'destroy.dtSelect', function () {
- disableMouseSelection( api );
- api.off( '.dtSelect' );
- } );
- }
- /**
- * Add one or more items (rows or columns) to the selection when shift clicking
- * in OS selection style
- *
- * @param {DataTable.Api} dt DataTable
- * @param {string} type Row or column range selector
- * @param {object} idx Item index to select to
- * @param {object} last Item index to select from
- * @private
- */
- function rowColumnRange( dt, type, idx, last )
- {
- // Add a range of rows from the last selected row to this one
- var indexes = dt[type+'s']( { search: 'applied' } ).indexes();
- var idx1 = $.inArray( last, indexes );
- var idx2 = $.inArray( idx, indexes );
- if ( ! dt[type+'s']( { selected: true } ).any() && idx1 === -1 ) {
- // select from top to here - slightly odd, but both Windows and Mac OS
- // do this
- indexes.splice( $.inArray( idx, indexes )+1, indexes.length );
- }
- else {
- // reverse so we can shift click 'up' as well as down
- if ( idx1 > idx2 ) {
- var tmp = idx2;
- idx2 = idx1;
- idx1 = tmp;
- }
- indexes.splice( idx2+1, indexes.length );
- indexes.splice( 0, idx1 );
- }
- if ( ! dt[type]( idx, { selected: true } ).any() ) {
- // Select range
- dt[type+'s']( indexes ).select();
- }
- else {
- // Deselect range - need to keep the clicked on row selected
- indexes.splice( $.inArray( idx, indexes ), 1 );
- dt[type+'s']( indexes ).deselect();
- }
- }
- /**
- * Clear all selected items
- *
- * @param {DataTable.settings} ctx Settings object of the host DataTable
- * @param {boolean} [force=false] Force the de-selection to happen, regardless
- * of selection style
- * @private
- */
- function clear( ctx, force )
- {
- if ( force || ctx._select.style === 'single' ) {
- var api = new DataTable.Api( ctx );
-
- api.rows( { selected: true } ).deselect();
- api.columns( { selected: true } ).deselect();
- api.cells( { selected: true } ).deselect();
- }
- }
- /**
- * Select items based on the current configuration for style and items.
- *
- * @param {object} e Mouse event object
- * @param {DataTables.Api} dt DataTable
- * @param {DataTable.settings} ctx Settings object of the host DataTable
- * @param {string} type Items to select
- * @param {int|object} idx Index of the item to select
- * @private
- */
- function typeSelect ( e, dt, ctx, type, idx )
- {
- var style = dt.select.style();
- var isSelected = dt[type]( idx, { selected: true } ).any();
- if ( style === 'os' ) {
- if ( e.ctrlKey || e.metaKey ) {
- // Add or remove from the selection
- dt[type]( idx ).select( ! isSelected );
- }
- else if ( e.shiftKey ) {
- if ( type === 'cell' ) {
- cellRange( dt, idx, ctx._select_lastCell || null );
- }
- else {
- rowColumnRange( dt, type, idx, ctx._select_lastCell ?
- ctx._select_lastCell[type] :
- null
- );
- }
- }
- else {
- // No cmd or shift click - deselect if selected, or select
- // this row only
- var selected = dt[type+'s']( { selected: true } );
- if ( isSelected && selected.flatten().length === 1 ) {
- dt[type]( idx ).deselect();
- }
- else {
- selected.deselect();
- dt[type]( idx ).select();
- }
- }
- } else if ( style == 'multi+shift' ) {
- if ( e.shiftKey ) {
- if ( type === 'cell' ) {
- cellRange( dt, idx, ctx._select_lastCell || null );
- }
- else {
- rowColumnRange( dt, type, idx, ctx._select_lastCell ?
- ctx._select_lastCell[type] :
- null
- );
- }
- }
- else {
- dt[ type ]( idx ).select( ! isSelected );
- }
- }
- else {
- dt[ type ]( idx ).select( ! isSelected );
- }
- }
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables selectors
- */
- // row and column are basically identical just assigned to different properties
- // and checking a different array, so we can dynamically create the functions to
- // reduce the code size
- $.each( [
- { type: 'row', prop: 'aoData' },
- { type: 'column', prop: 'aoColumns' }
- ], function ( i, o ) {
- DataTable.ext.selector[ o.type ].push( function ( settings, opts, indexes ) {
- var selected = opts.selected;
- var data;
- var out = [];
- if ( selected !== true && selected !== false ) {
- return indexes;
- }
- for ( var i=0, ien=indexes.length ; i<ien ; i++ ) {
- data = settings[ o.prop ][ indexes[i] ];
- if ( (selected === true && data._select_selected === true) ||
- (selected === false && ! data._select_selected )
- ) {
- out.push( indexes[i] );
- }
- }
- return out;
- } );
- } );
- DataTable.ext.selector.cell.push( function ( settings, opts, cells ) {
- var selected = opts.selected;
- var rowData;
- var out = [];
- if ( selected === undefined ) {
- return cells;
- }
- for ( var i=0, ien=cells.length ; i<ien ; i++ ) {
- rowData = settings.aoData[ cells[i].row ];
- if ( (selected === true && rowData._selected_cells && rowData._selected_cells[ cells[i].column ] === true) ||
- (selected === false && ( ! rowData._selected_cells || ! rowData._selected_cells[ cells[i].column ] ) )
- ) {
- out.push( cells[i] );
- }
- }
- return out;
- } );
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * DataTables API
- *
- * For complete documentation, please refer to the docs/api directory or the
- * DataTables site
- */
- // Local variables to improve compression
- var apiRegister = DataTable.Api.register;
- var apiRegisterPlural = DataTable.Api.registerPlural;
- apiRegister( 'select()', function () {
- return this.iterator( 'table', function ( ctx ) {
- DataTable.select.init( new DataTable.Api( ctx ) );
- } );
- } );
- apiRegister( 'select.blurable()', function ( flag ) {
- if ( flag === undefined ) {
- return this.context[0]._select.blurable;
- }
- return this.iterator( 'table', function ( ctx ) {
- ctx._select.blurable = flag;
- } );
- } );
- apiRegister( 'select.info()', function ( flag ) {
- if ( info === undefined ) {
- return this.context[0]._select.info;
- }
- return this.iterator( 'table', function ( ctx ) {
- ctx._select.info = flag;
- } );
- } );
- apiRegister( 'select.items()', function ( items ) {
- if ( items === undefined ) {
- return this.context[0]._select.items;
- }
- return this.iterator( 'table', function ( ctx ) {
- ctx._select.items = items;
- eventTrigger( new DataTable.Api( ctx ), 'selectItems', [ items ] );
- } );
- } );
- // Takes effect from the _next_ selection. None disables future selection, but
- // does not clear the current selection. Use the `deselect` methods for that
- apiRegister( 'select.style()', function ( style ) {
- if ( style === undefined ) {
- return this.context[0]._select.style;
- }
- return this.iterator( 'table', function ( ctx ) {
- ctx._select.style = style;
- if ( ! ctx._select_init ) {
- init( ctx );
- }
- // Add / remove mouse event handlers. They aren't required when only
- // API selection is available
- var dt = new DataTable.Api( ctx );
- disableMouseSelection( dt );
-
- if ( style !== 'api' ) {
- enableMouseSelection( dt );
- }
- eventTrigger( new DataTable.Api( ctx ), 'selectStyle', [ style ] );
- } );
- } );
- apiRegister( 'select.selector()', function ( selector ) {
- if ( selector === undefined ) {
- return this.context[0]._select.selector;
- }
- return this.iterator( 'table', function ( ctx ) {
- disableMouseSelection( new DataTable.Api( ctx ) );
- ctx._select.selector = selector;
- if ( ctx._select.style !== 'api' ) {
- enableMouseSelection( new DataTable.Api( ctx ) );
- }
- } );
- } );
- apiRegisterPlural( 'rows().select()', 'row().select()', function ( select ) {
- var api = this;
- if ( select === false ) {
- return this.deselect();
- }
- this.iterator( 'row', function ( ctx, idx ) {
- clear( ctx );
- ctx.aoData[ idx ]._select_selected = true;
- $( ctx.aoData[ idx ].nTr ).addClass( ctx._select.className );
- } );
- this.iterator( 'table', function ( ctx, i ) {
- eventTrigger( api, 'select', [ 'row', api[i] ], true );
- } );
- return this;
- } );
- apiRegisterPlural( 'columns().select()', 'column().select()', function ( select ) {
- var api = this;
- if ( select === false ) {
- return this.deselect();
- }
- this.iterator( 'column', function ( ctx, idx ) {
- clear( ctx );
- ctx.aoColumns[ idx ]._select_selected = true;
- var column = new DataTable.Api( ctx ).column( idx );
- $( column.header() ).addClass( ctx._select.className );
- $( column.footer() ).addClass( ctx._select.className );
- column.nodes().to$().addClass( ctx._select.className );
- } );
- this.iterator( 'table', function ( ctx, i ) {
- eventTrigger( api, 'select', [ 'column', api[i] ], true );
- } );
- return this;
- } );
- apiRegisterPlural( 'cells().select()', 'cell().select()', function ( select ) {
- var api = this;
- if ( select === false ) {
- return this.deselect();
- }
- this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {
- clear( ctx );
- var data = ctx.aoData[ rowIdx ];
- if ( data._selected_cells === undefined ) {
- data._selected_cells = [];
- }
- data._selected_cells[ colIdx ] = true;
- if ( data.anCells ) {
- $( data.anCells[ colIdx ] ).addClass( ctx._select.className );
- }
- } );
- this.iterator( 'table', function ( ctx, i ) {
- eventTrigger( api, 'select', [ 'cell', api[i] ], true );
- } );
- return this;
- } );
- apiRegisterPlural( 'rows().deselect()', 'row().deselect()', function () {
- var api = this;
- this.iterator( 'row', function ( ctx, idx ) {
- ctx.aoData[ idx ]._select_selected = false;
- $( ctx.aoData[ idx ].nTr ).removeClass( ctx._select.className );
- } );
- this.iterator( 'table', function ( ctx, i ) {
- eventTrigger( api, 'deselect', [ 'row', api[i] ], true );
- } );
- return this;
- } );
- apiRegisterPlural( 'columns().deselect()', 'column().deselect()', function () {
- var api = this;
- this.iterator( 'column', function ( ctx, idx ) {
- ctx.aoColumns[ idx ]._select_selected = false;
- var api = new DataTable.Api( ctx );
- var column = api.column( idx );
- $( column.header() ).removeClass( ctx._select.className );
- $( column.footer() ).removeClass( ctx._select.className );
- // Need to loop over each cell, rather than just using
- // `column().nodes()` as cells which are individually selected should
- // not have the `selected` class removed from them
- api.cells( null, idx ).indexes().each( function (cellIdx) {
- var data = ctx.aoData[ cellIdx.row ];
- var cellSelected = data._selected_cells;
- if ( data.anCells && (! cellSelected || ! cellSelected[ cellIdx.column ]) ) {
- $( data.anCells[ cellIdx.column ] ).removeClass( ctx._select.className );
- }
- } );
- } );
- this.iterator( 'table', function ( ctx, i ) {
- eventTrigger( api, 'deselect', [ 'column', api[i] ], true );
- } );
- return this;
- } );
- apiRegisterPlural( 'cells().deselect()', 'cell().deselect()', function () {
- var api = this;
- this.iterator( 'cell', function ( ctx, rowIdx, colIdx ) {
- var data = ctx.aoData[ rowIdx ];
- data._selected_cells[ colIdx ] = false;
- // Remove class only if the cells exist, and the cell is not column
- // selected, in which case the class should remain (since it is selected
- // in the column)
- if ( data.anCells && ! ctx.aoColumns[ colIdx ]._select_selected ) {
- $( data.anCells[ colIdx ] ).removeClass( ctx._select.className );
- }
- } );
- this.iterator( 'table', function ( ctx, i ) {
- eventTrigger( api, 'deselect', [ 'cell', api[i] ], true );
- } );
- return this;
- } );
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Buttons
- */
- function i18n( label, def ) {
- return function (dt) {
- return dt.i18n( 'buttons.'+label, def );
- };
- }
- // Common events with suitable namespaces
- function namespacedEvents ( config ) {
- var unique = config._eventNamespace;
- return 'draw.dt.DT'+unique+' select.dt.DT'+unique+' deselect.dt.DT'+unique;
- }
- function enabled ( dt, config ) {
- if ( $.inArray( 'rows', config.limitTo ) !== -1 && dt.rows( { selected: true } ).any() ) {
- return true;
- }
- if ( $.inArray( 'columns', config.limitTo ) !== -1 && dt.columns( { selected: true } ).any() ) {
- return true;
- }
- if ( $.inArray( 'cells', config.limitTo ) !== -1 && dt.cells( { selected: true } ).any() ) {
- return true;
- }
- return false;
- }
- var _buttonNamespace = 0;
- $.extend( DataTable.ext.buttons, {
- selected: {
- text: i18n( 'selected', 'Selected' ),
- className: 'buttons-selected',
- limitTo: [ 'rows', 'columns', 'cells' ],
- init: function ( dt, node, config ) {
- var that = this;
- config._eventNamespace = '.select'+(_buttonNamespace++);
- // .DT namespace listeners are removed by DataTables automatically
- // on table destroy
- dt.on( namespacedEvents(config), function () {
- that.enable( enabled(dt, config) );
- } );
- this.disable();
- },
- destroy: function ( dt, node, config ) {
- dt.off( config._eventNamespace );
- }
- },
- selectedSingle: {
- text: i18n( 'selectedSingle', 'Selected single' ),
- className: 'buttons-selected-single',
- init: function ( dt, node, config ) {
- var that = this;
- config._eventNamespace = '.select'+(_buttonNamespace++);
- dt.on( namespacedEvents(config), function () {
- var count = dt.rows( { selected: true } ).flatten().length +
- dt.columns( { selected: true } ).flatten().length +
- dt.cells( { selected: true } ).flatten().length;
- that.enable( count === 1 );
- } );
- this.disable();
- },
- destroy: function ( dt, node, config ) {
- dt.off( config._eventNamespace );
- }
- },
- selectAll: {
- text: i18n( 'selectAll', 'Select all' ),
- className: 'buttons-select-all',
- action: function () {
- var items = this.select.items();
- this[ items+'s' ]().select();
- }
- },
- selectNone: {
- text: i18n( 'selectNone', 'Deselect all' ),
- className: 'buttons-select-none',
- action: function () {
- clear( this.settings()[0], true );
- },
- init: function ( dt, node, config ) {
- var that = this;
- config._eventNamespace = '.select'+(_buttonNamespace++);
- dt.on( namespacedEvents(config), function () {
- var count = dt.rows( { selected: true } ).flatten().length +
- dt.columns( { selected: true } ).flatten().length +
- dt.cells( { selected: true } ).flatten().length;
- that.enable( count > 0 );
- } );
- this.disable();
- },
- destroy: function ( dt, node, config ) {
- dt.off( config._eventNamespace );
- }
- }
- } );
- $.each( [ 'Row', 'Column', 'Cell' ], function ( i, item ) {
- var lc = item.toLowerCase();
- DataTable.ext.buttons[ 'select'+item+'s' ] = {
- text: i18n( 'select'+item+'s', 'Select '+lc+'s' ),
- className: 'buttons-select-'+lc+'s',
- action: function () {
- this.select.items( lc );
- },
- init: function ( dt ) {
- var that = this;
- dt.on( 'selectItems.dt.DT', function ( e, ctx, items ) {
- that.active( items === lc );
- } );
- }
- };
- } );
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Initialisation
- */
- // DataTables creation - check if select has been defined in the options. Note
- // this required that the table be in the document! If it isn't then something
- // needs to trigger this method unfortunately. The next major release of
- // DataTables will rework the events and address this.
- $(document).on( 'preInit.dt.dtSelect', function (e, ctx) {
- if ( e.namespace !== 'dt' ) {
- return;
- }
- DataTable.select.init( new DataTable.Api( ctx ) );
- } );
- return DataTable.select;
- }));
- /*! Bootstrap 4 styling wrapper for Select
- * ©2018 SpryMedia Ltd - datatables.net/license
- */
- (function( factory ){
- if ( typeof define === 'function' && define.amd ) {
- // AMD
- define( ['jquery', 'datatables.net-bs4', 'datatables.net-select'], function ( $ ) {
- return factory( $, window, document );
- } );
- }
- else if ( typeof exports === 'object' ) {
- // CommonJS
- module.exports = function (root, $) {
- if ( ! root ) {
- root = window;
- }
- if ( ! $ || ! $.fn.dataTable ) {
- $ = require('datatables.net-bs4')(root, $).$;
- }
- if ( ! $.fn.dataTable.select ) {
- require('datatables.net-select')(root, $);
- }
- return factory( $, root, root.document );
- };
- }
- else {
- // Browser
- factory( jQuery, window, document );
- }
- }(function( $, window, document, undefined ) {
- return $.fn.dataTable;
- }));
- /**
- * @summary altEditor
- * @description Lightweight editor for DataTables
- * @version 3.0
- * @copyright Copyright 2016 Kingkode & Gotbootstrap.com
- */
- (function (factory) {
- if (typeof define === 'function' && define.amd) {
- // AMD
- define(['jquery', 'datatables.net'], function ($) {
- return factory($, window, document);
- });
- }
- else if (typeof exports === 'object') {
- // CommonJS
- module.exports = function (root, $) {
- if (!root) {
- root = window;
- }
- if (!$ || !$.fn.dataTable) {
- $ = require('datatables.net')(root, $).$;
- }
- return factory($, root, root.document);
- };
- }
- else {
- // Browser
- factory(jQuery, window, document);
- }
- })
- (function ($, window, document, undefined) {
- 'use strict';
- var DataTable = $.fn.dataTable;
- var _instance = 0;
- /**
- * altEditor provides modal editing of records for Datatables
- *
- * @class altEditor
- * @constructor
- * @param {object}
- * oTD DataTables settings object
- * @param {object}
- * oConfig Configuration object for altEditor
- */
- var altEditor = function (dt, opts) {
- if (!DataTable.versionCheck || !DataTable.versionCheck('1.10.8')) {
- throw ("Warning: altEditor requires DataTables 1.10.8 or greater");
- }
- // User and defaults configuration object
- this.c = $.extend(true, {}, DataTable.defaults.altEditor,
- altEditor.defaults, opts);
- /**
- * @namespace Settings object which contains customisable information
- * for altEditor instance
- */
- this.s = {
- /** @type {DataTable.Api} DataTables' API instance */
- dt: new DataTable.Api(dt),
- /** @type {String} Unique namespace for events attached to the document */
- namespace: '.altEditor' + (_instance++)
- };
- /**
- * @namespace Common and useful DOM elements for the class instance
- */
- this.dom = {
- /** @type {jQuery} altEditor handle */
- modal: $('<div class="dt-altEditor-handle"/>'),
- };
- /* Constructor logic */
- this._constructor();
- }
- $.extend(
- altEditor.prototype,
- {
- /***************************************************************
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Constructor * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- */
- /**
- * Initialise the RowReorder instance
- *
- * @private
- */
- _constructor: function () {
- var that = this;
- var dt = this.s.dt;
- if (dt.settings()[0].oInit.onAddRow)
- that.onAddRow = dt.settings()[0].oInit.onAddRow;
- if (dt.settings()[0].oInit.onDeleteRow)
- that.onDeleteRow = dt.settings()[0].oInit.onDeleteRow;
- if (dt.settings()[0].oInit.onEditRow)
- that.onEditRow = dt.settings()[0].oInit.onEditRow;
- this._setup();
- dt.on('destroy.altEditor', function () {
- dt.off('.altEditor');
- $(dt.table().body()).off(that.s.namespace);
- $(document.body).off(that.s.namespace);
- });
- },
- /***************************************************************
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Private methods * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- */
- /**
- * Setup dom and bind button actions
- *
- * @private
- */
- _setup: function () {
- var that = this;
- var dt = this.s.dt;
- var modal_id = 'altEditor-modal-' + ("" + Math.random()).replace(".", "");
- this.modal_selector = '#' + modal_id;
- var modal = '<div class="modal fade" id="' + modal_id + '" tabindex="-1" role="dialog">' +
- '<div class="modal-dialog">' +
- '<div class="modal-content">' +
- '<div class="modal-header">' +
- '<h5 class="modal-title"></h5>' +
- '<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true"><i class="fal fa-times"></i></span></button>' +
- '</div>' +
- '<div class="modal-body">' +
- '<p></p>' +
- '</div>' +
- '<div class="modal-footer">' +
- '<button type="button" class="btn btn-default" data-dismiss="modal">Cancel</button>' + //FIXME need i18n
- '<input type="submit" form="altEditor-form" class="btn btn-primary"></input>' +
- '</div>' +
- '</div>' +
- '</div>' +
- '</div>';
- // add modal
- $('body').append(modal);
- // add Edit Button
- if (dt.button('edit:name')) {
- dt.button('edit:name').action(function (e, dt, node, config) {
- that._openEditModal();
- });
- $(this.modal_selector).on('click', '#editRowBtn', function (e) {
- if (that._inputValidation()) {
- e.preventDefault();
- e.stopPropagation();
- that._editRowData();
- }
- });
- }
- // add Delete Button
- if (dt.button('delete:name')) {
- dt.button('delete:name').action(function (e, dt, node, config) {
- that._openDeleteModal();
- });
- $(this.modal_selector).on('click', '#deleteRowBtn', function (e) {
- e.preventDefault();
- e.stopPropagation();
- that._deleteRow();
- $(this).prop('disabled', true);
- });
- }
- // add Add Button
- if (dt.button('add:name')) {
- dt.button('add:name').action(function (e, dt, node, config) {
- that._openAddModal();
- });
- $(this.modal_selector).on('click', '#addRowBtn', function (e) {
- if (that._inputValidation()) {
- e.preventDefault();
- e.stopPropagation();
- that._addRowData();
- }
- });
- }
- // add Refresh button
- if (this.s.dt.button('refresh:name')) {
- this.s.dt.button('refresh:name').action(function (e, dt, node, config) {
- if (dt.ajax && dt.ajax.url()) {
- dt.ajax.reload();
- }
- });
- }
- },
- /**
- * Emit an event on the DataTable for listeners
- *
- * @param {string}
- * name Event name
- * @param {array}
- * args Event arguments
- * @private
- */
- _emitEvent: function (name, args) {
- this.s.dt.iterator('table', function (ctx, i) {
- $(ctx.nTable).triggerHandler(name + '.dt', args);
- });
- },
- /**
- * Open Edit Modal for selected row
- *
- * @private
- */
- _openEditModal: function () {
- var that = this;
- var dt = this.s.dt;
- var columnDefs = [];
- // Adding column attributes to object.
- // Please set the ID as readonly.
- for (var i in dt.context[0].aoColumns) {
- var obj = dt.context[0].aoColumns[i];
- columnDefs[i] = {
- title: obj.sTitle,
- name: obj.data ? obj.data : obj.mData,
- type: (obj.type ? obj.type : 'text'),
- options: (obj.options ? obj.options : []),
- msg: (obj.errorMsg ? obj.errorMsg : ''),
- hoverMsg: (obj.hoverMsg ? obj.hoverMsg : ''),
- pattern: (obj.pattern ? obj.pattern : '.*'),
- special: (obj.special ? obj.special : ''),
- unique: (obj.unique ? obj.unique : false),
- uniqueMsg: (obj.uniqueMsg ? obj.uniqueMsg : ''),
- placeholderMsg: (obj.placeholderMsg ? obj.placeholderMsg : ''),
- maxLength: (obj.maxLength ? obj.maxLength : false),
- multiple: (obj.multiple ? obj.multiple : false),
- select2: (obj.select2 ? obj.select2 : false),
- datepicker: (obj.datepicker ? obj.datepicker : false)
- };
- }
- var adata = dt.rows({
- selected: true
- });
- // Building edit-form
- var data = "";
- data += "<form name='altEditor-form' role='form' novalidate>";
- for (var j in columnDefs) {
- // handle hidden fields
- if (columnDefs[j].type.indexOf("hidden") >= 0) {
- data += "<input type='hidden' id='" + columnDefs[j].name + "' value='" + adata.data()[0][columnDefs[j].name] + "'></input>";
- }
- else {
- // handle fields that are visible to the user
- data += "<div class='form-group row'>"
- data += "<label for='" + columnDefs[j].name + "' class='col-lg-3 col-form-label form-label text-left text-lg-right'>" + columnDefs[j].title + ":</label>"
- data += "<div class='col-lg-9'>";
- // Adding text-inputs and errorlabels
- if (columnDefs[j].type.indexOf("text") >= 0 ||
- columnDefs[j].type.indexOf("date") >= 0 ||
- columnDefs[j].type.indexOf("email") >= 0 ||
- columnDefs[j].type.indexOf("password") >= 0 ||
- columnDefs[j].type.indexOf("number") >= 0 ||
- columnDefs[j].type.indexOf("time") >= 0) {
- data += "<input type='"
- + that._quoteattr(columnDefs[j].type)
- + "' id='"
- + that._quoteattr(columnDefs[j].name)
- + "' pattern='"
- + that._quoteattr(columnDefs[j].pattern)
- + "' title='"
- + that._quoteattr(columnDefs[j].hoverMsg)
- + "' name='"
- + that._quoteattr(columnDefs[j].title)
- + "' placeholder='"
- + that._quoteattr(columnDefs[j].placeholderMsg)
- + "' data-special='"
- + that._quoteattr(columnDefs[j].special)
- + "' data-errorMsg='"
- + that._quoteattr(columnDefs[j].msg)
- + "' data-uniqueMsg='"
- + that._quoteattr(columnDefs[j].uniqueMsg)
- + "' data-unique='"
- + columnDefs[j].unique
- + "'"
- + (columnDefs[j].maxLength == false ? "" : " maxlength='" + columnDefs[j].maxLength + "'")
- + " style='overflow:hidden' class='form-control' value='"
- + that._quoteattr(adata.data()[0][columnDefs[j].name]) + "'>";
- data += "<label id='" + columnDefs[j].name + "label"
- + "' class='errorLabel help-block text-danger' style='display: none'></label>";
- }
- // Adding readonly-fields
- if (columnDefs[j].type.indexOf("readonly") >= 0) {
- data += "<input type='text' readonly id='"
- + columnDefs[j].name
- + "' name='"
- + columnDefs[j].title
- + "' placeholder='"
- + columnDefs[j].placeholderMsg
- + "' style='overflow:hidden' class='form-control' value='"
- + adata.data()[0][columnDefs[j].name] + "'>";
- }
- // Adding select-fields
- if (columnDefs[j].type.indexOf("select") >= 0) {
- var options = "";
- for (var i = 0; i < columnDefs[j].options.length; i++) {
- // Assigning the selected value of the <selected> option
- if (adata.data()[0][columnDefs[j].name]
- .indexOf(columnDefs[j].options[i])>= 0) {
- options += "<option value='"
- + columnDefs[j].options[i] + "'selected>"
- + columnDefs[j].options[i] + "</option>";
- } else {
- options += "<option value='"
- + columnDefs[j].options[i] + "'>"
- + columnDefs[j].options[i] + "</option>";
- }
- }
- data += "<select class='custom-select form-control" + (columnDefs[j].select2 ? ' select2' : '') + "' id='" + columnDefs[j].name + "' name='" + columnDefs[j].title + "' " + (columnDefs[j].multiple ? 'multiple' : '') + ">" + options
- + "</select>";
- }
- data += "</div><div style='clear:both;'></div></div>";
- }
- }
- // close form
- data += "</form>";
- var selector = this.modal_selector;
- $(selector).on('show.bs.modal', function () {
- var btns = '<button type="button" data-content="remove" class="btn btn-default" data-dismiss="modal">Cancel</button>' +
- '<button type="button" data-content="remove" class="btn btn-primary" id="editRowBtn">Save</button>';
- $(selector).find('.modal-title').html('Edit Record');
- $(selector).find('.modal-body').html(data);
- $(selector).find('.modal-footer').html(btns);
- });
- $(selector).modal('show');
- $(selector + ' input[0]').focus();
- // enable select 2 items
- for (var j in columnDefs) {
- if (columnDefs[j].select2) {
- // require select2
- $(selector).find("select#" + columnDefs[j].name).select2(columnDefs[j].select2);
- }
- }
- // enable datepicker
- for (var j in columnDefs) {
- if (columnDefs[j].datepicker) {
- // Require jquery-ui
- $(selector).find("#" + columnDefs[j].name).datepicker(columnDefs[j].datepicker);
- }
- }
- },
- /**
- * Callback for "Edit" button
- */
- _editRowData: function () {
- var that = this;
- var dt = this.s.dt;
- // Complete new row data
- var rowDataArray = {};
- var adata = dt.rows({
- selected: true
- });
- // Getting the inputs from the edit-modal
- $('form[name="altEditor-form"] *').filter(':input').each(function (i) {
- rowDataArray[$(this).attr('id')] = $(this).val();
- });
- console.log(rowDataArray); //DEBUG
- that.onEditRow(that,
- rowDataArray,
- function(data,b,c,d,e){ that._editRowCallback(data,b,c,d,e); },
- function(data){ that._errorCallback(data);
- });
- },
- /**
- * Open Delete Modal for selected row
- *
- * @private
- */
- _openDeleteModal: function () {
- var that = this;
- var dt = this.s.dt;
- var columnDefs = [];
- // Adding attribute IDs and values to object
- for (var i in dt.context[0].aoColumns) {
- columnDefs.push({
- title: dt.context[0].aoColumns[i].sTitle,
- type: (dt.context[0].aoColumns[i].type ? dt.context[0].aoColumns[i].type : 'text'),
- name: dt.context[0].aoColumns[i].data ? dt.context[0].aoColumns[i].data : dt.context[0].aoColumns[i].mData
- });
- }
- var adata = dt.rows({
- selected: true
- });
- // Building delete-modal
- var data = "";
- data += "<form name='altEditor-form' role='form'><div class='row'>";
- for (var j in columnDefs) {
- if (columnDefs[j].type.indexOf("hidden") >= 0) {
- data += "<input type='hidden' id='" + columnDefs[j].title + "' value='" + adata.data()[0][columnDefs[j].name] + "' readonly></input>";
- }
- else {
- data += "<div class='col-12 mb-2'><label class='form-label' for='"
- + that._quoteattr(columnDefs[j].name)
- + "'>"
- + columnDefs[j].title
- + ": </label> <input type='hidden' id='"
- + that._quoteattr(columnDefs[j].title)
- + "' name='"
- + that._quoteattr(columnDefs[j].title)
- + "' placeholder='"
- + that._quoteattr(columnDefs[j].placeholderMsg)
- + "' style='overflow:hidden' class='form-control' value='"
- + that._quoteattr(adata.data()[0][columnDefs[j].name]) + "' >"
- + adata.data()[0][columnDefs[j].name]
- + "</input></div>";
- }
- }
- // close the form
- data += "</div></form>";
- var selector = this.modal_selector;
- $(selector).on('show.bs.modal', function () {
- var btns = '<button type="button" data-content="remove" class="btn btn-default" data-dismiss="modal">Cancel</button>' +
- '<button type="button" data-content="remove" class="btn btn-danger" id="deleteRowBtn"><i class="fal fa-times"></i> Delete</button>';
- $(selector).find('.modal-title').html('Delete Record');
- $(selector).find('.modal-body').html(data);
- $(selector).find('.modal-footer').html(btns);
- });
- $(selector).modal('show');
- $(selector + ' input[0]').focus();
- },
- /**
- * Callback for "Delete" button
- */
- _deleteRow: function () {
- var that = this;
- var dt = this.s.dt;
- var jsonDataArray = {};
- var adata = dt.rows({
- selected: true
- });
- // Getting the IDs and Values of the tablerow
- for (var i = 0; i < dt.context[0].aoColumns.length; i++) {
- // .data is the attribute name, if any; .idx is the column index, so it should always exists
- var name = dt.context[0].aoColumns[i].data ? dt.context[0].aoColumns[i].data :
- dt.context[0].aoColumns[i].mData ? dt.context[0].aoColumns[i].mData :
- dt.context[0].aoColumns[i].idx;
- jsonDataArray[name] = adata.data()[0][name];
- }
- that.onDeleteRow(that,
- jsonDataArray,
- function(data){ that._deleteRowCallback(data); },
- function(data){ that._errorCallback(data);
- });
- },
- /**
- * Open Add Modal for selected row
- *
- * @private
- */
- _openAddModal: function () {
- var that = this;
- var dt = this.s.dt;
- var columnDefs = [];
- // Adding column attributes to object.
- for (var i in dt.context[0].aoColumns) {
- var obj = dt.context[0].aoColumns[i];
- columnDefs[i] = {
- title: obj.sTitle,
- name: (obj.data ? obj.data : obj.mData),
- type: (obj.type ? obj.type : 'text'),
- defaultValue: (obj.defaultValue ? obj.defaultValue : ''),
- options: (obj.options ? obj.options : []),
- msg: (obj.errorMsg ? obj.errorMsg : ''),
- hoverMsg: (obj.hoverMsg ? obj.hoverMsg : ''),
- pattern: (obj.pattern ? obj.pattern : '.*'),
- special: (obj.special ? obj.special : ''),
- placeholderMsg: (obj.placeholderMsg ? obj.placeholderMsg : ''),
- unique: (obj.unique ? obj.unique : false),
- uniqueMsg: (obj.uniqueMsg ? obj.uniqueMsg : ''),
- maxLength: (obj.maxLength ? obj.maxLength : false),
- multiple: (obj.multiple ? obj.multiple : false),
- select2: (obj.select2 ? obj.select2 : false)
- }
- }
- // Building add-form
- var data = "";
- data += "<form name='altEditor-form' role='form' class='needs-validation' novalidate=''>";
- for (var j in columnDefs) {
- if (columnDefs[j].type.indexOf("hidden") >= 0) {
- // just do nothing for hidden fields!
- }
- else {
- data += "<div class='form-group row'>"
- + "<label class='form-label col-sm-3 col-form-label text-left text-sm-right' for='"
- + columnDefs[j].name
- + "'>"
- + columnDefs[j].title
- + ":</label><div class='col-lg-9'>";
- // Adding text-inputs and errorlabels
- if (columnDefs[j].type.indexOf("text") >= 0 ||
- columnDefs[j].type.indexOf("date") >= 0 ||
- columnDefs[j].type.indexOf("email") >= 0 ||
- columnDefs[j].type.indexOf("password") >= 0 ||
- columnDefs[j].type.indexOf("number") >= 0 ||
- columnDefs[j].type.indexOf("time") >= 0) {
- data += "<input type='"
- + that._quoteattr(columnDefs[j].type)
- + "' id='"
- + that._quoteattr(columnDefs[j].name)
- + "' pattern='"
- + that._quoteattr(columnDefs[j].pattern)
- + "' title='"
- + that._quoteattr(columnDefs[j].hoverMsg)
- + "' name='"
- + that._quoteattr(columnDefs[j].title)
- + "' placeholder='"
- + that._quoteattr(columnDefs[j].placeholderMsg)
- + "' value='"
- + that._quoteattr(columnDefs[j].defaultValue)
- + "' data-special='"
- + columnDefs[j].special
- + "' data-errorMsg='"
- + that._quoteattr(columnDefs[j].msg)
- + "' data-uniqueMsg='"
- + that._quoteattr(columnDefs[j].uniqueMsg)
- + "' data-unique='"
- + columnDefs[j].unique
- + "'"
- + (columnDefs[j].maxLength == false ? "" : " maxlength='" + columnDefs[j].maxLength + "'")
- + " style='overflow:hidden' class='form-control' value=''>";
- data += "<label id='" + that._quoteattr(columnDefs[j].name) + "label"
- + "' class='errorLabel help-block text-danger' style='display:none'></label>";
- }
- // Adding readonly-fields
- if (columnDefs[j].type.indexOf("readonly") >= 0) {
- data += "<input type='text' readonly id='"
- + that._quoteattr(columnDefs[j].name)
- + "' name='"
- + that._quoteattr(columnDefs[j].title)
- + "' placeholder='"
- + that._quoteattr(columnDefs[j].placeholderMsg)
- + "' style='overflow:hidden' class='form-control' value=''>";
- }
- // Adding select-fields
- if (columnDefs[j].type.indexOf("select") >= 0) {
- var options = "";
- for (var i = 0; i < columnDefs[j].options.length; i++) {
- options += "<option value='" + that._quoteattr(columnDefs[j].options[i])
- + "'>" + columnDefs[j].options[i] + "</option>";
- }
- data += "<select class='custom-select form-control" + (columnDefs[j].select2 ? ' select2' : '') + "' id='" + that._quoteattr(columnDefs[j].name) + "' name='" + that._quoteattr(columnDefs[j].title) + "' " + (columnDefs[j].multiple ? 'multiple' : '') + ">" + options
- + "</select>";
- }
- data += "</div><div style='clear:both;'></div></div>";
- }
- }
- data += "</form>";
- var selector = this.modal_selector;
- $(selector).on('show.bs.modal', function () {
- var btns = '<button type="button" data-content="remove" class="btn btn-default" data-dismiss="modal">Cancel</button>' +
- '<button type="button" data-content="remove" class="btn btn-primary" id="addRowBtn">Add</button>';
- $(selector).find('.modal-title').html(
- 'Add Record');
- $(selector).find('.modal-body')
- .html(data);
- $(selector)
- .find('.modal-footer')
- .html(btns);
- });
- $(selector).modal('show');
- $(selector + ' input[0]').focus();
- // enable select 2 items
- for (var j in columnDefs) {
- if (columnDefs[j].select2) {
- $(selector).find("select#" + columnDefs[j].name).select2(columnDefs[j].select2);
- }
- }
- },
- /**
- * Callback for "Add" button
- */
- _addRowData: function () {
- var that = this;
- var dt = this.s.dt;
- var rowDataArray = {};
- // Getting the inputs from the modal
- $('form[name="altEditor-form"] *').filter(':input').each(function (i) {
- rowDataArray[$(this).attr('id')] = $(this).val();
- });
- //console.log(rowDataArray); //DEBUG
- that.onAddRow(that,
- rowDataArray,
- function(data){ that._addRowCallback(data); },
- function(data){ that._errorCallback(data);
- });
- },
- /**
- * Called after a row has been deleted on server
- */
- _deleteRowCallback: function (response, status, more) {
- var selector = this.modal_selector;
- $(selector + ' .modal-body .alert').remove();
- var message = '<div class="alert alert-success" role="alert">' +
- '<strong>Success!</strong>' +
- '</div>';
- $(selector + ' .modal-body').append(message);
- this.s.dt.row({
- selected : true
- }).remove();
- this.s.dt.draw();
- // Disabling submit button
- $("div"+selector).find("button#addRowBtn").prop('disabled', true);
- $("div"+selector).find("button#editRowBtn").prop('disabled', true);
- $("div"+selector).find("button#deleteRowBtn").prop('disabled', true);
- //hide modal
- //$(this.modal_selector).modal('hide');
- $(selector).modal('hide');
- },
- /**
- * Called after a row has been inserted on server
- */
- _addRowCallback: function (response, status, more) {
-
- //TODO should honor dt.ajax().dataSrc
-
- var data = (typeof response === "string") ? JSON.parse(response) : response;
- var selector = this.modal_selector;
- $(selector + ' .modal-body .alert').remove();
- var message = '<div class="alert alert-success" role="alert">' +
- '<strong>Success!</strong>' +
- '</div>';
- $(selector + ' .modal-body').append(message);
- this.s.dt.row.add(data).draw(false);
- // Disabling submit button
- $("div" + selector).find("button#addRowBtn").prop('disabled', true);
- $("div" + selector).find("button#editRowBtn").prop('disabled', true);
- $("div" + selector).find("button#deleteRowBtn").prop('disabled', true);
- //hide modal
- //$(this.modal_selector).modal('hide');
- $(selector).modal('hide');
- },
- /**
- * Called after a row has been updated on server
- */
- _editRowCallback: function (response, status, more) {
- //TODO should honor dt.ajax().dataSrc
-
- var data = (typeof response === "string") ? JSON.parse(response) : response;
- var selector = this.modal_selector;
- $(selector + ' .modal-body .alert').remove();
- var message = '<div class="alert alert-success" role="alert">' +
- '<strong>Success!</strong>' +
- '</div>';
- $(selector + ' .modal-body').prepend(message);
- this.s.dt.row({
- selected : true
- }).data(data);
- this.s.dt.draw();
- // Disabling submit button
- $("div" + selector).find("button#addRowBtn").prop('disabled', true);
- $("div" + selector).find("button#editRowBtn").prop('disabled', true);
- $("div" + selector).find("button#deleteRowBtn").prop('disabled', true);
- //hide modal
- //$(this.modal_selector).modal('hide');
- $(selector).modal('hide');
- },
- /**
- * Called after AJAX server returned an error
- */
- _errorCallback: function (response, status, more) {
- var error = response;
- var selector = this.modal_selector;
- $(selector + ' .modal-body .alert').remove();
- var errstr = "There was an unknown error!";
- if (error.responseJSON && error.responseJSON.errors) {
- errstr = "";
- for (var key in error.responseJSON.errors) {
- errstr += error.responseJSON.errors[key][0];
- }
- }
- var message = '<div class="alert alert-danger" role="alert">' +
- '<strong>Error!</strong> ' + (error.status == null ? "" : 'Response code: ' + error.status) + " " + errstr +
- '</div>';
- $(selector + ' .modal-body').append(message);
- },
-
- /**
- * Default callback for insertion: mock webservice, always success.
- */
- onAddRow: function(dt, rowdata, success, error) {
- console.log("Missing AJAX configuration for INSERT");
- success(rowdata);
- },
- /**
- * Default callback for editing: mock webservice, always success.
- */
- onEditRow: function(dt, rowdata, success, error) {
- console.log("Missing AJAX configuration for UPDATE");
- success(rowdata);
- },
- /**
- * Default callback for deletion: mock webservice, always success.
- */
- onDeleteRow: function(dt, rowdata, success, error) {
- console.log("Missing AJAX configuration for DELETE");
- success(rowdata);
- },
- /**
- * Validates input
- * @returns {boolean}
- * @private
- */
- _inputValidation: function () {
- var that = this;
- var dt = this.s.dt;
- var isValid = false;
- var errorcount = 0;
- // Looping through all text fields
- $('form[name="altEditor-form"] *').filter(':text').each(
- function (i) {
- var errorLabel = "#" + $(this).attr("id") + "label";
- // reset error display
- $(errorLabel).hide();
- $(errorLabel).empty();
- if (!$(this).val().match($(this).attr("pattern"))) {
- $(errorLabel).html($(this).attr("data-errorMsg"));
- $(errorLabel).show();
- errorcount++;
- }
- // now check if its should be unique
- else if ($(this).attr("data-unique") == "true") {
- // go through each item in this column
- var colData = dt.column("th:contains('" + $(this).attr("name") + "')").data();
- var selectedCellData = null;
- if (dt.row({selected: true}).index() != null)
- selectedCellData = dt.cell(dt.row({selected: true}).index(), dt.column("th:contains('" + $(this).attr("name") + "')").index()).data();
- for (var j in colData) {
- // if the element is in the column and its not the selected one then its not unique
- if ($(this).val() == colData[j] && colData[j] != selectedCellData) {
- $(errorLabel).html($(this).attr("data-uniqueMsg"));
- $(errorLabel).show();
- errorcount++;
- }
- }
- }
- });
- if (errorcount == 0) {
- isValid = true;
- }
- return isValid;
- },
- /**
- * Sanitizes input for use in HTML
- * @param s
- * @param preserveCR
- * @returns {string}
- * @private
- */
- _quoteattr: function (s, preserveCR) {
- if (s == null)
- return "";
- preserveCR = preserveCR ? ' ' : '\n';
- return ('' + s) /* Forces the conversion to string. */
- .replace(/&/g, '&') /* This MUST be the 1st replacement. */
- .replace(/'/g, ''') /* The 4 other predefined entities, required. */
- .replace(/"/g, '"')
- .replace(/</g, '<')
- .replace(/>/g, '>')
- .replace(/\r\n/g, preserveCR) /* Must be before the next replacement. */
- .replace(/[\r\n]/g, preserveCR);
- },
- });
- /**
- * altEditor version
- *
- * @static
- * @type String
- */
- altEditor.version = '2.0';
- /**
- * altEditor defaults
- *
- * @namespace
- */
- altEditor.defaults = {
- /**
- * @type {Boolean} Ask user what they want to do, even for a single
- * option
- */
- alwaysAsk: false,
- /** @type {string|null} What will trigger a focus */
- focus: null, // focus, click, hover
- /** @type {column-selector} Columns to provide auto fill for */
- columns: '', // all
- /** @type {boolean|null} Update the cells after a drag */
- update: null, // false is editor given, true otherwise
- /** @type {DataTable.Editor} Editor instance for automatic submission */
- editor: null
- };
- /**
- * Classes used by altEditor that are configurable
- *
- * @namespace
- */
- altEditor.classes = {
- /** @type {String} Class used by the selection button */
- btn: 'btn'
- };
- // Attach a listener to the document which listens for DataTables
- // initialisation
- // events so we can automatically initialise
- $(document).on('preInit.dt.altEditor', function (e, settings, json) {
- if (e.namespace !== 'dt') {
- return;
- }
- var init = settings.oInit.altEditor;
- var defaults = DataTable.defaults.altEditor;
- if (init || defaults) {
- var opts = $.extend({}, init, defaults);
- if (init !== false) {
- new altEditor(settings, opts);
- }
- }
- });
- // Alias for access
- DataTable.altEditor = altEditor;
- return altEditor;
- });
|