dataTables.editor.js 225 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844
  1. /*! DataTables Editor v1.5.6
  2. *
  3. * ©2012-2016 SpryMedia Ltd, all rights reserved.
  4. * License: editor.datatables.net/license
  5. */
  6. /**
  7. * @summary DataTables Editor
  8. * @description Table editing library for DataTables
  9. * @version 1.5.6
  10. * @file dataTables.editor.js
  11. * @author SpryMedia Ltd
  12. * @contact www.datatables.net/contact
  13. */
  14. /*jslint evil: true, undef: true, browser: true */
  15. /*globals jQuery,alert,console */
  16. (function( factory ){
  17. if ( typeof define === 'function' && define.amd ) {
  18. // AMD
  19. define( ['jquery', 'datatables.net'], function ( $ ) {
  20. return factory( $, window, document );
  21. } );
  22. }
  23. else if ( typeof exports === 'object' ) {
  24. // CommonJS
  25. module.exports = function (root, $) {
  26. if ( ! root ) {
  27. root = window;
  28. }
  29. if ( ! $ || ! $.fn.dataTable ) {
  30. $ = require('datatables.net')(root, $).$;
  31. }
  32. return factory( $, root, root.document );
  33. };
  34. }
  35. else {
  36. // Browser
  37. factory( jQuery, window, document );
  38. }
  39. }(function( $, window, document, undefined ) {
  40. 'use strict';
  41. var DataTable = $.fn.dataTable;
  42. if ( ! DataTable || ! DataTable.versionCheck || ! DataTable.versionCheck('1.10.7') ) {
  43. throw 'Editor requires DataTables 1.10.7 or newer';
  44. }
  45. /**
  46. * Editor is a plug-in for <a href="http://datatables.net">DataTables</a> which
  47. * provides an interface for creating, reading, editing and deleting and entries
  48. * (a CRUD interface) in a DataTable. The documentation presented here is
  49. * primarily focused on presenting the API for Editor. For a full list of
  50. * features, examples and the server interface protocol, please refer to the <a
  51. * href="http://editor.datatables.net">Editor web-site</a>.
  52. *
  53. * Note that in this documentation, for brevity, the `DataTable` refers to the
  54. * jQuery parameter `jQuery.fn.dataTable` through which it may be accessed.
  55. * Therefore, when creating a new Editor instance, use `jQuery.fn.Editor` as
  56. * shown in the examples below.
  57. *
  58. * @class
  59. * @param {object} [oInit={}] Configuration object for Editor. Options
  60. * are defined by {@link Editor.defaults}.
  61. * @requires jQuery 1.7+
  62. * @requires DataTables 1.10+
  63. */
  64. var Editor = function ( opts )
  65. {
  66. if ( ! this instanceof Editor ) {
  67. alert( "DataTables Editor must be initialised as a 'new' instance'" );
  68. }
  69. this._constructor( opts );
  70. };
  71. // Export Editor as a DataTables property
  72. DataTable.Editor = Editor;
  73. $.fn.DataTable.Editor = Editor;
  74. // Internal methods
  75. /**
  76. * Get an Editor node based on the data-dte-e (element) attribute and return it
  77. * as a jQuery object.
  78. * @param {string} dis The data-dte-e attribute name to match for the element
  79. * @param {node} [ctx=document] The context for the search - recommended this
  80. * parameter is included for performance.
  81. * @returns {jQuery} jQuery object of found node(s).
  82. * @private
  83. */
  84. var _editor_el = function ( dis, ctx )
  85. {
  86. if ( ctx === undefined ) {
  87. ctx = document;
  88. }
  89. return $('*[data-dte-e="'+dis+'"]', ctx);
  90. };
  91. /** @internal Counter for unique event namespaces in the inline control */
  92. var __inlineCounter = 0;
  93. var _pluck = function ( a, prop )
  94. {
  95. var out = [];
  96. $.each( a, function ( idx, el ) {
  97. out.push( el[ prop ] );
  98. } );
  99. return out;
  100. };
  101. // Field class
  102. Editor.Field = function ( opts, classes, host ) {
  103. var that = this;
  104. var multiI18n = host.i18n.multi;
  105. opts = $.extend( true, {}, Editor.Field.defaults, opts );
  106. if ( ! Editor.fieldTypes[ opts.type ] ) {
  107. throw "Error adding field - unknown field type "+opts.type;
  108. }
  109. this.s = $.extend( {}, Editor.Field.settings, { // has to be a shallow copy!
  110. type: Editor.fieldTypes[ opts.type ],
  111. name: opts.name,
  112. classes: classes,
  113. host: host,
  114. opts: opts,
  115. multiValue: false
  116. } );
  117. // No id, so assign one to have the label reference work
  118. if ( ! opts.id ) {
  119. opts.id = 'DTE_Field_'+opts.name;
  120. }
  121. // Backwards compatibility
  122. if ( opts.dataProp ) {
  123. opts.data = opts.dataProp;
  124. }
  125. // If no `data` option is given, then we use the name from the field as the
  126. // data prop to read data for the field from DataTables
  127. if ( opts.data === '' ) {
  128. opts.data = opts.name;
  129. }
  130. // Get and set functions in the data object for the record
  131. var dtPrivateApi = DataTable.ext.oApi;
  132. this.valFromData = function ( d ) { // get val from data
  133. // wrapper to automatically pass `editor` as the type
  134. return dtPrivateApi._fnGetObjectDataFn( opts.data )( d, 'editor' );
  135. };
  136. this.valToData = dtPrivateApi._fnSetObjectDataFn( opts.data ); // set val to data
  137. // Field HTML structure
  138. var template = $(
  139. '<div class="'+classes.wrapper+' '+classes.typePrefix+opts.type+' '+classes.namePrefix+opts.name+' '+opts.className+'">'+
  140. '<label data-dte-e="label" class="'+classes.label+'" for="'+opts.id+'">'+
  141. opts.label+
  142. '<div data-dte-e="msg-label" class="'+classes['msg-label']+'">'+opts.labelInfo+'</div>'+
  143. '</label>'+
  144. '<div data-dte-e="input" class="'+classes.input+'">'+
  145. // Field specific HTML is added here if there is any
  146. '<div data-dte-e="input-control" class="'+classes.inputControl+'"/>'+
  147. '<div data-dte-e="multi-value" class="'+classes.multiValue+'">'+
  148. multiI18n.title+
  149. '<span data-dte-e="multi-info" class="'+classes.multiInfo+'">'+
  150. multiI18n.info+
  151. '</span>'+
  152. '</div>'+
  153. '<div data-dte-e="msg-multi" class="'+classes.multiRestore+'">'+
  154. multiI18n.restore+
  155. '</div>'+
  156. '<div data-dte-e="msg-error" class="'+classes['msg-error']+'"></div>'+
  157. '<div data-dte-e="msg-message" class="'+classes['msg-message']+'"></div>'+
  158. '<div data-dte-e="msg-info" class="'+classes['msg-info']+'">'+opts.fieldInfo+'</div>'+
  159. '</div>'+
  160. '</div>');
  161. var input = this._typeFn( 'create', opts );
  162. if ( input !== null ) {
  163. _editor_el('input-control', template).prepend( input );
  164. }
  165. else {
  166. template.css('display', "none");
  167. }
  168. this.dom = $.extend( true, {}, Editor.Field.models.dom, {
  169. container: template,
  170. inputControl: _editor_el('input-control', template),
  171. label: _editor_el('label', template),
  172. fieldInfo: _editor_el('msg-info', template),
  173. labelInfo: _editor_el('msg-label', template),
  174. fieldError: _editor_el('msg-error', template),
  175. fieldMessage: _editor_el('msg-message', template),
  176. multi: _editor_el('multi-value', template),
  177. multiReturn: _editor_el('msg-multi', template),
  178. multiInfo: _editor_el('multi-info', template)
  179. } );
  180. // On click - set a common value for the field
  181. this.dom.multi.on( 'click', function () {
  182. that.val('');
  183. } );
  184. this.dom.multiReturn.on( 'click', function () {
  185. that.s.multiValue = true;
  186. that._multiValueCheck();
  187. } );
  188. // Field type extension methods - add a method to the field for the public
  189. // methods that each field type defines beyond the default ones that already
  190. // exist as part of this instance
  191. $.each( this.s.type, function ( name, fn ) {
  192. if ( typeof fn === 'function' && that[name] === undefined ) {
  193. that[ name ] = function () {
  194. var args = Array.prototype.slice.call( arguments );
  195. args.unshift( name );
  196. var ret = that._typeFn.apply( that, args );
  197. // Return the given value if there is one, or the field instance
  198. // for chaining if there is no value
  199. return ret === undefined ?
  200. that :
  201. ret;
  202. };
  203. }
  204. } );
  205. };
  206. Editor.Field.prototype = {
  207. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  208. * Public
  209. */
  210. def: function ( set ) {
  211. var opts = this.s.opts;
  212. if ( set === undefined ) {
  213. // Backwards compat
  214. var def = opts['default'] !== undefined ?
  215. opts['default'] :
  216. opts.def;
  217. return $.isFunction( def ) ?
  218. def() :
  219. def;
  220. }
  221. opts.def = set;
  222. return this;
  223. },
  224. disable: function () {
  225. this._typeFn( 'disable' );
  226. return this;
  227. },
  228. displayed: function () {
  229. var container = this.dom.container;
  230. return container.parents('body').length && container.css('display') != 'none' ?
  231. true :
  232. false;
  233. },
  234. enable: function () {
  235. this._typeFn( 'enable' );
  236. return this;
  237. },
  238. error: function ( msg, fn ) {
  239. var classes = this.s.classes;
  240. // Add or remove the error class
  241. if ( msg ) {
  242. this.dom.container.addClass( classes.error );
  243. }
  244. else {
  245. this.dom.container.removeClass( classes.error );
  246. }
  247. return this._msg( this.dom.fieldError, msg, fn );
  248. },
  249. isMultiValue: function () {
  250. return this.s.multiValue;
  251. },
  252. inError: function () {
  253. return this.dom.container.hasClass( this.s.classes.error );
  254. },
  255. input: function () {
  256. return this.s.type.input ?
  257. this._typeFn( 'input' ) :
  258. $('input, select, textarea', this.dom.container);
  259. },
  260. focus: function () {
  261. if ( this.s.type.focus ) {
  262. this._typeFn( 'focus' );
  263. }
  264. else {
  265. $('input, select, textarea', this.dom.container).focus();
  266. }
  267. return this;
  268. },
  269. get: function () {
  270. // When multi-value a single get is undefined
  271. if ( this.isMultiValue() ) {
  272. return undefined;
  273. }
  274. var val = this._typeFn( 'get' );
  275. return val !== undefined ?
  276. val :
  277. this.def();
  278. },
  279. hide: function ( animate ) {
  280. var el = this.dom.container;
  281. if ( animate === undefined ) {
  282. animate = true;
  283. }
  284. if ( this.s.host.display() && animate ) {
  285. el.slideUp();
  286. }
  287. else {
  288. el.css( 'display', 'none' );
  289. }
  290. return this;
  291. },
  292. label: function ( str ) {
  293. var label = this.dom.label;
  294. if ( str === undefined ) {
  295. return label.html();
  296. }
  297. label.html( str );
  298. return this;
  299. },
  300. message: function ( msg, fn ) {
  301. return this._msg( this.dom.fieldMessage, msg, fn );
  302. },
  303. // There is no `multiVal()` as its arguments could be ambiguous
  304. // id is an idSrc value _only_
  305. multiGet: function ( id ) {
  306. var value;
  307. var multiValues = this.s.multiValues;
  308. var multiIds = this.s.multiIds;
  309. if ( id === undefined ) {
  310. // Get an object with the values for each item being edited
  311. value = {};
  312. for ( var i=0 ; i<multiIds.length ; i++ ) {
  313. value[ multiIds[i] ] = this.isMultiValue() ?
  314. multiValues[ multiIds[i] ] :
  315. this.val();
  316. }
  317. }
  318. else if ( this.isMultiValue() ) {
  319. // Individual value
  320. value = multiValues[ id ];
  321. }
  322. else {
  323. // Common value
  324. value = this.val();
  325. }
  326. return value;
  327. },
  328. multiSet: function ( id, val )
  329. {
  330. var multiValues = this.s.multiValues;
  331. var multiIds = this.s.multiIds;
  332. if ( val === undefined ) {
  333. val = id;
  334. id = undefined;
  335. }
  336. // Set
  337. var set = function ( idSrc, val ) {
  338. // Get an individual item's value - add the id to the edit ids if
  339. // it isn't already in the set.
  340. if ( $.inArray( multiIds ) === -1 ) {
  341. multiIds.push( idSrc );
  342. }
  343. multiValues[ idSrc ] = val;
  344. };
  345. if ( $.isPlainObject( val ) && id === undefined ) {
  346. // idSrc / value pairs passed in
  347. $.each( val, function ( idSrc, innerVal ) {
  348. set( idSrc, innerVal );
  349. } );
  350. }
  351. else if ( id === undefined ) {
  352. // Set same value for all existing ids
  353. $.each( multiIds, function ( i, idSrc ) {
  354. set( idSrc, val );
  355. } );
  356. }
  357. else {
  358. // Setting an individual property
  359. set( id, val );
  360. }
  361. this.s.multiValue = true;
  362. this._multiValueCheck();
  363. return this;
  364. },
  365. name: function () {
  366. return this.s.opts.name;
  367. },
  368. node: function () {
  369. return this.dom.container[0];
  370. },
  371. set: function ( val ) {
  372. var decodeFn = function ( d ) {
  373. return typeof d !== 'string' ?
  374. d :
  375. d
  376. .replace(/&gt;/g, '>')
  377. .replace(/&lt;/g, '<')
  378. .replace(/&amp;/g, '&')
  379. .replace(/&quot;/g, '"')
  380. .replace(/&#39;/g, '\'')
  381. .replace(/&#10;/g, '\n');
  382. };
  383. this.s.multiValue = false;
  384. var decode = this.s.opts.entityDecode;
  385. if ( decode === undefined || decode === true ) {
  386. if ( $.isArray( val ) ) {
  387. for ( var i=0, ien=val.length ; i<ien ; i++ ) {
  388. val[i] = decodeFn( val[i] );
  389. }
  390. }
  391. else {
  392. val = decodeFn( val );
  393. }
  394. }
  395. this._typeFn( 'set', val );
  396. this._multiValueCheck();
  397. return this;
  398. },
  399. show: function ( animate ) {
  400. var el = this.dom.container;
  401. if ( animate === undefined ) {
  402. animate = true;
  403. }
  404. if ( this.s.host.display() && animate ) {
  405. el.slideDown();
  406. }
  407. else {
  408. el.css( 'display', 'block' );
  409. }
  410. return this;
  411. },
  412. val: function ( val ) {
  413. return val === undefined ?
  414. this.get() :
  415. this.set( val );
  416. },
  417. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  418. * Internal - Called from Editor only and are not publicly documented -
  419. * these APIs can change!
  420. */
  421. dataSrc: function () {
  422. return this.s.opts.data;
  423. },
  424. destroy: function () {
  425. // remove element
  426. this.dom.container.remove();
  427. // field's own destroy method if there is one
  428. this._typeFn( 'destroy' );
  429. return this;
  430. },
  431. multiIds: function () {
  432. return this.s.multiIds;
  433. },
  434. multiInfoShown: function ( show ) {
  435. this.dom.multiInfo.css( { display: show ? 'block' : 'none' } );
  436. },
  437. multiReset: function () {
  438. this.s.multiIds = [];
  439. this.s.multiValues = {};
  440. },
  441. valFromData: null,
  442. valToData: null,
  443. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  444. * Private
  445. */
  446. _errorNode: function () {
  447. return this.dom.fieldError;
  448. },
  449. _msg: function ( el, msg, fn ) {
  450. if ( typeof msg === 'function' ) {
  451. var editor = this.s.host;
  452. msg = msg( editor, new DataTable.Api( editor.s.table ) );
  453. }
  454. if ( el.parent().is(":visible") ) {
  455. el.html( msg );
  456. if ( msg ) {
  457. el.slideDown( fn ); // fn can be undefined - so jQuery won't execute it
  458. }
  459. else {
  460. el.slideUp( fn );
  461. }
  462. }
  463. else {
  464. // Not visible, so immediately set, or blank out the element
  465. el
  466. .html( msg || '' )
  467. .css( 'display', msg ? 'block' : 'none' );
  468. if ( fn ) {
  469. fn();
  470. }
  471. }
  472. return this;
  473. },
  474. _multiValueCheck: function () {
  475. var last;
  476. var ids = this.s.multiIds;
  477. var values = this.s.multiValues;
  478. var val;
  479. var different = false;
  480. if ( ids ) {
  481. for ( var i=0 ; i<ids.length ; i++ ) {
  482. val = values[ ids[i] ];
  483. if ( i > 0 && val !== last ) {
  484. different = true;
  485. break;
  486. }
  487. last = val;
  488. }
  489. }
  490. if ( different && this.s.multiValue ) {
  491. // Different values
  492. this.dom.inputControl.css( { display: 'none' } );
  493. this.dom.multi.css( { display: 'block' } );
  494. }
  495. else {
  496. // All the same value
  497. this.dom.inputControl.css( { display: 'block' } );
  498. this.dom.multi.css( { display: 'none' } );
  499. if ( this.s.multiValue ) {
  500. this.val( last );
  501. }
  502. }
  503. this.dom.multiReturn.css( {
  504. display: ids && ids.length > 1 && different && ! this.s.multiValue ?
  505. 'block' :
  506. 'none'
  507. } );
  508. this.s.host._multiInfo();
  509. return true;
  510. },
  511. _typeFn: function ( name /*, ... */ ) {
  512. // Remove the name from the arguments list, so the rest can be passed
  513. // straight into the field type
  514. var args = Array.prototype.slice.call( arguments );
  515. args.shift();
  516. // Insert the options as the first parameter - all field type methods
  517. // take the field's configuration object as the first parameter
  518. args.unshift( this.s.opts );
  519. var fn = this.s.type[ name ];
  520. if ( fn ) {
  521. return fn.apply( this.s.host, args );
  522. }
  523. }
  524. };
  525. Editor.Field.models = {};
  526. /**
  527. * Initialisation options that can be given to Editor.Field at initialisation
  528. * time.
  529. * @namespace
  530. */
  531. Editor.Field.defaults = {
  532. /**
  533. * Class name to assign to the field's container element (in addition to the other
  534. * classes that Editor assigns by default).
  535. * @type string
  536. * @default <i>Empty string</i>
  537. */
  538. "className": "",
  539. /**
  540. * The data property (`mData` in DataTables terminology) that is used to
  541. * read from and write to the table. If not given then it will take the same
  542. * value as the `name` that is given in the field object. Note that `data`
  543. * can be given as null, which will result in Editor not using a DataTables
  544. * row property for the value of the field for either getting or setting
  545. * data.
  546. *
  547. * In previous versions of Editor (1.2-) this was called `dataProp`. The old
  548. * name can still be used for backwards compatibility, but the new form is
  549. * preferred.
  550. * @type string
  551. * @default <i>Empty string</i>
  552. */
  553. "data": "",
  554. /**
  555. * The default value for the field. Used when creating new rows (editing will
  556. * use the currently set value). If given as a function the function will be
  557. * executed and the returned value used as the default
  558. *
  559. * In Editor 1.2 and earlier this field was called `default` - however
  560. * `default` is a reserved word in Javascript, so it couldn't be used
  561. * unquoted. `default` will still work with Editor 1.3, but the new property
  562. * name of `def` is preferred.
  563. * @type string|function
  564. * @default <i>Empty string</i>
  565. */
  566. "def": "",
  567. /**
  568. * Helpful information text about the field that is shown below the input control.
  569. * @type string
  570. * @default <i>Empty string</i>
  571. */
  572. "fieldInfo": "",
  573. /**
  574. * The ID of the field. This is used by the `label` HTML tag as the "for" attribute
  575. * improved accessibility. Although this using this parameter is not mandatory,
  576. * it is a good idea to assign the ID to the DOM element that is the input for the
  577. * field (if this is applicable).
  578. * @type string
  579. * @default <i>Calculated</i>
  580. */
  581. "id": "",
  582. /**
  583. * The label to display for the field input (i.e. the name that is visually
  584. * assigned to the field).
  585. * @type string
  586. * @default <i>Empty string</i>
  587. */
  588. "label": "",
  589. /**
  590. * Helpful information text about the field that is shown below the field label.
  591. * @type string
  592. * @default <i>Empty string</i>
  593. */
  594. "labelInfo": "",
  595. /**
  596. * The name for the field that is submitted to the server. This is the only
  597. * mandatory parameter in the field description object.
  598. * @type string
  599. * @default <i>null</i>
  600. */
  601. "name": null,
  602. /**
  603. * The input control that is presented to the end user. The options available
  604. * are defined by {@link Editor.fieldTypes} and any extensions made
  605. * to that object.
  606. * @type string
  607. * @default text
  608. */
  609. "type": "text"
  610. };
  611. /**
  612. *
  613. * @namespace
  614. */
  615. Editor.Field.models.settings = {
  616. type: null,
  617. name: null,
  618. classes: null,
  619. opts: null,
  620. host: null
  621. };
  622. /**
  623. *
  624. * @namespace
  625. */
  626. Editor.Field.models.dom = {
  627. container: null,
  628. label: null,
  629. labelInfo: null,
  630. fieldInfo: null,
  631. fieldError: null,
  632. fieldMessage: null
  633. };
  634. /*
  635. * Models
  636. */
  637. /**
  638. * Object models container, for the various models that DataTables has available
  639. * to it. These models define the objects that are used to hold the active state
  640. * and configuration of the table.
  641. * @namespace
  642. */
  643. Editor.models = {};
  644. /**
  645. * Editor makes very few assumptions about how its form will actually be
  646. * displayed to the end user (where in the DOM, interaction etc), instead
  647. * focusing on providing form interaction controls only. To actually display
  648. * a form in the browser we need to use a display controller, and then select
  649. * which one we want to use at initialisation time using the `display`
  650. * option. For example a display controller could display the form in a
  651. * lightbox (as the default display controller does), it could completely
  652. * empty the document and put only the form in place, ir could work with
  653. * DataTables to use `fnOpen` / `fnClose` to show the form in a "details" row
  654. * and so on.
  655. *
  656. * Editor has two built-in display controllers ('lightbox' and 'envelope'),
  657. * but others can readily be created and installed for use as plug-ins. When
  658. * creating a display controller plug-in you **must** implement the methods
  659. * in this control. Additionally when closing the display internally you
  660. * **must** trigger a `requestClose` event which Editor will listen
  661. * for and act upon (this allows Editor to ask the user if they are sure
  662. * they want to close the form, for example).
  663. * @namespace
  664. */
  665. Editor.models.displayController = {
  666. /**
  667. * Initialisation method, called by Editor when itself, initialises.
  668. * @param {object} dte The DataTables Editor instance that has requested
  669. * the action - this allows access to the Editor API if required.
  670. * @returns {object} The object that Editor will use to run the 'open'
  671. * and 'close' methods against. If static methods are used then
  672. * just return the object that holds the init, open and close methods,
  673. * however, this allows the display to be created with a 'new'
  674. * instance of an object is the display controller calls for that.
  675. * @type function
  676. */
  677. "init": function ( dte ) {},
  678. /**
  679. * Display the form (add it to the visual display in the document)
  680. * @param {object} dte The DataTables Editor instance that has requested
  681. * the action - this allows access to the Editor API if required.
  682. * @param {element} append The DOM node that contains the form to be
  683. * displayed
  684. * @param {function} [fn] Callback function that is to be executed when
  685. * the form has been displayed. Note that this parameter is optional.
  686. */
  687. "open": function ( dte, append, fn ) {},
  688. /**
  689. * Hide the form (remove it form the visual display in the document)
  690. * @param {object} dte The DataTables Editor instance that has requested
  691. * the action - this allows access to the Editor API if required.
  692. * @param {function} [fn] Callback function that is to be executed when
  693. * the form has been hidden. Note that this parameter is optional.
  694. */
  695. "close": function ( dte, fn ) {}
  696. };
  697. /**
  698. * Model object for input types which are available to fields (assigned to
  699. * {@link Editor.fieldTypes}). Any plug-ins which add additional
  700. * input types to Editor **must** implement the methods in this object
  701. * (dummy functions are given in the model so they can be used as defaults
  702. * if extending this object).
  703. *
  704. * All functions in the model are executed in the Editor's instance scope,
  705. * so you have full access to the settings object and the API methods if
  706. * required.
  707. * @namespace
  708. * @example
  709. * // Add a simple text input (the 'text' type that is built into Editor
  710. * // does this, so you wouldn't implement this exactly as show, but it
  711. * // it is a good example.
  712. *
  713. * var Editor = $.fn.Editor;
  714. *
  715. * Editor.fieldTypes.myInput = $.extend( true, {}, Editor.models.type, {
  716. * "create": function ( conf ) {
  717. * // We store the 'input' element in the configuration object so
  718. * // we can easily access it again in future.
  719. * conf._input = document.createElement('input');
  720. * conf._input.id = conf.id;
  721. * return conf._input;
  722. * },
  723. *
  724. * "get": function ( conf ) {
  725. * return conf._input.value;
  726. * },
  727. *
  728. * "set": function ( conf, val ) {
  729. * conf._input.value = val;
  730. * },
  731. *
  732. * "enable": function ( conf ) {
  733. * conf._input.disabled = false;
  734. * },
  735. *
  736. * "disable": function ( conf ) {
  737. * conf._input.disabled = true;
  738. * }
  739. * } );
  740. */
  741. Editor.models.fieldType = {
  742. /**
  743. * Create the field - this is called when the field is added to the form.
  744. * Note that this is called at initialisation time, or when the
  745. * {@link Editor#add} API method is called, not when the form is displayed.
  746. * If you need to know when the form is shown, you can use the API to listen
  747. * for the `open` event.
  748. * @param {object} conf The configuration object for the field in question:
  749. * {@link Editor.models.field}.
  750. * @returns {element|null} The input element (or a wrapping element if a more
  751. * complex input is required) or null if nothing is to be added to the
  752. * DOM for this input type.
  753. * @type function
  754. */
  755. "create": function ( conf ) {},
  756. /**
  757. * Get the value from the field
  758. * @param {object} conf The configuration object for the field in question:
  759. * {@link Editor.models.field}.
  760. * @returns {*} The value from the field - the exact value will depend on the
  761. * formatting required by the input type control.
  762. * @type function
  763. */
  764. "get": function ( conf ) {},
  765. /**
  766. * Set the value for a field
  767. * @param {object} conf The configuration object for the field in question:
  768. * {@link Editor.models.field}.
  769. * @param {*} val The value to set the field to - the exact value will
  770. * depend on the formatting required by the input type control.
  771. * @type function
  772. */
  773. "set": function ( conf, val ) {},
  774. /**
  775. * Enable the field - i.e. allow user interface
  776. * @param {object} conf The configuration object for the field in question:
  777. * {@link Editor.models.field}.
  778. * @type function
  779. */
  780. "enable": function ( conf ) {},
  781. /**
  782. * Disable the field - i.e. disallow user interface
  783. * @param {object} conf The configuration object for the field in question:
  784. * {@link Editor.models.field}.
  785. * @type function
  786. */
  787. "disable": function ( conf ) {}
  788. };
  789. /**
  790. * Settings object for Editor - this provides the state for each instance of
  791. * Editor and can be accessed through the instance's `s` property. Note that the
  792. * settings object is considered to be "private" and thus is liable to change
  793. * between versions. As such if you do read any of the setting parameters,
  794. * please keep this in mind when upgrading!
  795. * @namespace
  796. */
  797. Editor.models.settings = {
  798. /**
  799. * URL to submit Ajax data to.
  800. * This is directly set by the initialisation parameter / default of the same name.
  801. * @type string
  802. * @default null
  803. */
  804. "ajaxUrl": null,
  805. /**
  806. * Ajax submit function.
  807. * This is directly set by the initialisation parameter / default of the same name.
  808. * @type function
  809. * @default null
  810. */
  811. "ajax": null,
  812. /**
  813. * Data source for get and set data actions. This allows Editor to perform
  814. * as an Editor for virtually any data source simply by defining additional
  815. * data sources.
  816. * @type object
  817. * @default null
  818. */
  819. "dataSource": null,
  820. /**
  821. * DataTable selector, can be anything that the Api supports
  822. * This is directly set by the initialisation parameter / default of the same name.
  823. * @type string
  824. * @default null
  825. */
  826. "domTable": null,
  827. /**
  828. * The initialisation object that was given by the user - stored for future reference.
  829. * This is directly set by the initialisation parameter / default of the same name.
  830. * @type string
  831. * @default null
  832. */
  833. "opts": null,
  834. /**
  835. * The display controller object for the Form.
  836. * This is directly set by the initialisation parameter / default of the same name.
  837. * @type string
  838. * @default null
  839. */
  840. "displayController": null,
  841. /**
  842. * The form fields - see {@link Editor.models.field} for details of the
  843. * objects held in this array.
  844. * @type object
  845. * @default null
  846. */
  847. "fields": {},
  848. /**
  849. * Field order - order that the fields will appear in on the form. Array of strings,
  850. * the names of the fields.
  851. * @type array
  852. * @default null
  853. */
  854. "order": [],
  855. /**
  856. * The ID of the row being edited (set to -1 on create and remove actions)
  857. * @type string
  858. * @default null
  859. */
  860. "id": -1,
  861. /**
  862. * Flag to indicate if the form is currently displayed (true) or not (false)
  863. * @type string
  864. * @default null
  865. */
  866. "displayed": false,
  867. /**
  868. * Flag to indicate if the form is current in a processing state (true) or not (false)
  869. * @type string
  870. * @default null
  871. */
  872. "processing": false,
  873. /**
  874. * Developer provided identifier for the elements to be edited (i.e. at
  875. * `dt-type row-selector` to select rows to edit or delete.
  876. * @type array
  877. * @default null
  878. */
  879. "modifier": null,
  880. /**
  881. * The current form action - 'create', 'edit' or 'remove'. If no current action then
  882. * it is set to null.
  883. * @type string
  884. * @default null
  885. */
  886. "action": null,
  887. /**
  888. * JSON property from which to read / write the row's ID property.
  889. * @type string
  890. * @default null
  891. */
  892. "idSrc": null
  893. };
  894. /**
  895. * Model of the buttons that can be used with the {@link Editor#buttons}
  896. * method for creating and displaying buttons (also the {@link Editor#button}
  897. * argument option for the {@link Editor#create}, {@link Editor#edit} and
  898. * {@link Editor#remove} methods). Although you don't need to extend this object,
  899. * it is available for reference to show the options available.
  900. * @namespace
  901. */
  902. Editor.models.button = {
  903. /**
  904. * The text to put into the button. This can be any HTML string you wish as
  905. * it will be rendered as HTML (allowing images etc to be shown inside the
  906. * button).
  907. * @type string
  908. * @default null
  909. */
  910. "label": null,
  911. /**
  912. * Callback function which the button is activated. For example for a 'submit'
  913. * button you would call the {@link Editor#submit} API method, while for a cancel button
  914. * you would call the {@link Editor#close} API method. Note that the function is executed
  915. * in the scope of the Editor instance, so you can call the Editor's API methods
  916. * using the `this` keyword.
  917. * @type function
  918. * @default null
  919. */
  920. "fn": null,
  921. /**
  922. * The CSS class(es) to apply to the button which can be useful for styling buttons
  923. * which preform different functions each with a distinctive visual appearance.
  924. * @type string
  925. * @default null
  926. */
  927. "className": null
  928. };
  929. /**
  930. * This is really an internal namespace
  931. *
  932. * @namespace
  933. */
  934. Editor.models.formOptions = {
  935. /**
  936. * Action to take when the return key is pressed when focused in a form
  937. * element. Cam be `submit` or `none`. Could also be `blur` or `close`, but
  938. * why would you ever want that. Replaces `submitOnReturn` from 1.4.
  939. *
  940. * @type string
  941. */
  942. onReturn: 'submit',
  943. /**
  944. * Action to take on blur. Can be `close`, `submit` or `none`. Replaces
  945. * `submitOnBlur` from 1.4
  946. *
  947. * @type string
  948. */
  949. onBlur: 'close',
  950. /**
  951. * Action to take when the lightbox background is clicked - can be `close`,
  952. * `submit`, `blur` or `none`. Replaces `blurOnBackground` from 1.4
  953. *
  954. * @type string
  955. */
  956. onBackground: 'blur',
  957. /**
  958. * Close for at the end of the Ajax request. Can be `close` or `none`.
  959. * Replaces `closeOnComplete` from 1.4.
  960. *
  961. * @type string
  962. */
  963. onComplete: 'close',
  964. /**
  965. * Action to take when the `esc` key is pressed when focused in the form -
  966. * can be `close`, `submit`, `blur` or `none`
  967. *
  968. * @type string
  969. */
  970. onEsc: 'close',
  971. /**
  972. * Data to submit to the server when submitting a form. If an option is
  973. * selected that results in no data being submitted, the Ajax request will
  974. * not be made Can be `all`, `changed` or `allIfChanged`. This effects the
  975. * edit action only.
  976. *
  977. * @type string
  978. */
  979. submit: 'all',
  980. /**
  981. * Field identifier to focus on
  982. *
  983. * @type null|integer|string
  984. */
  985. focus: 0,
  986. /**
  987. * Buttons to show in the form
  988. *
  989. * @type string|boolean|array|object
  990. */
  991. buttons: true,
  992. /**
  993. * Form title
  994. *
  995. * @type string|boolean
  996. */
  997. title: true,
  998. /**
  999. * Form message
  1000. *
  1001. * @type string|boolean
  1002. */
  1003. message: true,
  1004. /**
  1005. * DataTables redraw option
  1006. *
  1007. * @type string|boolean
  1008. */
  1009. drawType: false
  1010. };
  1011. /*
  1012. * Display controllers
  1013. */
  1014. /**
  1015. * Display controllers. See {@link Editor.models.displayController} for
  1016. * full information about the display controller options for Editor. The display
  1017. * controllers given in this object can be utilised by specifying the
  1018. * {@link Editor.defaults.display} option.
  1019. * @namespace
  1020. */
  1021. Editor.display = {};
  1022. (function(window, document, $, DataTable) {
  1023. var self;
  1024. Editor.display.lightbox = $.extend( true, {}, Editor.models.displayController, {
  1025. /*
  1026. * API methods
  1027. */
  1028. "init": function ( dte ) {
  1029. self._init();
  1030. return self;
  1031. },
  1032. "open": function ( dte, append, callback ) {
  1033. if ( self._shown ) {
  1034. if ( callback ) {
  1035. callback();
  1036. }
  1037. return;
  1038. }
  1039. self._dte = dte;
  1040. var content = self._dom.content;
  1041. content.children().detach();
  1042. content
  1043. .append( append )
  1044. .append( self._dom.close );
  1045. self._shown = true;
  1046. self._show( callback );
  1047. },
  1048. "close": function ( dte, callback ) {
  1049. if ( !self._shown ) {
  1050. if ( callback ) {
  1051. callback();
  1052. }
  1053. return;
  1054. }
  1055. self._dte = dte;
  1056. self._hide( callback );
  1057. self._shown = false;
  1058. },
  1059. node: function ( dte ) {
  1060. return self._dom.wrapper[0];
  1061. },
  1062. /*
  1063. * Private methods
  1064. */
  1065. "_init": function () {
  1066. if ( self._ready ) {
  1067. return;
  1068. }
  1069. var dom = self._dom;
  1070. dom.content = $('div.DTED_Lightbox_Content', self._dom.wrapper);
  1071. dom.wrapper.css( 'opacity', 0 );
  1072. dom.background.css( 'opacity', 0 );
  1073. },
  1074. "_show": function ( callback ) {
  1075. var that = this;
  1076. var dom = self._dom;
  1077. // Mobiles have very poor position fixed abilities, so we need to know
  1078. // when using mobile A media query isn't good enough
  1079. if ( window.orientation !== undefined ) {
  1080. $('body').addClass( 'DTED_Lightbox_Mobile' );
  1081. }
  1082. // Adjust size for the content
  1083. dom.content.css( 'height', 'auto' );
  1084. dom.wrapper.css( {
  1085. top: -self.conf.offsetAni
  1086. } );
  1087. $('body')
  1088. .append( self._dom.background )
  1089. .append( self._dom.wrapper );
  1090. self._heightCalc();
  1091. dom.wrapper
  1092. .stop()
  1093. .animate( {
  1094. opacity: 1,
  1095. top: 0
  1096. }, callback );
  1097. dom.background
  1098. .stop()
  1099. .animate( {
  1100. opacity: 1
  1101. } );
  1102. // Event handlers - assign on show (and unbind on hide) rather than init
  1103. // since we might need to refer to different editor instances - 12563
  1104. dom.close.bind( 'click.DTED_Lightbox', function (e) {
  1105. self._dte.close();
  1106. } );
  1107. dom.background.bind( 'click.DTED_Lightbox', function (e) {
  1108. self._dte.background();
  1109. } );
  1110. $('div.DTED_Lightbox_Content_Wrapper', dom.wrapper).bind( 'click.DTED_Lightbox', function (e) {
  1111. if ( $(e.target).hasClass('DTED_Lightbox_Content_Wrapper') ) {
  1112. self._dte.background();
  1113. }
  1114. } );
  1115. $(window).bind( 'resize.DTED_Lightbox', function () {
  1116. self._heightCalc();
  1117. } );
  1118. self._scrollTop = $('body').scrollTop();
  1119. // For smaller screens we need to hide the other elements in the
  1120. // document since iOS and Android both mess up display:fixed when
  1121. // the virtual keyboard is shown
  1122. if ( window.orientation !== undefined ) {
  1123. var kids = $('body').children().not( dom.background ).not( dom.wrapper );
  1124. $('body').append( '<div class="DTED_Lightbox_Shown"/>' );
  1125. $('div.DTED_Lightbox_Shown').append( kids );
  1126. }
  1127. },
  1128. "_heightCalc": function () {
  1129. // Set the max-height for the form content
  1130. var dom = self._dom;
  1131. var maxHeight = $(window).height() - (self.conf.windowPadding*2) -
  1132. $('div.DTE_Header', dom.wrapper).outerHeight() -
  1133. $('div.DTE_Footer', dom.wrapper).outerHeight();
  1134. $('div.DTE_Body_Content', dom.wrapper).css(
  1135. 'maxHeight',
  1136. maxHeight
  1137. );
  1138. },
  1139. "_hide": function ( callback ) {
  1140. var dom = self._dom;
  1141. if ( !callback ) {
  1142. callback = function () {};
  1143. }
  1144. if ( window.orientation !== undefined ) {
  1145. var show = $('div.DTED_Lightbox_Shown');
  1146. show.children().appendTo('body');
  1147. show.remove();
  1148. }
  1149. // Restore scroll state
  1150. $('body')
  1151. .removeClass( 'DTED_Lightbox_Mobile' )
  1152. .scrollTop( self._scrollTop );
  1153. dom.wrapper
  1154. .stop()
  1155. .animate( {
  1156. opacity: 0,
  1157. top: self.conf.offsetAni
  1158. }, function () {
  1159. $(this).detach();
  1160. callback();
  1161. } );
  1162. dom.background
  1163. .stop()
  1164. .animate( {
  1165. opacity: 0
  1166. }, function () {
  1167. $(this).detach();
  1168. } );
  1169. // Event handlers
  1170. dom.close.unbind( 'click.DTED_Lightbox' );
  1171. dom.background.unbind( 'click.DTED_Lightbox' );
  1172. $('div.DTED_Lightbox_Content_Wrapper', dom.wrapper).unbind( 'click.DTED_Lightbox' );
  1173. $(window).unbind( 'resize.DTED_Lightbox' );
  1174. },
  1175. /*
  1176. * Private properties
  1177. */
  1178. "_dte": null,
  1179. "_ready": false,
  1180. "_shown": false,
  1181. "_dom": {
  1182. "wrapper": $(
  1183. '<div class="DTED DTED_Lightbox_Wrapper">'+
  1184. '<div class="DTED_Lightbox_Container">'+
  1185. '<div class="DTED_Lightbox_Content_Wrapper">'+
  1186. '<div class="DTED_Lightbox_Content">'+
  1187. '</div>'+
  1188. '</div>'+
  1189. '</div>'+
  1190. '</div>'
  1191. ),
  1192. "background": $(
  1193. '<div class="DTED_Lightbox_Background"><div/></div>'
  1194. ),
  1195. "close": $(
  1196. '<div class="DTED_Lightbox_Close"></div>'
  1197. ),
  1198. "content": null
  1199. }
  1200. } );
  1201. self = Editor.display.lightbox;
  1202. self.conf = {
  1203. "offsetAni": 25,
  1204. "windowPadding": 25
  1205. };
  1206. }(window, document, jQuery, jQuery.fn.dataTable));
  1207. (function(window, document, $, DataTable) {
  1208. var self;
  1209. Editor.display.envelope = $.extend( true, {}, Editor.models.displayController, {
  1210. /*
  1211. * API methods
  1212. */
  1213. "init": function ( dte ) {
  1214. self._dte = dte;
  1215. self._init();
  1216. return self;
  1217. },
  1218. "open": function ( dte, append, callback ) {
  1219. self._dte = dte;
  1220. $(self._dom.content).children().detach();
  1221. self._dom.content.appendChild( append );
  1222. self._dom.content.appendChild( self._dom.close );
  1223. self._show( callback );
  1224. },
  1225. "close": function ( dte, callback ) {
  1226. self._dte = dte;
  1227. self._hide( callback );
  1228. },
  1229. node: function ( dte ) {
  1230. return self._dom.wrapper[0];
  1231. },
  1232. /*
  1233. * Private methods
  1234. */
  1235. "_init": function () {
  1236. if ( self._ready ) {
  1237. return;
  1238. }
  1239. self._dom.content = $('div.DTED_Envelope_Container', self._dom.wrapper)[0];
  1240. document.body.appendChild( self._dom.background );
  1241. document.body.appendChild( self._dom.wrapper );
  1242. // For IE6-8 we need to make it a block element to read the opacity...
  1243. self._dom.background.style.visbility = 'hidden';
  1244. self._dom.background.style.display = 'block';
  1245. self._cssBackgroundOpacity = $(self._dom.background).css('opacity');
  1246. self._dom.background.style.display = 'none';
  1247. self._dom.background.style.visbility = 'visible';
  1248. },
  1249. "_show": function ( callback ) {
  1250. var that = this;
  1251. var formHeight;
  1252. if ( !callback ) {
  1253. callback = function () {};
  1254. }
  1255. // Adjust size for the content
  1256. self._dom.content.style.height = 'auto';
  1257. var style = self._dom.wrapper.style;
  1258. style.opacity = 0;
  1259. style.display = 'block';
  1260. var targetRow = self._findAttachRow();
  1261. var height = self._heightCalc();
  1262. var width = targetRow.offsetWidth;
  1263. style.display = 'none';
  1264. style.opacity = 1;
  1265. // Prep the display
  1266. self._dom.wrapper.style.width = width+"px";
  1267. self._dom.wrapper.style.marginLeft = -(width/2)+"px";
  1268. self._dom.wrapper.style.top = ($(targetRow).offset().top + targetRow.offsetHeight)+"px";
  1269. self._dom.content.style.top = ((-1 * height) - 20)+"px";
  1270. // Start animating in the background
  1271. self._dom.background.style.opacity = 0;
  1272. self._dom.background.style.display = 'block';
  1273. $(self._dom.background).animate( {
  1274. 'opacity': self._cssBackgroundOpacity
  1275. }, 'normal' );
  1276. // Animate in the display
  1277. $(self._dom.wrapper).fadeIn();
  1278. // Slide the slider down to 'open' the view
  1279. if ( self.conf.windowScroll ) {
  1280. // Scroll the window so we can see the editor first
  1281. $('html,body').animate( {
  1282. "scrollTop": $(targetRow).offset().top + targetRow.offsetHeight - self.conf.windowPadding
  1283. }, function () {
  1284. // Now open the editor
  1285. $(self._dom.content).animate( {
  1286. "top": 0
  1287. }, 600, callback );
  1288. } );
  1289. }
  1290. else {
  1291. // Just open the editor without moving the document position
  1292. $(self._dom.content).animate( {
  1293. "top": 0
  1294. }, 600, callback );
  1295. }
  1296. // Event handlers
  1297. $(self._dom.close).bind( 'click.DTED_Envelope', function (e) {
  1298. self._dte.close();
  1299. } );
  1300. $(self._dom.background).bind( 'click.DTED_Envelope', function (e) {
  1301. self._dte.background();
  1302. } );
  1303. $('div.DTED_Lightbox_Content_Wrapper', self._dom.wrapper).bind( 'click.DTED_Envelope', function (e) {
  1304. if ( $(e.target).hasClass('DTED_Envelope_Content_Wrapper') ) {
  1305. self._dte.background();
  1306. }
  1307. } );
  1308. $(window).bind( 'resize.DTED_Envelope', function () {
  1309. self._heightCalc();
  1310. } );
  1311. },
  1312. "_heightCalc": function () {
  1313. var formHeight;
  1314. formHeight = self.conf.heightCalc ?
  1315. self.conf.heightCalc( self._dom.wrapper ) :
  1316. $(self._dom.content).children().height();
  1317. // Set the max-height for the form content
  1318. var maxHeight = $(window).height() - (self.conf.windowPadding*2) -
  1319. $('div.DTE_Header', self._dom.wrapper).outerHeight() -
  1320. $('div.DTE_Footer', self._dom.wrapper).outerHeight();
  1321. $('div.DTE_Body_Content', self._dom.wrapper).css('maxHeight', maxHeight);
  1322. return $(self._dte.dom.wrapper).outerHeight();
  1323. },
  1324. "_hide": function ( callback ) {
  1325. if ( !callback ) {
  1326. callback = function () {};
  1327. }
  1328. $(self._dom.content).animate( {
  1329. "top": -(self._dom.content.offsetHeight+50)
  1330. }, 600, function () {
  1331. $([self._dom.wrapper, self._dom.background]).fadeOut( 'normal', callback );
  1332. } );
  1333. // Event handlers
  1334. $(self._dom.close).unbind( 'click.DTED_Lightbox' );
  1335. $(self._dom.background).unbind( 'click.DTED_Lightbox' );
  1336. $('div.DTED_Lightbox_Content_Wrapper', self._dom.wrapper).unbind( 'click.DTED_Lightbox' );
  1337. $(window).unbind( 'resize.DTED_Lightbox' );
  1338. },
  1339. "_findAttachRow": function () {
  1340. var dt = $(self._dte.s.table).DataTable();
  1341. // Figure out where we want to put the form display
  1342. if ( self.conf.attach === 'head' ) {
  1343. return dt.table().header();
  1344. }
  1345. else if ( self._dte.s.action === 'create' ) {
  1346. return dt.table().header();
  1347. }
  1348. else {
  1349. return dt.row( self._dte.s.modifier ).node();
  1350. }
  1351. },
  1352. /*
  1353. * Private properties
  1354. */
  1355. "_dte": null,
  1356. "_ready": false,
  1357. "_cssBackgroundOpacity": 1, // read from the CSS dynamically, but stored for future reference
  1358. "_dom": {
  1359. "wrapper": $(
  1360. '<div class="DTED DTED_Envelope_Wrapper">'+
  1361. '<div class="DTED_Envelope_ShadowLeft"></div>'+
  1362. '<div class="DTED_Envelope_ShadowRight"></div>'+
  1363. '<div class="DTED_Envelope_Container"></div>'+
  1364. '</div>'
  1365. )[0],
  1366. "background": $(
  1367. '<div class="DTED_Envelope_Background"><div/></div>'
  1368. )[0],
  1369. "close": $(
  1370. '<div class="DTED_Envelope_Close">&times;</div>'
  1371. )[0],
  1372. "content": null
  1373. }
  1374. } );
  1375. // Assign to 'self' for easy referencing of our own object!
  1376. self = Editor.display.envelope;
  1377. // Configuration object - can be accessed globally using
  1378. // $.fn.Editor.display.envelope.conf (!)
  1379. self.conf = {
  1380. "windowPadding": 50,
  1381. "heightCalc": null,
  1382. "attach": "row",
  1383. "windowScroll": true
  1384. };
  1385. }(window, document, jQuery, jQuery.fn.dataTable));
  1386. /*
  1387. * Prototype includes
  1388. */
  1389. /**
  1390. * Add a new field to the from. This is the method that is called automatically when
  1391. * fields are given in the initialisation objects as {@link Editor.defaults.fields}.
  1392. * @memberOf Editor
  1393. * @param {object|array} field The object that describes the field (the full
  1394. * object is described by {@link Editor.model.field}. Note that multiple
  1395. * fields can be given by passing in an array of field definitions.
  1396. * @param {string} [after] Existing field to insert the new field after. This
  1397. * can be `undefined` (insert at end), `null` (insert at start) or `string`
  1398. * the field name to insert after.
  1399. */
  1400. Editor.prototype.add = function ( cfg, after )
  1401. {
  1402. // Allow multiple fields to be added at the same time
  1403. if ( $.isArray( cfg ) ) {
  1404. for ( var i=0, iLen=cfg.length ; i<iLen ; i++ ) {
  1405. this.add( cfg[i] );
  1406. }
  1407. }
  1408. else {
  1409. var name = cfg.name;
  1410. if ( name === undefined ) {
  1411. throw "Error adding field. The field requires a `name` option";
  1412. }
  1413. if ( this.s.fields[ name ] ) {
  1414. throw "Error adding field '"+name+"'. A field already exists with this name";
  1415. }
  1416. // Allow the data source to add / modify the field properties
  1417. // Dev: would this be better as an event `preAddField`? And have the
  1418. // data sources init only once, but can listen for such events? More
  1419. // complexity, but probably more flexible...
  1420. this._dataSource( 'initField', cfg );
  1421. this.s.fields[ name ] = new Editor.Field( cfg, this.classes.field, this );
  1422. if ( after === undefined ) {
  1423. this.s.order.push( name );
  1424. }
  1425. else if ( after === null ) {
  1426. this.s.order.unshift( name );
  1427. }
  1428. else {
  1429. var idx = $.inArray( after, this.s.order );
  1430. this.s.order.splice( idx+1, 0, name );
  1431. }
  1432. }
  1433. this._displayReorder( this.order() );
  1434. return this;
  1435. };
  1436. /**
  1437. * Perform background activation tasks.
  1438. *
  1439. * This is NOT publicly documented on the Editor web-site, but rather can be
  1440. * used by display controller plug-ins to perform the required task on
  1441. * background activation.
  1442. *
  1443. * @return {Editor} Editor instance, for chaining
  1444. */
  1445. Editor.prototype.background = function ()
  1446. {
  1447. var onBackground = this.s.editOpts.onBackground;
  1448. if ( onBackground === 'blur' ) {
  1449. this.blur();
  1450. }
  1451. else if ( onBackground === 'close' ) {
  1452. this.close();
  1453. }
  1454. else if ( onBackground === 'submit' ) {
  1455. this.submit();
  1456. }
  1457. return this;
  1458. };
  1459. /**
  1460. * Blur the currently displayed editor.
  1461. *
  1462. * A blur is different from a `close()` in that it might cause either a close or
  1463. * the form to be submitted. A typical example of a blur would be clicking on
  1464. * the background of the bubble or main editing forms - i.e. it might be a
  1465. * close, or it might submit depending upon the configuration, while a click on
  1466. * the close box is a very definite close.
  1467. *
  1468. * @return {Editor} Editor instance, for chaining
  1469. */
  1470. Editor.prototype.blur = function ()
  1471. {
  1472. this._blur();
  1473. return this;
  1474. };
  1475. Editor.prototype.bubble = function ( cells, fieldNames, show, opts )
  1476. {
  1477. var that = this;
  1478. // Some other field in inline edit mode?
  1479. if ( this._tidy( function () { that.bubble( cells, fieldNames, opts ); } ) ) {
  1480. return this;
  1481. }
  1482. // Argument shifting
  1483. if ( $.isPlainObject( fieldNames ) ) {
  1484. opts = fieldNames;
  1485. fieldNames = undefined;
  1486. show = true;
  1487. }
  1488. else if ( typeof fieldNames === 'boolean' ) {
  1489. show = fieldNames;
  1490. fieldNames = undefined;
  1491. opts = undefined;
  1492. }
  1493. if ( $.isPlainObject( show ) ) {
  1494. opts = show;
  1495. show = true;
  1496. }
  1497. if ( show === undefined ) {
  1498. show = true;
  1499. }
  1500. opts = $.extend( {}, this.s.formOptions.bubble, opts );
  1501. var editFields = this._dataSource( 'individual', cells, fieldNames );
  1502. this._edit( cells, editFields, 'bubble' );
  1503. var ret = this._preopen( 'bubble' );
  1504. if ( ! ret ) {
  1505. return this;
  1506. }
  1507. // Keep the bubble in position on resize
  1508. var namespace = this._formOptions( opts );
  1509. $(window).on( 'resize.'+namespace, function () {
  1510. that.bubblePosition();
  1511. } );
  1512. // Store the nodes that are being used so the bubble can be positioned
  1513. var nodes = [];
  1514. this.s.bubbleNodes = nodes.concat.apply( nodes, _pluck( editFields, 'attach' ) );
  1515. // Create container display
  1516. var classes = this.classes.bubble;
  1517. var background = $( '<div class="'+classes.bg+'"><div/></div>' );
  1518. var container = $(
  1519. '<div class="'+classes.wrapper+'">'+
  1520. '<div class="'+classes.liner+'">'+
  1521. '<div class="'+classes.table+'">'+
  1522. '<div class="'+classes.close+'" />'+
  1523. '</div>'+
  1524. '</div>'+
  1525. '<div class="'+classes.pointer+'" />'+
  1526. '</div>'
  1527. );
  1528. if ( show ) {
  1529. container.appendTo( 'body' );
  1530. background.appendTo( 'body' );
  1531. }
  1532. var liner = container.children().eq(0);
  1533. var table = liner.children();
  1534. var close = table.children();
  1535. liner.append( this.dom.formError );
  1536. table.prepend( this.dom.form );
  1537. if ( opts.message ) {
  1538. liner.prepend( this.dom.formInfo );
  1539. }
  1540. if ( opts.title ) {
  1541. liner.prepend( this.dom.header );
  1542. }
  1543. if ( opts.buttons ) {
  1544. table.append( this.dom.buttons );
  1545. }
  1546. var pair = $().add( container ).add( background );
  1547. this._closeReg( function ( submitComplete ) {
  1548. pair.animate(
  1549. { opacity: 0 },
  1550. function () {
  1551. pair.detach();
  1552. $(window).off( 'resize.'+namespace );
  1553. // Clear error messages "offline"
  1554. that._clearDynamicInfo();
  1555. }
  1556. );
  1557. } );
  1558. // Close event handlers
  1559. background.click( function () {
  1560. that.blur();
  1561. } );
  1562. close.click( function () {
  1563. that._close();
  1564. } );
  1565. this.bubblePosition();
  1566. pair.animate( { opacity: 1 } );
  1567. this._focus( this.s.includeFields, opts.focus );
  1568. this._postopen( 'bubble' );
  1569. return this;
  1570. };
  1571. /**
  1572. * Reposition the editing bubble (`bubble()`) when it is visible. This can be
  1573. * used to update the bubble position if other elements on the page change
  1574. * position. Editor will automatically call this method on window resize.
  1575. *
  1576. * @return {Editor} Editor instance, for chaining
  1577. */
  1578. Editor.prototype.bubblePosition = function ()
  1579. {
  1580. var
  1581. wrapper = $('div.DTE_Bubble'),
  1582. liner = $('div.DTE_Bubble_Liner'),
  1583. nodes = this.s.bubbleNodes;
  1584. // Average the node positions to insert the container
  1585. var position = { top: 0, left: 0, right: 0, bottom: 0 };
  1586. $.each( nodes, function (i, node) {
  1587. var pos = $(node).offset();
  1588. position.top += pos.top;
  1589. position.left += pos.left;
  1590. position.right += pos.left + node.offsetWidth;
  1591. position.bottom += pos.top + node.offsetHeight;
  1592. } );
  1593. position.top /= nodes.length;
  1594. position.left /= nodes.length;
  1595. position.right /= nodes.length;
  1596. position.bottom /= nodes.length;
  1597. var
  1598. top = position.top,
  1599. left = (position.left + position.right) / 2,
  1600. width = liner.outerWidth(),
  1601. visLeft = left - (width / 2),
  1602. visRight = visLeft + width,
  1603. docWidth = $(window).width(),
  1604. padding = 15,
  1605. classes = this.classes.bubble;
  1606. wrapper.css( {
  1607. top: top,
  1608. left: left
  1609. } );
  1610. // Correct for overflow from the top of the document by positioning below
  1611. // the field if needed
  1612. if ( liner.length && liner.offset().top < 0 ) {
  1613. wrapper
  1614. .css( 'top', position.bottom )
  1615. .addClass( 'below' );
  1616. }
  1617. else {
  1618. wrapper.removeClass( 'below' );
  1619. }
  1620. // Attempt to correct for overflow to the right of the document
  1621. if ( visRight+padding > docWidth ) {
  1622. var diff = visRight - docWidth;
  1623. // If left overflowing, that takes priority
  1624. liner.css( 'left', visLeft < padding ?
  1625. -(visLeft-padding) :
  1626. -(diff+padding)
  1627. );
  1628. }
  1629. else {
  1630. // Correct overflow to the left
  1631. liner.css( 'left', visLeft < padding ? -(visLeft-padding) : 0 );
  1632. }
  1633. return this;
  1634. };
  1635. /**
  1636. * Setup the buttons that will be shown in the footer of the form - calling this
  1637. * method will replace any buttons which are currently shown in the form.
  1638. * @param {array|object} buttons A single button definition to add to the form or
  1639. * an array of objects with the button definitions to add more than one button.
  1640. * The options for the button definitions are fully defined by the
  1641. * {@link Editor.models.button} object.
  1642. * @param {string} buttons.label The text to put into the button. This can be any
  1643. * HTML string you wish as it will be rendered as HTML (allowing images etc to
  1644. * be shown inside the button).
  1645. * @param {function} [buttons.fn] Callback function which the button is activated.
  1646. * For example for a 'submit' button you would call the {@link Editor#submit} method,
  1647. * while for a cancel button you would call the {@link Editor#close} method. Note that
  1648. * the function is executed in the scope of the Editor instance, so you can call
  1649. * the Editor's API methods using the `this` keyword.
  1650. * @param {string} [buttons.className] The CSS class(es) to apply to the button
  1651. * which can be useful for styling buttons which preform different functions
  1652. * each with a distinctive visual appearance.
  1653. * @return {Editor} Editor instance, for chaining
  1654. */
  1655. Editor.prototype.buttons = function ( buttons )
  1656. {
  1657. var that = this;
  1658. if ( buttons === '_basic' ) {
  1659. // Special string to create a basic button - undocumented
  1660. buttons = [ {
  1661. label: this.i18n[ this.s.action ].submit,
  1662. fn: function () { this.submit(); }
  1663. } ];
  1664. }
  1665. else if ( ! $.isArray( buttons ) ) {
  1666. // Allow a single button to be passed in as an object with an array
  1667. buttons = [ buttons ];
  1668. }
  1669. $(this.dom.buttons).empty();
  1670. $.each( buttons, function ( i, btn ) {
  1671. if ( typeof btn === 'string' ) {
  1672. btn = {
  1673. label: btn,
  1674. fn: function () { this.submit(); }
  1675. };
  1676. }
  1677. $( '<button/>', {
  1678. 'class': that.classes.form.button+(btn.className ? ' '+btn.className : '')
  1679. } )
  1680. .html( typeof btn.label === 'function' ?
  1681. btn.label( that ) :
  1682. btn.label || ''
  1683. )
  1684. .attr( 'tabindex', 0 )
  1685. .on( 'keyup', function (e) {
  1686. if ( e.keyCode === 13 && btn.fn ) {
  1687. btn.fn.call( that );
  1688. }
  1689. } )
  1690. .on( 'keypress', function (e) {
  1691. // Stop the browser activating the click event - if we don't
  1692. // have this and the Ajax return is fast, the keyup in
  1693. // `_formOptions()` might trigger another submit
  1694. if ( e.keyCode === 13 ) {
  1695. e.preventDefault();
  1696. }
  1697. } )
  1698. .on( 'click', function (e) {
  1699. e.preventDefault();
  1700. if ( btn.fn ) {
  1701. btn.fn.call( that );
  1702. }
  1703. } )
  1704. .appendTo( that.dom.buttons );
  1705. } );
  1706. return this;
  1707. };
  1708. /**
  1709. * Remove fields from the form (fields are those that have been added using the
  1710. * {@link Editor#add} method or the `fields` initialisation option). A single,
  1711. * multiple or all fields can be removed at a time based on the passed parameter.
  1712. * Fields are identified by the `name` property that was given to each field
  1713. * when added to the form.
  1714. * @param {string|array} [fieldName] Field or fields to remove from the form. If
  1715. * not given then all fields are removed from the form. If given as a string
  1716. * then the single matching field will be removed. If given as an array of
  1717. * strings, then all matching fields will be removed.
  1718. * @return {Editor} Editor instance, for chaining
  1719. *
  1720. * @example
  1721. * // Clear the form of current fields and then add a new field
  1722. * // before displaying a 'create' display
  1723. * editor.clear();
  1724. * editor.add( {
  1725. * "label": "User name",
  1726. * "name": "username"
  1727. * } );
  1728. * editor.create( "Create user" );
  1729. *
  1730. * @example
  1731. * // Remove an individual field
  1732. * editor.clear( "username" );
  1733. *
  1734. * @example
  1735. * // Remove multiple fields
  1736. * editor.clear( [ "first_name", "last_name" ] );
  1737. */
  1738. Editor.prototype.clear = function ( fieldName )
  1739. {
  1740. var that = this;
  1741. var fields = this.s.fields;
  1742. if ( typeof fieldName === 'string' ) {
  1743. // Remove an individual form element
  1744. fields[ fieldName ].destroy();
  1745. delete fields[ fieldName ];
  1746. var orderIdx = $.inArray( fieldName, this.s.order );
  1747. this.s.order.splice( orderIdx, 1 );
  1748. }
  1749. else {
  1750. $.each( this._fieldNames( fieldName ), function (i, name) {
  1751. that.clear( name );
  1752. } );
  1753. }
  1754. return this;
  1755. };
  1756. /**
  1757. * Close the form display.
  1758. *
  1759. * Note that `close()` will close any of the three Editor form types (main,
  1760. * bubble and inline).
  1761. *
  1762. * @return {Editor} Editor instance, for chaining
  1763. */
  1764. Editor.prototype.close = function ()
  1765. {
  1766. this._close( false );
  1767. return this;
  1768. };
  1769. /**
  1770. * Create a new record - show the form that allows the user to enter information
  1771. * for a new row and then subsequently submit that data.
  1772. * @param {boolean} [show=true] Show the form or not.
  1773. *
  1774. * @example
  1775. * // Show the create form with a submit button
  1776. * editor
  1777. * .title( 'Add new record' )
  1778. * .buttons( {
  1779. * "label": "Save",
  1780. * "fn": function () {
  1781. * this.submit();
  1782. * }
  1783. * } )
  1784. * .create();
  1785. *
  1786. * @example
  1787. * // Don't show the form and automatically submit it after programatically
  1788. * // setting the values of fields (and using the field defaults)
  1789. * editor
  1790. * create()
  1791. * set( 'name', 'Test user' )
  1792. * set( 'access', 'Read only' )
  1793. * submit();
  1794. */
  1795. Editor.prototype.create = function ( arg1, arg2, arg3, arg4 )
  1796. {
  1797. var that = this;
  1798. var fields = this.s.fields;
  1799. var count = 1;
  1800. // Some other field in inline edit mode?
  1801. if ( this._tidy( function () { that.create( arg1, arg2, arg3, arg4 ); } ) ) {
  1802. return this;
  1803. }
  1804. // Multi-row creation support (only supported by the 1.3+ style of calling
  1805. // this method, so a max of three arguments
  1806. if ( typeof arg1 === 'number' ) {
  1807. count = arg1;
  1808. arg1 = arg2;
  1809. arg2 = arg3;
  1810. }
  1811. // Set up the edit fields for submission
  1812. this.s.editFields = {};
  1813. for ( var i=0 ; i<count ; i++ ) {
  1814. this.s.editFields[ i ] = {
  1815. fields: this.s.fields
  1816. };
  1817. }
  1818. var argOpts = this._crudArgs( arg1, arg2, arg3, arg4 );
  1819. this.s.action = "create";
  1820. this.s.modifier = null;
  1821. this.dom.form.style.display = 'block';
  1822. this._actionClass();
  1823. // Allow all fields to be displayed for the create form
  1824. this._displayReorder( this.fields() );
  1825. // Set the default for the fields
  1826. $.each( fields, function ( name, field ) {
  1827. field.multiReset();
  1828. field.set( field.def() );
  1829. } );
  1830. this._event( 'initCreate' );
  1831. this._assembleMain();
  1832. this._formOptions( argOpts.opts );
  1833. argOpts.maybeOpen();
  1834. return this;
  1835. };
  1836. /**
  1837. * Create a dependent link between two or more fields. This method is used to
  1838. * listen for a change in a field's value which will trigger updating of the
  1839. * form. This update can consist of updating an options list, changing values
  1840. * or making fields hidden / visible.
  1841. *
  1842. * @param {string} parent The name of the field to listen to changes from
  1843. * @param {string|object|function} url Callback definition. This can be:
  1844. * * A string, which will be used as a URL to submit the request for update to
  1845. * * An object, which is used to extend an Ajax object for the request. The
  1846. * `url` parameter must be specified.
  1847. * * A function, which is used as a callback, allowing non-ajax updates.
  1848. * @return {Editor} Editor instance, for chaining
  1849. */
  1850. Editor.prototype.dependent = function ( parent, url, opts ) {
  1851. if ( $.isArray( parent ) ) {
  1852. for ( var i=0, ien=parent.length ; i<ien ; i++ ) {
  1853. this.dependent( parent[i], url, opts );
  1854. }
  1855. return this;
  1856. }
  1857. var that = this;
  1858. var field = this.field( parent );
  1859. var ajaxOpts = {
  1860. type: 'POST',
  1861. dataType: 'json'
  1862. };
  1863. opts = $.extend( {
  1864. event: 'change',
  1865. data: null,
  1866. preUpdate: null,
  1867. postUpdate: null
  1868. }, opts );
  1869. var update = function ( json ) {
  1870. if ( opts.preUpdate ) {
  1871. opts.preUpdate( json );
  1872. }
  1873. // Field specific
  1874. $.each( {
  1875. labels: 'label',
  1876. options: 'update',
  1877. values: 'val',
  1878. messages: 'message',
  1879. errors: 'error'
  1880. }, function ( jsonProp, fieldFn ) {
  1881. if ( json[ jsonProp ] ) {
  1882. $.each( json[ jsonProp ], function ( field, val ) {
  1883. that.field( field )[ fieldFn ]( val );
  1884. } );
  1885. }
  1886. } );
  1887. // Form level
  1888. $.each( [ 'hide', 'show', 'enable', 'disable' ], function ( i, key ) {
  1889. if ( json[ key ] ) {
  1890. that[ key ]( json[ key ] );
  1891. }
  1892. } );
  1893. if ( opts.postUpdate ) {
  1894. opts.postUpdate( json );
  1895. }
  1896. };
  1897. // Use a delegate handler to account for field elements which are added and
  1898. // removed after `depenedent` has been called
  1899. $(field.node()).on( opts.event, function (e) {
  1900. // Make sure that it was one of the input elements that triggered the ev
  1901. if ( $.inArray( e.target, field.input().toArray() ) === -1 ) {
  1902. return;
  1903. }
  1904. var data = {};
  1905. data.rows = that.s.editFields ?
  1906. _pluck( that.s.editFields, 'data' ) :
  1907. null;
  1908. data.row = data.rows ?
  1909. data.rows[0] :
  1910. null;
  1911. data.values = that.val();
  1912. if ( opts.data ) {
  1913. var ret = opts.data( data );
  1914. if ( ret ) {
  1915. opts.data = ret;
  1916. }
  1917. }
  1918. if ( typeof url === 'function' ) {
  1919. var o = url( field.val(), data, update );
  1920. if ( o ) {
  1921. update( o );
  1922. }
  1923. }
  1924. else {
  1925. if ( $.isPlainObject( url ) ) {
  1926. $.extend( ajaxOpts, url );
  1927. }
  1928. else {
  1929. ajaxOpts.url = url;
  1930. }
  1931. $.ajax( $.extend( ajaxOpts, {
  1932. url: url,
  1933. data: data,
  1934. success: update
  1935. } ) );
  1936. }
  1937. } );
  1938. return this;
  1939. };
  1940. /**
  1941. * Disable one or more field inputs, disallowing subsequent user interaction with the
  1942. * fields until they are re-enabled.
  1943. * @param {string|array} name The field name (from the `name` parameter given when
  1944. * originally setting up the field) to disable, or an array of field names to disable
  1945. * multiple fields with a single call.
  1946. * @return {Editor} Editor instance, for chaining
  1947. *
  1948. * @example
  1949. * // Show a 'create' record form, but with a field disabled
  1950. * editor.disable( 'account_type' );
  1951. * editor.create( 'Add new user', {
  1952. * "label": "Save",
  1953. * "fn": function () { this.submit(); }
  1954. * } );
  1955. *
  1956. * @example
  1957. * // Disable multiple fields by using an array of field names
  1958. * editor.disable( ['account_type', 'access_level'] );
  1959. */
  1960. Editor.prototype.disable = function ( name )
  1961. {
  1962. var fields = this.s.fields;
  1963. $.each( this._fieldNames( name ), function ( i, n ) {
  1964. fields[ n ].disable();
  1965. } );
  1966. return this;
  1967. };
  1968. /**
  1969. * Display, or remove the editing form from the display
  1970. * @param {boolean} show Show (`true`) or hide (`false`)
  1971. * @return {Editor} Editor instance, for chaining
  1972. */
  1973. Editor.prototype.display = function ( show )
  1974. {
  1975. if ( show === undefined ) {
  1976. return this.s.displayed;
  1977. }
  1978. return this[ show ? 'open' : 'close' ]();
  1979. };
  1980. /**
  1981. * Fields which are currently displayed
  1982. * @return {string[]} Field names that are shown
  1983. */
  1984. Editor.prototype.displayed = function ()
  1985. {
  1986. return $.map( this.s.fields, function ( field, name ) {
  1987. return field.displayed() ? name : null;
  1988. } );
  1989. };
  1990. /**
  1991. * Get display controller node
  1992. *
  1993. * @return {node} Display controller host element
  1994. */
  1995. Editor.prototype.displayNode = function ()
  1996. {
  1997. return this.s.displayController.node( this );
  1998. };
  1999. /**
  2000. * Edit a record - show the form, pre-populated with the data that is in the given
  2001. * DataTables row, that allows the user to enter information for the row to be modified
  2002. * and then subsequently submit that data.
  2003. * @param {node} items The TR element from the DataTable that is to be edited
  2004. * @param {boolean} [show=true] Show the form or not.
  2005. * @return {Editor} Editor instance, for chaining
  2006. *
  2007. * @example
  2008. * // Show the edit form for the first row in the DataTable with a submit button
  2009. * editor.edit( $('#example tbody tr:eq(0)')[0], 'Edit record', {
  2010. * "label": "Update",
  2011. * "fn": function () { this.submit(); }
  2012. * } );
  2013. *
  2014. * @example
  2015. * // Use the title and buttons API methods to show an edit form (this provides
  2016. * // the same result as example above, but is a different way of achieving it
  2017. * editor.title( 'Edit record' );
  2018. * editor.buttons( {
  2019. * "label": "Update",
  2020. * "fn": function () { this.submit(); }
  2021. * } );
  2022. * editor.edit( $('#example tbody tr:eq(0)')[0] );
  2023. *
  2024. * @example
  2025. * // Automatically submit an edit without showing the user the form
  2026. * editor.edit( TRnode, null, null, false );
  2027. * editor.set( 'name', 'Updated name' );
  2028. * editor.set( 'access', 'Read only' );
  2029. * editor.submit();
  2030. */
  2031. Editor.prototype.edit = function ( items, arg1, arg2, arg3, arg4 )
  2032. {
  2033. var that = this;
  2034. // Some other field in inline edit mode?
  2035. if ( this._tidy( function () { that.edit( items, arg1, arg2, arg3, arg4 ); } ) ) {
  2036. return this;
  2037. }
  2038. var fields = this.s.fields;
  2039. var argOpts = this._crudArgs( arg1, arg2, arg3, arg4 );
  2040. this._edit( items, this._dataSource( 'fields', items ), 'main' );
  2041. this._assembleMain();
  2042. this._formOptions( argOpts.opts );
  2043. argOpts.maybeOpen();
  2044. return this;
  2045. };
  2046. /**
  2047. * Enable one or more field inputs, restoring user interaction with the fields.
  2048. * @param {string|array} name The field name (from the `name` parameter given when
  2049. * originally setting up the field) to enable, or an array of field names to enable
  2050. * multiple fields with a single call.
  2051. * @return {Editor} Editor instance, for chaining
  2052. *
  2053. * @example
  2054. * // Show a 'create' form with buttons which will enable and disable certain fields
  2055. * editor.create( 'Add new user', [
  2056. * {
  2057. * "label": "User name only",
  2058. * "fn": function () {
  2059. * this.enable('username');
  2060. * this.disable( ['first_name', 'last_name'] );
  2061. * }
  2062. * }, {
  2063. * "label": "Name based",
  2064. * "fn": function () {
  2065. * this.disable('username');
  2066. * this.enable( ['first_name', 'last_name'] );
  2067. * }
  2068. * }, {
  2069. * "label": "Submit",
  2070. * "fn": function () { this.submit(); }
  2071. * }
  2072. * );
  2073. */
  2074. Editor.prototype.enable = function ( name )
  2075. {
  2076. var fields = this.s.fields;
  2077. $.each( this._fieldNames( name ), function ( i, n ) {
  2078. fields[ n ].enable();
  2079. } );
  2080. return this;
  2081. };
  2082. /**
  2083. * Show that a field, or the form globally, is in an error state. Note that
  2084. * errors are cleared on each submission of the form.
  2085. * @param {string} [name] The name of the field that is in error. If not
  2086. * given then the global form error display is used.
  2087. * @param {string} msg The error message to show
  2088. * @return {Editor} Editor instance, for chaining
  2089. *
  2090. * @example
  2091. * // Show an error if the field is required
  2092. * editor.create( 'Add new user', {
  2093. * "label": "Submit",
  2094. * "fn": function () {
  2095. * if ( this.get('username') === '' ) {
  2096. * this.error( 'username', 'A user name is required' );
  2097. * return;
  2098. * }
  2099. * this.submit();
  2100. * }
  2101. * } );
  2102. *
  2103. * @example
  2104. * // Show a field and a global error for a required field
  2105. * editor.create( 'Add new user', {
  2106. * "label": "Submit",
  2107. * "fn": function () {
  2108. * if ( this.get('username') === '' ) {
  2109. * this.error( 'username', 'A user name is required' );
  2110. * this.error( 'The data could not be saved because it is incomplete' );
  2111. * return;
  2112. * }
  2113. * this.submit();
  2114. * }
  2115. * } );
  2116. */
  2117. Editor.prototype.error = function ( name, msg )
  2118. {
  2119. if ( msg === undefined ) {
  2120. // Global error
  2121. this._message( this.dom.formError, name );
  2122. }
  2123. else {
  2124. // Field error
  2125. this.s.fields[ name ].error( msg );
  2126. }
  2127. return this;
  2128. };
  2129. /**
  2130. * Get a field object, configured for a named field, which can then be
  2131. * manipulated through its API. This function effectively acts as a
  2132. * proxy to the field extensions, allowing easy access to the methods
  2133. * for a named field. The methods that are available depend upon the field
  2134. * type plug-in for Editor.
  2135. *
  2136. * @param {string} name Field name to be obtained
  2137. * @return {Editor.Field} Field instance
  2138. *
  2139. * @example
  2140. * // Update the values available in a select list
  2141. * editor.field('island').update( [
  2142. * 'Lewis and Harris',
  2143. * 'South Uist',
  2144. * 'North Uist',
  2145. * 'Benbecula',
  2146. * 'Barra'
  2147. * ] );
  2148. *
  2149. * @example
  2150. * // Equivalent calls
  2151. * editor.field('name').set('John Smith');
  2152. *
  2153. * // results in the same action as:
  2154. * editor.set('John Smith');
  2155. */
  2156. Editor.prototype.field = function ( name )
  2157. {
  2158. return this.s.fields[ name ];
  2159. };
  2160. /**
  2161. * Get a list of the fields that are used by the Editor instance.
  2162. * @returns {string[]} Array of field names
  2163. *
  2164. * @example
  2165. * // Get current fields and move first item to the end
  2166. * var fields = editor.fields();
  2167. * var first = fields.shift();
  2168. * fields.push( first );
  2169. * editor.order( fields );
  2170. */
  2171. Editor.prototype.fields = function ()
  2172. {
  2173. return $.map( this.s.fields, function ( field, name ) {
  2174. return name;
  2175. } );
  2176. };
  2177. /**
  2178. * Get the value of a field
  2179. * @param {string|array} [name] The field name (from the `name` parameter given
  2180. * when originally setting up the field) to disable. If not given, then an
  2181. * object of fields is returned, with the value of each field from the
  2182. * instance represented in the array (the object properties are the field
  2183. * names). Also an array of field names can be given to get a collection of
  2184. * data from the form.
  2185. * @returns {*|object} Value from the named field
  2186. *
  2187. * @example
  2188. * // Client-side validation - check that a field has been given a value
  2189. * // before submitting the form
  2190. * editor.create( 'Add new user', {
  2191. * "label": "Submit",
  2192. * "fn": function () {
  2193. * if ( this.get('username') === '' ) {
  2194. * this.error( 'username', 'A user name is required' );
  2195. * return;
  2196. * }
  2197. * this.submit();
  2198. * }
  2199. * } );
  2200. */
  2201. Editor.prototype.get = function ( name )
  2202. {
  2203. var fields = this.s.fields;
  2204. if ( ! name ) {
  2205. name = this.fields();
  2206. }
  2207. if ( $.isArray( name ) ) {
  2208. var out = {};
  2209. $.each( name, function (i, n) {
  2210. out[n] = fields[n].get();
  2211. } );
  2212. return out;
  2213. }
  2214. return fields[ name ].get();
  2215. };
  2216. /**
  2217. * Remove a field from the form display. Note that the field will still be submitted
  2218. * with the other fields in the form, but it simply won't be visible to the user.
  2219. * @param {string|array} [name] The field name (from the `name` parameter given when
  2220. * originally setting up the field) to hide or an array of names. If not given then all
  2221. * fields are hidden.
  2222. * @param {boolean} [animate=true] Animate if visible
  2223. * @return {Editor} Editor instance, for chaining
  2224. *
  2225. * @example
  2226. * // Show a 'create' record form, but with some fields hidden
  2227. * editor.hide( 'account_type' );
  2228. * editor.hide( 'access_level' );
  2229. * editor.create( 'Add new user', {
  2230. * "label": "Save",
  2231. * "fn": function () { this.submit(); }
  2232. * } );
  2233. *
  2234. * @example
  2235. * // Show a single field by hiding all and then showing one
  2236. * editor.hide();
  2237. * editor.show('access_type');
  2238. */
  2239. Editor.prototype.hide = function ( names, animate )
  2240. {
  2241. var fields = this.s.fields;
  2242. $.each( this._fieldNames( names ), function (i, n) {
  2243. fields[ n ].hide( animate );
  2244. } );
  2245. return this;
  2246. };
  2247. /**
  2248. * Determine if there is an error state in the form, either the form's global
  2249. * error message, or one or more fields.
  2250. *
  2251. * @param {string|array|undefined} [inNames] The field names to check. All
  2252. * fields checked if undefined.
  2253. * @return {boolean} `true` if there is an error in the form
  2254. */
  2255. Editor.prototype.inError = function ( inNames )
  2256. {
  2257. // Is there a global error?
  2258. if ( $(this.dom.formError).is(':visible') ) {
  2259. return true;
  2260. }
  2261. // Field specific
  2262. var fields = this.s.fields;
  2263. var names = this._fieldNames( inNames );
  2264. for ( var i=0, ien=names.length ; i<ien ; i++ ) {
  2265. if ( fields[ names[i] ].inError() ) {
  2266. return true;
  2267. }
  2268. }
  2269. return false;
  2270. };
  2271. /**
  2272. * Inline editing for a single field. This method provides a method to allow
  2273. * end users to very quickly edit fields in place. For example, a user could
  2274. * simply click on a cell in a table, the contents of which would be replaced
  2275. * with the editing input field for that cell.
  2276. *
  2277. * @param {string|node|DataTables.Api|cell-selector} cell The cell or field to
  2278. * be edited (note that for table editing this must be a cell - for standalone
  2279. * editing it can also be the field name to edit).
  2280. * @param {string} [fieldName] The field name to be edited. This parameter is
  2281. * optional. If not provided, Editor will attempt to resolve the correct field
  2282. * from the cell / element given as the first parameter. If it is unable to do
  2283. * so, it will throw an error.
  2284. * @param {object} [opts] Inline editing options - see the `form-options` type
  2285. * @return {Editor} Editor instance, for chaining
  2286. */
  2287. Editor.prototype.inline = function ( cell, fieldName, opts )
  2288. {
  2289. var that = this;
  2290. // Argument shifting
  2291. if ( $.isPlainObject( fieldName ) ) {
  2292. opts = fieldName;
  2293. fieldName = undefined;
  2294. }
  2295. opts = $.extend( {}, this.s.formOptions.inline, opts );
  2296. var editFields = this._dataSource( 'individual', cell, fieldName );
  2297. var node, field;
  2298. var countOuter=0, countInner;
  2299. var closed=false;
  2300. // Read the individual cell information from the editFields object
  2301. $.each( editFields, function ( i, editField ) {
  2302. // Only a single row
  2303. if ( countOuter > 0 ) {
  2304. throw 'Cannot edit more than one row inline at a time';
  2305. }
  2306. node = $(editField.attach[0]);
  2307. // Only a single item in that row
  2308. countInner = 0;
  2309. $.each( editField.displayFields, function ( j, f ) {
  2310. if ( countInner > 0 ) {
  2311. throw 'Cannot edit more than one field inline at a time';
  2312. }
  2313. field = f;
  2314. countInner++;
  2315. } );
  2316. countOuter++;
  2317. // If only changed values are to be submitted, then only allow the
  2318. // individual field that we are editing to be edited.
  2319. // This is currently disabled, as I'm not convinced that it is actually
  2320. // useful!
  2321. // if ( opts.submit === 'changed' ) {
  2322. // editField.fields = editField.displayFields;
  2323. // }
  2324. } );
  2325. // Already in edit mode for this cell?
  2326. if ( $('div.DTE_Field', node).length ) {
  2327. return this;
  2328. }
  2329. // Some other field in inline edit mode?
  2330. if ( this._tidy( function () { that.inline( cell, fieldName, opts ); } ) ) {
  2331. return this;
  2332. }
  2333. // Start a full row edit, but don't display - we will be showing the field
  2334. this._edit( cell, editFields, 'inline' );
  2335. var namespace = this._formOptions( opts );
  2336. var ret = this._preopen( 'inline' );
  2337. if ( ! ret ) {
  2338. return this;
  2339. }
  2340. // Remove from DOM, keeping event handlers, and include text nodes in remove
  2341. var children = node.contents().detach();
  2342. node.append( $(
  2343. '<div class="DTE DTE_Inline">'+
  2344. '<div class="DTE_Inline_Field"/>'+
  2345. '<div class="DTE_Inline_Buttons"/>'+
  2346. '</div>'
  2347. ) );
  2348. node.find('div.DTE_Inline_Field').append( field.node() );
  2349. if ( opts.buttons ) {
  2350. // Use prepend for the CSS, so we can float the buttons right
  2351. node.find('div.DTE_Inline_Buttons').append( this.dom.buttons );
  2352. }
  2353. this._closeReg( function ( submitComplete ) {
  2354. // Mark that this specific inline edit has closed
  2355. closed = true;
  2356. $(document).off( 'click'+namespace );
  2357. // If there was no submit, we need to put the DOM back as it was. If
  2358. // there was a submit, the write of the new value will set the DOM to
  2359. // how it should be
  2360. if ( ! submitComplete ) {
  2361. node.contents().detach();
  2362. node.append( children );
  2363. }
  2364. // Clear error messages "offline"
  2365. that._clearDynamicInfo();
  2366. } );
  2367. // Submit and blur actions
  2368. setTimeout( function () {
  2369. // If already closed, possibly due to some other aspect of the event
  2370. // that triggered the inline call, don't add the event listener - it
  2371. // isn't needed (and is dangerous)
  2372. if ( closed ) {
  2373. return;
  2374. }
  2375. $(document).on( 'click'+namespace, function ( e ) {
  2376. // Was the click inside or owned by the editing node? If not, then
  2377. // come out of editing mode.
  2378. // andSelf is deprecated in jQ1.8, but we want 1.7 compat
  2379. var back = $.fn.addBack ? 'addBack' : 'andSelf';
  2380. if ( ! field._typeFn( 'owns', e.target ) &&
  2381. $.inArray( node[0], $(e.target).parents()[ back ]() ) === -1 )
  2382. {
  2383. that.blur();
  2384. }
  2385. } );
  2386. }, 0 );
  2387. this._focus( [ field ], opts.focus );
  2388. this._postopen( 'inline' );
  2389. return this;
  2390. };
  2391. /**
  2392. * Show an information message for the form as a whole, or for an individual
  2393. * field. This can be used to provide helpful information to a user about an
  2394. * individual field, or more typically the form (for example when deleting
  2395. * a record and asking for confirmation).
  2396. * @param {string} [name] The name of the field to show the message for. If not
  2397. * given then a global message is shown for the form
  2398. * @param {string|function} msg The message to show
  2399. * @return {Editor} Editor instance, for chaining
  2400. *
  2401. * @example
  2402. * // Show a global message for a 'create' form
  2403. * editor.message( 'Add a new user to the database by completing the fields below' );
  2404. * editor.create( 'Add new user', {
  2405. * "label": "Submit",
  2406. * "fn": function () { this.submit(); }
  2407. * } );
  2408. *
  2409. * @example
  2410. * // Show a message for an individual field when a 'help' icon is clicked on
  2411. * $('#user_help').click( function () {
  2412. * editor.message( 'user', 'The user name is what the system user will login with' );
  2413. * } );
  2414. */
  2415. Editor.prototype.message = function ( name, msg )
  2416. {
  2417. if ( msg === undefined ) {
  2418. // Global message
  2419. this._message( this.dom.formInfo, name );
  2420. }
  2421. else {
  2422. // Field message
  2423. this.s.fields[ name ].message( msg );
  2424. }
  2425. return this;
  2426. };
  2427. /**
  2428. * Get which mode of operation the Editor form is in
  2429. * @return {string} `create`, `edit`, `remove` or `null` if no active state.
  2430. */
  2431. Editor.prototype.mode = function ()
  2432. {
  2433. return this.s.action;
  2434. };
  2435. /**
  2436. * Get the modifier that was used to trigger the edit or delete action.
  2437. * @return {*} The identifier that was used for the editing / remove method
  2438. * called.
  2439. */
  2440. Editor.prototype.modifier = function ()
  2441. {
  2442. return this.s.modifier;
  2443. };
  2444. /**
  2445. * Get the values from one or more fields, taking into account multiple data
  2446. * points being edited at the same time.
  2447. *
  2448. * @param {string|array} fieldNames A single field name or an array of field
  2449. * names.
  2450. * @return {object} If a string is given as the first parameter an object that
  2451. * contains the value for each row being edited is returned. If an array is
  2452. * given, then the object has the field names as the parameter name and the
  2453. * value is the value object with values for each row being edited.
  2454. */
  2455. Editor.prototype.multiGet = function ( fieldNames )
  2456. {
  2457. var fields = this.s.fields;
  2458. if ( fieldNames === undefined ) {
  2459. fieldNames = this.fields();
  2460. }
  2461. if ( $.isArray( fieldNames ) ) {
  2462. var out = {};
  2463. $.each( fieldNames, function ( i, name ) {
  2464. out[ name ] = fields[ name ].multiGet();
  2465. } );
  2466. return out;
  2467. }
  2468. return fields[ fieldNames ].multiGet();
  2469. };
  2470. /**
  2471. * Set the values for one or more fields, taking into account multiple data
  2472. * points being edited at the same time.
  2473. *
  2474. * @param {object|string} fieldNames The name of the field to set, or an object
  2475. * with the field names as the parameters that contains the value object to
  2476. * set for each field.
  2477. * @param {*} [val] Value to set if first parameter is given as a string.
  2478. * Otherwise it is ignored.
  2479. * @return {Editor} Editor instance, for chaining
  2480. */
  2481. Editor.prototype.multiSet = function ( fieldNames, val )
  2482. {
  2483. var fields = this.s.fields;
  2484. if ( $.isPlainObject( fieldNames ) && val === undefined ) {
  2485. $.each( fieldNames, function ( name, value ) {
  2486. fields[ name ].multiSet( value );
  2487. } );
  2488. }
  2489. else {
  2490. fields[ fieldNames ].multiSet( val );
  2491. }
  2492. return this;
  2493. };
  2494. /**
  2495. * Get the container node for an individual field.
  2496. * @param {string|array} name The field name (from the `name` parameter given
  2497. * when originally setting up the field) to get the DOM node for.
  2498. * @return {node|array} Field container node
  2499. *
  2500. * @example
  2501. * // Dynamically add a class to a field's container
  2502. * $(editor.node( 'account_type' )).addClass( 'account' );
  2503. */
  2504. Editor.prototype.node = function ( name )
  2505. {
  2506. var fields = this.s.fields;
  2507. if ( ! name ) {
  2508. name = this.order();
  2509. }
  2510. return $.isArray( name ) ?
  2511. $.map( name, function (n) {
  2512. return fields[ n ].node();
  2513. } ) :
  2514. fields[ name ].node();
  2515. };
  2516. /**
  2517. * Remove a bound event listener to the editor instance. This method provides a
  2518. * shorthand way of binding jQuery events that would be the same as writing
  2519. * `$(editor).off(...)` for convenience.
  2520. * @param {string} name Event name to remove the listeners for - event names are
  2521. * defined by {@link Editor}.
  2522. * @param {function} [fn] The function to remove. If not given, all functions which
  2523. * are assigned to the given event name will be removed.
  2524. * @return {Editor} Editor instance, for chaining
  2525. *
  2526. * @example
  2527. * // Add an event to alert when the form is shown and then remove the listener
  2528. * // so it will only fire once
  2529. * editor.on( 'open', function () {
  2530. * alert('Form displayed!');
  2531. * editor.off( 'open' );
  2532. * } );
  2533. */
  2534. Editor.prototype.off = function ( name, fn )
  2535. {
  2536. $(this).off( this._eventName( name ), fn );
  2537. return this;
  2538. };
  2539. /**
  2540. * Listen for an event which is fired off by Editor when it performs certain
  2541. * actions. This method provides a shorthand way of binding jQuery events that
  2542. * would be the same as writing `$(editor).on(...)` for convenience.
  2543. * @param {string} name Event name to add the listener for - event names are
  2544. * defined by {@link Editor}.
  2545. * @param {function} fn The function to run when the event is triggered.
  2546. * @return {Editor} Editor instance, for chaining
  2547. *
  2548. * @example
  2549. * // Log events on the console when they occur
  2550. * editor.on( 'open', function () { console.log( 'Form opened' ); } );
  2551. * editor.on( 'close', function () { console.log( 'Form closed' ); } );
  2552. * editor.on( 'submit', function () { console.log( 'Form submitted' ); } );
  2553. */
  2554. Editor.prototype.on = function ( name, fn )
  2555. {
  2556. $(this).on( this._eventName( name ), fn );
  2557. return this;
  2558. };
  2559. /**
  2560. * Listen for a single event event which is fired off by Editor when it performs
  2561. * certain actions. This method provides a shorthand way of binding jQuery
  2562. * events that would be the same as writing `$(editor).one(...)` for
  2563. * convenience.
  2564. * @param {string} name Event name to add the listener for - event names are
  2565. * defined by {@link Editor}.
  2566. * @param {function} fn The function to run when the event is triggered.
  2567. * @return {Editor} Editor instance, for chaining
  2568. */
  2569. Editor.prototype.one = function ( name, fn )
  2570. {
  2571. $(this).one( this._eventName( name ), fn );
  2572. return this;
  2573. };
  2574. /**
  2575. * Display the main form editor to the end user in the web-browser.
  2576. *
  2577. * Note that the `close()` method will close any of the three Editor form types
  2578. * (main, bubble and inline), but this method will open only the main type.
  2579. * @return {Editor} Editor instance, for chaining
  2580. *
  2581. * @example
  2582. * // Build a 'create' form, but don't display it until some values have
  2583. * // been set. When done, then display the form.
  2584. * editor.create( 'Create user', {
  2585. * "label": "Submit",
  2586. * "fn": function () { this.submit(); }
  2587. * }, false );
  2588. * editor.set( 'name', 'Test user' );
  2589. * editor.set( 'access', 'Read only' );
  2590. * editor.open();
  2591. */
  2592. Editor.prototype.open = function ()
  2593. {
  2594. var that = this;
  2595. // Insert the display elements in order
  2596. this._displayReorder();
  2597. // Define how to do a close
  2598. this._closeReg( function ( submitComplete ) {
  2599. that.s.displayController.close( that, function () {
  2600. that._clearDynamicInfo();
  2601. } );
  2602. } );
  2603. // Run the standard open with common events
  2604. var ret = this._preopen( 'main' );
  2605. if ( ! ret ) {
  2606. return this;
  2607. }
  2608. this.s.displayController.open( this, this.dom.wrapper );
  2609. this._focus(
  2610. $.map( this.s.order, function (name) {
  2611. return that.s.fields[ name ];
  2612. } ),
  2613. this.s.editOpts.focus
  2614. );
  2615. this._postopen( 'main' );
  2616. return this;
  2617. };
  2618. /**
  2619. * Get or set the ordering of fields, as they are displayed in the form. When used as
  2620. * a getter, the field names are returned in an array, in their current order, and when
  2621. * used as a setting you can alter the field ordering by passing in an array with all
  2622. * field names in their new order.
  2623. *
  2624. * Note that all fields *must* be included when reordering, and no additional fields can
  2625. * be added here (use {@link Editor#add} to add more fields). Finally, for setting the
  2626. * order, you can pass an array of the field names, or give the field names as individual
  2627. * parameters (see examples below).
  2628. * @param {array|string} [set] Field order to set.
  2629. * @return {Editor} Editor instance, for chaining
  2630. *
  2631. * @example
  2632. * // Get field ordering
  2633. * var order = editor.order();
  2634. *
  2635. * @example
  2636. * // Set the field order
  2637. * var order = editor.order();
  2638. * order.unshift( order.pop() ); // move the last field into the first position
  2639. * editor.order( order );
  2640. *
  2641. * @example
  2642. * // Set the field order as arguments
  2643. * editor.order( "pupil", "grade", "dept", "exam-board" );
  2644. *
  2645. */
  2646. Editor.prototype.order = function ( set /*, ... */ )
  2647. {
  2648. if ( !set ) {
  2649. return this.s.order;
  2650. }
  2651. // Allow new layout to be passed in as arguments
  2652. if ( arguments.length && ! $.isArray( set ) ) {
  2653. set = Array.prototype.slice.call(arguments);
  2654. }
  2655. // Sanity check - array must exactly match the fields we have available
  2656. if ( this.s.order.slice().sort().join('-') !== set.slice().sort().join('-') ) {
  2657. throw "All fields, and no additional fields, must be provided for ordering.";
  2658. }
  2659. // Copy the new array into the order (so the reference is maintained)
  2660. $.extend( this.s.order, set );
  2661. this._displayReorder();
  2662. return this;
  2663. };
  2664. /**
  2665. * Remove (delete) entries from the table. The rows to remove are given as
  2666. * either a single DOM node or an array of DOM nodes (including a jQuery
  2667. * object).
  2668. * @param {node|array} items The row, or array of nodes, to delete
  2669. * @param {boolean} [show=true] Show the form or not.
  2670. * @return {Editor} Editor instance, for chaining
  2671. *
  2672. * @example
  2673. * // Delete a given row with a message to let the user know exactly what is
  2674. * // happening
  2675. * editor.message( "Are you sure you want to remove this row?" );
  2676. * editor.remove( row_to_delete, 'Delete row', {
  2677. * "label": "Confirm",
  2678. * "fn": function () { this.submit(); }
  2679. * } );
  2680. *
  2681. * @example
  2682. * // Delete the first row in a table without asking the user for confirmation
  2683. * editor.remove( '', $('#example tbody tr:eq(0)')[0], null, false );
  2684. * editor.submit();
  2685. *
  2686. * @example
  2687. * // Delete all rows in a table with a submit button
  2688. * editor.remove( $('#example tbody tr'), 'Delete all rows', {
  2689. * "label": "Delete all",
  2690. * "fn": function () { this.submit(); }
  2691. * } );
  2692. */
  2693. Editor.prototype.remove = function ( items, arg1, arg2, arg3, arg4 )
  2694. {
  2695. var that = this;
  2696. // Some other field in inline edit mode?
  2697. if ( this._tidy( function () { that.remove( items, arg1, arg2, arg3, arg4 ); } ) ) {
  2698. return this;
  2699. }
  2700. // Allow a single row node to be passed in to remove, Can't use $.isArray
  2701. // as we also allow array like objects to be passed in (API, jQuery)
  2702. if ( items.length === undefined ) {
  2703. items = [ items ];
  2704. }
  2705. var argOpts = this._crudArgs( arg1, arg2, arg3, arg4 );
  2706. var editFields = this._dataSource( 'fields', items );
  2707. this.s.action = "remove";
  2708. this.s.modifier = items;
  2709. this.s.editFields = editFields;
  2710. this.dom.form.style.display = 'none';
  2711. this._actionClass();
  2712. this._event( 'initRemove', [
  2713. _pluck( editFields, 'node' ),
  2714. _pluck( editFields, 'data' ),
  2715. items
  2716. ] );
  2717. this._event( 'initMultiRemove', [
  2718. editFields,
  2719. items
  2720. ] );
  2721. this._assembleMain();
  2722. this._formOptions( argOpts.opts );
  2723. argOpts.maybeOpen();
  2724. var opts = this.s.editOpts;
  2725. if ( opts.focus !== null ) {
  2726. $('button', this.dom.buttons).eq( opts.focus ).focus();
  2727. }
  2728. return this;
  2729. };
  2730. /**
  2731. * Set the value of a field
  2732. * @param {string|object} name The field name (from the `name` parameter given
  2733. * when originally setting up the field) to set the value of. If given as an
  2734. * object the object parameter name will be the value of the field to set and
  2735. * the value the value to set for the field.
  2736. * @param {*} [val] The value to set the field to. The format of the value will
  2737. * depend upon the field type. Not required if the first parameter is given
  2738. * as an object.
  2739. * @return {Editor} Editor instance, for chaining
  2740. *
  2741. * @example
  2742. * // Set the values of a few fields before then automatically submitting the form
  2743. * editor.create( null, null, false );
  2744. * editor.set( 'name', 'Test user' );
  2745. * editor.set( 'access', 'Read only' );
  2746. * editor.submit();
  2747. */
  2748. Editor.prototype.set = function ( set, val )
  2749. {
  2750. var fields = this.s.fields;
  2751. if ( ! $.isPlainObject( set ) ) {
  2752. var o = {};
  2753. o[ set ] = val;
  2754. set = o;
  2755. }
  2756. $.each( set, function (n, v) {
  2757. fields[ n ].set( v );
  2758. } );
  2759. return this;
  2760. };
  2761. /**
  2762. * Show a field in the display that was previously hidden.
  2763. * @param {string|array} [names] The field name (from the `name` parameter
  2764. * given when originally setting up the field) to make visible, or an array of
  2765. * field names to make visible. If not given all fields are shown.
  2766. * @param {boolean} [animate=true] Animate if visible
  2767. * @return {Editor} Editor instance, for chaining
  2768. *
  2769. * @example
  2770. * // Shuffle the fields that are visible, hiding one field and making two
  2771. * // others visible before then showing the {@link Editor#create} record form.
  2772. * editor.hide( 'username' );
  2773. * editor.show( 'account_type' );
  2774. * editor.show( 'access_level' );
  2775. * editor.create( 'Add new user', {
  2776. * "label": "Save",
  2777. * "fn": function () { this.submit(); }
  2778. * } );
  2779. *
  2780. * @example
  2781. * // Show all fields
  2782. * editor.show();
  2783. */
  2784. Editor.prototype.show = function ( names, animate )
  2785. {
  2786. var fields = this.s.fields;
  2787. $.each( this._fieldNames( names ), function (i, n) {
  2788. fields[ n ].show( animate );
  2789. } );
  2790. return this;
  2791. };
  2792. /**
  2793. * Submit a form to the server for processing. The exact action performed will depend
  2794. * on which of the methods {@link Editor#create}, {@link Editor#edit} or
  2795. * {@link Editor#remove} were called to prepare the form - regardless of which one is
  2796. * used, you call this method to submit data.
  2797. * @param {function} [successCallback] Callback function that is executed once the
  2798. * form has been successfully submitted to the server and no errors occurred.
  2799. * @param {function} [errorCallback] Callback function that is executed if the
  2800. * server reports an error due to the submission (this includes a JSON formatting
  2801. * error should the error return invalid JSON).
  2802. * @param {function} [formatdata] Callback function that is passed in the data
  2803. * that will be submitted to the server, allowing pre-formatting of the data,
  2804. * removal of data or adding of extra fields.
  2805. * @param {boolean} [hide=true] When the form is successfully submitted, by default
  2806. * the form display will be hidden - this option allows that to be overridden.
  2807. * @return {Editor} Editor instance, for chaining
  2808. *
  2809. * @example
  2810. * // Submit data from a form button
  2811. * editor.create( 'Add new record', {
  2812. * "label": "Save",
  2813. * "fn": function () {
  2814. * this.submit();
  2815. * }
  2816. * } );
  2817. *
  2818. * @example
  2819. * // Submit without showing the user the form
  2820. * editor.create( null, null, false );
  2821. * editor.submit();
  2822. *
  2823. * @example
  2824. * // Provide success and error callback methods
  2825. * editor.create( 'Add new record', {
  2826. * "label": "Save",
  2827. * "fn": function () {
  2828. * this.submit( function () {
  2829. * alert( 'Form successfully submitted!' );
  2830. * }, function () {
  2831. * alert( 'Form encountered an error :-(' );
  2832. * }
  2833. * );
  2834. * }
  2835. * } );
  2836. *
  2837. * @example
  2838. * // Add an extra field to the data
  2839. * editor.create( 'Add new record', {
  2840. * "label": "Save",
  2841. * "fn": function () {
  2842. * this.submit( null, null, function (data) {
  2843. * data.extra = "Extra information";
  2844. * } );
  2845. * }
  2846. * } );
  2847. *
  2848. * @example
  2849. * // Don't hide the form immediately - change the title and then close the form
  2850. * // after a small amount of time
  2851. * editor.create( 'Add new record', {
  2852. * "label": "Save",
  2853. * "fn": function () {
  2854. * this.submit(
  2855. * function () {
  2856. * var that = this;
  2857. * this.title( 'Data successfully added!' );
  2858. * setTimeout( function () {
  2859. * that.close();
  2860. * }, 1000 );
  2861. * },
  2862. * null,
  2863. * null,
  2864. * false
  2865. * );
  2866. * }
  2867. * } );
  2868. *
  2869. */
  2870. Editor.prototype.submit = function ( successCallback, errorCallback, formatdata, hide )
  2871. {
  2872. var
  2873. that = this,
  2874. fields = this.s.fields,
  2875. errorFields = [],
  2876. errorReady = 0,
  2877. sent = false;
  2878. if ( this.s.processing || ! this.s.action ) {
  2879. return this;
  2880. }
  2881. this._processing( true );
  2882. // If there are fields in error, we want to wait for the error notification
  2883. // to be cleared before the form is submitted - errorFields tracks the
  2884. // fields which are in the error state, while errorReady tracks those which
  2885. // are ready to submit
  2886. var send = function () {
  2887. if ( errorFields.length !== errorReady || sent ) {
  2888. return;
  2889. }
  2890. sent = true;
  2891. that._submit( successCallback, errorCallback, formatdata, hide );
  2892. };
  2893. // Remove the global error (don't know if the form is still in an error
  2894. // state!)
  2895. this.error();
  2896. // Count how many fields are in error
  2897. $.each( fields, function ( name, field ) {
  2898. if ( field.inError() ) {
  2899. errorFields.push( name );
  2900. }
  2901. } );
  2902. // Remove the error display
  2903. $.each( errorFields, function ( i, name ) {
  2904. fields[ name ].error('', function () {
  2905. errorReady++;
  2906. send();
  2907. } );
  2908. } );
  2909. send();
  2910. return this;
  2911. };
  2912. /**
  2913. * Set the title of the form
  2914. * @param {string|function} title The title to give to the form
  2915. * @return {Editor} Editor instance, for chaining
  2916. *
  2917. * @example
  2918. * // Create an edit display used the title, buttons and edit methods (note that
  2919. * // this is just an example, typically you would use the parameters of the edit
  2920. * // method to achieve this.
  2921. * editor.title( 'Edit record' );
  2922. * editor.buttons( {
  2923. * "label": "Update",
  2924. * "fn": function () { this.submit(); }
  2925. * } );
  2926. * editor.edit( TR_to_edit );
  2927. *
  2928. * @example
  2929. * // Show a create form, with a timer for the duration that the form is open
  2930. * editor.create( 'Add new record - time on form: 0s', {
  2931. * "label": "Save",
  2932. * "fn": function () { this.submit(); }
  2933. * } );
  2934. *
  2935. * // Add an event to the editor to stop the timer when the display is removed
  2936. * var runTimer = true;
  2937. * var timer = 0;
  2938. * editor.on( 'close', function () {
  2939. * runTimer = false;
  2940. * editor.off( 'close' );
  2941. * } );
  2942. * // Start the timer running
  2943. * updateTitle();
  2944. *
  2945. * // Local function to update the title once per second
  2946. * function updateTitle() {
  2947. * editor.title( 'Add new record - time on form: '+timer+'s' );
  2948. * timer++;
  2949. * if ( runTimer ) {
  2950. * setTimeout( function() {
  2951. * updateTitle();
  2952. * }, 1000 );
  2953. * }
  2954. * }
  2955. */
  2956. Editor.prototype.title = function ( title )
  2957. {
  2958. var header = $(this.dom.header).children( 'div.'+this.classes.header.content );
  2959. if ( title === undefined ) {
  2960. return header.html();
  2961. }
  2962. if ( typeof title === 'function' ) {
  2963. title = title( this, new DataTable.Api(this.s.table) );
  2964. }
  2965. header.html( title );
  2966. return this;
  2967. };
  2968. /**
  2969. * Get or set the value of a specific field, or get the value of all fields in
  2970. * the form.
  2971. *
  2972. * @param {string|array} [names] The field name(s) to get or set the value of.
  2973. * If not given, then the value of all fields will be obtained.
  2974. * @param {*} [value] Value to set
  2975. * @return {Editor|object|*} Editor instance, for chaining if used as a setter,
  2976. * an object containing the values of the requested fields if used as a
  2977. * getter with multiple fields requested, or the value of the requested field
  2978. * if a single field is requested.
  2979. */
  2980. Editor.prototype.val = function ( field, value )
  2981. {
  2982. if ( value === undefined ) {
  2983. return this.get( field ); // field can be undefined to get all
  2984. }
  2985. return this.set( field, value );
  2986. };
  2987. /*
  2988. * DataTables 1.10 API integration. Provides the ability to control basic Editor
  2989. * aspects from the DataTables API. Full control does of course require use of
  2990. * the Editor API though.
  2991. */
  2992. var apiRegister = DataTable.Api.register;
  2993. function __getInst( api ) {
  2994. var ctx = api.context[0];
  2995. return ctx.oInit.editor || ctx._editor;
  2996. }
  2997. // Set sensible defaults for the editing options
  2998. function __setBasic( inst, opts, type, plural ) {
  2999. if ( ! opts ) {
  3000. opts = {};
  3001. }
  3002. if ( opts.buttons === undefined ) {
  3003. opts.buttons = '_basic';
  3004. }
  3005. if ( opts.title === undefined ) {
  3006. opts.title = inst.i18n[ type ].title;
  3007. }
  3008. if ( opts.message === undefined ) {
  3009. if ( type === 'remove' ) {
  3010. var confirm = inst.i18n[ type ].confirm;
  3011. opts.message = plural!==1 ? confirm._.replace(/%d/, plural) : confirm['1'];
  3012. }
  3013. else {
  3014. opts.message = '';
  3015. }
  3016. }
  3017. return opts;
  3018. }
  3019. apiRegister( 'editor()', function () {
  3020. return __getInst( this );
  3021. } );
  3022. // Row editing
  3023. apiRegister( 'row.create()', function ( opts ) {
  3024. // main
  3025. var inst = __getInst( this );
  3026. inst.create( __setBasic( inst, opts, 'create' ) );
  3027. return this;
  3028. } );
  3029. apiRegister( 'row().edit()', function ( opts ) {
  3030. // main
  3031. var inst = __getInst( this );
  3032. inst.edit( this[0][0], __setBasic( inst, opts, 'edit' ) );
  3033. return this;
  3034. } );
  3035. apiRegister( 'rows().edit()', function ( opts ) {
  3036. // main
  3037. var inst = __getInst( this );
  3038. inst.edit( this[0], __setBasic( inst, opts, 'edit' ) );
  3039. return this;
  3040. } );
  3041. apiRegister( 'row().delete()', function ( opts ) {
  3042. // main
  3043. var inst = __getInst( this );
  3044. inst.remove( this[0][0], __setBasic( inst, opts, 'remove', 1 ) );
  3045. return this;
  3046. } );
  3047. apiRegister( 'rows().delete()', function ( opts ) {
  3048. // main
  3049. var inst = __getInst( this );
  3050. inst.remove( this[0], __setBasic( inst, opts, 'remove', this[0].length ) );
  3051. return this;
  3052. } );
  3053. apiRegister( 'cell().edit()', function ( type, opts ) {
  3054. // inline or bubble
  3055. if ( ! type ) {
  3056. type = 'inline';
  3057. }
  3058. else if ( $.isPlainObject( type ) ) {
  3059. opts = type;
  3060. type = 'inline';
  3061. }
  3062. __getInst( this )[ type ]( this[0][0], opts );
  3063. return this;
  3064. } );
  3065. apiRegister( 'cells().edit()', function ( opts ) {
  3066. // bubble only at the moment
  3067. __getInst( this ).bubble( this[0], opts );
  3068. return this;
  3069. } );
  3070. apiRegister( 'file()', function ( name, id ) {
  3071. return Editor.files[ name ][ id ];
  3072. } );
  3073. apiRegister( 'files()', function ( name, value ) {
  3074. if ( ! name ) {
  3075. return Editor.files;
  3076. }
  3077. if ( ! value ) {
  3078. return Editor.files[ name ];
  3079. }
  3080. // The setter option of this method is not publicly documented
  3081. Editor.files[ name ] = value;
  3082. return this;
  3083. } );
  3084. // Global listener for file information updates via DataTables' Ajax JSON
  3085. $(document).on( 'xhr.dt', function (e, ctx, json) {
  3086. if ( e.namespace !== 'dt' ) {
  3087. return;
  3088. }
  3089. if ( json && json.files ) {
  3090. $.each( json.files, function ( name, files ) {
  3091. Editor.files[ name ] = files;
  3092. } );
  3093. }
  3094. } );
  3095. /**
  3096. * Common error message emitter. This method is not (yet) publicly documented on
  3097. * the Editor site. It might be in future.
  3098. *
  3099. * @param {string} msg Error message
  3100. * @param {int} tn Tech note link
  3101. */
  3102. Editor.error = function ( msg, tn )
  3103. {
  3104. throw tn ?
  3105. msg +' For more information, please refer to https://datatables.net/tn/'+tn :
  3106. msg;
  3107. };
  3108. /**
  3109. * Obtain label / value pairs of data from a data source, be it an array or
  3110. * object, for use in an input that requires label / value pairs such as
  3111. * `select`, `radio` and `checkbox` inputs.
  3112. *
  3113. * A callback function is triggered for each label / value pair found, so the
  3114. * caller can add it to the input as required.
  3115. *
  3116. * @static
  3117. * @param {object|array} An object or array of data to iterate over getting the
  3118. * label / value pairs.
  3119. * @param {object} props When an array of objects is passed in as the data
  3120. * source by default the label will be read from the `label` property and
  3121. * the value from the `value` property of the object. This option can alter
  3122. * that behaviour.
  3123. * @param {function} fn Callback function. Takes three parameters: the label,
  3124. * the value and the iterator index.
  3125. */
  3126. Editor.pairs = function ( data, props, fn )
  3127. {
  3128. var i, ien, dataPoint;
  3129. // Define default properties to read the data from if using an object.
  3130. // The passed in `props` object and override.
  3131. props = $.extend( {
  3132. label: 'label',
  3133. value: 'value'
  3134. }, props );
  3135. if ( $.isArray( data ) ) {
  3136. // As an array, we iterate each item which can be an object or value
  3137. for ( i=0, ien=data.length ; i<ien ; i++ ) {
  3138. dataPoint = data[i];
  3139. if ( $.isPlainObject( dataPoint ) ) {
  3140. fn(
  3141. dataPoint[ props.value ] === undefined ?
  3142. dataPoint[ props.label ] :
  3143. dataPoint[ props.value ],
  3144. dataPoint[ props.label ],
  3145. i
  3146. );
  3147. }
  3148. else {
  3149. fn( dataPoint, dataPoint, i );
  3150. }
  3151. }
  3152. }
  3153. else {
  3154. // As an object the key is the label and the value is the value
  3155. i = 0;
  3156. $.each( data, function ( key, val ) {
  3157. fn( val, key, i );
  3158. i++;
  3159. } );
  3160. }
  3161. };
  3162. /**
  3163. * Make a string safe to use as a DOM ID. This is primarily for use by field
  3164. * plug-in authors.
  3165. *
  3166. * @static
  3167. * @param {string} String to make safe
  3168. * @param {string} Safe string
  3169. */
  3170. Editor.safeId = function ( id )
  3171. {
  3172. return id.replace(/\./g, '-');
  3173. };
  3174. /**
  3175. * Field specific upload method. This can be used to upload a file to the Editor
  3176. * libraries. This method is not (yet) publicly documented on the Editor site.
  3177. * It might be in future.
  3178. *
  3179. * @static
  3180. * @param {Editor} editor The Editor instance operating on
  3181. * @param {object} conf Field configuration object
  3182. * @param {Files} files The file(s) to upload
  3183. * @param {function} progressCallback Upload progress callback
  3184. * @param {function} completeCallback Callback function for once the file has
  3185. * been uploaded
  3186. */
  3187. Editor.upload = function ( editor, conf, files, progressCallback, completeCallback )
  3188. {
  3189. var reader = new FileReader();
  3190. var counter = 0;
  3191. var ids = [];
  3192. var generalError = 'A server error occurred while uploading the file';
  3193. // Clear any existing errors, as the new upload might not be in error
  3194. editor.error( conf.name, '' );
  3195. progressCallback( conf, conf.fileReadText || "<i>Uploading file</i>" );
  3196. reader.onload = function ( e ) {
  3197. var data = new FormData();
  3198. var ajax;
  3199. data.append( 'action', 'upload' );
  3200. data.append( 'uploadField', conf.name );
  3201. data.append( 'upload', files[ counter ] );
  3202. if ( conf.ajaxData ) {
  3203. conf.ajaxData( data );
  3204. }
  3205. if ( conf.ajax ) {
  3206. ajax = conf.ajax;
  3207. }
  3208. else if ( typeof editor.s.ajax === 'string' || $.isPlainObject( editor.s.ajax ) ) {
  3209. ajax = editor.s.ajax;
  3210. }
  3211. if ( ! ajax ) {
  3212. throw 'No Ajax option specified for upload plug-in';
  3213. }
  3214. if ( typeof ajax === 'string' ) {
  3215. ajax = { url: ajax };
  3216. }
  3217. // Use preSubmit to stop form submission during an upload, since the
  3218. // value won't be known until that point.
  3219. var submit = false;
  3220. editor
  3221. .on( 'preSubmit.DTE_Upload', function () {
  3222. submit = true;
  3223. return false;
  3224. } );
  3225. $.ajax( $.extend( {}, ajax, {
  3226. type: 'post',
  3227. data: data,
  3228. dataType: 'json',
  3229. contentType: false,
  3230. processData: false,
  3231. xhr: function () {
  3232. var xhr = $.ajaxSettings.xhr();
  3233. if ( xhr.upload ) {
  3234. xhr.upload.onprogress = function ( e ) {
  3235. if ( e.lengthComputable ) {
  3236. var percent = (e.loaded/e.total*100).toFixed(0)+"%";
  3237. progressCallback( conf, files.length === 1 ?
  3238. percent :
  3239. counter+':'+files.length+' '+percent
  3240. );
  3241. }
  3242. };
  3243. xhr.upload.onloadend = function ( e ) {
  3244. progressCallback( conf );
  3245. };
  3246. }
  3247. return xhr;
  3248. },
  3249. success: function ( json ) {
  3250. editor.off( 'preSubmit.DTE_Upload' );
  3251. if ( json.fieldErrors && json.fieldErrors.length ) {
  3252. var errors = json.fieldErrors;
  3253. for ( var i=0, ien=errors.length ; i<ien ; i++ ) {
  3254. editor.error( errors[i].name, errors[i].status );
  3255. }
  3256. }
  3257. else if ( json.error ) {
  3258. editor.error( json.error );
  3259. }
  3260. else if ( ! json.upload || ! json.upload.id ) {
  3261. editor.error( conf.name, generalError );
  3262. }
  3263. else {
  3264. if ( json.files ) {
  3265. $.each( json.files, function ( name, value ) {
  3266. Editor.files[ name ] = value;
  3267. } );
  3268. }
  3269. ids.push( json.upload.id );
  3270. if ( counter < files.length-1 ) {
  3271. counter++;
  3272. reader.readAsDataURL( files[counter] );
  3273. }
  3274. else {
  3275. completeCallback.call( editor, ids );
  3276. if ( submit ) {
  3277. editor.submit();
  3278. }
  3279. }
  3280. }
  3281. },
  3282. error: function () {
  3283. editor.error( conf.name, generalError );
  3284. }
  3285. } ) );
  3286. };
  3287. reader.readAsDataURL( files[0] );
  3288. };
  3289. /**
  3290. * Editor constructor - take the developer configuration and apply it to the instance.
  3291. * @param {object} init The initialisation options provided by the developer - see
  3292. * {@link Editor.defaults} for a full list of options.
  3293. * @private
  3294. */
  3295. Editor.prototype._constructor = function ( init )
  3296. {
  3297. init = $.extend( true, {}, Editor.defaults, init );
  3298. this.s = $.extend( true, {}, Editor.models.settings, {
  3299. table: init.domTable || init.table,
  3300. dbTable: init.dbTable || null, // legacy
  3301. ajaxUrl: init.ajaxUrl,
  3302. ajax: init.ajax,
  3303. idSrc: init.idSrc,
  3304. dataSource: init.domTable || init.table ?
  3305. Editor.dataSources.dataTable :
  3306. Editor.dataSources.html,
  3307. formOptions: init.formOptions,
  3308. legacyAjax: init.legacyAjax
  3309. } );
  3310. this.classes = $.extend( true, {}, Editor.classes );
  3311. this.i18n = init.i18n;
  3312. var that = this;
  3313. var classes = this.classes;
  3314. this.dom = {
  3315. "wrapper": $(
  3316. '<div class="'+classes.wrapper+'">'+
  3317. '<div data-dte-e="processing" class="'+classes.processing.indicator+'"></div>'+
  3318. '<div data-dte-e="body" class="'+classes.body.wrapper+'">'+
  3319. '<div data-dte-e="body_content" class="'+classes.body.content+'"/>'+
  3320. '</div>'+
  3321. '<div data-dte-e="foot" class="'+classes.footer.wrapper+'">'+
  3322. '<div class="'+classes.footer.content+'"/>'+
  3323. '</div>'+
  3324. '</div>'
  3325. )[0],
  3326. "form": $(
  3327. '<form data-dte-e="form" class="'+classes.form.tag+'">'+
  3328. '<div data-dte-e="form_content" class="'+classes.form.content+'"/>'+
  3329. '</form>'
  3330. )[0],
  3331. "formError": $('<div data-dte-e="form_error" class="'+classes.form.error+'"/>')[0],
  3332. "formInfo": $('<div data-dte-e="form_info" class="'+classes.form.info+'"/>')[0],
  3333. "header": $('<div data-dte-e="head" class="'+classes.header.wrapper+'"><div class="'+classes.header.content+'"/></div>')[0],
  3334. "buttons": $('<div data-dte-e="form_buttons" class="'+classes.form.buttons+'"/>')[0]
  3335. };
  3336. // Customise the TableTools buttons with the i18n settings - worth noting that
  3337. // this could easily be done outside the Editor instance, but this makes things
  3338. // a bit easier to understand and more cohesive. Also worth noting that when
  3339. // there are two or more Editor instances, the init sequence should be
  3340. // Editor / DataTables, Editor / DataTables etc, since the value of these button
  3341. // instances matter when you create the TableTools buttons for the DataTable.
  3342. if ( $.fn.dataTable.TableTools ) {
  3343. var ttButtons = $.fn.dataTable.TableTools.BUTTONS;
  3344. var i18n = this.i18n;
  3345. $.each(['create', 'edit', 'remove'], function (i, val) {
  3346. ttButtons['editor_'+val].sButtonText = i18n[val].button;
  3347. } );
  3348. }
  3349. // Bind callback methods
  3350. $.each( init.events, function (evt, fn) {
  3351. that.on( evt, function () {
  3352. // When giving events in the constructor the event argument was not
  3353. // given in 1.2-, so we remove it here. This is solely for
  3354. // backwards compatibility as the events in the initialisation are
  3355. // not documented in 1.3+.
  3356. var args = Array.prototype.slice.call(arguments);
  3357. args.shift();
  3358. fn.apply( that, args );
  3359. } );
  3360. } );
  3361. // Cache the DOM nodes
  3362. var dom = this.dom;
  3363. var wrapper = dom.wrapper;
  3364. dom.formContent = _editor_el('form_content', dom.form)[0];
  3365. dom.footer = _editor_el('foot', wrapper)[0];
  3366. dom.body = _editor_el('body', wrapper)[0];
  3367. dom.bodyContent = _editor_el('body_content', wrapper)[0];
  3368. dom.processing = _editor_el('processing', wrapper)[0];
  3369. // Add any fields which are given on initialisation
  3370. if ( init.fields ) {
  3371. this.add( init.fields );
  3372. }
  3373. $(document)
  3374. .on( 'init.dt.dte', function (e, settings, json) {
  3375. // Attempt to attach to a DataTable automatically when the table is
  3376. // initialised
  3377. if ( that.s.table && settings.nTable === $(that.s.table).get(0) ) {
  3378. settings._editor = that;
  3379. }
  3380. } )
  3381. .on( 'xhr.dt', function (e, settings, json) {
  3382. // Automatically update fields which have a field name defined in
  3383. // the returned json - saves an `initComplete` for the user
  3384. if ( json && that.s.table && settings.nTable === $(that.s.table).get(0) ) {
  3385. that._optionsUpdate( json );
  3386. }
  3387. } );
  3388. // Prep the display controller
  3389. this.s.displayController = Editor.display[init.display].init( this );
  3390. this._event( 'initComplete', [] );
  3391. };
  3392. /*global __inlineCounter*/
  3393. /**
  3394. * Set the class on the form to relate to the action that is being performed.
  3395. * This allows styling to be applied to the form to reflect the state that
  3396. * it is in.
  3397. *
  3398. * @private
  3399. */
  3400. Editor.prototype._actionClass = function ()
  3401. {
  3402. var classesActions = this.classes.actions;
  3403. var action = this.s.action;
  3404. var wrapper = $(this.dom.wrapper);
  3405. wrapper.removeClass( [classesActions.create, classesActions.edit, classesActions.remove].join(' ') );
  3406. if ( action === "create" ) {
  3407. wrapper.addClass( classesActions.create );
  3408. }
  3409. else if ( action === "edit" ) {
  3410. wrapper.addClass( classesActions.edit );
  3411. }
  3412. else if ( action === "remove" ) {
  3413. wrapper.addClass( classesActions.remove );
  3414. }
  3415. };
  3416. /**
  3417. * Create an Ajax request in the same style as DataTables 1.10, with full
  3418. * backwards compatibility for Editor 1.2.
  3419. *
  3420. * @param {object} data Data to submit
  3421. * @param {function} success Success callback
  3422. * @param {function} error Error callback
  3423. * @private
  3424. */
  3425. Editor.prototype._ajax = function ( data, success, error )
  3426. {
  3427. var opts = {
  3428. type: 'POST',
  3429. dataType: 'json',
  3430. data: null,
  3431. error: error,
  3432. success: function ( json, status, xhr ) {
  3433. if ( xhr.status === 204 ) {
  3434. json = {};
  3435. }
  3436. success( json );
  3437. }
  3438. };
  3439. var a;
  3440. var action = this.s.action;
  3441. var ajaxSrc = this.s.ajax || this.s.ajaxUrl;
  3442. var id = action === 'edit' || action === 'remove' ?
  3443. _pluck( this.s.editFields, 'idSrc' ) :
  3444. null;
  3445. if ( $.isArray( id ) ) {
  3446. id = id.join(',');
  3447. }
  3448. // Get the correct object for rest style
  3449. if ( $.isPlainObject( ajaxSrc ) && ajaxSrc[ action ] ) {
  3450. ajaxSrc = ajaxSrc[ action ];
  3451. }
  3452. if ( $.isFunction( ajaxSrc ) ) {
  3453. // As a function, execute it, passing in the required parameters
  3454. var uri = null;
  3455. var method = null;
  3456. // If the old style ajaxSrc is given, we need to process it for
  3457. // backwards compatibility with 1.2-. Unfortunate otherwise this would
  3458. // be a very simply function!
  3459. if ( this.s.ajaxUrl ) {
  3460. var url = this.s.ajaxUrl;
  3461. if ( url.create ) {
  3462. uri = url[ action ];
  3463. }
  3464. if ( uri.indexOf(' ') !== -1 ) {
  3465. a = uri.split(' ');
  3466. method = a[0];
  3467. uri = a[1];
  3468. }
  3469. uri = uri.replace( /_id_/, id );
  3470. }
  3471. ajaxSrc( method, uri, data, success, error );
  3472. return;
  3473. }
  3474. else if ( typeof ajaxSrc === 'string' ) {
  3475. // As a string it gives the URL. For backwards compatibility it can also
  3476. // give the method.
  3477. if ( ajaxSrc.indexOf(' ') !== -1 ) {
  3478. a = ajaxSrc.split(' ');
  3479. opts.type = a[0];
  3480. opts.url = a[1];
  3481. }
  3482. else {
  3483. opts.url = ajaxSrc;
  3484. }
  3485. }
  3486. else {
  3487. // As an object, we extend the defaults
  3488. opts = $.extend( {}, opts, ajaxSrc || {} );
  3489. }
  3490. // URL macros
  3491. opts.url = opts.url.replace( /_id_/, id );
  3492. // Data processing option like in DataTables
  3493. if ( opts.data ) {
  3494. var newData = $.isFunction( opts.data ) ?
  3495. opts.data( data ) : // fn can manipulate data or return an object
  3496. opts.data; // object or array to merge
  3497. // If the function returned something, use that alone
  3498. data = $.isFunction( opts.data ) && newData ?
  3499. newData :
  3500. $.extend( true, data, newData );
  3501. }
  3502. opts.data = data;
  3503. // If a DELETE method is used there are a number of servers which will
  3504. // reject the request if it has a body. So we need to append to the URL.
  3505. //
  3506. // http://stackoverflow.com/questions/15088955
  3507. // http://bugs.jquery.com/ticket/11586
  3508. if ( opts.type === 'DELETE' ) {
  3509. var params = $.param( opts.data );
  3510. opts.url += opts.url.indexOf('?') === -1 ?
  3511. '?'+params :
  3512. '&'+params;
  3513. delete opts.data;
  3514. }
  3515. // Finally, make the ajax call
  3516. $.ajax( opts );
  3517. };
  3518. /**
  3519. * Create the DOM structure from the source elements for the main form.
  3520. * This is required since the elements can be moved around for other form types
  3521. * (bubble).
  3522. *
  3523. * @private
  3524. */
  3525. Editor.prototype._assembleMain = function ()
  3526. {
  3527. var dom = this.dom;
  3528. $(dom.wrapper)
  3529. .prepend( dom.header );
  3530. $(dom.footer)
  3531. .append( dom.formError )
  3532. .append( dom.buttons );
  3533. $(dom.bodyContent)
  3534. .append( dom.formInfo )
  3535. .append( dom.form );
  3536. };
  3537. /**
  3538. * Blur the editing window. A blur is different from a close in that it might
  3539. * cause either a close or the form to be submitted. A typical example of a
  3540. * blur would be clicking on the background of the bubble or main editing forms
  3541. * - i.e. it might be a close, or it might submit depending upon the
  3542. * configuration, while a click on the close box is a very definite close.
  3543. *
  3544. * @private
  3545. */
  3546. Editor.prototype._blur = function ()
  3547. {
  3548. var opts = this.s.editOpts;
  3549. if ( this._event( 'preBlur' ) === false ) {
  3550. return;
  3551. }
  3552. if ( opts.onBlur === 'submit' ) {
  3553. this.submit();
  3554. }
  3555. else if ( opts.onBlur === 'close' ) {
  3556. this._close();
  3557. }
  3558. };
  3559. /**
  3560. * Clear all of the information that might have been dynamically set while
  3561. * the form was visible - specifically errors and dynamic messages
  3562. *
  3563. * @private
  3564. */
  3565. Editor.prototype._clearDynamicInfo = function ()
  3566. {
  3567. var errorClass = this.classes.field.error;
  3568. var fields = this.s.fields;
  3569. $('div.'+errorClass, this.dom.wrapper).removeClass( errorClass );
  3570. $.each( fields, function (name, field) {
  3571. field
  3572. .error('')
  3573. .message('');
  3574. } );
  3575. this
  3576. .error('')
  3577. .message('');
  3578. };
  3579. /**
  3580. * Close an editing display, firing callbacks and events as needed
  3581. *
  3582. * @param {function} submitComplete Function to call after the preClose event
  3583. * @private
  3584. */
  3585. Editor.prototype._close = function ( submitComplete )
  3586. {
  3587. // Allow preClose event to cancel the opening of the display
  3588. if ( this._event( 'preClose' ) === false ) {
  3589. return;
  3590. }
  3591. if ( this.s.closeCb ) {
  3592. this.s.closeCb( submitComplete );
  3593. this.s.closeCb = null;
  3594. }
  3595. if ( this.s.closeIcb ) {
  3596. this.s.closeIcb();
  3597. this.s.closeIcb = null;
  3598. }
  3599. // Remove focus control
  3600. $('body').off( 'focus.editor-focus' );
  3601. this.s.displayed = false;
  3602. this._event( 'close' );
  3603. };
  3604. /**
  3605. * Register a function to be called when the editing display is closed. This is
  3606. * used by function that create the editing display to tidy up the display on
  3607. * close - for example removing event handlers to prevent memory leaks.
  3608. *
  3609. * @param {function} fn Function to call on close
  3610. * @private
  3611. */
  3612. Editor.prototype._closeReg = function ( fn )
  3613. {
  3614. this.s.closeCb = fn;
  3615. };
  3616. /**
  3617. * Argument shifting for the create(), edit() and remove() methods. In Editor
  3618. * 1.3 the preferred form of calling those three methods is with just two
  3619. * parameters (one in the case of create() - the id and the show flag), while in
  3620. * previous versions four / three parameters could be passed in, including the
  3621. * buttons and title options. In 1.3 the chaining API is preferred, but we want
  3622. * to support the old form as well, so this function is provided to perform
  3623. * that argument shifting, common to all three.
  3624. *
  3625. * @private
  3626. */
  3627. Editor.prototype._crudArgs = function ( arg1, arg2, arg3, arg4 )
  3628. {
  3629. var that = this;
  3630. var title;
  3631. var buttons;
  3632. var show;
  3633. var opts;
  3634. if ( $.isPlainObject( arg1 ) ) {
  3635. // Form options passed in as the first option
  3636. opts = arg1;
  3637. }
  3638. else if ( typeof arg1 === 'boolean' ) {
  3639. // Show / hide passed in as the first option - form options second
  3640. show = arg1;
  3641. opts = arg2; // can be undefined
  3642. }
  3643. else {
  3644. // Old style arguments
  3645. title = arg1; // can be undefined
  3646. buttons = arg2; // can be undefined
  3647. show = arg3; // can be undefined
  3648. opts = arg4; // can be undefined
  3649. }
  3650. // If all undefined, then fall into here
  3651. if ( show === undefined ) {
  3652. show = true;
  3653. }
  3654. if ( title ) {
  3655. that.title( title );
  3656. }
  3657. if ( buttons ) {
  3658. that.buttons( buttons );
  3659. }
  3660. return {
  3661. opts: $.extend( {}, this.s.formOptions.main, opts ),
  3662. maybeOpen: function () {
  3663. if ( show ) {
  3664. that.open();
  3665. }
  3666. }
  3667. };
  3668. };
  3669. /**
  3670. * Execute the data source abstraction layer functions. This is simply a case
  3671. * of executing the function with the Editor scope, passing in the remaining
  3672. * parameters.
  3673. *
  3674. * @param {string) name Function name to execute
  3675. * @private
  3676. */
  3677. Editor.prototype._dataSource = function ( name /*, ... */ )
  3678. {
  3679. // Remove the name from the arguments list, so the rest can be passed
  3680. // straight into the field type
  3681. var args = Array.prototype.slice.call( arguments );
  3682. args.shift();
  3683. var fn = this.s.dataSource[ name ];
  3684. if ( fn ) {
  3685. return fn.apply( this, args );
  3686. }
  3687. };
  3688. /**
  3689. * Insert the fields into the DOM, in the correct order
  3690. *
  3691. * @private
  3692. */
  3693. Editor.prototype._displayReorder = function ( includeFields )
  3694. {
  3695. var formContent = $(this.dom.formContent);
  3696. var fields = this.s.fields;
  3697. var order = this.s.order;
  3698. if ( includeFields ) {
  3699. this.s.includeFields = includeFields;
  3700. }
  3701. else {
  3702. includeFields = this.s.includeFields;
  3703. }
  3704. // Empty before adding in the required fields
  3705. formContent.children().detach();
  3706. $.each( order, function (i, fieldOrName) {
  3707. var name = fieldOrName instanceof Editor.Field ?
  3708. fieldOrName.name() :
  3709. fieldOrName;
  3710. if ( $.inArray( name, includeFields ) !== -1 ) {
  3711. formContent.append( fields[ name ].node() );
  3712. }
  3713. } );
  3714. this._event( 'displayOrder', [
  3715. this.s.displayed,
  3716. this.s.action,
  3717. formContent
  3718. ] );
  3719. };
  3720. /**
  3721. * Generic editing handler. This can be called by the three editing modes (main,
  3722. * bubble and inline) to configure Editor for a row edit, and fire the required
  3723. * events to ensure that the editing interfaces all provide a common API.
  3724. *
  3725. * @param {*} rows Identifier for the item(s) to be edited
  3726. * @param {string} type Editing type - for the initEdit event
  3727. * @private
  3728. */
  3729. Editor.prototype._edit = function ( items, editFields, type )
  3730. {
  3731. var that = this;
  3732. var fields = this.s.fields;
  3733. var usedFields = [];
  3734. var includeInOrder;
  3735. this.s.editFields = editFields;
  3736. this.s.modifier = items;
  3737. this.s.action = "edit";
  3738. this.dom.form.style.display = 'block';
  3739. this._actionClass();
  3740. // Setup the field values for editing
  3741. $.each( fields, function ( name, field ) {
  3742. field.multiReset();
  3743. includeInOrder = true;
  3744. $.each( editFields, function ( idSrc, edit ) {
  3745. if ( edit.fields[ name ] ) {
  3746. var val = field.valFromData( edit.data );
  3747. field.multiSet( idSrc, val !== undefined ?
  3748. val :
  3749. field.def()
  3750. );
  3751. // If there is an displayFields object, we need to know if this
  3752. // field is present in it or not. If not, then the field isn't
  3753. // displayed
  3754. if ( edit.displayFields && ! edit.displayFields[ name ] ) {
  3755. includeInOrder = false;
  3756. }
  3757. }
  3758. } );
  3759. // If the field is used, then add it to the fields to be shown
  3760. if ( field.multiIds().length !== 0 && includeInOrder ) {
  3761. usedFields.push( name );
  3762. }
  3763. } );
  3764. // Remove the fields that are not required from the display
  3765. var currOrder = this.order().slice();
  3766. for ( var i=currOrder.length ; i >= 0 ; i-- ) {
  3767. if ( $.inArray( currOrder[i], usedFields ) === -1 ) {
  3768. currOrder.splice( i, 1 );
  3769. }
  3770. }
  3771. this._displayReorder( currOrder );
  3772. // Save the set data values so we can decided in submit if data has changed
  3773. this.s.editData = $.extend( true, {}, this.multiGet() );
  3774. // Events
  3775. this._event( 'initEdit', [
  3776. _pluck( editFields, 'node' )[0],
  3777. _pluck( editFields, 'data' )[0],
  3778. items,
  3779. type
  3780. ] );
  3781. this._event( 'initMultiEdit', [
  3782. editFields,
  3783. items,
  3784. type
  3785. ] );
  3786. };
  3787. /**
  3788. * Fire callback functions and trigger events.
  3789. *
  3790. * @param {string|array} trigger Name(s) of the jQuery custom event to trigger
  3791. * @param {array) args Array of arguments to pass to the triggered event
  3792. * @return {*} Return from the event
  3793. * @private
  3794. */
  3795. Editor.prototype._event = function ( trigger, args )
  3796. {
  3797. if ( ! args ) {
  3798. args = [];
  3799. }
  3800. // Allow an array to be passed in for the trigger to fire multiple events
  3801. if ( $.isArray( trigger ) ) {
  3802. for ( var i=0, ien=trigger.length ; i<ien ; i++ ) {
  3803. this._event( trigger[i], args );
  3804. }
  3805. }
  3806. else {
  3807. var e = $.Event( trigger );
  3808. $(this).triggerHandler( e, args );
  3809. return e.result;
  3810. }
  3811. };
  3812. /**
  3813. * 'Modernise' event names, from the old style `on[A-Z]` names to camelCase.
  3814. * This is done to provide backwards compatibility with Editor 1.2- event names.
  3815. * The names themselves were updated for consistency with DataTables.
  3816. *
  3817. * @param {string} Event name to modernise
  3818. * @return {string} String with new event name structure
  3819. * @private
  3820. */
  3821. Editor.prototype._eventName = function ( input )
  3822. {
  3823. var name;
  3824. var names = input.split( ' ' );
  3825. for ( var i=0, ien=names.length ; i<ien ; i++ ) {
  3826. name = names[i];
  3827. // Strip the 'on' part and lowercase the first character
  3828. var onStyle = name.match(/^on([A-Z])/);
  3829. if ( onStyle ) {
  3830. name = onStyle[1].toLowerCase() + name.substring( 3 );
  3831. }
  3832. names[i] = name;
  3833. }
  3834. return names.join( ' ' );
  3835. };
  3836. /**
  3837. * Convert a field name input parameter to an array of field names.
  3838. *
  3839. * Many of the API methods provide the ability to pass `undefined` a string or
  3840. * array of strings to identify fields. This method harmonises that.
  3841. *
  3842. * @param {array|string} [fieldNames] Field names to get
  3843. * @return {array} Field names
  3844. * @private
  3845. */
  3846. Editor.prototype._fieldNames = function ( fieldNames )
  3847. {
  3848. if ( fieldNames === undefined ) {
  3849. return this.fields();
  3850. }
  3851. else if ( ! $.isArray( fieldNames ) ) {
  3852. return [ fieldNames ];
  3853. }
  3854. return fieldNames;
  3855. };
  3856. /**
  3857. * Focus on a field. Providing the logic to allow complex focus expressions
  3858. *
  3859. * @param {array} fields Array of Field instances or field names for the fields
  3860. * that are shown
  3861. * @param {null|string|integer} focus Field identifier to focus on
  3862. * @private
  3863. */
  3864. Editor.prototype._focus = function ( fieldsIn, focus )
  3865. {
  3866. var that = this;
  3867. var field;
  3868. var fields = $.map( fieldsIn, function ( fieldOrName ) {
  3869. return typeof fieldOrName === 'string' ?
  3870. that.s.fields[ fieldOrName ] :
  3871. fieldOrName;
  3872. } );
  3873. if ( typeof focus === 'number' ) {
  3874. field = fields[ focus ];
  3875. }
  3876. else if ( focus ) {
  3877. if ( focus.indexOf( 'jq:' ) === 0 ) {
  3878. field = $('div.DTE '+focus.replace(/^jq:/, ''));
  3879. }
  3880. else {
  3881. field = this.s.fields[ focus ];
  3882. }
  3883. }
  3884. this.s.setFocus = field;
  3885. if ( field ) {
  3886. field.focus();
  3887. }
  3888. };
  3889. /**
  3890. * Form options - common function so all editing methods can provide the same
  3891. * basic options, DRY.
  3892. *
  3893. * @param {object} opts Editing options. See model.formOptions
  3894. * @private
  3895. */
  3896. Editor.prototype._formOptions = function ( opts )
  3897. {
  3898. var that = this;
  3899. var inlineCount = __inlineCounter++;
  3900. var namespace = '.dteInline'+inlineCount;
  3901. // Backwards compatibility with 1.4
  3902. if ( opts.closeOnComplete !== undefined ) {
  3903. opts.onComplete = opts.closeOnComplete ? 'close' : 'none';
  3904. }
  3905. if ( opts.submitOnBlur !== undefined ) {
  3906. opts.onBlur = opts.submitOnBlur ? 'submit' : 'close';
  3907. }
  3908. if ( opts.submitOnReturn !== undefined ) {
  3909. opts.onReturn = opts.submitOnReturn ? 'submit' : 'none';
  3910. }
  3911. if ( opts.blurOnBackground !== undefined ) {
  3912. opts.onBackground = opts.blurOnBackground ? 'blur' : 'none';
  3913. }
  3914. this.s.editOpts = opts;
  3915. // When submitting by Ajax we don't want to close a form that has been
  3916. // opened during the ajax request, so we keep a count of the form opening
  3917. this.s.editCount = inlineCount;
  3918. if ( typeof opts.title === 'string' || typeof opts.title === 'function' ) {
  3919. this.title( opts.title );
  3920. opts.title = true;
  3921. }
  3922. if ( typeof opts.message === 'string' || typeof opts.message === 'function' ) {
  3923. this.message( opts.message );
  3924. opts.message = true;
  3925. }
  3926. if ( typeof opts.buttons !== 'boolean' ) {
  3927. this.buttons( opts.buttons );
  3928. opts.buttons = true;
  3929. }
  3930. $(document).on( 'keydown'+namespace, function ( e ) {
  3931. var el = $(document.activeElement);
  3932. var name = el.length ? el[0].nodeName.toLowerCase() : null;
  3933. var type = $(el).attr('type');
  3934. var returnFriendlyNode = name === 'input';
  3935. if ( that.s.displayed && opts.onReturn === 'submit' && e.keyCode === 13 && returnFriendlyNode ) { // return
  3936. e.preventDefault();
  3937. that.submit();
  3938. }
  3939. else if ( e.keyCode === 27 ) { // esc
  3940. e.preventDefault();
  3941. switch( opts.onEsc ) {
  3942. case 'blur':
  3943. that.blur();
  3944. break;
  3945. case 'close':
  3946. that.close();
  3947. break;
  3948. case 'submit':
  3949. that.submit();
  3950. break;
  3951. default: // 'none' - no action
  3952. break;
  3953. }
  3954. }
  3955. else if ( el.parents('.DTE_Form_Buttons').length ) {
  3956. if ( e.keyCode === 37 ) { // left
  3957. el.prev( 'button' ).focus();
  3958. }
  3959. else if ( e.keyCode === 39 ) { // right
  3960. el.next( 'button' ).focus();
  3961. }
  3962. }
  3963. } );
  3964. this.s.closeIcb = function () {
  3965. $(document).off( 'keydown'+namespace );
  3966. };
  3967. return namespace;
  3968. };
  3969. /**
  3970. * Convert from the 1.5+ data interchange format to the 1.4- format if suitable.
  3971. *
  3972. * @param {string} direction 'send' or 'receive'
  3973. * @param {string} action CRUD action
  3974. * @param {object} data Data object to transform
  3975. * @private
  3976. */
  3977. Editor.prototype._legacyAjax = function ( direction, action, data )
  3978. {
  3979. if ( ! this.s.legacyAjax ) {
  3980. return;
  3981. }
  3982. if ( direction === 'send' ) {
  3983. if ( action === 'create' || action === 'edit' ) {
  3984. var id;
  3985. $.each( data.data, function ( rowId, values ) {
  3986. if ( id !== undefined ) {
  3987. throw 'Editor: Multi-row editing is not supported by the legacy Ajax data format';
  3988. }
  3989. id = rowId;
  3990. } );
  3991. data.data = data.data[ id ];
  3992. if ( action === 'edit' ) {
  3993. data.id = id;
  3994. }
  3995. }
  3996. else {
  3997. data.id = $.map( data.data, function ( values, id ) {
  3998. return id;
  3999. } );
  4000. delete data.data;
  4001. }
  4002. }
  4003. else {
  4004. if ( ! data.data && data.row ) {
  4005. // 1.4 libraries retuned data in the `row` property
  4006. data.data = [ data.row ];
  4007. }
  4008. else {
  4009. // 1.4- allowed data not to be returned - 1.5 requires it
  4010. data.data = [];
  4011. }
  4012. }
  4013. };
  4014. /**
  4015. * Update the field options from a JSON data source
  4016. *
  4017. * @param {object} json JSON object from the server
  4018. * @private
  4019. */
  4020. Editor.prototype._optionsUpdate = function ( json )
  4021. {
  4022. var that = this;
  4023. if ( json.options ) {
  4024. $.each( this.s.fields, function (name, field) {
  4025. if ( json.options[ name ] !== undefined ) {
  4026. var fieldInst = that.field( name );
  4027. if ( fieldInst && fieldInst.update ) {
  4028. fieldInst.update( json.options[ name ] );
  4029. }
  4030. }
  4031. } );
  4032. }
  4033. };
  4034. /**
  4035. * Show a message in the form. This can be used for error messages or dynamic
  4036. * messages (information display) as the structure for each is basically the
  4037. * same. This method will take into account if the form is visible or not - if
  4038. * so then the message is shown with an effect for the end user, otherwise
  4039. * it is just set immediately.
  4040. *
  4041. * @param {element} el The field display node to use
  4042. * @param {string|function} msg The message to show
  4043. * @private
  4044. */
  4045. Editor.prototype._message = function ( el, msg )
  4046. {
  4047. if ( typeof msg === 'function' ) {
  4048. msg = msg( this, new DataTable.Api(this.s.table) );
  4049. }
  4050. el = $(el);
  4051. if ( ! msg && this.s.displayed ) {
  4052. // Clear the message with visual effect since the form is visible
  4053. el
  4054. .stop()
  4055. .fadeOut( function () {
  4056. el.html( '' );
  4057. } );
  4058. }
  4059. else if ( ! msg ) {
  4060. // Clear the message without visual effect
  4061. el
  4062. .html( '' )
  4063. .css('display', 'none');
  4064. }
  4065. else if ( this.s.displayed ) {
  4066. // Show the message with visual effect
  4067. el
  4068. .stop()
  4069. .html( msg )
  4070. .fadeIn();
  4071. }
  4072. else {
  4073. // Show the message without visual effect
  4074. el
  4075. .html( msg )
  4076. .css('display', 'block');
  4077. }
  4078. };
  4079. /**
  4080. * Update the multi-value information display to not show redundant information
  4081. *
  4082. * @private
  4083. */
  4084. Editor.prototype._multiInfo = function ()
  4085. {
  4086. var fields = this.s.fields;
  4087. var include = this.s.includeFields;
  4088. var show = true;
  4089. if ( ! include ) {
  4090. return;
  4091. }
  4092. for ( var i=0, ien=include.length ; i<ien ; i++ ) {
  4093. var field = fields[ include[i] ];
  4094. if ( field.isMultiValue() && show ) {
  4095. fields[ include[i] ].multiInfoShown( show );
  4096. show = false;
  4097. }
  4098. else {
  4099. fields[ include[i] ].multiInfoShown( false );
  4100. }
  4101. }
  4102. };
  4103. /**
  4104. * Common display editing form method called by all editing methods after the
  4105. * form has been configured and displayed. This is to ensure all fire the same
  4106. * events.
  4107. *
  4108. * @param {string} Editing type
  4109. * @return {boolean} `true`
  4110. * @private
  4111. */
  4112. Editor.prototype._postopen = function ( type )
  4113. {
  4114. var that = this;
  4115. var focusCapture = this.s.displayController.captureFocus;
  4116. if ( focusCapture === undefined ) {
  4117. focusCapture = true;
  4118. }
  4119. $(this.dom.form)
  4120. .off( 'submit.editor-internal' )
  4121. .on( 'submit.editor-internal', function (e) {
  4122. e.preventDefault();
  4123. } );
  4124. // Focus capture - when the Editor form is shown we capture the browser's
  4125. // focus action. Without doing this is would result in the user being able
  4126. // to control items under the Editor display - triggering actions that
  4127. // shouldn't be possible while the editing is shown.
  4128. if ( focusCapture && (type === 'main' || type === 'bubble') ) {
  4129. $('body').on( 'focus.editor-focus', function () {
  4130. if ( $(document.activeElement).parents('.DTE').length === 0 &&
  4131. $(document.activeElement).parents('.DTED').length === 0
  4132. ) {
  4133. if ( that.s.setFocus ) {
  4134. that.s.setFocus.focus();
  4135. }
  4136. }
  4137. } );
  4138. }
  4139. this._multiInfo();
  4140. this._event( 'open', [type, this.s.action] );
  4141. return true;
  4142. };
  4143. /**
  4144. * Common display editing form method called by all editing methods before the
  4145. * form has been configured and displayed. This is to ensure all fire the same
  4146. * events.
  4147. *
  4148. * @param {string} Editing type
  4149. * @return {boolean} `false` if the open is cancelled by the preOpen event,
  4150. * otherwise `true`
  4151. * @private
  4152. */
  4153. Editor.prototype._preopen = function ( type )
  4154. {
  4155. // Allow preOpen event to cancel the opening of the display
  4156. if ( this._event( 'preOpen', [type, this.s.action] ) === false ) {
  4157. // Tidy- this would normally be done on close, but we never get that far
  4158. this._clearDynamicInfo();
  4159. return false;
  4160. }
  4161. this.s.displayed = type;
  4162. return true;
  4163. };
  4164. /**
  4165. * Set the form into processing mode or take it out of processing mode. In
  4166. * processing mode a processing indicator is shown and user interaction with the
  4167. * form buttons is blocked
  4168. *
  4169. * @param {boolean} processing true if to go into processing mode and false if
  4170. * to come out of processing mode
  4171. * @private
  4172. */
  4173. Editor.prototype._processing = function ( processing )
  4174. {
  4175. var wrapper = $(this.dom.wrapper);
  4176. var procStyle = this.dom.processing.style;
  4177. var procClass = this.classes.processing.active;
  4178. if ( processing ) {
  4179. procStyle.display = 'block';
  4180. wrapper.addClass( procClass );
  4181. $('div.DTE').addClass( procClass );
  4182. }
  4183. else {
  4184. procStyle.display = 'none';
  4185. wrapper.removeClass( procClass );
  4186. $('div.DTE').removeClass( procClass );
  4187. }
  4188. this.s.processing = processing;
  4189. this._event( 'processing', [processing] );
  4190. };
  4191. /**
  4192. * Submit a form to the server for processing. This is the private method that is used
  4193. * by the 'submit' API method, which should always be called in preference to calling
  4194. * this method directly.
  4195. *
  4196. * @param {function} [successCallback] Callback function that is executed once the
  4197. * form has been successfully submitted to the server and no errors occurred.
  4198. * @param {function} [errorCallback] Callback function that is executed if the
  4199. * server reports an error due to the submission (this includes a JSON formatting
  4200. * error should the error return invalid JSON).
  4201. * @param {function} [formatdata] Callback function that is passed in the data
  4202. * that will be submitted to the server, allowing pre-formatting of the data,
  4203. * removal of data or adding of extra fields.
  4204. * @param {boolean} [hide=true] When the form is successfully submitted, by default
  4205. * the form display will be hidden - this option allows that to be overridden.
  4206. * @private
  4207. */
  4208. Editor.prototype._submit = function ( successCallback, errorCallback, formatdata, hide )
  4209. {
  4210. var that = this;
  4211. var i, iLen, eventRet, errorNodes;
  4212. var changed = false, allData = {}, changedData = {};
  4213. var setBuilder = DataTable.ext.oApi._fnSetObjectDataFn;
  4214. var dataSource = this.s.dataSource;
  4215. var fields = this.s.fields;
  4216. var action = this.s.action;
  4217. var editCount = this.s.editCount;
  4218. var modifier = this.s.modifier;
  4219. var editFields = this.s.editFields;
  4220. var editData = this.s.editData;
  4221. var opts = this.s.editOpts;
  4222. var changedSubmit = opts.submit;
  4223. var submitParams = {
  4224. "action": this.s.action,
  4225. "data": {}
  4226. };
  4227. var submitParamsLocal;
  4228. // For backwards compatibility
  4229. if ( this.s.dbTable ) {
  4230. submitParams.table = this.s.dbTable;
  4231. }
  4232. // Gather the data that is to be submitted
  4233. if ( action === "create" || action === "edit" ) {
  4234. $.each( editFields, function ( idSrc, edit ) {
  4235. var allRowData = {};
  4236. var changedRowData = {};
  4237. $.each( fields, function (name, field) {
  4238. if ( edit.fields[ name ] ) {
  4239. var value = field.multiGet( idSrc );
  4240. var builder = setBuilder( name );
  4241. var manyBuilder = $.isArray( value ) && name.indexOf('[]') !== -1 ?
  4242. setBuilder( name.replace(/\[.*$/,'')+'-many-count' ) :
  4243. null;
  4244. builder( allRowData, value );
  4245. // We need to tell the server-side if an array submission
  4246. // actually has no elements so it knows if the array was
  4247. // being submitted or not (since otherwise it doesn't know
  4248. // if the array was empty, or just not being submitted)
  4249. if ( manyBuilder ) {
  4250. manyBuilder( allRowData, value.length );
  4251. }
  4252. // Build a changed object for if that is the selected data
  4253. // type
  4254. if ( action === 'edit' && value !== editData[ name ][ idSrc ] ) {
  4255. builder( changedRowData, value );
  4256. changed = true;
  4257. if ( manyBuilder ) {
  4258. manyBuilder( changedRowData, value.length );
  4259. }
  4260. }
  4261. }
  4262. } );
  4263. if ( ! $.isEmptyObject( allRowData ) ) {
  4264. allData[ idSrc ] = allRowData;
  4265. }
  4266. if ( ! $.isEmptyObject( changedRowData ) ) {
  4267. changedData[ idSrc ] = changedRowData;
  4268. }
  4269. } );
  4270. // Decide what data to submit to the server for edit (create is all, always)
  4271. if ( action === 'create' || changedSubmit === 'all' || (changedSubmit === 'allIfChanged' && changed) ) {
  4272. submitParams.data = allData;
  4273. }
  4274. else if ( changedSubmit === 'changed' && changed ) {
  4275. submitParams.data = changedData;
  4276. }
  4277. else {
  4278. // Nothing to submit
  4279. this.s.action = null;
  4280. if ( opts.onComplete === 'close' && (hide === undefined || hide) ) {
  4281. this._close( false );
  4282. }
  4283. if ( successCallback ) {
  4284. successCallback.call( this );
  4285. }
  4286. this._processing( false );
  4287. this._event( 'submitComplete' );
  4288. return;
  4289. }
  4290. }
  4291. else if ( action === "remove" ) {
  4292. $.each( editFields, function ( idSrc, edit ) {
  4293. submitParams.data[ idSrc ] = edit.data;
  4294. } );
  4295. }
  4296. this._legacyAjax( 'send', action, submitParams );
  4297. // Local copy of the submit parameters, needed for the data lib prep since
  4298. // the preSubmit can modify the format and we need to know what the format is
  4299. submitParamsLocal = $.extend( true, {}, submitParams );
  4300. // Allow the data to be submitted to the server to be preprocessed by callback
  4301. // and event functions
  4302. if ( formatdata ) {
  4303. formatdata( submitParams );
  4304. }
  4305. if ( this._event( 'preSubmit', [submitParams, action] ) === false ) {
  4306. this._processing( false );
  4307. return;
  4308. }
  4309. // Submit to the server (or whatever method is defined in the settings)
  4310. this._ajax(
  4311. submitParams,
  4312. function (json) {
  4313. var setData;
  4314. that._legacyAjax( 'receive', action, json );
  4315. that._event( 'postSubmit', [json, submitParams, action] );
  4316. if ( !json.error ) {
  4317. json.error = "";
  4318. }
  4319. if ( !json.fieldErrors ) {
  4320. json.fieldErrors = [];
  4321. }
  4322. if ( json.error || json.fieldErrors.length ) {
  4323. // Global form error
  4324. that.error( json.error );
  4325. // Field specific errors
  4326. $.each( json.fieldErrors, function (i, err) {
  4327. var field = fields[ err.name ];
  4328. field.error( err.status || "Error" );
  4329. if ( i === 0 ) {
  4330. // Scroll the display to the first error and focus
  4331. $(that.dom.bodyContent, that.s.wrapper).animate( {
  4332. "scrollTop": $(field.node()).position().top
  4333. }, 500 );
  4334. field.focus();
  4335. }
  4336. } );
  4337. if ( errorCallback ) {
  4338. errorCallback.call( that, json );
  4339. }
  4340. }
  4341. else {
  4342. // Create a data store that the data source can use, which is
  4343. // unique to this action
  4344. var store = {};
  4345. that._dataSource( 'prep', action, modifier, submitParamsLocal, json.data, store );
  4346. if ( action === "create" || action === "edit" ) {
  4347. for ( i=0 ; i<json.data.length ; i++ ) {
  4348. setData = json.data[ i ];
  4349. that._event( 'setData', [json, setData, action] );
  4350. if ( action === "create" ) {
  4351. // New row was created to add it to the DT
  4352. that._event( 'preCreate', [json, setData] );
  4353. that._dataSource( 'create', fields, setData, store );
  4354. that._event( ['create', 'postCreate'], [json, setData] );
  4355. }
  4356. else if ( action === "edit" ) {
  4357. // Row was updated, so tell the DT
  4358. that._event( 'preEdit', [json, setData] );
  4359. that._dataSource( 'edit', modifier, fields, setData, store );
  4360. that._event( ['edit', 'postEdit'], [json, setData] );
  4361. }
  4362. }
  4363. }
  4364. else if ( action === "remove" ) {
  4365. // Remove the rows given and then redraw the table
  4366. that._event( 'preRemove', [json] );
  4367. that._dataSource( 'remove', modifier, fields, store );
  4368. that._event( ['remove', 'postRemove'], [json] );
  4369. }
  4370. that._dataSource( 'commit', action, modifier, json.data, store );
  4371. // Submission complete
  4372. if ( editCount === that.s.editCount ) {
  4373. that.s.action = null;
  4374. if ( opts.onComplete === 'close' && (hide === undefined || hide) ) {
  4375. that._close( true );
  4376. }
  4377. }
  4378. // All done - fire off the callbacks and events
  4379. if ( successCallback ) {
  4380. successCallback.call( that, json );
  4381. }
  4382. that._event( 'submitSuccess', [json, setData] );
  4383. }
  4384. that._processing( false );
  4385. that._event( 'submitComplete', [json, setData] );
  4386. },
  4387. function (xhr, err, thrown) {
  4388. that._event( 'postSubmit', [xhr, err, thrown, submitParams] );
  4389. that.error( that.i18n.error.system );
  4390. that._processing( false );
  4391. if ( errorCallback ) {
  4392. errorCallback.call( that, xhr, err, thrown );
  4393. }
  4394. that._event( ['submitError', 'submitComplete'], [xhr, err, thrown, submitParams] );
  4395. }
  4396. ); // /ajax submit
  4397. };
  4398. /**
  4399. * Check to see if the form needs to be tidied before a new action can be performed.
  4400. * This includes if the from is currently processing an old action and if it
  4401. * is inline editing.
  4402. *
  4403. * @param {function} fn Callback function
  4404. * @returns {boolean} `true` if was in inline mode, `false` otherwise
  4405. * @private
  4406. */
  4407. Editor.prototype._tidy = function ( fn )
  4408. {
  4409. var that = this;
  4410. var dt = this.s.table ?
  4411. new $.fn.dataTable.Api( this.s.table ) :
  4412. null;
  4413. var ssp = false;
  4414. if ( dt ) {
  4415. ssp = dt.settings()[0].oFeatures.bServerSide;
  4416. }
  4417. if ( this.s.processing ) {
  4418. // If currently processing, wait until the action is complete
  4419. this.one( 'submitComplete', function () {
  4420. // If server-side processing is being used in DataTables, first
  4421. // check that we are still processing (might not be if nothing was
  4422. // submitted) and then wait for the draw to finished
  4423. if ( ssp ) {
  4424. dt.one( 'draw', fn );
  4425. }
  4426. else {
  4427. setTimeout( function () {
  4428. fn();
  4429. }, 10 );
  4430. }
  4431. } );
  4432. return true;
  4433. }
  4434. else if ( this.display() === 'inline' || this.display() === 'bubble' ) {
  4435. // If there is an inline edit box, it needs to be tidied
  4436. this
  4437. .one( 'close', function () {
  4438. // On close if processing then we need to wait for the submit to
  4439. // complete before running the callback as onBlur was set to
  4440. // submit
  4441. if ( ! that.s.processing ) {
  4442. // IE needs a small timeout, otherwise it may not focus on a
  4443. // field if one already has focus
  4444. setTimeout( function () {
  4445. fn();
  4446. }, 10 );
  4447. }
  4448. else {
  4449. // Need to wait for the submit to finish
  4450. that.one( 'submitComplete', function ( e, json ) {
  4451. // If SSP then need to wait for the draw
  4452. if ( ssp && json ) {
  4453. dt.one( 'draw', fn );
  4454. }
  4455. else {
  4456. setTimeout( function () {
  4457. fn();
  4458. }, 10 );
  4459. }
  4460. } );
  4461. }
  4462. } )
  4463. .blur();
  4464. return true;
  4465. }
  4466. return false;
  4467. };
  4468. /*
  4469. * Defaults
  4470. */
  4471. // Dev node - although this file is held in the models directory (because it
  4472. // really is a model, it is assigned to Editor.defaults for easy
  4473. // and sensible access to set the defaults for Editor.
  4474. /**
  4475. * Initialisation options that can be given to Editor at initialisation time.
  4476. * @namespace
  4477. */
  4478. Editor.defaults = {
  4479. /**
  4480. * jQuery selector that can be used to identify the table you wish to apply
  4481. * this editor instance to.
  4482. *
  4483. * In previous versions of Editor (1.2 and earlier), this parameter was
  4484. * called `table`. The name has been altered in 1.3+ to simplify the
  4485. * initialisation. This is a backwards compatible change - if you pass in
  4486. * a `table` option it will be used.
  4487. * @type string
  4488. * @default <i>Empty string</i>
  4489. *
  4490. * @example
  4491. * $(document).ready(function() {
  4492. * var editor = new $.fn.Editor( {
  4493. * "ajax": "php/index.php",
  4494. * "table": "#example"
  4495. * } );
  4496. * } );
  4497. */
  4498. "table": null,
  4499. /**
  4500. * The URL, or collection of URLs when using a REST interface, which will accept
  4501. * the data for the create, edit and remove functions. The target script / program
  4502. * must accept data in the format defined by Editor and return the expected JSON as
  4503. * required by Editor. When given as an object, the `create`, `edit` and `remove`
  4504. * properties should be defined, each being the URL to send the data to for that
  4505. * action. When used as an object, the string `_id_` will be replaced for the edit
  4506. * and remove actions, allowing a URL to be dynamically created for those actions.
  4507. * @type string|object
  4508. * @default <i>Empty string</i>
  4509. * @deprecated This option has been deprecated in favour of the `ajax` option.
  4510. * It can still be used, but it is recommended that you use the `ajax` option
  4511. * which provides all of the abilities of this old option and more.
  4512. */
  4513. "ajaxUrl": null,
  4514. /**
  4515. * Fields to initialise the form with - see {@link Editor.models.field} for
  4516. * a full list of the options available to each field. Note that if fields are not
  4517. * added to the form at initialisation time using this option, they can be added using
  4518. * the {@link Editor#add} API method.
  4519. * @type array
  4520. * @default []
  4521. *
  4522. * @example
  4523. * $(document).ready(function() {
  4524. * var editor = new $.fn.Editor( {
  4525. * "ajax": "php/index.php",
  4526. * "table": "#example",
  4527. * "fields": [ {
  4528. * "label": "User name:",
  4529. * "name": "username"
  4530. * }
  4531. * // More fields would typically be added here!
  4532. * } ]
  4533. * } );
  4534. * } );
  4535. */
  4536. "fields": [],
  4537. /**
  4538. * The display controller for the form. The form itself is just a collection of
  4539. * DOM elements which require a display container. This display controller allows
  4540. * the visual appearance of the form to be significantly altered without major
  4541. * alterations to the Editor code. There are two display controllers built into
  4542. * Editor *lightbox* and *envelope*. The value of this property will
  4543. * be used to access the display controller defined in {@link Editor.display}
  4544. * for the given name. Additional display controllers can be added by adding objects
  4545. * to that object, through extending the displayController model:
  4546. * {@link Editor.models.displayController}.
  4547. * @type string
  4548. * @default lightbox
  4549. *
  4550. * @example
  4551. * $(document).ready(function() {
  4552. * var editor = new $.fn.Editor( {
  4553. * "ajax": "php/index.php",
  4554. * "table": "#example",
  4555. * "display": 'envelope'
  4556. * } );
  4557. * } );
  4558. */
  4559. "display": 'lightbox',
  4560. /**
  4561. * Control how the Ajax call to update data on the server.
  4562. *
  4563. * This option matches the `dt-init ajax` option in that is can be provided
  4564. * in one of three different ways:
  4565. *
  4566. * * string - As a string, the value given is used as the url to target
  4567. * the Ajax request to, using the default Editor Ajax options. Note that
  4568. * for backwards compatibility you can use the form "METHOD URL" - for
  4569. * example: `"PUT api/users"`, although it is recommended you use the
  4570. * object form described below.
  4571. * * object - As an object, the `ajax` property has two forms:
  4572. * * Used to extend and override the default Ajax options that Editor
  4573. * uses. This can be very useful for adding extra data for example, or
  4574. * changing the HTTP request type.
  4575. * * With `create`, `edit` and `remove` properties, Editor will use the
  4576. * option for the action that it is taking, which can be useful for
  4577. * REST style interfaces. The value of each property can be a string,
  4578. * object or function, using exactly the same options as the main `ajax`
  4579. * option. All three options must be defined if this form is to be used.
  4580. * * function - As a function this gives complete control over the method
  4581. * used to update the server (if indeed a server is being used!). For
  4582. * example, you could use a different data store such as localStorage,
  4583. * Firebase or route the data through a web-socket.
  4584. *
  4585. * @example
  4586. * // As a string - all actions are submitted to this URI as POST requests
  4587. * $(document).ready(function() {
  4588. * var editor = new $.fn.Editor( {
  4589. * "ajax": 'php/index.php',
  4590. * "table": "#example"
  4591. * } );
  4592. * } );
  4593. *
  4594. * @example
  4595. * // As an object - using GET rather than POST
  4596. * $(document).ready(function() {
  4597. * var editor = new $.fn.Editor( {
  4598. * "ajax": {
  4599. * "type": 'GET',
  4600. * "url": 'php/index.php
  4601. * },
  4602. * "table": "#example"
  4603. * } );
  4604. * } );
  4605. *
  4606. * @example
  4607. * // As an object - each action is submitted to a different URI as POST requests
  4608. * $(document).ready(function() {
  4609. * var editor = new $.fn.Editor( {
  4610. * "ajax": {
  4611. * "create": "/rest/user/create",
  4612. * "edit": "/rest/user/_id_/edit",
  4613. * "remove": "/rest/user/_id_/delete"
  4614. * },
  4615. * "table": "#example"
  4616. * } );
  4617. * } );
  4618. *
  4619. * @example
  4620. * // As an object - with different HTTP methods for each action
  4621. * $(document).ready(function() {
  4622. * var editor = new $.fn.Editor( {
  4623. * "ajax": {
  4624. * "create": {
  4625. * type: 'POST',
  4626. * url: '/rest/user/create'
  4627. * },
  4628. * "edit": {
  4629. * type: 'PUT',
  4630. * url: '/rest/user/edit/_id_'
  4631. * },
  4632. * "remove": {
  4633. * type: 'DELETE',
  4634. * url: '/rest/user/delete'
  4635. * }
  4636. * },
  4637. * "table": "#example"
  4638. * } );
  4639. * } );
  4640. *
  4641. * // As a function - Making a custom `$.ajax` call
  4642. * $(document).ready(function() {
  4643. * var editor = new $.fn.Editor( {
  4644. * "ajax": "php/index.php",
  4645. * "table": "#example",
  4646. * "ajax": function ( method, url, data, successCallback, errorCallback ) {
  4647. * $.ajax( {
  4648. * "type": method,
  4649. * "url": url,
  4650. * "data": data,
  4651. * "dataType": "json",
  4652. * "success": function (json) {
  4653. * successCallback( json );
  4654. * },
  4655. * "error": function (xhr, error, thrown) {
  4656. * errorCallback( xhr, error, thrown );
  4657. * }
  4658. * } );
  4659. * }
  4660. * } );
  4661. * } );
  4662. */
  4663. "ajax": null,
  4664. /**
  4665. * JSON property from which to read / write the row's ID property (i.e. its
  4666. * unique column index that identifies the row to the database). By default
  4667. * Editor will use the `DT_RowId` property from the data source object
  4668. * (DataTable's magic property to set the DOM id for the row).
  4669. *
  4670. * If you want to read a parameter from the data source object instead of
  4671. * using `DT_RowId`, set this option to the property name to use.
  4672. *
  4673. * Like other data source options the `srcId` option can be given in dotted
  4674. * object notation to read nested objects.
  4675. * @type null|string
  4676. * @default DT_RowId
  4677. *
  4678. * @example
  4679. * // Using a data source such as:
  4680. * // { "id":12, "browser":"Chrome", ... }
  4681. * $(document).ready(function() {
  4682. * var editor = new $.fn.Editor( {
  4683. * "ajax": "php/index.php",
  4684. * "table": "#example",
  4685. * "idSrc": "id"
  4686. * } );
  4687. * } );
  4688. */
  4689. "idSrc": 'DT_RowId',
  4690. /**
  4691. * Events / callbacks - event handlers can be assigned as an individual function
  4692. * during initialisation using the parameters in this name space. The names, and
  4693. * the parameters passed to each callback match their event equivalent in the
  4694. * {@link Editor} object.
  4695. * @namespace
  4696. * @deprecated Since 1.3. Use the `on()` API method instead. Note that events
  4697. * passed in do still operate as they did in 1.2- but are no longer
  4698. * individually documented.
  4699. */
  4700. "events": {},
  4701. /**
  4702. * Internationalisation options for Editor. All client-side strings that the
  4703. * end user can see in the interface presented by Editor can be modified here.
  4704. *
  4705. * You may also wish to refer to the <a href="http://datatables.net/usage/i18n">
  4706. * DataTables internationalisation options</a> to provide a fully language
  4707. * customised table interface.
  4708. * @namespace
  4709. *
  4710. * @example
  4711. * // Set the 'create' button text. All other strings used are the
  4712. * // default values.
  4713. * var editor = new $.fn.Editor( {
  4714. * "ajax": "data/source",
  4715. * "table": "#example",
  4716. * "i18n": {
  4717. * "create": {
  4718. * "button": "New user"
  4719. * }
  4720. * }
  4721. * } );
  4722. *
  4723. * @example
  4724. * // Set the submit text for all three actions
  4725. * var editor = new $.fn.Editor( {
  4726. * "ajax": "data/source",
  4727. * "table": "#example",
  4728. * "i18n": {
  4729. * "create": {
  4730. * "submit": "Create new user"
  4731. * },
  4732. * "edit": {
  4733. * "submit": "Update user"
  4734. * },
  4735. * "remove": {
  4736. * "submit": "Remove user"
  4737. * }
  4738. * }
  4739. * } );
  4740. */
  4741. "i18n": {
  4742. /**
  4743. * Strings used when working with the Editor 'create' action (creating new
  4744. * records).
  4745. * @namespace
  4746. */
  4747. "create": {
  4748. /**
  4749. * TableTools button text
  4750. * @type string
  4751. * @default New
  4752. */
  4753. "button": "New",
  4754. /**
  4755. * Display container title (when showing the editor display)
  4756. * @type string
  4757. * @default Create new entry
  4758. */
  4759. "title": "Create new entry",
  4760. /**
  4761. * Submit button text
  4762. * @type string
  4763. * @default Create
  4764. */
  4765. "submit": "Create"
  4766. },
  4767. /**
  4768. * Strings used when working with the Editor 'edit' action (editing existing
  4769. * records).
  4770. * @namespace
  4771. */
  4772. "edit": {
  4773. /**
  4774. * TableTools button text
  4775. * @type string
  4776. * @default Edit
  4777. */
  4778. "button": "Edit",
  4779. /**
  4780. * Display container title (when showing the editor display)
  4781. * @type string
  4782. * @default Edit entry
  4783. */
  4784. "title": "Edit entry",
  4785. /**
  4786. * Submit button text
  4787. * @type string
  4788. * @default Update
  4789. */
  4790. "submit": "Update"
  4791. },
  4792. /**
  4793. * Strings used when working with the Editor 'delete' action (deleting
  4794. * existing records).
  4795. * @namespace
  4796. */
  4797. "remove": {
  4798. /**
  4799. * TableTools button text
  4800. * @type string
  4801. * @default Delete
  4802. */
  4803. "button": "Delete",
  4804. /**
  4805. * Display container title (when showing the editor display)
  4806. * @type string
  4807. * @default Delete
  4808. */
  4809. "title": "Delete",
  4810. /**
  4811. * Submit button text
  4812. * @type string
  4813. * @default Delete
  4814. */
  4815. "submit": "Delete",
  4816. /**
  4817. * Deletion confirmation message.
  4818. *
  4819. * As Editor has the ability to delete either a single or multiple rows
  4820. * at a time, this option can be given as either a string (which will be
  4821. * used regardless of how many records are selected) or as an object
  4822. * where the property "_" will be used (with %d substituted for the number
  4823. * of records to be deleted) as the delete message, unless there is a
  4824. * key with the number of records to be deleted. This allows Editor
  4825. * to consider the different pluralisation characteristics of different
  4826. * languages.
  4827. * @type object|string
  4828. * @default Are you sure you wish to delete %d rows?
  4829. *
  4830. * @example
  4831. * // String - no plural consideration
  4832. * var editor = new $.fn.Editor( {
  4833. * "ajax": "data/source",
  4834. * "table": "#example",
  4835. * "i18n": {
  4836. * "remove": {
  4837. * "confirm": "Are you sure you wish to delete %d record(s)?"
  4838. * }
  4839. * }
  4840. * } );
  4841. *
  4842. * @example
  4843. * // Basic 1 (singular) or _ (plural)
  4844. * var editor = new $.fn.Editor( {
  4845. * "ajax": "data/source",
  4846. * "table": "#example",
  4847. * "i18n": {
  4848. * "remove": {
  4849. * "confirm": {
  4850. * "_": "Confirm deletion of %d records.",
  4851. * "1": "Confirm deletion of record."
  4852. * }
  4853. * }
  4854. * } );
  4855. *
  4856. * @example
  4857. * // Singular, dual and plural
  4858. * var editor = new $.fn.Editor( {
  4859. * "ajax": "data/source",
  4860. * "table": "#example",
  4861. * "i18n": {
  4862. * "remove": {
  4863. * "confirm": {
  4864. * "_": "Confirm deletion of %d records.",
  4865. * "1": "Confirm deletion of record.",
  4866. * "2": "Confirm deletion of both record."
  4867. * }
  4868. * }
  4869. * } );
  4870. *
  4871. */
  4872. "confirm": {
  4873. "_": "Are you sure you wish to delete %d rows?",
  4874. "1": "Are you sure you wish to delete 1 row?"
  4875. }
  4876. },
  4877. /**
  4878. * Strings used for error conditions.
  4879. * @namespace
  4880. */
  4881. "error": {
  4882. /**
  4883. * Generic server error message
  4884. * @type string
  4885. * @default A system error has occurred (<a target=\"_blank\" href=\"//datatables.net/tn/12\">More information</a>)
  4886. */
  4887. "system": "A system error has occurred (<a target=\"_blank\" href=\"//datatables.net/tn/12\">More information</a>)."
  4888. },
  4889. /**
  4890. * Strings used for multi-value editing
  4891. * @namespace
  4892. */
  4893. "multi": {
  4894. /**
  4895. * Shown in place of the field value when a field has multiple values
  4896. */
  4897. "title": "Multiple values",
  4898. /**
  4899. * Shown below the multi title text, although only the first
  4900. * instance of this text is shown in the form to reduce redundancy
  4901. */
  4902. "info": "The selected items contain different values for this input. To edit and set all items for this input to the same value, click or tap here, otherwise they will retain their individual values.",
  4903. /**
  4904. * Shown below the field input when group editing a value to allow
  4905. * the user to return to the original multiple values
  4906. */
  4907. "restore": "Undo changes"
  4908. },
  4909. "datetime": {
  4910. previous: 'Previous',
  4911. next: 'Next',
  4912. months: [ 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December' ],
  4913. weekdays: [ 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ],
  4914. amPm: [ 'am', 'pm' ],
  4915. unknown: '-'
  4916. }
  4917. },
  4918. formOptions: {
  4919. bubble: $.extend( {}, Editor.models.formOptions, {
  4920. title: false,
  4921. message: false,
  4922. buttons: '_basic',
  4923. submit: 'changed'
  4924. } ),
  4925. inline: $.extend( {}, Editor.models.formOptions, {
  4926. buttons: false,
  4927. submit: 'changed'
  4928. } ),
  4929. main: $.extend( {}, Editor.models.formOptions )
  4930. },
  4931. /**
  4932. * Submit data to the server in the 1.4- data format (`true`) or in the 1.5+
  4933. * data format (`false` - default).
  4934. *
  4935. * @type Boolean
  4936. */
  4937. legacyAjax: false
  4938. };
  4939. /*
  4940. * Extensions
  4941. */
  4942. (function(){
  4943. var __dataSources = Editor.dataSources = {};
  4944. /* - - - - - - - - - -
  4945. * DataTables editor interface
  4946. */
  4947. var __dtIsSsp = function ( dt, editor ) {
  4948. // If the draw type is `none`, then we still need to use the DT API to
  4949. // update the display with the new data
  4950. return dt.settings()[0].oFeatures.bServerSide &&
  4951. editor.s.editOpts.drawType !== 'none';
  4952. };
  4953. var __dtApi = function ( table ) {
  4954. return $(table).DataTable();
  4955. };
  4956. var __dtHighlight = function ( node ) {
  4957. // Highlight a row using CSS transitions. The timeouts need to match the
  4958. // transition duration from the CSS
  4959. node = $(node);
  4960. setTimeout( function () {
  4961. node.addClass( 'highlight' );
  4962. setTimeout( function () {
  4963. node
  4964. .addClass( 'noHighlight' )
  4965. .removeClass( 'highlight' );
  4966. setTimeout( function () {
  4967. node.removeClass( 'noHighlight' );
  4968. }, 550 );
  4969. }, 500 );
  4970. }, 20 );
  4971. };
  4972. var __dtRowSelector = function ( out, dt, identifier, fields, idFn )
  4973. {
  4974. dt.rows( identifier ).indexes().each( function ( idx ) {
  4975. var row = dt.row( idx );
  4976. var data = row.data();
  4977. var idSrc = idFn( data );
  4978. if ( idSrc === undefined ) {
  4979. Editor.error( 'Unable to find row identifier', 14 );
  4980. }
  4981. out[ idSrc ] = {
  4982. idSrc: idSrc,
  4983. data: data,
  4984. node: row.node(),
  4985. fields: fields,
  4986. type: 'row'
  4987. };
  4988. } );
  4989. };
  4990. var __dtColumnSelector = function ( out, dt, identifier, fields, idFn )
  4991. {
  4992. dt.cells( null, identifier ).indexes().each( function ( idx ) {
  4993. __dtCellSelector( out, dt, idx, fields, idFn );
  4994. } );
  4995. };
  4996. var __dtCellSelector = function ( out, dt, identifier, allFields, idFn, forceFields )
  4997. {
  4998. dt.cells( identifier ).indexes().each( function ( idx ) {
  4999. var cell = dt.cell( idx );
  5000. var row = dt.row( idx.row );
  5001. var data = row.data();
  5002. var idSrc = idFn( data );
  5003. var fields = forceFields || __dtFieldsFromIdx( dt, allFields, idx.column );
  5004. // Use the row selector to get the row information
  5005. __dtRowSelector(out, dt, idx.row, allFields, idFn);
  5006. out[ idSrc ].attach = typeof identifier === 'object' && identifier.nodeName ?
  5007. [ identifier ] :
  5008. [ cell.node() ];
  5009. out[ idSrc ].displayFields = fields; // all fields are edited, but only
  5010. // these are actually displayed
  5011. } );
  5012. };
  5013. var __dtFieldsFromIdx = function ( dt, fields, idx )
  5014. {
  5015. var field;
  5016. var col = dt.settings()[0].aoColumns[ idx ];
  5017. var dataSrc = col.editField !== undefined ?
  5018. col.editField :
  5019. col.mData;
  5020. var resolvedFields = {};
  5021. var run = function ( field, dataSrc ) {
  5022. if ( field.dataSrc() === dataSrc ) {
  5023. resolvedFields[ field.name() ] = field;
  5024. }
  5025. };
  5026. $.each( fields, function ( name, fieldInst ) {
  5027. if ( $.isArray( dataSrc ) ) {
  5028. for ( var i=0 ; i<dataSrc.length ; i++ ) {
  5029. run( fieldInst, dataSrc[i] );
  5030. }
  5031. }
  5032. else {
  5033. run( fieldInst, dataSrc );
  5034. }
  5035. } );
  5036. if ( $.isEmptyObject( resolvedFields ) ) {
  5037. Editor.error('Unable to automatically determine field from source. Please specify the field name.', 11);
  5038. }
  5039. return resolvedFields;
  5040. };
  5041. __dataSources.dataTable = {
  5042. individual: function ( identifier, fieldNames ) {
  5043. var idFn = DataTable.ext.oApi._fnGetObjectDataFn( this.s.idSrc );
  5044. var dt = __dtApi( this.s.table );
  5045. var fields = this.s.fields;
  5046. var out = {};
  5047. var forceFields;
  5048. var responsiveNode;
  5049. // Responsive field - this isn't really needed as of DT 1.10.11. To be
  5050. // removed in Editor 1.6
  5051. if ( identifier.nodeName && $(identifier).hasClass( 'dtr-data' ) ) {
  5052. responsiveNode = identifier;
  5053. // Overwrite the selector so that DataTables will select based on
  5054. // the cell that Responsive is showing data for
  5055. identifier = dt.responsive.index( $(identifier).closest('li') );
  5056. }
  5057. if ( fieldNames ) {
  5058. if ( ! $.isArray( fieldNames ) ) {
  5059. fieldNames = [ fieldNames ];
  5060. }
  5061. forceFields = {};
  5062. $.each( fieldNames, function ( i, name ) {
  5063. forceFields[ name ] = fields[ name ];
  5064. } );
  5065. }
  5066. __dtCellSelector( out, dt, identifier, fields, idFn, forceFields );
  5067. // If Responsive, we want it to attach the editor to the element that
  5068. // was clicked on
  5069. if ( responsiveNode ) {
  5070. $.each( out, function ( i, val ) {
  5071. val.attach = [ responsiveNode ];
  5072. } );
  5073. }
  5074. return out;
  5075. },
  5076. // get idSrc, fields to edit, data and node for each item
  5077. fields: function ( identifier )
  5078. {
  5079. var idFn = DataTable.ext.oApi._fnGetObjectDataFn( this.s.idSrc );
  5080. var dt = __dtApi( this.s.table );
  5081. var fields = this.s.fields;
  5082. var out = {};
  5083. if ( $.isPlainObject( identifier ) && ( identifier.rows !== undefined || identifier.columns !== undefined || identifier.cells !== undefined ) ) {
  5084. // Multi-item type selector
  5085. if ( identifier.rows !== undefined ) {
  5086. __dtRowSelector( out, dt, identifier.rows, fields, idFn );
  5087. }
  5088. if ( identifier.columns !== undefined ) {
  5089. __dtColumnSelector( out, dt, identifier.columns, fields, idFn );
  5090. }
  5091. if ( identifier.cells !== undefined ) {
  5092. __dtCellSelector( out, dt, identifier.cells, fields, idFn );
  5093. }
  5094. }
  5095. else {
  5096. // Just a rows selector
  5097. __dtRowSelector( out, dt, identifier, fields, idFn );
  5098. }
  5099. return out;
  5100. },
  5101. create: function ( fields, data ) {
  5102. var dt = __dtApi( this.s.table );
  5103. if ( ! __dtIsSsp( dt, this ) ) {
  5104. var row = dt.row.add( data );
  5105. __dtHighlight( row.node() );
  5106. }
  5107. },
  5108. edit: function ( identifier, fields, data, store ) {
  5109. var dt = __dtApi( this.s.table );
  5110. // No point in doing anything when server-side processing - the commit
  5111. // will redraw the table
  5112. if ( ! __dtIsSsp( dt, this ) ) {
  5113. // The identifier can select one or more rows, but the data will
  5114. // refer to just a single row. We need to determine which row from
  5115. // the set is the one to operator on.
  5116. var idFn = DataTable.ext.oApi._fnGetObjectDataFn( this.s.idSrc );
  5117. var rowId = idFn( data );
  5118. var row;
  5119. // Find the row to edit - attempt to do an id look up first for speed
  5120. row = dt.row( '#'+rowId );
  5121. // If not found, then we need to do it the slow way
  5122. if ( ! row.any() ) {
  5123. row = dt.row( function ( rowIdx, rowData, rowNode ) {
  5124. return rowId == idFn( rowData );
  5125. } );
  5126. }
  5127. if ( row.any() ) {
  5128. row.data( data );
  5129. // Remove the item from the list of indexes now that is has been
  5130. // updated
  5131. var idx = $.inArray( rowId, store.rowIds );
  5132. store.rowIds.splice( idx, 1 );
  5133. }
  5134. else {
  5135. // If not found, then its a new row (change in pkey possibly)
  5136. row = dt.row.add( data );
  5137. }
  5138. __dtHighlight( row.node() );
  5139. }
  5140. },
  5141. remove: function ( identifier, fields ) {
  5142. // No confirmation from the server
  5143. var dt = __dtApi( this.s.table );
  5144. if ( ! __dtIsSsp( dt, this ) ) {
  5145. dt.rows( identifier ).remove();
  5146. }
  5147. },
  5148. prep: function ( action, identifier, submit, data, store ) {
  5149. // On edit we store the ids of the rows that are being edited
  5150. if ( action === 'edit' ) {
  5151. store.rowIds = $.map( submit.data, function ( val, key ) {
  5152. if ( ! $.isEmptyObject( submit.data[ key ] ) ) {
  5153. return key;
  5154. }
  5155. } );
  5156. }
  5157. },
  5158. commit: function ( action, identifier, data, store ) {
  5159. // Updates complete - redraw
  5160. var dt = __dtApi( this.s.table );
  5161. // On edit, if there are any rows left in the `store.rowIds`, then they
  5162. // were not returned by the server and should be removed (they might not
  5163. // meet filtering requirements any more for example)
  5164. if ( action === 'edit' && store.rowIds.length ) {
  5165. var ids = store.rowIds;
  5166. var idFn = DataTable.ext.oApi._fnGetObjectDataFn( this.s.idSrc );
  5167. var row;
  5168. for ( var i=0, ien=ids.length ; i<ien ; i++ ) {
  5169. // Find the row to edit - attempt to do an id look up first for speed
  5170. row = dt.row( '#'+ids[i] );
  5171. // If not found, then we need to do it the slow way
  5172. if ( ! row.any() ) {
  5173. row = dt.row( function ( rowIdx, rowData, rowNode ) {
  5174. return ids[i] === idFn( rowData );
  5175. } );
  5176. }
  5177. if ( row.any() ) {
  5178. row.remove();
  5179. }
  5180. }
  5181. }
  5182. var drawType = this.s.editOpts.drawType;
  5183. if ( drawType !== 'none' ) {
  5184. dt.draw( drawType );
  5185. }
  5186. }
  5187. };
  5188. /* - - - - - - - -
  5189. * HTML editor interface
  5190. */
  5191. function __html_set( identifier, fields, data ) {
  5192. $.each( fields, function ( name, field ) {
  5193. var val = field.valFromData( data );
  5194. if ( val !== undefined ) {
  5195. __html_el( identifier, field.dataSrc() )
  5196. .each( function () {
  5197. // This is very frustrating, but in IE if you just write directly
  5198. // to innerHTML, and elements that are overwritten are GC'ed,
  5199. // even if there is a reference to them elsewhere
  5200. while ( this.childNodes.length ) {
  5201. this.removeChild( this.firstChild );
  5202. }
  5203. } )
  5204. .html( val );
  5205. }
  5206. } );
  5207. }
  5208. function __html_els ( identifier, names ) {
  5209. var out = $();
  5210. for ( var i=0, ien=names.length ; i<ien ; i++ ) {
  5211. out = out.add( __html_el( identifier, names[i] ) );
  5212. }
  5213. return out;
  5214. }
  5215. function __html_el ( identifier, name ) {
  5216. var context = identifier === 'keyless' ?
  5217. document :
  5218. $('[data-editor-id="'+identifier+'"]');
  5219. return $('[data-editor-field="'+name+'"]', context);
  5220. }
  5221. __dataSources.html = {
  5222. initField: function ( cfg ) {
  5223. // This is before the field has been initialised so can't use it API
  5224. var label = $('[data-editor-label="'+(cfg.data || cfg.name)+'"]');
  5225. if ( ! cfg.label && label.length ) {
  5226. cfg.label = label.html();
  5227. }
  5228. },
  5229. individual: function ( identifier, fieldNames ) {
  5230. // Auto detection of the field name and id
  5231. if ( identifier instanceof $ || identifier.nodeName ) {
  5232. if ( ! fieldNames ) {
  5233. fieldNames = [ $( identifier ).attr('data-editor-field') ];
  5234. }
  5235. identifier = $( identifier ).parents('[data-editor-id]').data('editor-id');
  5236. }
  5237. // no id given and none found
  5238. if ( ! identifier ) {
  5239. identifier = 'keyless';
  5240. }
  5241. // no field name - cannot continue
  5242. if ( fieldNames && ! $.isArray( fieldNames ) ) {
  5243. fieldNames = [ fieldNames ];
  5244. }
  5245. if ( ! fieldNames || fieldNames.length === 0 ) {
  5246. throw 'Cannot automatically determine field name from data source';
  5247. }
  5248. var out = __dataSources.html.fields.call( this, identifier );
  5249. var fields = this.s.fields;
  5250. var forceFields = {};
  5251. $.each( fieldNames, function ( i, name ) {
  5252. forceFields[ name ] = fields[ name ];
  5253. } );
  5254. $.each( out, function ( id, set ) {
  5255. set.type = 'cell';
  5256. set.attach = __html_els( identifier, fieldNames ).toArray();
  5257. set.fields = fields;
  5258. set.displayFields = forceFields;
  5259. } );
  5260. return out;
  5261. },
  5262. // get idSrc, fields to edit, data and node for each item
  5263. fields: function ( identifier )
  5264. {
  5265. var out = {};
  5266. var data = {};
  5267. var fields = this.s.fields;
  5268. if ( ! identifier ) {
  5269. identifier = 'keyless';
  5270. }
  5271. $.each( fields, function ( name, field ) {
  5272. var val = __html_el( identifier, field.dataSrc() ).html();
  5273. // If no HTML element is present, jQuery returns null. We want undefined
  5274. field.valToData( data, val === null ? undefined : val );
  5275. } );
  5276. out[ identifier ] = {
  5277. idSrc: identifier,
  5278. data: data,
  5279. node: document,
  5280. fields: fields,
  5281. type: 'row'
  5282. };
  5283. return out;
  5284. },
  5285. create: function ( fields, data ) {
  5286. // If there is an element with the id that has been created, then use it
  5287. // to assign the values
  5288. if ( data ) {
  5289. var idFn = DataTable.ext.oApi._fnGetObjectDataFn( this.s.idSrc );
  5290. var id = idFn( data );
  5291. if ( $('[data-editor-id="'+id+'"]').length ) {
  5292. __html_set( id, fields, data );
  5293. }
  5294. }
  5295. },
  5296. edit: function ( identifier, fields, data ) {
  5297. // Get the ids from the returned data or `keyless` if not found
  5298. var idFn = DataTable.ext.oApi._fnGetObjectDataFn( this.s.idSrc );
  5299. var id = idFn( data ) || 'keyless';
  5300. __html_set( id, fields, data );
  5301. },
  5302. remove: function ( identifier, fields ) {
  5303. // If there is an element with an ID property matching the identifier,
  5304. // remove it
  5305. $('[data-editor-id="'+identifier+'"]').remove();
  5306. }
  5307. };
  5308. }());
  5309. /**
  5310. * Class names that are used by Editor for its various display components.
  5311. * A copy of this object is taken when an Editor instance is initialised, thus
  5312. * allowing different classes to be used in different instances if required.
  5313. * Class name changes can be useful for easy integration with CSS frameworks,
  5314. * for example Twitter Bootstrap.
  5315. * @namespace
  5316. */
  5317. Editor.classes = {
  5318. /**
  5319. * Applied to the base DIV element that contains all other Editor elements
  5320. */
  5321. "wrapper": "DTE",
  5322. /**
  5323. * Processing classes
  5324. * @namespace
  5325. */
  5326. "processing": {
  5327. /**
  5328. * Processing indicator element
  5329. */
  5330. "indicator": "DTE_Processing_Indicator",
  5331. /**
  5332. * Added to the base element ("wrapper") when the form is "processing"
  5333. */
  5334. "active": "DTE_Processing"
  5335. },
  5336. /**
  5337. * Display header classes
  5338. * @namespace
  5339. */
  5340. "header": {
  5341. /**
  5342. * Container for the header elements
  5343. */
  5344. "wrapper": "DTE_Header",
  5345. /**
  5346. * Liner for the header content
  5347. */
  5348. "content": "DTE_Header_Content"
  5349. },
  5350. /**
  5351. * Display body classes
  5352. * @namespace
  5353. */
  5354. "body": {
  5355. /**
  5356. * Container for the body elements
  5357. */
  5358. "wrapper": "DTE_Body",
  5359. /**
  5360. * Liner for the body content
  5361. */
  5362. "content": "DTE_Body_Content"
  5363. },
  5364. /**
  5365. * Display footer classes
  5366. * @namespace
  5367. */
  5368. "footer": {
  5369. /**
  5370. * Container for the footer elements
  5371. */
  5372. "wrapper": "DTE_Footer",
  5373. /**
  5374. * Liner for the footer content
  5375. */
  5376. "content": "DTE_Footer_Content"
  5377. },
  5378. /**
  5379. * Form classes
  5380. * @namespace
  5381. */
  5382. "form": {
  5383. /**
  5384. * Container for the form elements
  5385. */
  5386. "wrapper": "DTE_Form",
  5387. /**
  5388. * Liner for the form content
  5389. */
  5390. "content": "DTE_Form_Content",
  5391. /**
  5392. * Applied to the <form> tag
  5393. */
  5394. "tag": "",
  5395. /**
  5396. * Global form information
  5397. */
  5398. "info": "DTE_Form_Info",
  5399. /**
  5400. * Global error imformation
  5401. */
  5402. "error": "DTE_Form_Error",
  5403. /**
  5404. * Buttons container
  5405. */
  5406. "buttons": "DTE_Form_Buttons",
  5407. /**
  5408. * Buttons container
  5409. */
  5410. "button": "btn"
  5411. },
  5412. /**
  5413. * Field classes
  5414. * @namespace
  5415. */
  5416. "field": {
  5417. /**
  5418. * Container for each field
  5419. */
  5420. "wrapper": "DTE_Field",
  5421. /**
  5422. * Class prefix for the field type - field type is added to the end allowing
  5423. * styling based on field type.
  5424. */
  5425. "typePrefix": "DTE_Field_Type_",
  5426. /**
  5427. * Class prefix for the field name - field name is added to the end allowing
  5428. * styling based on field name.
  5429. */
  5430. "namePrefix": "DTE_Field_Name_",
  5431. /**
  5432. * Field label
  5433. */
  5434. "label": "DTE_Label",
  5435. /**
  5436. * Field input container
  5437. */
  5438. "input": "DTE_Field_Input",
  5439. /**
  5440. * Input elements wrapper
  5441. */
  5442. "inputControl": "DTE_Field_InputControl",
  5443. /**
  5444. * Field error state (added to the field.wrapper element when in error state
  5445. */
  5446. "error": "DTE_Field_StateError",
  5447. /**
  5448. * Label information text
  5449. */
  5450. "msg-label": "DTE_Label_Info",
  5451. /**
  5452. * Error information text
  5453. */
  5454. "msg-error": "DTE_Field_Error",
  5455. /**
  5456. * Live messaging (API) information text
  5457. */
  5458. "msg-message": "DTE_Field_Message",
  5459. /**
  5460. * General information text
  5461. */
  5462. "msg-info": "DTE_Field_Info",
  5463. /**
  5464. * Multi-value information display wrapper
  5465. */
  5466. "multiValue": "multi-value",
  5467. /**
  5468. * Multi-value information descriptive text
  5469. */
  5470. "multiInfo": "multi-info",
  5471. /**
  5472. * Multi-value information display
  5473. */
  5474. "multiRestore": "multi-restore"
  5475. },
  5476. /**
  5477. * Action classes - these are added to the Editor base element ("wrapper")
  5478. * and allows styling based on the type of form view that is being employed.
  5479. * @namespace
  5480. */
  5481. "actions": {
  5482. /**
  5483. * Editor is in 'create' state
  5484. */
  5485. "create": "DTE_Action_Create",
  5486. /**
  5487. * Editor is in 'edit' state
  5488. */
  5489. "edit": "DTE_Action_Edit",
  5490. /**
  5491. * Editor is in 'remove' state
  5492. */
  5493. "remove": "DTE_Action_Remove"
  5494. },
  5495. /**
  5496. * Bubble editing classes - these are used to display the bubble editor
  5497. * @namespace
  5498. */
  5499. "bubble": {
  5500. /**
  5501. * Bubble container element
  5502. */
  5503. "wrapper": "DTE DTE_Bubble",
  5504. /**
  5505. * Bubble content liner
  5506. */
  5507. "liner": "DTE_Bubble_Liner",
  5508. /**
  5509. * Bubble table display wrapper, so the buttons and form can be shown
  5510. * as table cells (via css)
  5511. */
  5512. "table": "DTE_Bubble_Table",
  5513. /**
  5514. * Close button
  5515. */
  5516. "close": "DTE_Bubble_Close",
  5517. /**
  5518. * Pointer shown which node is being edited
  5519. */
  5520. "pointer": "DTE_Bubble_Triangle",
  5521. /**
  5522. * Fixed background
  5523. */
  5524. "bg": "DTE_Bubble_Background"
  5525. }
  5526. };
  5527. /*
  5528. * Add helpful TableTool buttons to make life easier
  5529. *
  5530. * Note that the values that require a string to make any sense (the button text
  5531. * for example) are set by Editor when Editor is initialised through the i18n
  5532. * options.
  5533. */
  5534. if ( DataTable.TableTools ) {
  5535. var ttButtons = DataTable.TableTools.BUTTONS;
  5536. var ttButtonBase = {
  5537. sButtonText: null,
  5538. editor: null,
  5539. formTitle: null
  5540. };
  5541. ttButtons.editor_create = $.extend( true, ttButtons.text, ttButtonBase, {
  5542. formButtons: [ {
  5543. label: null,
  5544. fn: function (e) { this.submit(); }
  5545. } ],
  5546. fnClick: function( button, config ) {
  5547. var editor = config.editor;
  5548. var i18nCreate = editor.i18n.create;
  5549. var buttons = config.formButtons;
  5550. if ( ! buttons[0].label ) {
  5551. buttons[0].label = i18nCreate.submit;
  5552. }
  5553. editor.create( {
  5554. title: i18nCreate.title,
  5555. buttons: buttons
  5556. } );
  5557. }
  5558. } );
  5559. ttButtons.editor_edit = $.extend( true, ttButtons.select_single, ttButtonBase, {
  5560. formButtons: [ {
  5561. label: null,
  5562. fn: function (e) { this.submit(); }
  5563. } ],
  5564. fnClick: function( button, config ) {
  5565. var selected = this.fnGetSelectedIndexes();
  5566. if ( selected.length !== 1 ) {
  5567. return;
  5568. }
  5569. var editor = config.editor;
  5570. var i18nEdit = editor.i18n.edit;
  5571. var buttons = config.formButtons;
  5572. if ( ! buttons[0].label ) {
  5573. buttons[0].label = i18nEdit.submit;
  5574. }
  5575. editor.edit( selected[0], {
  5576. title: i18nEdit.title,
  5577. buttons: buttons
  5578. } );
  5579. }
  5580. } );
  5581. ttButtons.editor_remove = $.extend( true, ttButtons.select, ttButtonBase, {
  5582. question: null,
  5583. formButtons: [
  5584. {
  5585. label: null,
  5586. fn: function (e) {
  5587. // Executed in the Form instance's scope
  5588. var that = this;
  5589. this.submit( function ( json ) {
  5590. var tt = $.fn.dataTable.TableTools.fnGetInstance(
  5591. $(that.s.table).DataTable().table().node()
  5592. );
  5593. tt.fnSelectNone();
  5594. } );
  5595. }
  5596. }
  5597. ],
  5598. fnClick: function( button, config ) {
  5599. var rows = this.fnGetSelectedIndexes();
  5600. if ( rows.length === 0 ) {
  5601. return;
  5602. }
  5603. var editor = config.editor;
  5604. var i18nRemove = editor.i18n.remove;
  5605. var buttons = config.formButtons;
  5606. var question = typeof i18nRemove.confirm === 'string' ?
  5607. i18nRemove.confirm :
  5608. i18nRemove.confirm[rows.length] ?
  5609. i18nRemove.confirm[rows.length] : i18nRemove.confirm._;
  5610. if ( ! buttons[0].label ) {
  5611. buttons[0].label = i18nRemove.submit;
  5612. }
  5613. editor.remove( rows, {
  5614. message: question.replace( /%d/g, rows.length ),
  5615. title: i18nRemove.title,
  5616. buttons: buttons
  5617. } );
  5618. }
  5619. } );
  5620. }
  5621. $.extend( DataTable.ext.buttons, {
  5622. create: {
  5623. text: function ( dt, node, config ) {
  5624. return dt.i18n( 'buttons.create', config.editor.i18n.create.button );
  5625. },
  5626. className: 'buttons-create',
  5627. editor: null,
  5628. formButtons: {
  5629. label: function ( editor ) {
  5630. return editor.i18n.create.submit;
  5631. },
  5632. fn: function (e) {
  5633. this.submit();
  5634. }
  5635. },
  5636. formMessage: null,
  5637. formTitle: null,
  5638. action: function( e, dt, node, config ) {
  5639. var editor = config.editor;
  5640. var buttons = config.formButtons;
  5641. editor.create( {
  5642. buttons: config.formButtons,
  5643. message: config.formMessage,
  5644. title: config.formTitle || editor.i18n.create.title
  5645. } );
  5646. }
  5647. },
  5648. edit: {
  5649. extend: 'selected',
  5650. text: function ( dt, node, config ) {
  5651. return dt.i18n( 'buttons.edit', config.editor.i18n.edit.button );
  5652. },
  5653. className: 'buttons-edit',
  5654. editor: null,
  5655. formButtons: {
  5656. label: function ( editor ) {
  5657. return editor.i18n.edit.submit;
  5658. },
  5659. fn: function (e) {
  5660. this.submit();
  5661. }
  5662. },
  5663. formMessage: null,
  5664. formTitle: null,
  5665. action: function( e, dt, node, config ) {
  5666. var editor = config.editor;
  5667. var rows = dt.rows( { selected: true } ).indexes();
  5668. var columns = dt.columns( { selected: true } ).indexes();
  5669. var cells = dt.cells( { selected: true } ).indexes();
  5670. var items = columns.length || cells.length ?
  5671. {
  5672. rows: rows,
  5673. columns: columns,
  5674. cells: cells
  5675. } :
  5676. rows;
  5677. editor.edit( items, {
  5678. message: config.formMessage,
  5679. buttons: config.formButtons,
  5680. title: config.formTitle || editor.i18n.edit.title
  5681. } );
  5682. }
  5683. },
  5684. remove: {
  5685. extend: 'selected',
  5686. text: function ( dt, node, config ) {
  5687. return dt.i18n( 'buttons.remove', config.editor.i18n.remove.button );
  5688. },
  5689. className: 'buttons-remove',
  5690. editor: null,
  5691. formButtons: {
  5692. label: function ( editor ) {
  5693. return editor.i18n.remove.submit;
  5694. },
  5695. fn: function (e) {
  5696. this.submit();
  5697. }
  5698. },
  5699. formMessage: function ( editor, dt ) {
  5700. var rows = dt.rows( { selected: true } ).indexes();
  5701. var i18n = editor.i18n.remove;
  5702. var question = typeof i18n.confirm === 'string' ?
  5703. i18n.confirm :
  5704. i18n.confirm[rows.length] ?
  5705. i18n.confirm[rows.length] : i18n.confirm._;
  5706. return question.replace( /%d/g, rows.length );
  5707. },
  5708. formTitle: null,
  5709. action: function( e, dt, node, config ) {
  5710. var editor = config.editor;
  5711. editor.remove( dt.rows( { selected: true } ).indexes(), {
  5712. buttons: config.formButtons,
  5713. message: config.formMessage,
  5714. title: config.formTitle || editor.i18n.remove.title
  5715. } );
  5716. }
  5717. }
  5718. } );
  5719. /**
  5720. * Field types array - this can be used to add field types or modify the pre-
  5721. * defined options. See the online Editor documentation for information about
  5722. * the built in field types.
  5723. *
  5724. * Note that we use a DataTables ext object to allow plug-ins to be loaded
  5725. * before Editor itself. This is useful for the online DataTables download
  5726. * builder.
  5727. *
  5728. * @namespace
  5729. */
  5730. Editor.fieldTypes = {};
  5731. /*
  5732. * This file provides a DateTime GUI picker (calender and time input). Only the
  5733. * format YYYY-MM-DD is supported without additional software, but the end user
  5734. * experience can be greatly enhanced by including the momentjs library which
  5735. * provides date / time parsing and formatting options.
  5736. *
  5737. * This functionality is required because the HTML5 date and datetime input
  5738. * types are not widely supported in desktop browsers.
  5739. *
  5740. * Constructed by using:
  5741. *
  5742. * new Editor.DateTime( input, opts )
  5743. *
  5744. * where `input` is the HTML input element to use and `opts` is an object of
  5745. * options based on the `Editor.DateTime.defaults` object.
  5746. */
  5747. Editor.DateTime = function ( input, opts ) {
  5748. this.c = $.extend( true, {}, Editor.DateTime.defaults, opts );
  5749. var classPrefix = this.c.classPrefix;
  5750. var i18n = this.c.i18n;
  5751. // Only IS8601 dates are supported without moment
  5752. if ( ! window.moment && this.c.format !== 'YYYY-MM-DD' ) {
  5753. throw "Editor datetime: Without momentjs only the format 'YYYY-MM-DD' can be used";
  5754. }
  5755. var timeBlock = function ( type ) {
  5756. return '<div class="'+classPrefix+'-timeblock">'+
  5757. '<div class="'+classPrefix+'-iconUp">'+
  5758. '<button>'+i18n.previous+'</button>'+
  5759. '</div>'+
  5760. '<div class="'+classPrefix+'-label">'+
  5761. '<span/>'+
  5762. '<select class="'+classPrefix+'-'+type+'"/>'+
  5763. '</div>'+
  5764. '<div class="'+classPrefix+'-iconDown">'+
  5765. '<button>'+i18n.next+'</button>'+
  5766. '</div>'+
  5767. '</div>';
  5768. };
  5769. var gap = function () {
  5770. return '<span>:</span>';
  5771. };
  5772. // DOM structure
  5773. var structure = $(
  5774. '<div class="'+classPrefix+'">'+
  5775. '<div class="'+classPrefix+'-date">'+
  5776. '<div class="'+classPrefix+'-title">'+
  5777. '<div class="'+classPrefix+'-iconLeft">'+
  5778. '<button>'+i18n.previous+'</button>'+
  5779. '</div>'+
  5780. '<div class="'+classPrefix+'-iconRight">'+
  5781. '<button>'+i18n.next+'</button>'+
  5782. '</div>'+
  5783. '<div class="'+classPrefix+'-label">'+
  5784. '<span/>'+
  5785. '<select class="'+classPrefix+'-month"/>'+
  5786. '</div>'+
  5787. '<div class="'+classPrefix+'-label">'+
  5788. '<span/>'+
  5789. '<select class="'+classPrefix+'-year"/>'+
  5790. '</div>'+
  5791. '</div>'+
  5792. '<div class="'+classPrefix+'-calendar"/>'+
  5793. '</div>'+
  5794. '<div class="'+classPrefix+'-time">'+
  5795. timeBlock( 'hours' )+
  5796. gap()+
  5797. timeBlock( 'minutes' )+
  5798. gap()+
  5799. timeBlock( 'seconds' )+
  5800. timeBlock( 'ampm' )+
  5801. '</div>'+
  5802. '</div>'
  5803. );
  5804. this.dom = {
  5805. container: structure,
  5806. date: structure.find( '.'+classPrefix+'-date' ),
  5807. title: structure.find( '.'+classPrefix+'-title' ),
  5808. calendar: structure.find( '.'+classPrefix+'-calendar' ),
  5809. time: structure.find( '.'+classPrefix+'-time' ),
  5810. input: $(input)
  5811. };
  5812. this.s = {
  5813. /** @type {Date} Date value that the picker has currently selected */
  5814. d: null,
  5815. /** @type {Date} Date of the calender - might not match the value */
  5816. display: null,
  5817. /** @type {String} Unique namespace string for this instance */
  5818. namespace: 'editor-dateime-'+(Editor.DateTime._instance++),
  5819. /** @type {Object} Parts of the picker that should be shown */
  5820. parts: {
  5821. date: this.c.format.match( /[YMD]/ ) !== null,
  5822. time: this.c.format.match( /[Hhm]/ ) !== null,
  5823. seconds: this.c.format.indexOf( 's' ) !== -1,
  5824. hours12: this.c.format.match( /[haA]/ ) !== null
  5825. }
  5826. };
  5827. this.dom.container
  5828. .append( this.dom.date )
  5829. .append( this.dom.time );
  5830. this.dom.date
  5831. .append( this.dom.title )
  5832. .append( this.dom.calendar );
  5833. this._constructor();
  5834. };
  5835. $.extend( Editor.DateTime.prototype, {
  5836. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  5837. * Public
  5838. */
  5839. /**
  5840. * Destroy the control
  5841. */
  5842. destroy: function () {
  5843. this._hide();
  5844. this.dom.container().off('').empty();
  5845. this.dom.input.off('.editor-datetime');
  5846. },
  5847. max: function ( date ) {
  5848. this.c.maxDate = date;
  5849. this._optionsTitle();
  5850. this._setCalander();
  5851. },
  5852. min: function ( date ) {
  5853. this.c.minDate = date;
  5854. this._optionsTitle();
  5855. this._setCalander();
  5856. },
  5857. /**
  5858. * Check if an element belongs to this control
  5859. *
  5860. * @param {node} node Element to check
  5861. * @return {boolean} true if owned by this control, false otherwise
  5862. */
  5863. owns: function ( node ) {
  5864. return $(node).parents().filter( this.dom.container ).length > 0;
  5865. },
  5866. /**
  5867. * Get / set the value
  5868. *
  5869. * @param {string|Date} set Value to set
  5870. * @param {boolean} [write=true] Flag to indicate if the formatted value
  5871. * should be written into the input element
  5872. */
  5873. val: function ( set, write ) {
  5874. if ( set === undefined ) {
  5875. return this.s.d;
  5876. }
  5877. if ( set instanceof Date ) {
  5878. this.s.d = this._dateToUtc( set );
  5879. }
  5880. else if ( set === null || set === '' ) {
  5881. this.s.d = null;
  5882. }
  5883. else if ( typeof set === 'string' ) {
  5884. if ( window.moment ) {
  5885. // Use moment if possible (even for ISO8601 strings, since it
  5886. // will correctly handle 0000-00-00 and the like)
  5887. var m = window.moment.utc( set, this.c.format, this.c.momentLocale, this.c.momentStrict );
  5888. this.s.d = m.isValid() ? m.toDate() : null;
  5889. }
  5890. else {
  5891. // Else must be using ISO8601 without moment (constructor would
  5892. // have thrown an error otherwise)
  5893. var match = set.match(/(\d{4})\-(\d{2})\-(\d{2})/ );
  5894. this.s.d = match ?
  5895. new Date( Date.UTC(match[1], match[2]-1, match[3]) ) :
  5896. null;
  5897. }
  5898. }
  5899. if ( write || write === undefined ) {
  5900. if ( this.s.d ) {
  5901. this._writeOutput();
  5902. }
  5903. else {
  5904. // The input value was not valid...
  5905. this.dom.input.val( set );
  5906. }
  5907. }
  5908. // We need a date to be able to display the calendar at all
  5909. if ( ! this.s.d ) {
  5910. this.s.d = this._dateToUtc( new Date() );
  5911. }
  5912. this.s.display = new Date( this.s.d.toString() );
  5913. // Update the display elements for the new value
  5914. this._setTitle();
  5915. this._setCalander();
  5916. this._setTime();
  5917. },
  5918. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  5919. * Constructor
  5920. */
  5921. /**
  5922. * Build the control and assign initial event handlers
  5923. *
  5924. * @private
  5925. */
  5926. _constructor: function () {
  5927. var that = this;
  5928. var classPrefix = this.c.classPrefix;
  5929. var container = this.dom.container;
  5930. var i18n = this.c.i18n;
  5931. if ( ! this.s.parts.date ) {
  5932. this.dom.date.css( 'display', 'none' );
  5933. }
  5934. if ( ! this.s.parts.time ) {
  5935. this.dom.time.css( 'display', 'none' );
  5936. }
  5937. if ( ! this.s.parts.seconds ) {
  5938. this.dom.time.children('div.editor-datetime-timeblock').eq(2).remove();
  5939. this.dom.time.children('span').eq(1).remove();
  5940. }
  5941. if ( ! this.s.parts.hours12 ) {
  5942. this.dom.time.children('div.editor-datetime-timeblock').last().remove();
  5943. }
  5944. // Render the options
  5945. this._optionsTitle();
  5946. this._optionsTime( 'hours', this.s.parts.hours12 ? 12 : 24, 1 );
  5947. this._optionsTime( 'minutes', 60, this.c.minutesIncrement );
  5948. this._optionsTime( 'seconds', 60, this.c.secondsIncrement );
  5949. this._options( 'ampm', [ 'am', 'pm' ], i18n.amPm );
  5950. // Trigger the display of the widget when clicking or focusing on the
  5951. // input element
  5952. this.dom.input
  5953. .on('focus.editor-datetime click.editor-datetime', function () {
  5954. // If already visible - don't do anything
  5955. if ( that.dom.container.is(':visible') || that.dom.input.is(':disabled') ) {
  5956. return;
  5957. }
  5958. // In case the value has changed by text
  5959. that.val( that.dom.input.val(), false );
  5960. that._show();
  5961. } )
  5962. .on('keyup.editor-datetime', function () {
  5963. // Update the calender's displayed value as the user types
  5964. if ( that.dom.container.is(':visible') ) {
  5965. that.val( that.dom.input.val(), false );
  5966. }
  5967. } );
  5968. // Main event handlers for input in the widget
  5969. this.dom.container
  5970. .on( 'change', 'select', function () {
  5971. var select = $(this);
  5972. var val = select.val();
  5973. if ( select.hasClass(classPrefix+'-month') ) {
  5974. // Month select
  5975. that._correctMonth( that.s.display, val );
  5976. that._setTitle();
  5977. that._setCalander();
  5978. }
  5979. else if ( select.hasClass(classPrefix+'-year') ) {
  5980. // Year select
  5981. that.s.display.setUTCFullYear( val );
  5982. that._setTitle();
  5983. that._setCalander();
  5984. }
  5985. else if ( select.hasClass(classPrefix+'-hours') || select.hasClass(classPrefix+'-ampm') ) {
  5986. // Hours - need to take account of AM/PM input if present
  5987. if ( that.s.parts.hours12 ) {
  5988. var hours = $(that.dom.container).find('.'+classPrefix+'-hours').val() * 1;
  5989. var pm = $(that.dom.container).find('.'+classPrefix+'-ampm').val() === 'pm';
  5990. that.s.d.setUTCHours( hours === 12 && !pm ?
  5991. 0 :
  5992. pm && hours !== 12 ?
  5993. hours + 12 :
  5994. hours
  5995. );
  5996. }
  5997. else {
  5998. that.s.d.setUTCHours( val );
  5999. }
  6000. that._setTime();
  6001. that._writeOutput( true );
  6002. }
  6003. else if ( select.hasClass(classPrefix+'-minutes') ) {
  6004. // Minutes select
  6005. that.s.d.setUTCMinutes( val );
  6006. that._setTime();
  6007. that._writeOutput( true );
  6008. }
  6009. else if ( select.hasClass(classPrefix+'-seconds') ) {
  6010. // Seconds select
  6011. that.s.d.setSeconds( val );
  6012. that._setTime();
  6013. that._writeOutput( true );
  6014. }
  6015. that.dom.input.focus();
  6016. that._position();
  6017. } )
  6018. .on( 'click', function (e) {
  6019. var nodeName = e.target.nodeName.toLowerCase();
  6020. if ( nodeName === 'select' ) {
  6021. return;
  6022. }
  6023. e.stopPropagation();
  6024. if ( nodeName === 'button' ) {
  6025. var button = $(e.target);
  6026. var parent = button.parent();
  6027. var select;
  6028. if ( parent.hasClass('disabled') ) {
  6029. return;
  6030. }
  6031. if ( parent.hasClass(classPrefix+'-iconLeft') ) {
  6032. // Previous month
  6033. that.s.display.setUTCMonth( that.s.display.getUTCMonth()-1 );
  6034. that._setTitle();
  6035. that._setCalander();
  6036. that.dom.input.focus();
  6037. }
  6038. else if ( parent.hasClass(classPrefix+'-iconRight') ) {
  6039. // Next month
  6040. that._correctMonth( that.s.display, that.s.display.getUTCMonth()+1 );
  6041. that._setTitle();
  6042. that._setCalander();
  6043. that.dom.input.focus();
  6044. }
  6045. else if ( parent.hasClass(classPrefix+'-iconUp') ) {
  6046. // Value increase - common to all time selects
  6047. select = parent.parent().find('select')[0];
  6048. select.selectedIndex = select.selectedIndex !== select.options.length - 1 ?
  6049. select.selectedIndex+1 :
  6050. 0;
  6051. $(select).change();
  6052. }
  6053. else if ( parent.hasClass(classPrefix+'-iconDown') ) {
  6054. // Value decrease - common to all time selects
  6055. select = parent.parent().find('select')[0];
  6056. select.selectedIndex = select.selectedIndex === 0 ?
  6057. select.options.length - 1 :
  6058. select.selectedIndex-1;
  6059. $(select).change();
  6060. }
  6061. else {
  6062. // Calender click
  6063. if ( ! that.s.d ) {
  6064. that.s.d = that._dateToUtc( new Date() );
  6065. }
  6066. that.s.d.setUTCFullYear( button.data('year') );
  6067. that.s.d.setUTCMonth( button.data('month') );
  6068. that.s.d.setUTCDate( button.data('day') );
  6069. that._writeOutput( true );
  6070. // This is annoying but IE has some kind of async
  6071. // behaviour with focus and the focus from the above
  6072. // write would occur after this hide - resulting in the
  6073. // calender opening immediately
  6074. setTimeout( function () {
  6075. that._hide();
  6076. }, 10 );
  6077. }
  6078. }
  6079. else {
  6080. // Click anywhere else in the widget - return focus to the
  6081. // input element
  6082. that.dom.input.focus();
  6083. }
  6084. } );
  6085. },
  6086. /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  6087. * Private
  6088. */
  6089. /**
  6090. * Compare the date part only of two dates - this is made super easy by the
  6091. * toDateString method!
  6092. *
  6093. * @param {Date} a Date 1
  6094. * @param {Date} b Date 2
  6095. * @private
  6096. */
  6097. _compareDates: function( a, b ) {
  6098. // Can't use toDateString as that converts to local time
  6099. return this._dateToUtcString(a) === this._dateToUtcString(b);
  6100. },
  6101. /**
  6102. * When changing month, take account of the fact that some months don't have
  6103. * the same number of days. For example going from January to February you
  6104. * can have the 31st of Jan selected and just add a month since the date
  6105. * would still be 31, and thus drop you into March.
  6106. *
  6107. * @param {Date} date Date - will be modified
  6108. * @param {integer} month Month to set
  6109. * @private
  6110. */
  6111. _correctMonth: function ( date, month ) {
  6112. var days = this._daysInMonth( date.getUTCFullYear(), month );
  6113. var correctDays = date.getUTCDate() > days;
  6114. date.setUTCMonth( month );
  6115. if ( correctDays ) {
  6116. date.setUTCDate( days );
  6117. date.setUTCMonth( month );
  6118. }
  6119. },
  6120. /**
  6121. * Get the number of days in a method. Based on
  6122. * http://stackoverflow.com/a/4881951 by Matti Virkkunen
  6123. *
  6124. * @param {integer} year Year
  6125. * @param {integer} month Month (starting at 0)
  6126. * @private
  6127. */
  6128. _daysInMonth: function ( year, month ) {
  6129. //
  6130. var isLeap = ((year % 4) === 0 && ((year % 100) !== 0 || (year % 400) === 0));
  6131. var months = [31, (isLeap ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31];
  6132. return months[month];
  6133. },
  6134. /**
  6135. * Create a new date object which has the UTC values set to the local time.
  6136. * This allows the local time to be used directly for the library which
  6137. * always bases its calculations and display on UTC.
  6138. *
  6139. * @param {Date} s Date to "convert"
  6140. * @return {Date} Shifted date
  6141. */
  6142. _dateToUtc: function ( s ) {
  6143. return new Date( Date.UTC(
  6144. s.getFullYear(), s.getMonth(), s.getDate(),
  6145. s.getHours(), s.getMinutes(), s.getSeconds()
  6146. ) );
  6147. },
  6148. /**
  6149. * Create a UTC ISO8601 date part from a date object
  6150. *
  6151. * @param {Date} d Date to "convert"
  6152. * @return {string} ISO formatted date
  6153. */
  6154. _dateToUtcString: function ( d ) {
  6155. return d.getUTCFullYear()+'-'+
  6156. this._pad(d.getUTCMonth()+1)+'-'+
  6157. this._pad(d.getUTCDate());
  6158. },
  6159. /**
  6160. * Hide the control and remove events related to its display
  6161. *
  6162. * @private
  6163. */
  6164. _hide: function () {
  6165. var namespace = this.s.namespace;
  6166. this.dom.container.detach();
  6167. $(window).off( '.'+namespace );
  6168. $(document).off( 'keydown.'+namespace );
  6169. $('div.DTE_Body_Content').off( 'scroll.'+namespace );
  6170. $('body').off( 'click.'+namespace );
  6171. },
  6172. /**
  6173. * Convert a 24 hour value to a 12 hour value
  6174. *
  6175. * @param {integer} val 24 hour value
  6176. * @return {integer} 12 hour value
  6177. * @private
  6178. */
  6179. _hours24To12: function ( val ) {
  6180. return val === 0 ?
  6181. 12 :
  6182. val > 12 ?
  6183. val - 12 :
  6184. val;
  6185. },
  6186. /**
  6187. * Generate the HTML for a single day in the calender - this is basically
  6188. * and HTML cell with a button that has data attributes so we know what was
  6189. * clicked on (if it is clicked on) and a bunch of classes for styling.
  6190. *
  6191. * @param {object} day Day object from the `_htmlMonth` method
  6192. * @return {string} HTML cell
  6193. */
  6194. _htmlDay: function( day )
  6195. {
  6196. if ( day.empty ) {
  6197. return '<td class="empty"></td>';
  6198. }
  6199. var classes = [ 'day' ];
  6200. var classPrefix = this.c.classPrefix;
  6201. if ( day.disabled ) {
  6202. classes.push( 'disabled' );
  6203. }
  6204. if ( day.today ) {
  6205. classes.push( 'today' );
  6206. }
  6207. if ( day.selected ) {
  6208. classes.push( 'selected' );
  6209. }
  6210. return '<td data-day="' + day.day + '" class="' + classes.join(' ') + '">' +
  6211. '<button class="'+classPrefix+'-button '+classPrefix+'-day" type="button" ' +'data-year="' + day.year + '" data-month="' + day.month + '" data-day="' + day.day + '">' +
  6212. day.day +
  6213. '</button>' +
  6214. '</td>';
  6215. },
  6216. /**
  6217. * Create the HTML for a month to be displayed in the calender table.
  6218. *
  6219. * Based upon the logic used in Pikaday - MIT licensed
  6220. * Copyright (c) 2014 David Bushell
  6221. * https://github.com/dbushell/Pikaday
  6222. *
  6223. * @param {integer} year Year
  6224. * @param {integer} month Month (starting at 0)
  6225. * @return {string} Calender month HTML
  6226. * @private
  6227. */
  6228. _htmlMonth: function ( year, month ) {
  6229. var now = new Date(),
  6230. days = this._daysInMonth( year, month ),
  6231. before = new Date( Date.UTC(year, month, 1) ).getUTCDay(),
  6232. data = [],
  6233. row = [];
  6234. if ( this.c.firstDay > 0 ) {
  6235. before -= this.c.firstDay;
  6236. if (before < 0) {
  6237. before += 7;
  6238. }
  6239. }
  6240. var cells = days + before,
  6241. after = cells;
  6242. while ( after > 7 ) {
  6243. after -= 7;
  6244. }
  6245. cells += 7 - after;
  6246. var minDate = this.c.minDate;
  6247. var maxDate = this.c.maxDate;
  6248. if ( minDate ) {
  6249. minDate.setUTCHours(0);
  6250. minDate.setUTCMinutes(0);
  6251. minDate.setSeconds(0);
  6252. }
  6253. if ( maxDate ) {
  6254. maxDate.setUTCHours(23);
  6255. maxDate.setUTCMinutes(59);
  6256. maxDate.setSeconds(59);
  6257. }
  6258. for ( var i=0, r=0 ; i<cells ; i++ ) {
  6259. var day = new Date( Date.UTC(year, month, 1 + (i - before)) ),
  6260. selected = this.s.d ? this._compareDates(day, this.s.d) : false,
  6261. today = this._compareDates(day, now),
  6262. empty = i < before || i >= (days + before),
  6263. disabled = (minDate && day < minDate) ||
  6264. (maxDate && day > maxDate);
  6265. var disableDays = this.c.disableDays;
  6266. if ( $.isArray( disableDays ) && $.inArray( day.getUTCDay(), disableDays ) !== -1 ) {
  6267. disabled = true;
  6268. }
  6269. else if ( typeof disableDays === 'function' && disableDays( day ) === true ) {
  6270. disabled = true;
  6271. }
  6272. var dayConfig = {
  6273. day: 1 + (i - before),
  6274. month: month,
  6275. year: year,
  6276. selected: selected,
  6277. today: today,
  6278. disabled: disabled,
  6279. empty: empty
  6280. };
  6281. row.push( this._htmlDay(dayConfig) );
  6282. if ( ++r === 7 ) {
  6283. if ( this.c.showWeekNumber ) {
  6284. row.unshift( this._htmlWeekOfYear(i - before, month, year) );
  6285. }
  6286. data.push( '<tr>'+row.join('')+'</tr>' );
  6287. row = [];
  6288. r = 0;
  6289. }
  6290. }
  6291. var className = this.c.classPrefix+'-table';
  6292. if ( this.c.showWeekNumber ) {
  6293. className += ' weekNumber';
  6294. }
  6295. return '<table class="'+className+'">' +
  6296. '<thead>'+
  6297. this._htmlMonthHead() +
  6298. '</thead>'+
  6299. '<tbody>'+
  6300. data.join('') +
  6301. '</tbody>'+
  6302. '</table>';
  6303. },
  6304. /**
  6305. * Create the calender table's header (week days)
  6306. *
  6307. * @return {string} HTML cells for the row
  6308. * @private
  6309. */
  6310. _htmlMonthHead: function () {
  6311. var a = [];
  6312. var firstDay = this.c.firstDay;
  6313. var i18n = this.c.i18n;
  6314. // Take account of the first day shift
  6315. var dayName = function ( day ) {
  6316. day += firstDay;
  6317. while (day >= 7) {
  6318. day -= 7;
  6319. }
  6320. return i18n.weekdays[day];
  6321. };
  6322. // Empty cell in the header
  6323. if ( this.c.showWeekNumber ) {
  6324. a.push( '<th></th>' );
  6325. }
  6326. for ( var i=0 ; i<7 ; i++ ) {
  6327. a.push( '<th>'+dayName( i )+'</th>' );
  6328. }
  6329. return a.join('');
  6330. },
  6331. /**
  6332. * Create a cell that contains week of the year - ISO style
  6333. *
  6334. * Based on http://javascript.about.com/library/blweekyear.htm
  6335. *
  6336. * @param {integer} d Day of month
  6337. * @param {integer} m Month of year (zero index)
  6338. * @param {integer} y Year
  6339. * @return {string}
  6340. * @private
  6341. */
  6342. _htmlWeekOfYear: function ( d, m, y ) {
  6343. var onejan = new Date(y, 0, 1),
  6344. weekNum = Math.ceil((((new Date(y, m, d) - onejan) / 86400000) + onejan.getUTCDay()+1)/7);
  6345. return '<td class="'+this.c.classPrefix+'-week">' + weekNum + '</td>';
  6346. },
  6347. /**
  6348. * Create option elements from a range in an array
  6349. *
  6350. * @param {string} selector Class name unique to the select element to use
  6351. * @param {array} values Array of values
  6352. * @param {array} [labels] Array of labels. If given must be the same
  6353. * length as the values parameter.
  6354. * @private
  6355. */
  6356. _options: function ( selector, values, labels ) {
  6357. if ( ! labels ) {
  6358. labels = values;
  6359. }
  6360. var select = this.dom.container.find('select.'+this.c.classPrefix+'-'+selector);
  6361. select.empty();
  6362. for ( var i=0, ien=values.length ; i<ien ; i++ ) {
  6363. select.append( '<option value="'+values[i]+'">'+labels[i]+'</option>' );
  6364. }
  6365. },
  6366. /**
  6367. * Set an option and update the option's span pair (since the select element
  6368. * has opacity 0 for styling)
  6369. *
  6370. * @param {string} selector Class name unique to the select element to use
  6371. * @param {*} val Value to set
  6372. * @private
  6373. */
  6374. _optionSet: function ( selector, val ) {
  6375. var select = this.dom.container.find('select.'+this.c.classPrefix+'-'+selector);
  6376. var span = select.parent().children('span');
  6377. select.val( val );
  6378. var selected = select.find('option:selected');
  6379. span.html( selected.length !== 0 ?
  6380. selected.text() :
  6381. this.c.i18n.unknown
  6382. );
  6383. },
  6384. /**
  6385. * Create time option list. Can't just use `_options` for the time as the
  6386. * hours can be a little complex and we want to be able to control the
  6387. * increment option for the minutes and seconds.
  6388. *
  6389. * @param {jQuery} select Select element to operate on
  6390. * @param {integer} count Value to loop to
  6391. * @param {integer} inc Increment value
  6392. * @private
  6393. */
  6394. _optionsTime: function ( select, count, inc ) {
  6395. var classPrefix = this.c.classPrefix;
  6396. var sel = this.dom.container.find('select.'+classPrefix+'-'+select);
  6397. var start=0, end=count;
  6398. var render = count === 12 ?
  6399. function (i) { return i; } :
  6400. this._pad;
  6401. if ( count === 12 ) {
  6402. start = 1;
  6403. end = 13;
  6404. }
  6405. for ( var i=start ; i<end ; i+=inc ) {
  6406. sel.append( '<option value="'+i+'">'+render(i)+'</option>' );
  6407. }
  6408. },
  6409. /**
  6410. * Create the options for the month and year
  6411. *
  6412. * @param {integer} year Year
  6413. * @param {integer} month Month (starting at 0)
  6414. * @private
  6415. */
  6416. _optionsTitle: function ( year, month ) {
  6417. var classPrefix = this.c.classPrefix;
  6418. var i18n = this.c.i18n;
  6419. var min = this.c.minDate;
  6420. var max = this.c.maxDate;
  6421. var minYear = min ? min.getFullYear() : null;
  6422. var maxYear = max ? max.getFullYear() : null;
  6423. var i = minYear !== null ? minYear : new Date().getFullYear() - this.c.yearRange;
  6424. var j = maxYear !== null ? maxYear : new Date().getFullYear() + this.c.yearRange;
  6425. this._options( 'month', this._range( 0, 11 ), i18n.months );
  6426. this._options( 'year', this._range( i, j ) );
  6427. },
  6428. /**
  6429. * Simple two digit pad
  6430. *
  6431. * @param {integer} i Value that might need padding
  6432. * @return {string|integer} Padded value
  6433. * @private
  6434. */
  6435. _pad: function ( i ) {
  6436. return i<10 ? '0'+i : i;
  6437. },
  6438. /**
  6439. * Position the calender to look attached to the input element
  6440. * @private
  6441. */
  6442. _position: function () {
  6443. var offset = this.dom.input.offset();
  6444. var container = this.dom.container;
  6445. var inputHeight = this.dom.input.outerHeight();
  6446. container
  6447. .css( {
  6448. top: offset.top + inputHeight,
  6449. left: offset.left
  6450. } )
  6451. .appendTo( 'body' );
  6452. var calHeight = container.outerHeight();
  6453. var scrollTop = $('body').scrollTop();
  6454. if ( offset.top + inputHeight + calHeight - scrollTop > $(window).height() ) {
  6455. var newTop = offset.top - calHeight;
  6456. container.css( 'top', newTop < 0 ? 0 : newTop );
  6457. }
  6458. },
  6459. /**
  6460. * Create a simple array with a range of values
  6461. *
  6462. * @param {integer} start Start value (inclusive)
  6463. * @param {integer} end End value (inclusive)
  6464. * @return {array} Created array
  6465. * @private
  6466. */
  6467. _range: function ( start, end ) {
  6468. var a = [];
  6469. for ( var i=start ; i<=end ; i++ ) {
  6470. a.push( i );
  6471. }
  6472. return a;
  6473. },
  6474. /**
  6475. * Redraw the calender based on the display date - this is a destructive
  6476. * operation
  6477. *
  6478. * @private
  6479. */
  6480. _setCalander: function () {
  6481. this.dom.calendar
  6482. .empty()
  6483. .append( this._htmlMonth(
  6484. this.s.display.getUTCFullYear(),
  6485. this.s.display.getUTCMonth()
  6486. ) );
  6487. },
  6488. /**
  6489. * Set the month and year for the calender based on the current display date
  6490. *
  6491. * @private
  6492. */
  6493. _setTitle: function () {
  6494. this._optionSet( 'month', this.s.display.getUTCMonth() );
  6495. this._optionSet( 'year', this.s.display.getUTCFullYear() );
  6496. },
  6497. /**
  6498. * Set the time based on the current value of the widget
  6499. *
  6500. * @private
  6501. */
  6502. _setTime: function () {
  6503. var d = this.s.d;
  6504. var hours = d ? d.getUTCHours() : 0;
  6505. if ( this.s.parts.hours12 ) {
  6506. this._optionSet( 'hours', this._hours24To12( hours ) );
  6507. this._optionSet( 'ampm', hours < 12 ? 'am' : 'pm' );
  6508. }
  6509. else {
  6510. this._optionSet( 'hours', hours );
  6511. }
  6512. this._optionSet( 'minutes', d ? d.getUTCMinutes() : 0 );
  6513. this._optionSet( 'seconds', d ? d.getSeconds() : 0 );
  6514. },
  6515. /**
  6516. * Show the widget and add events to the document required only while it
  6517. * is displayed
  6518. *
  6519. * @private
  6520. */
  6521. _show: function () {
  6522. var that = this;
  6523. var namespace = this.s.namespace;
  6524. this._position();
  6525. // Need to reposition on scroll
  6526. $(window).on( 'scroll.'+namespace+' resize.'+namespace, function () {
  6527. that._position();
  6528. } );
  6529. $('div.DTE_Body_Content').on( 'scroll.'+namespace, function () {
  6530. that._position();
  6531. } );
  6532. // On tab focus will move to a different field (no keyboard navigation
  6533. // in the date picker - this might need to be changed).
  6534. // On esc the Editor might close. Even if it doesn't the date picker
  6535. // should
  6536. $(document).on( 'keydown.'+namespace, function (e) {
  6537. if (
  6538. e.keyCode === 9 || // tab
  6539. e.keyCode === 27 || // esc
  6540. e.keyCode === 13 // return
  6541. ) {
  6542. that._hide();
  6543. }
  6544. } );
  6545. // Hide if clicking outside of the widget - but in a different click
  6546. // event from the one that was used to trigger the show (bubble and
  6547. // inline)
  6548. setTimeout( function () {
  6549. $('body').on( 'click.'+namespace, function (e) {
  6550. var parents = $(e.target).parents();
  6551. if ( ! parents.filter( that.dom.container ).length && e.target !== that.dom.input[0] ) {
  6552. that._hide();
  6553. }
  6554. } );
  6555. }, 10 );
  6556. },
  6557. /**
  6558. * Write the formatted string to the input element this control is attached
  6559. * to
  6560. *
  6561. * @private
  6562. */
  6563. _writeOutput: function ( focus ) {
  6564. var date = this.s.d;
  6565. // Use moment if possible - otherwise it must be ISO8601 (or the
  6566. // constructor would have thrown an error)
  6567. var out = window.moment ?
  6568. window.moment.utc( date, undefined, this.c.momentLocale, this.c.momentStrict ).format( this.c.format ) :
  6569. date.getUTCFullYear() +'-'+
  6570. this._pad(date.getUTCMonth() + 1) +'-'+
  6571. this._pad(date.getUTCDate());
  6572. this.dom.input.val( out );
  6573. if ( focus ) {
  6574. this.dom.input.focus();
  6575. }
  6576. }
  6577. } );
  6578. /**
  6579. * For generating unique namespaces
  6580. *
  6581. * @type {Number}
  6582. * @private
  6583. */
  6584. Editor.DateTime._instance = 0;
  6585. /**
  6586. * Defaults for the date time picker
  6587. *
  6588. * @type {Object}
  6589. */
  6590. Editor.DateTime.defaults = {
  6591. // Not documented - could be an internal property
  6592. classPrefix: 'editor-datetime',
  6593. // function or array of ints
  6594. disableDays: null,
  6595. // first day of the week (0: Sunday, 1: Monday, etc)
  6596. firstDay: 1,
  6597. format: 'YYYY-MM-DD',
  6598. // Not documented as i18n is done by the Editor.defaults.i18n obj
  6599. i18n: Editor.defaults.i18n.datetime,
  6600. maxDate: null,
  6601. minDate: null,
  6602. minutesIncrement: 1,
  6603. momentStrict: true,
  6604. momentLocale: 'en',
  6605. secondsIncrement: 1,
  6606. // show the ISO week number at the head of the row
  6607. showWeekNumber: false,
  6608. // overruled by max / min date
  6609. yearRange: 10
  6610. };
  6611. (function() {
  6612. var fieldTypes = Editor.fieldTypes;
  6613. // Upload private helper method
  6614. function _buttonText ( conf, text )
  6615. {
  6616. if ( text === null || text === undefined ) {
  6617. text = conf.uploadText || "Choose file...";
  6618. }
  6619. conf._input.find('div.upload button').html( text );
  6620. }
  6621. function _commonUpload ( editor, conf, dropCallback )
  6622. {
  6623. var btnClass = editor.classes.form.button;
  6624. var container = $(
  6625. '<div class="editor_upload">'+
  6626. '<div class="eu_table">'+
  6627. '<div class="row">'+
  6628. '<div class="cell upload">'+
  6629. '<button class="'+btnClass+'" />'+
  6630. '<input type="file"/>'+
  6631. '</div>'+
  6632. '<div class="cell clearValue">'+
  6633. '<button class="'+btnClass+'" />'+
  6634. '</div>'+
  6635. '</div>'+
  6636. '<div class="row second">'+
  6637. '<div class="cell">'+
  6638. '<div class="drop"><span/></div>'+
  6639. '</div>'+
  6640. '<div class="cell">'+
  6641. '<div class="rendered"/>'+
  6642. '</div>'+
  6643. '</div>'+
  6644. '</div>'+
  6645. '</div>'
  6646. );
  6647. conf._input = container;
  6648. conf._enabled = true;
  6649. _buttonText( conf );
  6650. if ( window.FileReader && conf.dragDrop !== false ) {
  6651. container.find('div.drop span').text(
  6652. conf.dragDropText || "Drag and drop a file here to upload"
  6653. );
  6654. var dragDrop = container.find('div.drop');
  6655. dragDrop
  6656. .on( 'drop', function (e) {
  6657. if ( conf._enabled ) {
  6658. Editor.upload( editor, conf, e.originalEvent.dataTransfer.files, _buttonText, dropCallback );
  6659. dragDrop.removeClass('over');
  6660. }
  6661. return false;
  6662. } )
  6663. .on( 'dragleave dragexit', function (e) {
  6664. if ( conf._enabled ) {
  6665. dragDrop.removeClass('over');
  6666. }
  6667. return false;
  6668. } )
  6669. .on( 'dragover', function (e) {
  6670. if ( conf._enabled ) {
  6671. dragDrop.addClass('over');
  6672. }
  6673. return false;
  6674. } );
  6675. // When an Editor is open with a file upload input there is a
  6676. // reasonable chance that the user will miss the drop point when
  6677. // dragging and dropping. Rather than loading the file in the browser,
  6678. // we want nothing to happen, otherwise the form will be lost.
  6679. editor
  6680. .on( 'open', function () {
  6681. $('body').on( 'dragover.DTE_Upload drop.DTE_Upload', function (e) {
  6682. return false;
  6683. } );
  6684. } )
  6685. .on( 'close', function () {
  6686. $('body').off( 'dragover.DTE_Upload drop.DTE_Upload' );
  6687. } );
  6688. }
  6689. else {
  6690. container.addClass( 'noDrop' );
  6691. container.append( container.find('div.rendered') );
  6692. }
  6693. container.find('div.clearValue button').on( 'click', function () {
  6694. Editor.fieldTypes.upload.set.call( editor, conf, '' );
  6695. } );
  6696. container.find('input[type=file]').on('change', function () {
  6697. Editor.upload( editor, conf, this.files, _buttonText, function (ids) {
  6698. dropCallback.call( editor, ids );
  6699. // Clear the value so change will happen on the next file select,
  6700. // even if it is the same file
  6701. container.find('input[type=file]').val('');
  6702. } );
  6703. } );
  6704. return container;
  6705. }
  6706. // Typically a change event caused by the end user will be added to a queue that
  6707. // the browser will handle when no other script is running. However, using
  6708. // `$().trigger()` will cause it to happen immediately, so in order to simulate
  6709. // the standard browser behaviour we use setTimeout. This also means that
  6710. // `dependent()` and other change event listeners will trigger when the field
  6711. // values have all been set, rather than as they are being set - 31594
  6712. function _triggerChange ( input ) {
  6713. setTimeout( function () {
  6714. input.trigger( 'change', {editor: true, editorSet: true} ); // editorSet legacy
  6715. }, 0 );
  6716. }
  6717. // A number of the fields in this file use the same get, set, enable and disable
  6718. // methods (specifically the text based controls), so in order to reduce the code
  6719. // size, we just define them once here in our own local base model for the field
  6720. // types.
  6721. var baseFieldType = $.extend( true, {}, Editor.models.fieldType, {
  6722. get: function ( conf ) {
  6723. return conf._input.val();
  6724. },
  6725. set: function ( conf, val ) {
  6726. conf._input.val( val );
  6727. _triggerChange( conf._input );
  6728. },
  6729. enable: function ( conf ) {
  6730. conf._input.prop( 'disabled', false );
  6731. },
  6732. disable: function ( conf ) {
  6733. conf._input.prop( 'disabled', true );
  6734. }
  6735. } );
  6736. fieldTypes.hidden = {
  6737. create: function ( conf ) {
  6738. conf._val = conf.value;
  6739. return null;
  6740. },
  6741. get: function ( conf ) {
  6742. return conf._val;
  6743. },
  6744. set: function ( conf, val ) {
  6745. conf._val = val;
  6746. }
  6747. };
  6748. fieldTypes.readonly = $.extend( true, {}, baseFieldType, {
  6749. create: function ( conf ) {
  6750. conf._input = $('<input/>').attr( $.extend( {
  6751. id: Editor.safeId( conf.id ),
  6752. type: 'text',
  6753. readonly: 'readonly'
  6754. }, conf.attr || {} ) );
  6755. return conf._input[0];
  6756. }
  6757. } );
  6758. fieldTypes.text = $.extend( true, {}, baseFieldType, {
  6759. create: function ( conf ) {
  6760. conf._input = $('<input/>').attr( $.extend( {
  6761. id: Editor.safeId( conf.id ),
  6762. type: 'text'
  6763. }, conf.attr || {} ) );
  6764. return conf._input[0];
  6765. }
  6766. } );
  6767. fieldTypes.password = $.extend( true, {}, baseFieldType, {
  6768. create: function ( conf ) {
  6769. conf._input = $('<input/>').attr( $.extend( {
  6770. id: Editor.safeId( conf.id ),
  6771. type: 'password'
  6772. }, conf.attr || {} ) );
  6773. return conf._input[0];
  6774. }
  6775. } );
  6776. fieldTypes.textarea = $.extend( true, {}, baseFieldType, {
  6777. create: function ( conf ) {
  6778. conf._input = $('<textarea/>').attr( $.extend( {
  6779. id: Editor.safeId( conf.id )
  6780. }, conf.attr || {} ) );
  6781. return conf._input[0];
  6782. }
  6783. } );
  6784. fieldTypes.select = $.extend( true, {}, baseFieldType, {
  6785. // Locally "private" function that can be reused for the create and update methods
  6786. _addOptions: function ( conf, opts ) {
  6787. var elOpts = conf._input[0].options;
  6788. var countOffset = 0;
  6789. elOpts.length = 0;
  6790. if ( conf.placeholder !== undefined ) {
  6791. countOffset += 1;
  6792. elOpts[0] = new Option( conf.placeholder, conf.placeholderValue !== undefined ?
  6793. conf.placeholderValue :
  6794. ''
  6795. );
  6796. var disabled = conf.placeholderDisabled !== undefined ?
  6797. conf.placeholderDisabled :
  6798. true;
  6799. elOpts[0].hidden = disabled; // can't be hidden if not disabled!
  6800. elOpts[0].disabled = disabled;
  6801. }
  6802. if ( opts ) {
  6803. Editor.pairs( opts, conf.optionsPair, function ( val, label, i ) {
  6804. elOpts[ i+countOffset ] = new Option( label, val );
  6805. elOpts[ i+countOffset ]._editor_val = val;
  6806. } );
  6807. }
  6808. },
  6809. create: function ( conf ) {
  6810. conf._input = $('<select/>')
  6811. .attr( $.extend( {
  6812. id: Editor.safeId( conf.id ),
  6813. multiple: conf.multiple === true
  6814. }, conf.attr || {} ) )
  6815. .on( 'change.dte', function (e, d) {
  6816. // On change, get the user selected value and store it as the
  6817. // last set, so `update` can reflect it. This way `_lastSet`
  6818. // always gives the intended value, be it set via the API or by
  6819. // the end user.
  6820. if ( ! d || ! d.editor ) {
  6821. conf._lastSet = fieldTypes.select.get( conf );
  6822. }
  6823. } );
  6824. fieldTypes.select._addOptions( conf, conf.options || conf.ipOpts );
  6825. return conf._input[0];
  6826. },
  6827. update: function ( conf, options ) {
  6828. fieldTypes.select._addOptions( conf, options );
  6829. // Attempt to set the last selected value (set by the API or the end
  6830. // user, they get equal priority)
  6831. var lastSet = conf._lastSet;
  6832. if ( lastSet !== undefined ) {
  6833. fieldTypes.select.set( conf, lastSet, true );
  6834. }
  6835. _triggerChange( conf._input );
  6836. },
  6837. get: function ( conf ) {
  6838. var val = conf._input.find('option:selected').map( function () {
  6839. return this._editor_val;
  6840. } ).toArray();
  6841. if ( conf.multiple ) {
  6842. return conf.separator ?
  6843. val.join( conf.separator ) :
  6844. val;
  6845. }
  6846. return val.length ? val[0] : null;
  6847. },
  6848. set: function ( conf, val, localUpdate ) {
  6849. if ( ! localUpdate ) {
  6850. conf._lastSet = val;
  6851. }
  6852. // Can't just use `$().val()` because it won't work with strong types
  6853. if ( conf.multiple && conf.separator && ! $.isArray( val ) ) {
  6854. val = val.split( conf.separator );
  6855. }
  6856. else if ( ! $.isArray( val ) ) {
  6857. val = [ val ];
  6858. }
  6859. var i, len=val.length, found, allFound = false;
  6860. var options = conf._input.find('option');
  6861. conf._input.find('option').each( function () {
  6862. found = false;
  6863. for ( i=0 ; i<len ; i++ ) {
  6864. // Weak typing
  6865. if ( this._editor_val == val[i] ) {
  6866. found = true;
  6867. allFound = true;
  6868. break;
  6869. }
  6870. }
  6871. this.selected = found;
  6872. } );
  6873. // If there is a placeholder, we might need to select it if nothing else
  6874. // was selected. It doesn't make sense to select when multi is enabled
  6875. if ( conf.placeholder && ! allFound && ! conf.multiple && options.length ) {
  6876. options[0].selected = true;
  6877. }
  6878. // Update will call change itself, otherwise multiple might be called
  6879. if ( ! localUpdate ) {
  6880. _triggerChange( conf._input );
  6881. }
  6882. return allFound;
  6883. },
  6884. destroy: function ( conf ) {
  6885. conf._input.off( 'change.dte' );
  6886. }
  6887. } );
  6888. fieldTypes.checkbox = $.extend( true, {}, baseFieldType, {
  6889. // Locally "private" function that can be reused for the create and update methods
  6890. _addOptions: function ( conf, opts ) {
  6891. var val, label;
  6892. var elOpts = conf._input[0].options;
  6893. var jqInput = conf._input.empty();
  6894. if ( opts ) {
  6895. Editor.pairs( opts, conf.optionsPair, function ( val, label, i ) {
  6896. jqInput.append(
  6897. '<div>'+
  6898. '<input id="'+Editor.safeId( conf.id )+'_'+i+'" type="checkbox" />'+
  6899. '<label for="'+Editor.safeId( conf.id )+'_'+i+'">'+label+'</label>'+
  6900. '</div>'
  6901. );
  6902. $('input:last', jqInput).attr('value', val)[0]._editor_val = val;
  6903. } );
  6904. }
  6905. },
  6906. create: function ( conf ) {
  6907. conf._input = $('<div />');
  6908. fieldTypes.checkbox._addOptions( conf, conf.options || conf.ipOpts );
  6909. return conf._input[0];
  6910. },
  6911. get: function ( conf ) {
  6912. var out = [];
  6913. conf._input.find('input:checked').each( function () {
  6914. out.push( this._editor_val );
  6915. } );
  6916. return ! conf.separator ?
  6917. out :
  6918. out.length === 1 ?
  6919. out[0] :
  6920. out.join(conf.separator);
  6921. },
  6922. set: function ( conf, val ) {
  6923. var jqInputs = conf._input.find('input');
  6924. if ( ! $.isArray(val) && typeof val === 'string' ) {
  6925. val = val.split( conf.separator || '|' );
  6926. }
  6927. else if ( ! $.isArray(val) ) {
  6928. val = [ val ];
  6929. }
  6930. var i, len=val.length, found;
  6931. jqInputs.each( function () {
  6932. found = false;
  6933. for ( i=0 ; i<len ; i++ ) {
  6934. if ( this._editor_val == val[i] ) {
  6935. found = true;
  6936. break;
  6937. }
  6938. }
  6939. this.checked = found;
  6940. } );
  6941. _triggerChange( jqInputs );
  6942. },
  6943. enable: function ( conf ) {
  6944. conf._input.find('input').prop('disabled', false);
  6945. },
  6946. disable: function ( conf ) {
  6947. conf._input.find('input').prop('disabled', true);
  6948. },
  6949. update: function ( conf, options ) {
  6950. // Get the current value
  6951. var checkbox = fieldTypes.checkbox;
  6952. var currVal = checkbox.get( conf );
  6953. checkbox._addOptions( conf, options );
  6954. checkbox.set( conf, currVal );
  6955. }
  6956. } );
  6957. fieldTypes.radio = $.extend( true, {}, baseFieldType, {
  6958. // Locally "private" function that can be reused for the create and update methods
  6959. _addOptions: function ( conf, opts ) {
  6960. var val, label;
  6961. var elOpts = conf._input[0].options;
  6962. var jqInput = conf._input.empty();
  6963. if ( opts ) {
  6964. Editor.pairs( opts, conf.optionsPair, function ( val, label, i ) {
  6965. jqInput.append(
  6966. '<div>'+
  6967. '<input id="'+Editor.safeId( conf.id )+'_'+i+'" type="radio" name="'+conf.name+'" />'+
  6968. '<label for="'+Editor.safeId( conf.id )+'_'+i+'">'+label+'</label>'+
  6969. '</div>'
  6970. );
  6971. $('input:last', jqInput).attr('value', val)[0]._editor_val = val;
  6972. } );
  6973. }
  6974. },
  6975. create: function ( conf ) {
  6976. conf._input = $('<div />');
  6977. fieldTypes.radio._addOptions( conf, conf.options || conf.ipOpts );
  6978. // this is ugly, but IE6/7 has a problem with radio elements that are created
  6979. // and checked before being added to the DOM! Basically it doesn't check them. As
  6980. // such we use the _preChecked property to set cache the checked button and then
  6981. // check it again when the display is shown. This has no effect on other browsers
  6982. // other than to cook a few clock cycles.
  6983. this.on('open', function () {
  6984. conf._input.find('input').each( function () {
  6985. if ( this._preChecked ) {
  6986. this.checked = true;
  6987. }
  6988. } );
  6989. } );
  6990. return conf._input[0];
  6991. },
  6992. get: function ( conf ) {
  6993. var el = conf._input.find('input:checked');
  6994. return el.length ? el[0]._editor_val : undefined;
  6995. },
  6996. set: function ( conf, val ) {
  6997. var that = this;
  6998. conf._input.find('input').each( function () {
  6999. this._preChecked = false;
  7000. if ( this._editor_val == val ) {
  7001. this.checked = true;
  7002. this._preChecked = true;
  7003. }
  7004. else {
  7005. // In a detached DOM tree, there is no relationship between the
  7006. // input elements, so we need to uncheck any element that does
  7007. // not match the value
  7008. this.checked = false;
  7009. this._preChecked = false;
  7010. }
  7011. } );
  7012. _triggerChange( conf._input.find('input:checked') );
  7013. },
  7014. enable: function ( conf ) {
  7015. conf._input.find('input').prop('disabled', false);
  7016. },
  7017. disable: function ( conf ) {
  7018. conf._input.find('input').prop('disabled', true);
  7019. },
  7020. update: function ( conf, options ) {
  7021. var radio = fieldTypes.radio;
  7022. var currVal = radio.get( conf );
  7023. radio._addOptions( conf, options );
  7024. // Select the current value if it exists in the new data set, otherwise
  7025. // select the first radio input so there is always a value selected
  7026. var inputs = conf._input.find('input');
  7027. radio.set( conf, inputs.filter('[value="'+currVal+'"]').length ?
  7028. currVal :
  7029. inputs.eq(0).attr('value')
  7030. );
  7031. }
  7032. } );
  7033. fieldTypes.date = $.extend( true, {}, baseFieldType, {
  7034. create: function ( conf ) {
  7035. conf._input = $('<input />').attr( $.extend( {
  7036. id: Editor.safeId( conf.id ),
  7037. type: 'text'
  7038. }, conf.attr ) );
  7039. if ( $.datepicker ) {
  7040. // jQuery UI date picker
  7041. conf._input.addClass( 'jqueryui' );
  7042. if ( ! conf.dateFormat ) {
  7043. conf.dateFormat = $.datepicker.RFC_2822;
  7044. }
  7045. if ( conf.dateImage === undefined ) {
  7046. conf.dateImage = "../../images/calender.png";
  7047. }
  7048. // Allow the element to be attached to the DOM
  7049. setTimeout( function () {
  7050. $( conf._input ).datepicker( $.extend( {
  7051. showOn: "both",
  7052. dateFormat: conf.dateFormat,
  7053. buttonImage: conf.dateImage,
  7054. buttonImageOnly: true
  7055. }, conf.opts ) );
  7056. $('#ui-datepicker-div').css('display','none');
  7057. }, 10 );
  7058. }
  7059. else {
  7060. // HTML5 (only Chrome and Edge on the desktop support this atm)
  7061. conf._input.attr( 'type', 'date' );
  7062. }
  7063. return conf._input[0];
  7064. },
  7065. // use default get method as will work for all
  7066. set: function ( conf, val ) {
  7067. if ( $.datepicker && conf._input.hasClass('hasDatepicker') ) {
  7068. // Due to the async init of the control it is possible that we might
  7069. // try to set a value before it has been initialised!
  7070. conf._input.datepicker( "setDate" , val ).change();
  7071. }
  7072. else {
  7073. $(conf._input).val( val );
  7074. }
  7075. },
  7076. enable: function ( conf ) {
  7077. $.datepicker ?
  7078. conf._input.datepicker( "enable" ) :
  7079. $(conf._input).prop( 'disabled', false );
  7080. },
  7081. disable: function ( conf ) {
  7082. $.datepicker ?
  7083. conf._input.datepicker( "disable" ) :
  7084. $(conf._input).prop( 'disabled', true );
  7085. },
  7086. owns: function ( conf, node ) {
  7087. return $(node).parents('div.ui-datepicker').length || $(node).parents('div.ui-datepicker-header').length ?
  7088. true :
  7089. false;
  7090. }
  7091. } );
  7092. fieldTypes.datetime = $.extend( true, {}, baseFieldType, {
  7093. create: function ( conf ) {
  7094. conf._input = $('<input />').attr( $.extend( true, {
  7095. id: Editor.safeId( conf.id ),
  7096. type: 'text'
  7097. }, conf.attr ) );
  7098. conf._picker = new Editor.DateTime( conf._input, $.extend( {
  7099. format: conf.format, // can be undefined
  7100. i18n: this.i18n.datetime
  7101. }, conf.opts ) );
  7102. return conf._input[0];
  7103. },
  7104. // default get, disable and enable options are okay
  7105. set: function ( conf, val ) {
  7106. conf._picker.val( val );
  7107. _triggerChange( conf._input );
  7108. },
  7109. owns: function ( conf, node ) {
  7110. return conf._picker.owns( node );
  7111. },
  7112. destroy: function ( conf ) {
  7113. conf._picker.destroy();
  7114. },
  7115. minDate: function ( conf, min ) {
  7116. conf._picker.min( min );
  7117. },
  7118. maxDate: function ( conf, max ) {
  7119. conf._picker.max( max );
  7120. }
  7121. } );
  7122. fieldTypes.upload = $.extend( true, {}, baseFieldType, {
  7123. create: function ( conf ) {
  7124. var editor = this;
  7125. var container = _commonUpload( editor, conf, function ( val ) {
  7126. Editor.fieldTypes.upload.set.call( editor, conf, val[0] );
  7127. } );
  7128. return container;
  7129. },
  7130. get: function ( conf ) {
  7131. return conf._val;
  7132. },
  7133. set: function ( conf, val ) {
  7134. conf._val = val;
  7135. var container = conf._input;
  7136. if ( conf.display ) {
  7137. var rendered = container.find('div.rendered');
  7138. if ( conf._val ) {
  7139. rendered.html( conf.display( conf._val ) );
  7140. }
  7141. else {
  7142. rendered
  7143. .empty()
  7144. .append( '<span>'+( conf.noFileText || 'No file' )+'</span>' );
  7145. }
  7146. }
  7147. var button = container.find('div.clearValue button');
  7148. if ( val && conf.clearText ) {
  7149. button.html( conf.clearText );
  7150. container.removeClass( 'noClear' );
  7151. }
  7152. else {
  7153. container.addClass( 'noClear' );
  7154. }
  7155. conf._input.find('input').triggerHandler( 'upload.editor', [ conf._val ] );
  7156. },
  7157. enable: function ( conf ) {
  7158. conf._input.find('input').prop('disabled', false);
  7159. conf._enabled = true;
  7160. },
  7161. disable: function ( conf ) {
  7162. conf._input.find('input').prop('disabled', true);
  7163. conf._enabled = false;
  7164. }
  7165. } );
  7166. fieldTypes.uploadMany = $.extend( true, {}, baseFieldType, {
  7167. create: function ( conf ) {
  7168. var editor = this;
  7169. var container = _commonUpload( editor, conf, function ( val ) {
  7170. conf._val = conf._val.concat( val );
  7171. Editor.fieldTypes.uploadMany.set.call( editor, conf, conf._val );
  7172. } );
  7173. container
  7174. .addClass( 'multi' )
  7175. .on( 'click', 'button.remove', function (e) {
  7176. e.stopPropagation();
  7177. var idx = $(this).data('idx');
  7178. conf._val.splice( idx, 1 );
  7179. Editor.fieldTypes.uploadMany.set.call( editor, conf, conf._val );
  7180. } );
  7181. return container;
  7182. },
  7183. get: function ( conf ) {
  7184. return conf._val;
  7185. },
  7186. set: function ( conf, val ) {
  7187. // Default value for fields is an empty string, whereas we want []
  7188. if ( ! val ) {
  7189. val = [];
  7190. }
  7191. if ( ! $.isArray( val ) ) {
  7192. throw 'Upload collections must have an array as a value';
  7193. }
  7194. conf._val = val;
  7195. var that = this;
  7196. var container = conf._input;
  7197. if ( conf.display ) {
  7198. var rendered = container.find('div.rendered').empty();
  7199. if ( val.length ) {
  7200. var list = $('<ul/>').appendTo( rendered );
  7201. $.each( val, function ( i, file ) {
  7202. list.append(
  7203. '<li>'+
  7204. conf.display( file, i )+
  7205. ' <button class="'+that.classes.form.button+' remove" data-idx="'+i+'">&times;</button>'+
  7206. '</li>'
  7207. );
  7208. } );
  7209. }
  7210. else {
  7211. rendered.append( '<span>'+( conf.noFileText || 'No files' )+'</span>' );
  7212. }
  7213. }
  7214. conf._input.find('input').triggerHandler( 'upload.editor', [ conf._val ] );
  7215. },
  7216. enable: function ( conf ) {
  7217. conf._input.find('input').prop('disabled', false);
  7218. conf._enabled = true;
  7219. },
  7220. disable: function ( conf ) {
  7221. conf._input.find('input').prop('disabled', true);
  7222. conf._enabled = false;
  7223. }
  7224. } );
  7225. }());
  7226. // If there are field types available on DataTables we copy them in (after the
  7227. // built in ones to allow overrides) and then expose the field types object.
  7228. if ( DataTable.ext.editorFields ) {
  7229. $.extend( Editor.fieldTypes, DataTable.ext.editorFields );
  7230. }
  7231. DataTable.ext.editorFields = Editor.fieldTypes;
  7232. /**
  7233. * File information for uploads
  7234. */
  7235. Editor.files = {};
  7236. /**
  7237. * Name of this class
  7238. * @constant CLASS
  7239. * @type String
  7240. * @default Editor
  7241. */
  7242. Editor.prototype.CLASS = "Editor";
  7243. /**
  7244. * DataTables Editor version
  7245. * @constant Editor.VERSION
  7246. * @type String
  7247. * @default See code
  7248. * @static
  7249. */
  7250. Editor.version = "1.5.6";
  7251. // Event documentation for JSDoc
  7252. /**
  7253. * Processing event, fired when Editor submits data to the server for processing.
  7254. * This can be used to provide your own processing indicator if your UI framework
  7255. * already has one.
  7256. * @name Editor#processing
  7257. * @event
  7258. * @param {event} e jQuery event object
  7259. * @param {boolean} processing Flag for if the processing is running (true) or
  7260. * not (false).
  7261. */
  7262. /**
  7263. * Form displayed event, fired when the form is made available in the DOM. This
  7264. * can be useful for fields that require height and width calculations to be
  7265. * performed since the element is not available in the document until the
  7266. * form is displayed.
  7267. * @name Editor#open
  7268. * @event
  7269. * @param {event} e jQuery event object
  7270. * @param {string} type Editing type
  7271. */
  7272. /**
  7273. * Before a form is displayed, this event is fired. It allows the open action to be
  7274. * cancelled by returning false from the function.
  7275. * @name Editor#preOpen
  7276. * @event
  7277. * @param {event} e jQuery event object
  7278. */
  7279. /**
  7280. * Form hidden event, fired when the form is removed from the document. The
  7281. * of the compliment `open` event.
  7282. * @name Editor#close
  7283. * @event
  7284. * @param {event} e jQuery event object
  7285. */
  7286. /**
  7287. * Before a form is closed, this event is fired. It allows the close action to be
  7288. * cancelled by returning false from the function. This can be useful for confirming
  7289. * that the user actually wants to close the display (if they have unsaved changes
  7290. * for example).
  7291. * @name Editor#preClose
  7292. * @event
  7293. * @param {event} e jQuery event object
  7294. * @param {string} trigger Action that caused the close event - can be undefined.
  7295. * Typically defined by the display controller.
  7296. */
  7297. /**
  7298. * Emitted before a form blur occurs. A form blur is similar to a close, but
  7299. * is triggered by a user, typically, clicking on the background, while a close
  7300. * occurs due to a click on the close button. A blur can precede a close.
  7301. * @name Editor#preBlur
  7302. * @event
  7303. * @param {event} e jQuery event object
  7304. */
  7305. /**
  7306. * Pre-submit event for the form, fired just before the data is submitted to
  7307. * the server. This event allows you to modify the data that will be submitted
  7308. * to the server. Note that this event runs after the 'formatdata' callback
  7309. * function of the {@link Editor#submit} API method.
  7310. * @name Editor#preSubmit
  7311. * @event
  7312. * @param {event} e jQuery event object
  7313. * @param {object} data The data object that will be submitted to the server
  7314. * @param {string} action The action type for this submit - `create`, `edit` or
  7315. * `remove`.
  7316. */
  7317. /**
  7318. * Post-submit event for the form, fired immediately after the data has been
  7319. * loaded by the Ajax call, allowing modification or any other interception
  7320. * of the data returned form the server.
  7321. * @name Editor#postSubmit
  7322. * @event
  7323. * @param {event} e jQuery event object
  7324. * @param {object} json The JSON object returned from the server
  7325. * @param {object} data The data object that was be submitted to the server
  7326. * @param {string} action The action type for this submit - `create`, `edit` or
  7327. * `remove`.
  7328. */
  7329. /**
  7330. * Submission complete event, fired when data has been submitted to the server and
  7331. * after any of the return handling code has been run (updating the DataTable
  7332. * for example). Note that unlike `submitSuccess` and `submitError`, `submitComplete`
  7333. * will be fired for both a successful submission and an error. Additionally this
  7334. * event will be fired after `submitSuccess` or `submitError`.
  7335. * @name Editor#submitComplete
  7336. * @event
  7337. * @param {event} e jQuery event object
  7338. * @param {object} json The JSON object returned from the server
  7339. * @param {object} data The data that was used to update the DataTable
  7340. */
  7341. /**
  7342. * Submission complete and successful event, fired when data has been successfully
  7343. * submitted to the server and all actions required by the returned data (inserting
  7344. * or updating a row) have been completed.
  7345. * @name Editor#submitSuccess
  7346. * @event
  7347. * @param {event} e jQuery event object
  7348. * @param {object} json The JSON object returned from the server
  7349. * @param {object} data The data that was used to update the DataTable
  7350. */
  7351. /**
  7352. * Submission complete, but in error event, fired when data has been submitted to
  7353. * the server but an error occurred on the server (typically a JSON formatting error)
  7354. * @name Editor#submitError
  7355. * @event
  7356. * @param {event} e jQuery event object
  7357. * @param {object} xhr The Ajax object
  7358. * @param {string} err The error message from jQuery
  7359. * @param {object} thrown The exception thrown by jQuery
  7360. * @param {object} data The data that was used to update the DataTable
  7361. */
  7362. /**
  7363. * Create method activated event, fired when the create API method has been called,
  7364. * just prior to the form being shown. Useful for manipulating the form specifically
  7365. * for the create state.
  7366. * @name Editor#initCreate
  7367. * @event
  7368. * @param {event} e jQuery event object
  7369. */
  7370. /**
  7371. * Pre-create new row event, fired just before DataTables calls the fnAddData method
  7372. * to add new data to the DataTable, allowing modification of the data that will be
  7373. * used to insert into the table.
  7374. * @name Editor#preCreate
  7375. * @event
  7376. * @param {event} e jQuery event object
  7377. * @param {object} json The JSON object returned from the server
  7378. * @param {object} data The data that will be used to update the DataTable
  7379. */
  7380. /**
  7381. * Create new row event, fired when a new row has been created in the DataTable by
  7382. * a form submission. This is called just after the fnAddData call to the DataTable.
  7383. * @name Editor#create
  7384. * @event
  7385. * @param {event} e jQuery event object
  7386. * @param {object} json The JSON object returned from the server
  7387. * @param {object} data The data that was used to update the DataTable
  7388. */
  7389. /**
  7390. * As per the `create` event - included for naming consistency.
  7391. * @name Editor#postCreate
  7392. * @event
  7393. * @param {event} e jQuery event object
  7394. * @param {object} json The JSON object returned from the server
  7395. * @param {object} data The data that was used to update the DataTable
  7396. */
  7397. /**
  7398. * Edit method activated event, fired when the edit API method has been called,
  7399. * just prior to the form being shown. Useful for manipulating the form specifically
  7400. * for the edit state.
  7401. * @name Editor#initEdit
  7402. * @event
  7403. * @param {event} e jQuery event object
  7404. * @param {node} tr TR element of the row to be edited
  7405. * @param {array|object} data Data source array / object for the row to be
  7406. * edited
  7407. */
  7408. /**
  7409. * Pre-edit row event, fired just before DataTables calls the fnUpdate method
  7410. * to edit data in a DataTables row, allowing modification of the data that will be
  7411. * used to update the table.
  7412. * @name Editor#preEdit
  7413. * @event
  7414. * @param {event} e jQuery event object
  7415. * @param {object} json The JSON object returned from the server
  7416. * @param {object} data The data that will be used to update the DataTable
  7417. */
  7418. /**
  7419. * Edit row event, fired when a row has been edited in the DataTable by a form
  7420. * submission. This is called just after the fnUpdate call to the DataTable.
  7421. * @name Editor#edit
  7422. * @event
  7423. * @param {event} e jQuery event object
  7424. * @param {object} json The JSON object returned from the server
  7425. * @param {object} data The data that was used to update the DataTable
  7426. */
  7427. /**
  7428. * As per the `edit` event - included for naming consistency.
  7429. * @name Editor#postEdit
  7430. * @event
  7431. * @param {event} e jQuery event object
  7432. * @param {object} json The JSON object returned from the server
  7433. * @param {object} data The data that was used to update the DataTable
  7434. */
  7435. /**
  7436. * Remove method activated event, fired when the remove API method has been
  7437. * called, just prior to the form being shown. Useful for manipulating the form
  7438. * specifically for the remove state.
  7439. * @name Editor#initRemove
  7440. * @event
  7441. * @param {event} e jQuery event object
  7442. * @param {array} trs Array of the TR elements for the removed to be deleted
  7443. * @param {array} data Array of the data source array / objects for the rows to
  7444. * be deleted. This is in the same index order as the TR nodes in the second
  7445. * parameter.
  7446. */
  7447. /**
  7448. * Pre-remove row event, fired just before DataTables calls the fnDeleteRow method
  7449. * to delete a DataTables row.
  7450. * @name Editor#preRemove
  7451. * @event
  7452. * @param {event} e jQuery event object
  7453. * @param {object} json The JSON object returned from the server
  7454. */
  7455. /**
  7456. * Row removed event, fired when a row has been removed in the DataTable by a form
  7457. * submission. This is called just after the fnDeleteRow call to the DataTable.
  7458. * @name Editor#remove
  7459. * @event
  7460. * @param {event} e jQuery event object
  7461. * @param {object} json The JSON object returned from the server
  7462. */
  7463. /**
  7464. * As per the `postRemove` event - included for naming consistency.
  7465. * @name Editor#postRemove
  7466. * @event
  7467. * @param {event} e jQuery event object
  7468. * @param {object} json The JSON object returned from the server
  7469. */
  7470. /**
  7471. * Set data event, fired when the data is gathered from the form to be used
  7472. * to update the DataTable. This is a "global" version of `preCreate`, `preEdit`
  7473. * and `preRemove` and can be used to manipulate the data that will be added
  7474. * to the DataTable for all three actions
  7475. * @name Editor#setData
  7476. * @event
  7477. * @param {event} e jQuery event object
  7478. * @param {object} json The JSON object returned from the server
  7479. * @param {object} data The data that will be used to update the DataTable
  7480. * @param {string} action The action being performed by the form - 'create',
  7481. * 'edit' or 'remove'.
  7482. */
  7483. /**
  7484. * Initialisation of the Editor instance has been completed.
  7485. * @name Editor#initComplete
  7486. * @event
  7487. * @param {event} e jQuery event object
  7488. */
  7489. return Editor;
  7490. }));