functions.js 157 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444
  1. "use strict";
  2. function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
  3. /* global isStorageSupported */
  4. // js/config.js
  5. /* global ChartType, ColumnType, DataTable, JQPlotChartFactory */
  6. // js/chart.js
  7. /* global DatabaseStructure */
  8. // js/database/structure.js
  9. /* global mysqlDocBuiltin, mysqlDocKeyword */
  10. // js/doclinks.js
  11. /* global Indexes */
  12. // js/indexes.js
  13. /* global firstDayOfCalendar, maxInputVars, mysqlDocTemplate, themeImagePath */
  14. // templates/javascript/variables.twig
  15. /* global MicroHistory */
  16. // js/microhistory.js
  17. /* global sprintf */
  18. // js/vendor/sprintf.js
  19. /* global zxcvbn */
  20. // js/vendor/zxcvbn.js
  21. /**
  22. * general function, usually for data manipulation pages
  23. *
  24. */
  25. var Functions = {};
  26. /**
  27. * @var sqlBoxLocked lock for the sqlbox textarea in the querybox
  28. */
  29. var sqlBoxLocked = false;
  30. /**
  31. * @var {array} holds elements which content should only selected once
  32. */
  33. var onlyOnceElements = [];
  34. /**
  35. * @var {int} ajaxMessageCount Number of AJAX messages shown since page load
  36. */
  37. var ajaxMessageCount = 0;
  38. /**
  39. * @var codeMirrorEditor object containing CodeMirror editor of the query editor in SQL tab
  40. */
  41. var codeMirrorEditor = false;
  42. /**
  43. * @var codeMirrorInlineEditor object containing CodeMirror editor of the inline query editor
  44. */
  45. var codeMirrorInlineEditor = false;
  46. /**
  47. * @var {boolean} sqlAutoCompleteInProgress shows if Table/Column name autocomplete AJAX is in progress
  48. */
  49. var sqlAutoCompleteInProgress = false;
  50. /**
  51. * @var sqlAutoComplete object containing list of columns in each table
  52. */
  53. var sqlAutoComplete = false;
  54. /**
  55. * @var {string} sqlAutoCompleteDefaultTable string containing default table to autocomplete columns
  56. */
  57. var sqlAutoCompleteDefaultTable = '';
  58. /**
  59. * @var {array} centralColumnList array to hold the columns in central list per db.
  60. */
  61. var centralColumnList = [];
  62. /**
  63. * @var {array} primaryIndexes array to hold 'Primary' index columns.
  64. */
  65. // eslint-disable-next-line no-unused-vars
  66. var primaryIndexes = [];
  67. /**
  68. * @var {array} uniqueIndexes array to hold 'Unique' index columns.
  69. */
  70. // eslint-disable-next-line no-unused-vars
  71. var uniqueIndexes = [];
  72. /**
  73. * @var {array} indexes array to hold 'Index' columns.
  74. */
  75. // eslint-disable-next-line no-unused-vars
  76. var indexes = [];
  77. /**
  78. * @var {array} fulltextIndexes array to hold 'Fulltext' columns.
  79. */
  80. // eslint-disable-next-line no-unused-vars
  81. var fulltextIndexes = [];
  82. /**
  83. * @var {array} spatialIndexes array to hold 'Spatial' columns.
  84. */
  85. // eslint-disable-next-line no-unused-vars
  86. var spatialIndexes = [];
  87. /**
  88. * Make sure that ajax requests will not be cached
  89. * by appending a random variable to their parameters
  90. */
  91. $.ajaxPrefilter(function (options, originalOptions) {
  92. var nocache = new Date().getTime() + '' + Math.floor(Math.random() * 1000000);
  93. if (typeof options.data === 'string') {
  94. options.data += '&_nocache=' + nocache + '&token=' + encodeURIComponent(CommonParams.get('token'));
  95. } else if (_typeof(options.data) === 'object') {
  96. options.data = $.extend(originalOptions.data, {
  97. '_nocache': nocache,
  98. 'token': CommonParams.get('token')
  99. });
  100. }
  101. });
  102. /**
  103. * Adds a date/time picker to an element
  104. *
  105. * @param {object} $thisElement a jQuery object pointing to the element
  106. */
  107. Functions.addDatepicker = function ($thisElement, type, options) {
  108. if (type !== 'date' && type !== 'time' && type !== 'datetime' && type !== 'timestamp') {
  109. return;
  110. }
  111. var showTimepicker = true;
  112. if (type === 'date') {
  113. showTimepicker = false;
  114. } // Getting the current Date and time
  115. var currentDateTime = new Date();
  116. var defaultOptions = {
  117. timeInput: true,
  118. hour: currentDateTime.getHours(),
  119. minute: currentDateTime.getMinutes(),
  120. second: currentDateTime.getSeconds(),
  121. showOn: 'button',
  122. buttonImage: themeImagePath + 'b_calendar.png',
  123. buttonImageOnly: true,
  124. stepMinutes: 1,
  125. stepHours: 1,
  126. showSecond: true,
  127. showMillisec: true,
  128. showMicrosec: true,
  129. showTimepicker: showTimepicker,
  130. showButtonPanel: false,
  131. changeYear: true,
  132. dateFormat: 'yy-mm-dd',
  133. // yy means year with four digits
  134. timeFormat: 'HH:mm:ss.lc',
  135. constrainInput: false,
  136. altFieldTimeOnly: false,
  137. showAnim: '',
  138. beforeShow: function beforeShow(input, inst) {
  139. // Remember that we came from the datepicker; this is used
  140. // in table/change.js by verificationsAfterFieldChange()
  141. $thisElement.data('comes_from', 'datepicker');
  142. if ($(input).closest('.cEdit').length > 0) {
  143. setTimeout(function () {
  144. inst.dpDiv.css({
  145. top: 0,
  146. left: 0,
  147. position: 'relative'
  148. });
  149. }, 0);
  150. }
  151. setTimeout(function () {
  152. // Fix wrong timepicker z-index, doesn't work without timeout
  153. $('#ui-timepicker-div').css('z-index', $('#ui-datepicker-div').css('z-index')); // Integrate tooltip text into dialog
  154. var tooltip = $thisElement.tooltip('instance');
  155. if (typeof tooltip !== 'undefined') {
  156. tooltip.disable();
  157. var $note = $('<p class="note"></div>');
  158. $note.text(tooltip.option('content'));
  159. $('div.ui-datepicker').append($note);
  160. }
  161. }, 0);
  162. },
  163. onSelect: function onSelect() {
  164. $thisElement.data('datepicker').inline = true;
  165. },
  166. onClose: function onClose() {
  167. // The value is no more from the date picker
  168. $thisElement.data('comes_from', '');
  169. if (typeof $thisElement.data('datepicker') !== 'undefined') {
  170. $thisElement.data('datepicker').inline = false;
  171. }
  172. var tooltip = $thisElement.tooltip('instance');
  173. if (typeof tooltip !== 'undefined') {
  174. tooltip.enable();
  175. }
  176. }
  177. };
  178. if (type === 'time') {
  179. $thisElement.timepicker($.extend(defaultOptions, options)); // Add a tip regarding entering MySQL allowed-values for TIME data-type
  180. Functions.tooltip($thisElement, 'input', Messages.strMysqlAllowedValuesTipTime);
  181. } else {
  182. $thisElement.datetimepicker($.extend(defaultOptions, options));
  183. }
  184. };
  185. /**
  186. * Add a date/time picker to each element that needs it
  187. * (only when jquery-ui-timepicker-addon.js is loaded)
  188. */
  189. Functions.addDateTimePicker = function () {
  190. if ($.timepicker !== undefined) {
  191. $('input.timefield, input.datefield, input.datetimefield').each(function () {
  192. var decimals = $(this).parent().attr('data-decimals');
  193. var type = $(this).parent().attr('data-type');
  194. var showMillisec = false;
  195. var showMicrosec = false;
  196. var timeFormat = 'HH:mm:ss';
  197. var hourMax = 23; // check for decimal places of seconds
  198. if (decimals > 0 && type.indexOf('time') !== -1) {
  199. if (decimals > 3) {
  200. showMillisec = true;
  201. showMicrosec = true;
  202. timeFormat = 'HH:mm:ss.lc';
  203. } else {
  204. showMillisec = true;
  205. timeFormat = 'HH:mm:ss.l';
  206. }
  207. }
  208. if (type === 'time') {
  209. hourMax = 99;
  210. }
  211. Functions.addDatepicker($(this), type, {
  212. showMillisec: showMillisec,
  213. showMicrosec: showMicrosec,
  214. timeFormat: timeFormat,
  215. hourMax: hourMax,
  216. firstDay: firstDayOfCalendar
  217. }); // Add a tip regarding entering MySQL allowed-values
  218. // for TIME and DATE data-type
  219. if ($(this).hasClass('timefield')) {
  220. Functions.tooltip($(this), 'input', Messages.strMysqlAllowedValuesTipTime);
  221. } else if ($(this).hasClass('datefield')) {
  222. Functions.tooltip($(this), 'input', Messages.strMysqlAllowedValuesTipDate);
  223. }
  224. });
  225. }
  226. };
  227. /**
  228. * Handle redirect and reload flags sent as part of AJAX requests
  229. *
  230. * @param data ajax response data
  231. */
  232. Functions.handleRedirectAndReload = function (data) {
  233. if (parseInt(data.redirect_flag) === 1) {
  234. // add one more GET param to display session expiry msg
  235. if (window.location.href.indexOf('?') === -1) {
  236. window.location.href += '?session_expired=1';
  237. } else {
  238. window.location.href += CommonParams.get('arg_separator') + 'session_expired=1';
  239. }
  240. window.location.reload();
  241. } else if (parseInt(data.reload_flag) === 1) {
  242. window.location.reload();
  243. }
  244. };
  245. /**
  246. * Creates an SQL editor which supports auto completing etc.
  247. *
  248. * @param $textarea jQuery object wrapping the textarea to be made the editor
  249. * @param options optional options for CodeMirror
  250. * @param resize optional resizing ('vertical', 'horizontal', 'both')
  251. * @param lintOptions additional options for lint
  252. */
  253. Functions.getSqlEditor = function ($textarea, options, resize, lintOptions) {
  254. var resizeType = resize;
  255. if ($textarea.length > 0 && typeof CodeMirror !== 'undefined') {
  256. // merge options for CodeMirror
  257. var defaults = {
  258. lineNumbers: true,
  259. matchBrackets: true,
  260. extraKeys: {
  261. 'Ctrl-Space': 'autocomplete'
  262. },
  263. hintOptions: {
  264. 'completeSingle': false,
  265. 'completeOnSingleClick': true
  266. },
  267. indentUnit: 4,
  268. mode: 'text/x-mysql',
  269. lineWrapping: true
  270. };
  271. if (CodeMirror.sqlLint) {
  272. $.extend(defaults, {
  273. gutters: ['CodeMirror-lint-markers'],
  274. lint: {
  275. 'getAnnotations': CodeMirror.sqlLint,
  276. 'async': true,
  277. 'lintOptions': lintOptions
  278. }
  279. });
  280. }
  281. $.extend(true, defaults, options); // create CodeMirror editor
  282. var codemirrorEditor = CodeMirror.fromTextArea($textarea[0], defaults); // allow resizing
  283. if (!resizeType) {
  284. resizeType = 'vertical';
  285. }
  286. var handles = '';
  287. if (resizeType === 'vertical') {
  288. handles = 's';
  289. }
  290. if (resizeType === 'both') {
  291. handles = 'all';
  292. }
  293. if (resizeType === 'horizontal') {
  294. handles = 'e, w';
  295. }
  296. $(codemirrorEditor.getWrapperElement()).css('resize', resizeType).resizable({
  297. handles: handles,
  298. resize: function resize() {
  299. codemirrorEditor.setSize($(this).width(), $(this).height());
  300. }
  301. }); // enable autocomplete
  302. codemirrorEditor.on('inputRead', Functions.codeMirrorAutoCompleteOnInputRead); // page locking
  303. codemirrorEditor.on('change', function (e) {
  304. e.data = {
  305. value: 3,
  306. content: codemirrorEditor.isClean()
  307. };
  308. AJAX.lockPageHandler(e);
  309. });
  310. return codemirrorEditor;
  311. }
  312. return null;
  313. };
  314. /**
  315. * Clear text selection
  316. */
  317. Functions.clearSelection = function () {
  318. if (document.selection && document.selection.empty) {
  319. document.selection.empty();
  320. } else if (window.getSelection) {
  321. var sel = window.getSelection();
  322. if (sel.empty) {
  323. sel.empty();
  324. }
  325. if (sel.removeAllRanges) {
  326. sel.removeAllRanges();
  327. }
  328. }
  329. };
  330. /**
  331. * Create a jQuery UI tooltip
  332. *
  333. * @param $elements jQuery object representing the elements
  334. * @param item the item
  335. * (see https://api.jqueryui.com/tooltip/#option-items)
  336. * @param myContent content of the tooltip
  337. * @param additionalOptions to override the default options
  338. *
  339. */
  340. Functions.tooltip = function ($elements, item, myContent, additionalOptions) {
  341. if ($('#no_hint').length > 0) {
  342. return;
  343. }
  344. var defaultOptions = {
  345. content: myContent,
  346. items: item,
  347. tooltipClass: 'tooltip',
  348. track: true,
  349. show: false,
  350. hide: false
  351. };
  352. $elements.tooltip($.extend(true, defaultOptions, additionalOptions));
  353. };
  354. /**
  355. * HTML escaping
  356. */
  357. Functions.escapeHtml = function (unsafe) {
  358. if (typeof unsafe !== 'undefined') {
  359. return unsafe.toString().replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;').replace(/'/g, '&#039;');
  360. } else {
  361. return false;
  362. }
  363. };
  364. Functions.escapeJsString = function (unsafe) {
  365. if (typeof unsafe !== 'undefined') {
  366. return unsafe.toString().replace('\x00', '').replace('\\', '\\\\').replace('\'', '\\\'').replace('&#039;', '\\&#039;').replace('"', '\\"').replace('&quot;', '\\&quot;').replace('\n', '\n').replace('\r', '\r').replace(/<\/script/gi, '</\' + \'script');
  367. } else {
  368. return false;
  369. }
  370. };
  371. Functions.escapeBacktick = function (s) {
  372. return s.replace('`', '``');
  373. };
  374. Functions.escapeSingleQuote = function (s) {
  375. return s.replace('\\', '\\\\').replace('\'', '\\\'');
  376. };
  377. Functions.sprintf = function () {
  378. return sprintf.apply(this, arguments);
  379. };
  380. /**
  381. * Hides/shows the default value input field, depending on the default type
  382. * Ticks the NULL checkbox if NULL is chosen as default value.
  383. */
  384. Functions.hideShowDefaultValue = function ($defaultType) {
  385. if ($defaultType.val() === 'USER_DEFINED') {
  386. $defaultType.siblings('.default_value').show().trigger('focus');
  387. } else {
  388. $defaultType.siblings('.default_value').hide();
  389. if ($defaultType.val() === 'NULL') {
  390. var $nullCheckbox = $defaultType.closest('tr').find('.allow_null');
  391. $nullCheckbox.prop('checked', true);
  392. }
  393. }
  394. };
  395. /**
  396. * Hides/shows the input field for column expression based on whether
  397. * VIRTUAL/PERSISTENT is selected
  398. *
  399. * @param $virtuality virtuality dropdown
  400. */
  401. Functions.hideShowExpression = function ($virtuality) {
  402. if ($virtuality.val() === '') {
  403. $virtuality.siblings('.expression').hide();
  404. } else {
  405. $virtuality.siblings('.expression').show();
  406. }
  407. };
  408. /**
  409. * Show notices for ENUM columns; add/hide the default value
  410. *
  411. */
  412. Functions.verifyColumnsProperties = function () {
  413. $('select.column_type').each(function () {
  414. Functions.showNoticeForEnum($(this));
  415. });
  416. $('select.default_type').each(function () {
  417. Functions.hideShowDefaultValue($(this));
  418. });
  419. $('select.virtuality').each(function () {
  420. Functions.hideShowExpression($(this));
  421. });
  422. };
  423. /**
  424. * Add a hidden field to the form to indicate that this will be an
  425. * Ajax request (only if this hidden field does not exist)
  426. *
  427. * @param $form object the form
  428. */
  429. Functions.prepareForAjaxRequest = function ($form) {
  430. if (!$form.find('input:hidden').is('#ajax_request_hidden')) {
  431. $form.append('<input type="hidden" id="ajax_request_hidden" name="ajax_request" value="true">');
  432. }
  433. };
  434. Functions.checkPasswordStrength = function (value, meterObject, meterObjectLabel, username) {
  435. // List of words we don't want to appear in the password
  436. var customDict = ['phpmyadmin', 'mariadb', 'mysql', 'php', 'my', 'admin'];
  437. if (username !== null) {
  438. customDict.push(username);
  439. }
  440. var zxcvbnObject = zxcvbn(value, customDict);
  441. var strength = zxcvbnObject.score;
  442. strength = parseInt(strength);
  443. meterObject.val(strength);
  444. switch (strength) {
  445. case 0:
  446. meterObjectLabel.html(Messages.strExtrWeak);
  447. break;
  448. case 1:
  449. meterObjectLabel.html(Messages.strVeryWeak);
  450. break;
  451. case 2:
  452. meterObjectLabel.html(Messages.strWeak);
  453. break;
  454. case 3:
  455. meterObjectLabel.html(Messages.strGood);
  456. break;
  457. case 4:
  458. meterObjectLabel.html(Messages.strStrong);
  459. }
  460. };
  461. /**
  462. * Generate a new password and copy it to the password input areas
  463. *
  464. * @param {object} passwordForm the form that holds the password fields
  465. *
  466. * @return {boolean} always true
  467. */
  468. Functions.suggestPassword = function (passwordForm) {
  469. // restrict the password to just letters and numbers to avoid problems:
  470. // "editors and viewers regard the password as multiple words and
  471. // things like double click no longer work"
  472. var pwchars = 'abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWYXZ@!_.*/()[]-';
  473. var passwordlength = 16; // do we want that to be dynamic? no, keep it simple :)
  474. var passwd = passwordForm.generated_pw; // eslint-disable-next-line compat/compat
  475. var randomWords = new Int32Array(passwordlength);
  476. passwd.value = '';
  477. var i; // First we're going to try to use a built-in CSPRNG
  478. // eslint-disable-next-line compat/compat
  479. if (window.crypto && window.crypto.getRandomValues) {
  480. // eslint-disable-next-line compat/compat
  481. window.crypto.getRandomValues(randomWords);
  482. } else if (window.msCrypto && window.msCrypto.getRandomValues) {
  483. // Because of course IE calls it msCrypto instead of being standard
  484. window.msCrypto.getRandomValues(randomWords);
  485. } else {
  486. // Fallback to Math.random
  487. for (i = 0; i < passwordlength; i++) {
  488. randomWords[i] = Math.floor(Math.random() * pwchars.length);
  489. }
  490. }
  491. for (i = 0; i < passwordlength; i++) {
  492. passwd.value += pwchars.charAt(Math.abs(randomWords[i]) % pwchars.length);
  493. }
  494. var $jQueryPasswordForm = $(passwordForm);
  495. passwordForm.elements.pma_pw.value = passwd.value;
  496. passwordForm.elements.pma_pw2.value = passwd.value;
  497. var meterObj = $jQueryPasswordForm.find('meter[name="pw_meter"]').first();
  498. var meterObjLabel = $jQueryPasswordForm.find('span[name="pw_strength"]').first();
  499. Functions.checkPasswordStrength(passwd.value, meterObj, meterObjLabel);
  500. return true;
  501. };
  502. /**
  503. * Version string to integer conversion.
  504. */
  505. Functions.parseVersionString = function (str) {
  506. if (typeof str !== 'string') {
  507. return false;
  508. }
  509. var add = 0; // Parse possible alpha/beta/rc/
  510. var state = str.split('-');
  511. if (state.length >= 2) {
  512. if (state[1].substr(0, 2) === 'rc') {
  513. add = -20 - parseInt(state[1].substr(2), 10);
  514. } else if (state[1].substr(0, 4) === 'beta') {
  515. add = -40 - parseInt(state[1].substr(4), 10);
  516. } else if (state[1].substr(0, 5) === 'alpha') {
  517. add = -60 - parseInt(state[1].substr(5), 10);
  518. } else if (state[1].substr(0, 3) === 'dev') {
  519. /* We don't handle dev, it's git snapshot */
  520. add = 0;
  521. }
  522. } // Parse version
  523. var x = str.split('.'); // Use 0 for non existing parts
  524. var maj = parseInt(x[0], 10) || 0;
  525. var min = parseInt(x[1], 10) || 0;
  526. var pat = parseInt(x[2], 10) || 0;
  527. var hotfix = parseInt(x[3], 10) || 0;
  528. return maj * 100000000 + min * 1000000 + pat * 10000 + hotfix * 100 + add;
  529. };
  530. /**
  531. * Indicates current available version on main page.
  532. */
  533. Functions.currentVersion = function (data) {
  534. if (data && data.version && data.date) {
  535. var current = Functions.parseVersionString($('span.version').text());
  536. var latest = Functions.parseVersionString(data.version);
  537. var url = 'https://www.phpmyadmin.net/files/' + Functions.escapeHtml(encodeURIComponent(data.version)) + '/';
  538. var versionInformationMessage = document.createElement('span');
  539. versionInformationMessage.className = 'latest';
  540. var versionInformationMessageLink = document.createElement('a');
  541. versionInformationMessageLink.href = url;
  542. versionInformationMessageLink.className = 'disableAjax';
  543. var versionInformationMessageLinkText = document.createTextNode(data.version);
  544. versionInformationMessageLink.appendChild(versionInformationMessageLinkText);
  545. var prefixMessage = document.createTextNode(Messages.strLatestAvailable + ' ');
  546. versionInformationMessage.appendChild(prefixMessage);
  547. versionInformationMessage.appendChild(versionInformationMessageLink);
  548. if (latest > current) {
  549. var message = Functions.sprintf(Messages.strNewerVersion, Functions.escapeHtml(data.version), Functions.escapeHtml(data.date));
  550. var htmlClass = 'alert alert-primary';
  551. if (Math.floor(latest / 10000) === Math.floor(current / 10000)) {
  552. /* Security update */
  553. htmlClass = 'alert alert-danger';
  554. }
  555. $('#newer_version_notice').remove();
  556. var mainContainerDiv = document.createElement('div');
  557. mainContainerDiv.id = 'newer_version_notice';
  558. mainContainerDiv.className = htmlClass;
  559. var mainContainerDivLink = document.createElement('a');
  560. mainContainerDivLink.href = url;
  561. mainContainerDivLink.className = 'disableAjax';
  562. var mainContainerDivLinkText = document.createTextNode(message);
  563. mainContainerDivLink.appendChild(mainContainerDivLinkText);
  564. mainContainerDiv.appendChild(mainContainerDivLink);
  565. $('#maincontainer').append($(mainContainerDiv));
  566. }
  567. if (latest === current) {
  568. versionInformationMessage = document.createTextNode(' (' + Messages.strUpToDate + ')');
  569. }
  570. /* Remove extra whitespace */
  571. var versionInfo = $('#li_pma_version').contents().get(2);
  572. if (typeof versionInfo !== 'undefined') {
  573. versionInfo.textContent = versionInfo.textContent.trim();
  574. }
  575. var $liPmaVersion = $('#li_pma_version');
  576. $liPmaVersion.find('span.latest').remove();
  577. $liPmaVersion.append($(versionInformationMessage));
  578. }
  579. };
  580. /**
  581. * Loads Git revision data from ajax for index.php
  582. */
  583. Functions.displayGitRevision = function () {
  584. $('#is_git_revision').remove();
  585. $('#li_pma_version_git').remove();
  586. $.get('index.php?route=/git-revision', {
  587. 'server': CommonParams.get('server'),
  588. 'ajax_request': true,
  589. 'no_debug': true
  590. }, function (data) {
  591. if (typeof data !== 'undefined' && data.success === true) {
  592. $(data.message).insertAfter('#li_pma_version');
  593. }
  594. });
  595. };
  596. /**
  597. * for PhpMyAdmin\Display\ChangePassword and /user-password
  598. */
  599. Functions.displayPasswordGenerateButton = function () {
  600. var generatePwdRow = $('<tr></tr>').addClass('vmiddle');
  601. $('<td></td>').html(Messages.strGeneratePassword).appendTo(generatePwdRow);
  602. var pwdCell = $('<td></td>').appendTo(generatePwdRow);
  603. var pwdButton = $('<input>').attr({
  604. type: 'button',
  605. id: 'button_generate_password',
  606. value: Messages.strGenerate
  607. }).addClass('btn btn-secondary button').on('click', function () {
  608. Functions.suggestPassword(this.form);
  609. });
  610. var pwdTextbox = $('<input>').attr({
  611. type: 'text',
  612. name: 'generated_pw',
  613. id: 'generated_pw'
  614. });
  615. pwdCell.append(pwdButton).append(pwdTextbox);
  616. if (document.getElementById('button_generate_password') === null) {
  617. $('#tr_element_before_generate_password').parent().append(generatePwdRow);
  618. }
  619. var generatePwdDiv = $('<div></div>').addClass('item');
  620. $('<label></label>').attr({
  621. for: 'button_generate_password'
  622. }).html(Messages.strGeneratePassword + ':').appendTo(generatePwdDiv);
  623. var optionsSpan = $('<span></span>').addClass('options').appendTo(generatePwdDiv);
  624. pwdButton.clone(true).appendTo(optionsSpan);
  625. pwdTextbox.clone(true).appendTo(generatePwdDiv);
  626. if (document.getElementById('button_generate_password') === null) {
  627. $('#div_element_before_generate_password').parent().append(generatePwdDiv);
  628. }
  629. };
  630. /**
  631. * selects the content of a given object, f.e. a textarea
  632. *
  633. * @param {object} element element of which the content will be selected
  634. * @param {var} lock variable which holds the lock for this element or true, if no lock exists
  635. * @param {boolean} onlyOnce boolean if true this is only done once f.e. only on first focus
  636. */
  637. Functions.selectContent = function (element, lock, onlyOnce) {
  638. if (onlyOnce && onlyOnceElements[element.name]) {
  639. return;
  640. }
  641. onlyOnceElements[element.name] = true;
  642. if (lock) {
  643. return;
  644. }
  645. element.select();
  646. };
  647. /**
  648. * Displays a confirmation box before submitting a "DROP/DELETE/ALTER" query.
  649. * This function is called while clicking links
  650. *
  651. * @param theLink object the link
  652. * @param theSqlQuery object the sql query to submit
  653. *
  654. * @return boolean whether to run the query or not
  655. */
  656. Functions.confirmLink = function (theLink, theSqlQuery) {
  657. // Confirmation is not required in the configuration file
  658. // or browser is Opera (crappy js implementation)
  659. if (Messages.strDoYouReally === '' || typeof window.opera !== 'undefined') {
  660. return true;
  661. }
  662. var isConfirmed = confirm(Functions.sprintf(Messages.strDoYouReally, theSqlQuery));
  663. if (isConfirmed) {
  664. if (typeof theLink.href !== 'undefined') {
  665. theLink.href += CommonParams.get('arg_separator') + 'is_js_confirmed=1';
  666. } else if (typeof theLink.form !== 'undefined') {
  667. theLink.form.action += '?is_js_confirmed=1';
  668. }
  669. }
  670. return isConfirmed;
  671. };
  672. /**
  673. * Confirms a "DROP/DELETE/ALTER" query before
  674. * submitting it if required.
  675. * This function is called by the 'Functions.checkSqlQuery()' js function.
  676. *
  677. * @param theForm1 object the form
  678. * @param sqlQuery1 string the sql query string
  679. *
  680. * @return boolean whether to run the query or not
  681. *
  682. * @see Functions.checkSqlQuery()
  683. */
  684. Functions.confirmQuery = function (theForm1, sqlQuery1) {
  685. // Confirmation is not required in the configuration file
  686. if (Messages.strDoYouReally === '') {
  687. return true;
  688. } // Confirms a "DROP/DELETE/ALTER/TRUNCATE" statement
  689. //
  690. // TODO: find a way (if possible) to use the parser-analyser
  691. // for this kind of verification
  692. // For now, I just added a ^ to check for the statement at
  693. // beginning of expression
  694. var doConfirmRegExp0 = new RegExp('^\\s*DROP\\s+(IF EXISTS\\s+)?(TABLE|PROCEDURE)\\s', 'i');
  695. var doConfirmRegExp1 = new RegExp('^\\s*ALTER\\s+TABLE\\s+((`[^`]+`)|([A-Za-z0-9_$]+))\\s+DROP\\s', 'i');
  696. var doConfirmRegExp2 = new RegExp('^\\s*DELETE\\s+FROM\\s', 'i');
  697. var doConfirmRegExp3 = new RegExp('^\\s*TRUNCATE\\s', 'i');
  698. var doConfirmRegExp4 = new RegExp('^(?=.*UPDATE\\b)^((?!WHERE).)*$', 'i');
  699. if (doConfirmRegExp0.test(sqlQuery1) || doConfirmRegExp1.test(sqlQuery1) || doConfirmRegExp2.test(sqlQuery1) || doConfirmRegExp3.test(sqlQuery1) || doConfirmRegExp4.test(sqlQuery1)) {
  700. var message;
  701. if (sqlQuery1.length > 100) {
  702. message = sqlQuery1.substr(0, 100) + '\n ...';
  703. } else {
  704. message = sqlQuery1;
  705. }
  706. var isConfirmed = confirm(Functions.sprintf(Messages.strDoYouReally, message)); // statement is confirmed -> update the
  707. // "is_js_confirmed" form field so the confirm test won't be
  708. // run on the server side and allows to submit the form
  709. if (isConfirmed) {
  710. theForm1.elements.is_js_confirmed.value = 1;
  711. return true;
  712. } else {
  713. // statement is rejected -> do not submit the form
  714. window.focus();
  715. return false;
  716. } // end if (handle confirm box result)
  717. } // end if (display confirm box)
  718. return true;
  719. };
  720. /**
  721. * Displays an error message if the user submitted the sql query form with no
  722. * sql query, else checks for "DROP/DELETE/ALTER" statements
  723. *
  724. * @param theForm object the form
  725. *
  726. * @return boolean always false
  727. *
  728. * @see Functions.confirmQuery()
  729. */
  730. Functions.checkSqlQuery = function (theForm) {
  731. // get the textarea element containing the query
  732. var sqlQuery;
  733. if (codeMirrorEditor) {
  734. codeMirrorEditor.save();
  735. sqlQuery = codeMirrorEditor.getValue();
  736. } else {
  737. sqlQuery = theForm.elements.sql_query.value;
  738. }
  739. var spaceRegExp = new RegExp('\\s+');
  740. if (typeof theForm.elements.sql_file !== 'undefined' && theForm.elements.sql_file.value.replace(spaceRegExp, '') !== '') {
  741. return true;
  742. }
  743. if (typeof theForm.elements.id_bookmark !== 'undefined' && (theForm.elements.id_bookmark.value !== null || theForm.elements.id_bookmark.value !== '') && theForm.elements.id_bookmark.selectedIndex !== 0) {
  744. return true;
  745. }
  746. var result = false; // Checks for "DROP/DELETE/ALTER" statements
  747. if (sqlQuery.replace(spaceRegExp, '') !== '') {
  748. result = Functions.confirmQuery(theForm, sqlQuery);
  749. } else {
  750. alert(Messages.strFormEmpty);
  751. }
  752. if (codeMirrorEditor) {
  753. codeMirrorEditor.focus();
  754. } else if (codeMirrorInlineEditor) {
  755. codeMirrorInlineEditor.focus();
  756. }
  757. return result;
  758. };
  759. /**
  760. * Check if a form's element is empty.
  761. * An element containing only spaces is also considered empty
  762. *
  763. * @param {object} theForm the form
  764. * @param {string} theFieldName the name of the form field to put the focus on
  765. *
  766. * @return {boolean} whether the form field is empty or not
  767. */
  768. Functions.emptyCheckTheField = function (theForm, theFieldName) {
  769. var theField = theForm.elements[theFieldName];
  770. var spaceRegExp = new RegExp('\\s+');
  771. return theField.value.replace(spaceRegExp, '') === '';
  772. };
  773. /**
  774. * Ensures a value submitted in a form is numeric and is in a range
  775. *
  776. * @param object the form
  777. * @param string the name of the form field to check
  778. * @param integer the minimum authorized value
  779. * @param integer the maximum authorized value
  780. *
  781. * @return boolean whether a valid number has been submitted or not
  782. */
  783. Functions.checkFormElementInRange = function (theForm, theFieldName, message, minimum, maximum) {
  784. var theField = theForm.elements[theFieldName];
  785. var val = parseInt(theField.value, 10);
  786. var min = 0;
  787. var max = Number.MAX_VALUE;
  788. if (typeof minimum !== 'undefined') {
  789. min = minimum;
  790. }
  791. if (typeof maximum !== 'undefined' && maximum !== null) {
  792. max = maximum;
  793. }
  794. if (isNaN(val)) {
  795. theField.select();
  796. alert(Messages.strEnterValidNumber);
  797. theField.focus();
  798. return false;
  799. } else if (val < min || val > max) {
  800. theField.select();
  801. alert(Functions.sprintf(message, val));
  802. theField.focus();
  803. return false;
  804. } else {
  805. theField.value = val;
  806. }
  807. return true;
  808. };
  809. Functions.checkTableEditForm = function (theForm, fieldsCnt) {
  810. // TODO: avoid sending a message if user just wants to add a line
  811. // on the form but has not completed at least one field name
  812. var atLeastOneField = 0;
  813. var i;
  814. var elm;
  815. var elm2;
  816. var elm3;
  817. var val;
  818. var id;
  819. for (i = 0; i < fieldsCnt; i++) {
  820. id = '#field_' + i + '_2';
  821. elm = $(id);
  822. val = elm.val();
  823. if (val === 'VARCHAR' || val === 'CHAR' || val === 'BIT' || val === 'VARBINARY' || val === 'BINARY') {
  824. elm2 = $('#field_' + i + '_3');
  825. val = parseInt(elm2.val(), 10);
  826. elm3 = $('#field_' + i + '_1');
  827. if (isNaN(val) && elm3.val() !== '') {
  828. elm2.select();
  829. alert(Messages.strEnterValidLength);
  830. elm2.focus();
  831. return false;
  832. }
  833. }
  834. if (atLeastOneField === 0) {
  835. id = 'field_' + i + '_1';
  836. if (!Functions.emptyCheckTheField(theForm, id)) {
  837. atLeastOneField = 1;
  838. }
  839. }
  840. }
  841. if (atLeastOneField === 0) {
  842. var theField = theForm.elements.field_0_1;
  843. alert(Messages.strFormEmpty);
  844. theField.focus();
  845. return false;
  846. } // at least this section is under jQuery
  847. var $input = $('input.textfield[name=\'table\']');
  848. if ($input.val() === '') {
  849. alert(Messages.strFormEmpty);
  850. $input.trigger('focus');
  851. return false;
  852. }
  853. return true;
  854. };
  855. /**
  856. * True if last click is to check a row.
  857. */
  858. var lastClickChecked = false;
  859. /**
  860. * Zero-based index of last clicked row.
  861. * Used to handle the shift + click event in the code above.
  862. */
  863. var lastClickedRow = -1;
  864. /**
  865. * Zero-based index of last shift clicked row.
  866. */
  867. var lastShiftClickedRow = -1;
  868. var idleSecondsCounter = 0;
  869. var incInterval;
  870. var updateTimeout;
  871. AJAX.registerTeardown('functions.js', function () {
  872. clearTimeout(updateTimeout);
  873. clearInterval(incInterval);
  874. $(document).off('mousemove');
  875. });
  876. AJAX.registerOnload('functions.js', function () {
  877. document.onclick = function () {
  878. idleSecondsCounter = 0;
  879. };
  880. $(document).on('mousemove', function () {
  881. idleSecondsCounter = 0;
  882. });
  883. document.onkeypress = function () {
  884. idleSecondsCounter = 0;
  885. };
  886. function guid() {
  887. function s4() {
  888. return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
  889. }
  890. return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
  891. }
  892. function SetIdleTime() {
  893. idleSecondsCounter++;
  894. }
  895. function UpdateIdleTime() {
  896. var href = 'index.php?route=/';
  897. var guid = 'default';
  898. if (isStorageSupported('sessionStorage')) {
  899. guid = window.sessionStorage.guid;
  900. }
  901. var params = {
  902. 'ajax_request': true,
  903. 'server': CommonParams.get('server'),
  904. 'db': CommonParams.get('db'),
  905. 'guid': guid,
  906. 'access_time': idleSecondsCounter,
  907. 'check_timeout': 1
  908. };
  909. $.ajax({
  910. type: 'POST',
  911. url: href,
  912. data: params,
  913. success: function success(data) {
  914. if (data.success) {
  915. if (CommonParams.get('LoginCookieValidity') - idleSecondsCounter < 0) {
  916. /* There is other active window, let's reset counter */
  917. idleSecondsCounter = 0;
  918. }
  919. var remaining = Math.min(
  920. /* Remaining login validity */
  921. CommonParams.get('LoginCookieValidity') - idleSecondsCounter,
  922. /* Remaining time till session GC */
  923. CommonParams.get('session_gc_maxlifetime'));
  924. var interval = 1000;
  925. if (remaining > 5) {
  926. // max value for setInterval() function
  927. interval = Math.min((remaining - 1) * 1000, Math.pow(2, 31) - 1);
  928. }
  929. updateTimeout = window.setTimeout(UpdateIdleTime, interval);
  930. } else {
  931. // timeout occurred
  932. clearInterval(incInterval);
  933. if (isStorageSupported('sessionStorage')) {
  934. window.sessionStorage.clear();
  935. } // append the login form on the page, disable all the forms which were not disabled already, close all the open jqueryui modal boxes
  936. if (!$('#modalOverlay').length) {
  937. $('fieldset').not(':disabled').attr('disabled', 'disabled').addClass('disabled_for_expiration');
  938. $('body').append(data.error);
  939. $('.ui-dialog').each(function () {
  940. $('#' + $(this).attr('aria-describedby')).dialog('close');
  941. });
  942. $('#input_username').trigger('focus');
  943. } else {
  944. CommonParams.set('token', data.new_token);
  945. $('input[name=token]').val(data.new_token);
  946. }
  947. idleSecondsCounter = 0;
  948. Functions.handleRedirectAndReload(data);
  949. }
  950. }
  951. });
  952. }
  953. if (CommonParams.get('logged_in')) {
  954. incInterval = window.setInterval(SetIdleTime, 1000);
  955. var sessionTimeout = Math.min(CommonParams.get('LoginCookieValidity'), CommonParams.get('session_gc_maxlifetime'));
  956. if (isStorageSupported('sessionStorage')) {
  957. window.sessionStorage.setItem('guid', guid());
  958. }
  959. var interval = (sessionTimeout - 5) * 1000;
  960. if (interval > Math.pow(2, 31) - 1) {
  961. // max value for setInterval() function
  962. interval = Math.pow(2, 31) - 1;
  963. }
  964. updateTimeout = window.setTimeout(UpdateIdleTime, interval);
  965. }
  966. });
  967. /**
  968. * Unbind all event handlers before tearing down a page
  969. */
  970. AJAX.registerTeardown('functions.js', function () {
  971. $(document).off('click', 'input:checkbox.checkall');
  972. });
  973. AJAX.registerOnload('functions.js', function () {
  974. /**
  975. * Row marking in horizontal mode (use "on" so that it works also for
  976. * next pages reached via AJAX); a tr may have the class noclick to remove
  977. * this behavior.
  978. */
  979. $(document).on('click', 'input:checkbox.checkall', function (e) {
  980. var $this = $(this);
  981. var $tr = $this.closest('tr');
  982. var $table = $this.closest('table');
  983. if (!e.shiftKey || lastClickedRow === -1) {
  984. // usual click
  985. var $checkbox = $tr.find(':checkbox.checkall');
  986. var checked = $this.prop('checked');
  987. $checkbox.prop('checked', checked).trigger('change');
  988. if (checked) {
  989. $tr.addClass('marked table-active');
  990. } else {
  991. $tr.removeClass('marked table-active');
  992. }
  993. lastClickChecked = checked; // remember the last clicked row
  994. lastClickedRow = lastClickChecked ? $table.find('tbody tr:not(.noclick)').index($tr) : -1;
  995. lastShiftClickedRow = -1;
  996. } else {
  997. // handle the shift click
  998. Functions.clearSelection();
  999. var start;
  1000. var end; // clear last shift click result
  1001. if (lastShiftClickedRow >= 0) {
  1002. if (lastShiftClickedRow >= lastClickedRow) {
  1003. start = lastClickedRow;
  1004. end = lastShiftClickedRow;
  1005. } else {
  1006. start = lastShiftClickedRow;
  1007. end = lastClickedRow;
  1008. }
  1009. $tr.parent().find('tr:not(.noclick)').slice(start, end + 1).removeClass('marked table-active').find(':checkbox').prop('checked', false).trigger('change');
  1010. } // handle new shift click
  1011. var currRow = $table.find('tbody tr:not(.noclick)').index($tr);
  1012. if (currRow >= lastClickedRow) {
  1013. start = lastClickedRow;
  1014. end = currRow;
  1015. } else {
  1016. start = currRow;
  1017. end = lastClickedRow;
  1018. }
  1019. $tr.parent().find('tr:not(.noclick)').slice(start, end + 1).addClass('marked table-active').find(':checkbox').prop('checked', true).trigger('change'); // remember the last shift clicked row
  1020. lastShiftClickedRow = currRow;
  1021. }
  1022. });
  1023. Functions.addDateTimePicker();
  1024. /**
  1025. * Add attribute to text boxes for iOS devices (based on bugID: 3508912)
  1026. */
  1027. if (navigator.userAgent.match(/(iphone|ipod|ipad)/i)) {
  1028. $('input[type=text]').attr('autocapitalize', 'off').attr('autocorrect', 'off');
  1029. }
  1030. });
  1031. /**
  1032. * Checks/unchecks all options of a <select> element
  1033. *
  1034. * @param string the form name
  1035. * @param string the element name
  1036. * @param boolean whether to check or to uncheck options
  1037. *
  1038. * @return boolean always true
  1039. */
  1040. Functions.setSelectOptions = function (theForm, theSelect, doCheck) {
  1041. $('form[name=\'' + theForm + '\'] select[name=\'' + theSelect + '\']').find('option').prop('selected', doCheck);
  1042. return true;
  1043. };
  1044. /**
  1045. * Sets current value for query box.
  1046. */
  1047. Functions.setQuery = function (query) {
  1048. if (codeMirrorEditor) {
  1049. codeMirrorEditor.setValue(query);
  1050. codeMirrorEditor.focus();
  1051. } else if (document.sqlform) {
  1052. document.sqlform.sql_query.value = query;
  1053. document.sqlform.sql_query.focus();
  1054. }
  1055. };
  1056. /**
  1057. * Handles 'Simulate query' button on SQL query box.
  1058. *
  1059. * @return void
  1060. */
  1061. Functions.handleSimulateQueryButton = function () {
  1062. var updateRegExp = new RegExp('^\\s*UPDATE\\s+((`[^`]+`)|([A-Za-z0-9_$]+))\\s+SET\\s', 'i');
  1063. var deleteRegExp = new RegExp('^\\s*DELETE\\s+FROM\\s', 'i');
  1064. var query = '';
  1065. if (codeMirrorEditor) {
  1066. query = codeMirrorEditor.getValue();
  1067. } else {
  1068. query = $('#sqlquery').val();
  1069. }
  1070. var $simulateDml = $('#simulate_dml');
  1071. if (updateRegExp.test(query) || deleteRegExp.test(query)) {
  1072. if (!$simulateDml.length) {
  1073. $('#button_submit_query').before('<input type="button" id="simulate_dml"' + 'tabindex="199" class="btn btn-primary" value="' + Messages.strSimulateDML + '">');
  1074. }
  1075. } else {
  1076. if ($simulateDml.length) {
  1077. $simulateDml.remove();
  1078. }
  1079. }
  1080. };
  1081. /**
  1082. * Create quick sql statements.
  1083. *
  1084. */
  1085. Functions.insertQuery = function (queryType) {
  1086. var table;
  1087. if (queryType === 'clear') {
  1088. Functions.setQuery('');
  1089. return;
  1090. } else if (queryType === 'format') {
  1091. if (codeMirrorEditor) {
  1092. $('#querymessage').html(Messages.strFormatting + '&nbsp;<img class="ajaxIcon" src="' + themeImagePath + 'ajax_clock_small.gif" alt="">');
  1093. var params = {
  1094. 'ajax_request': true,
  1095. 'sql': codeMirrorEditor.getValue(),
  1096. 'server': CommonParams.get('server')
  1097. };
  1098. $.ajax({
  1099. type: 'POST',
  1100. url: 'index.php?route=/database/sql/format',
  1101. data: params,
  1102. success: function success(data) {
  1103. if (data.success) {
  1104. codeMirrorEditor.setValue(data.sql);
  1105. }
  1106. $('#querymessage').html('');
  1107. }
  1108. });
  1109. }
  1110. return;
  1111. } else if (queryType === 'saved') {
  1112. var db = $('input[name="db"]').val();
  1113. table = $('input[name="table"]').val();
  1114. var key = db;
  1115. if (table !== undefined) {
  1116. key += '.' + table;
  1117. }
  1118. key = 'autoSavedSql_' + key;
  1119. if (isStorageSupported('localStorage') && typeof window.localStorage.getItem(key) === 'string') {
  1120. Functions.setQuery(window.localStorage.getItem(key));
  1121. } else if (Cookies.get(key)) {
  1122. Functions.setQuery(Cookies.get(key));
  1123. } else {
  1124. Functions.ajaxShowMessage(Messages.strNoAutoSavedQuery);
  1125. }
  1126. return;
  1127. }
  1128. var query = '';
  1129. var myListBox = document.sqlform.dummy;
  1130. table = document.sqlform.table.value;
  1131. if (myListBox.options.length > 0) {
  1132. sqlBoxLocked = true;
  1133. var columnsList = '';
  1134. var valDis = '';
  1135. var editDis = '';
  1136. var NbSelect = 0;
  1137. for (var i = 0; i < myListBox.options.length; i++) {
  1138. NbSelect++;
  1139. if (NbSelect > 1) {
  1140. columnsList += ', ';
  1141. valDis += ',';
  1142. editDis += ',';
  1143. }
  1144. columnsList += myListBox.options[i].value;
  1145. valDis += '\'[value-' + NbSelect + ']\'';
  1146. editDis += myListBox.options[i].value + '=\'[value-' + NbSelect + ']\'';
  1147. }
  1148. if (queryType === 'selectall') {
  1149. query = 'SELECT * FROM `' + table + '` WHERE 1';
  1150. } else if (queryType === 'select') {
  1151. query = 'SELECT ' + columnsList + ' FROM `' + table + '` WHERE 1';
  1152. } else if (queryType === 'insert') {
  1153. query = 'INSERT INTO `' + table + '`(' + columnsList + ') VALUES (' + valDis + ')';
  1154. } else if (queryType === 'update') {
  1155. query = 'UPDATE `' + table + '` SET ' + editDis + ' WHERE 1';
  1156. } else if (queryType === 'delete') {
  1157. query = 'DELETE FROM `' + table + '` WHERE 0';
  1158. }
  1159. Functions.setQuery(query);
  1160. sqlBoxLocked = false;
  1161. }
  1162. };
  1163. /**
  1164. * Inserts multiple fields.
  1165. *
  1166. */
  1167. Functions.insertValueQuery = function () {
  1168. var myQuery = document.sqlform.sql_query;
  1169. var myListBox = document.sqlform.dummy;
  1170. if (myListBox.options.length > 0) {
  1171. sqlBoxLocked = true;
  1172. var columnsList = '';
  1173. var NbSelect = 0;
  1174. for (var i = 0; i < myListBox.options.length; i++) {
  1175. if (myListBox.options[i].selected) {
  1176. NbSelect++;
  1177. if (NbSelect > 1) {
  1178. columnsList += ', ';
  1179. }
  1180. columnsList += myListBox.options[i].value;
  1181. }
  1182. }
  1183. /* CodeMirror support */
  1184. if (codeMirrorEditor) {
  1185. codeMirrorEditor.replaceSelection(columnsList);
  1186. codeMirrorEditor.focus(); // IE support
  1187. } else if (document.selection) {
  1188. myQuery.focus();
  1189. var sel = document.selection.createRange();
  1190. sel.text = columnsList; // MOZILLA/NETSCAPE support
  1191. } else if (document.sqlform.sql_query.selectionStart || document.sqlform.sql_query.selectionStart === '0') {
  1192. var startPos = document.sqlform.sql_query.selectionStart;
  1193. var endPos = document.sqlform.sql_query.selectionEnd;
  1194. var SqlString = document.sqlform.sql_query.value;
  1195. myQuery.value = SqlString.substring(0, startPos) + columnsList + SqlString.substring(endPos, SqlString.length);
  1196. myQuery.focus();
  1197. } else {
  1198. myQuery.value += columnsList;
  1199. } // eslint-disable-next-line no-unused-vars
  1200. sqlBoxLocked = false;
  1201. }
  1202. };
  1203. /**
  1204. * Updates the input fields for the parameters based on the query
  1205. */
  1206. Functions.updateQueryParameters = function () {
  1207. if ($('#parameterized').is(':checked')) {
  1208. var query = codeMirrorEditor ? codeMirrorEditor.getValue() : $('#sqlquery').val();
  1209. var allParameters = query.match(/:[a-zA-Z0-9_]+/g);
  1210. var parameters = []; // get unique parameters
  1211. if (allParameters) {
  1212. $.each(allParameters, function (i, parameter) {
  1213. if ($.inArray(parameter, parameters) === -1) {
  1214. parameters.push(parameter);
  1215. }
  1216. });
  1217. } else {
  1218. $('#parametersDiv').text(Messages.strNoParam);
  1219. return;
  1220. }
  1221. var $temp = $('<div></div>');
  1222. $temp.append($('#parametersDiv').children());
  1223. $('#parametersDiv').empty();
  1224. $.each(parameters, function (i, parameter) {
  1225. var paramName = parameter.substring(1);
  1226. var $param = $temp.find('#paramSpan_' + paramName);
  1227. if (!$param.length) {
  1228. $param = $('<span class="parameter" id="paramSpan_' + paramName + '"></span>');
  1229. $('<label for="param_' + paramName + '"></label>').text(parameter).appendTo($param);
  1230. $('<input type="text" name="parameters[' + parameter + ']" id="param_' + paramName + '">').appendTo($param);
  1231. }
  1232. $('#parametersDiv').append($param);
  1233. });
  1234. } else {
  1235. $('#parametersDiv').empty();
  1236. }
  1237. };
  1238. /**
  1239. * Refresh/resize the WYSIWYG scratchboard
  1240. */
  1241. Functions.refreshLayout = function () {
  1242. var $elm = $('#pdflayout');
  1243. var orientation = $('#orientation_opt').val();
  1244. var paper = 'A4';
  1245. var $paperOpt = $('#paper_opt');
  1246. if ($paperOpt.length === 1) {
  1247. paper = $paperOpt.val();
  1248. }
  1249. var posa = 'y';
  1250. var posb = 'x';
  1251. if (orientation === 'P') {
  1252. posa = 'x';
  1253. posb = 'y';
  1254. }
  1255. $elm.css('width', Functions.pdfPaperSize(paper, posa) + 'px');
  1256. $elm.css('height', Functions.pdfPaperSize(paper, posb) + 'px');
  1257. };
  1258. /**
  1259. * Initializes positions of elements.
  1260. */
  1261. Functions.tableDragInit = function () {
  1262. $('.pdflayout_table').each(function () {
  1263. var $this = $(this);
  1264. var number = $this.data('number');
  1265. var x = $('#c_table_' + number + '_x').val();
  1266. var y = $('#c_table_' + number + '_y').val();
  1267. $this.css('left', x + 'px');
  1268. $this.css('top', y + 'px');
  1269. /* Make elements draggable */
  1270. $this.draggable({
  1271. containment: 'parent',
  1272. drag: function drag(evt, ui) {
  1273. var number = $this.data('number');
  1274. $('#c_table_' + number + '_x').val(parseInt(ui.position.left, 10));
  1275. $('#c_table_' + number + '_y').val(parseInt(ui.position.top, 10));
  1276. }
  1277. });
  1278. });
  1279. };
  1280. /**
  1281. * Resets drag and drop positions.
  1282. */
  1283. Functions.resetDrag = function () {
  1284. $('.pdflayout_table').each(function () {
  1285. var $this = $(this);
  1286. var x = $this.data('x');
  1287. var y = $this.data('y');
  1288. $this.css('left', x + 'px');
  1289. $this.css('top', y + 'px');
  1290. });
  1291. };
  1292. /**
  1293. * User schema handlers.
  1294. */
  1295. $(function () {
  1296. /* Move in scratchboard on manual change */
  1297. $(document).on('change', '.position-change', function () {
  1298. var $this = $(this);
  1299. var $elm = $('#table_' + $this.data('number'));
  1300. $elm.css($this.data('axis'), $this.val() + 'px');
  1301. });
  1302. /* Refresh on paper size/orientation change */
  1303. $(document).on('change', '.paper-change', function () {
  1304. var $elm = $('#pdflayout');
  1305. if ($elm.css('visibility') === 'visible') {
  1306. Functions.refreshLayout();
  1307. Functions.tableDragInit();
  1308. }
  1309. });
  1310. /* Show/hide the WYSIWYG scratchboard */
  1311. $(document).on('click', '#toggle-dragdrop', function () {
  1312. var $elm = $('#pdflayout');
  1313. if ($elm.css('visibility') === 'hidden') {
  1314. Functions.refreshLayout();
  1315. Functions.tableDragInit();
  1316. $elm.css('visibility', 'visible');
  1317. $elm.css('display', 'block');
  1318. $('#showwysiwyg').val('1');
  1319. } else {
  1320. $elm.css('visibility', 'hidden');
  1321. $elm.css('display', 'none');
  1322. $('#showwysiwyg').val('0');
  1323. }
  1324. });
  1325. /* Reset scratchboard */
  1326. $(document).on('click', '#reset-dragdrop', function () {
  1327. Functions.resetDrag();
  1328. });
  1329. });
  1330. /**
  1331. * Returns paper sizes for a given format
  1332. */
  1333. Functions.pdfPaperSize = function (format, axis) {
  1334. switch (format.toUpperCase()) {
  1335. case '4A0':
  1336. if (axis === 'x') {
  1337. return 4767.87;
  1338. }
  1339. return 6740.79;
  1340. case '2A0':
  1341. if (axis === 'x') {
  1342. return 3370.39;
  1343. }
  1344. return 4767.87;
  1345. case 'A0':
  1346. if (axis === 'x') {
  1347. return 2383.94;
  1348. }
  1349. return 3370.39;
  1350. case 'A1':
  1351. if (axis === 'x') {
  1352. return 1683.78;
  1353. }
  1354. return 2383.94;
  1355. case 'A2':
  1356. if (axis === 'x') {
  1357. return 1190.55;
  1358. }
  1359. return 1683.78;
  1360. case 'A3':
  1361. if (axis === 'x') {
  1362. return 841.89;
  1363. }
  1364. return 1190.55;
  1365. case 'A4':
  1366. if (axis === 'x') {
  1367. return 595.28;
  1368. }
  1369. return 841.89;
  1370. case 'A5':
  1371. if (axis === 'x') {
  1372. return 419.53;
  1373. }
  1374. return 595.28;
  1375. case 'A6':
  1376. if (axis === 'x') {
  1377. return 297.64;
  1378. }
  1379. return 419.53;
  1380. case 'A7':
  1381. if (axis === 'x') {
  1382. return 209.76;
  1383. }
  1384. return 297.64;
  1385. case 'A8':
  1386. if (axis === 'x') {
  1387. return 147.40;
  1388. }
  1389. return 209.76;
  1390. case 'A9':
  1391. if (axis === 'x') {
  1392. return 104.88;
  1393. }
  1394. return 147.40;
  1395. case 'A10':
  1396. if (axis === 'x') {
  1397. return 73.70;
  1398. }
  1399. return 104.88;
  1400. case 'B0':
  1401. if (axis === 'x') {
  1402. return 2834.65;
  1403. }
  1404. return 4008.19;
  1405. case 'B1':
  1406. if (axis === 'x') {
  1407. return 2004.09;
  1408. }
  1409. return 2834.65;
  1410. case 'B2':
  1411. if (axis === 'x') {
  1412. return 1417.32;
  1413. }
  1414. return 2004.09;
  1415. case 'B3':
  1416. if (axis === 'x') {
  1417. return 1000.63;
  1418. }
  1419. return 1417.32;
  1420. case 'B4':
  1421. if (axis === 'x') {
  1422. return 708.66;
  1423. }
  1424. return 1000.63;
  1425. case 'B5':
  1426. if (axis === 'x') {
  1427. return 498.90;
  1428. }
  1429. return 708.66;
  1430. case 'B6':
  1431. if (axis === 'x') {
  1432. return 354.33;
  1433. }
  1434. return 498.90;
  1435. case 'B7':
  1436. if (axis === 'x') {
  1437. return 249.45;
  1438. }
  1439. return 354.33;
  1440. case 'B8':
  1441. if (axis === 'x') {
  1442. return 175.75;
  1443. }
  1444. return 249.45;
  1445. case 'B9':
  1446. if (axis === 'x') {
  1447. return 124.72;
  1448. }
  1449. return 175.75;
  1450. case 'B10':
  1451. if (axis === 'x') {
  1452. return 87.87;
  1453. }
  1454. return 124.72;
  1455. case 'C0':
  1456. if (axis === 'x') {
  1457. return 2599.37;
  1458. }
  1459. return 3676.54;
  1460. case 'C1':
  1461. if (axis === 'x') {
  1462. return 1836.85;
  1463. }
  1464. return 2599.37;
  1465. case 'C2':
  1466. if (axis === 'x') {
  1467. return 1298.27;
  1468. }
  1469. return 1836.85;
  1470. case 'C3':
  1471. if (axis === 'x') {
  1472. return 918.43;
  1473. }
  1474. return 1298.27;
  1475. case 'C4':
  1476. if (axis === 'x') {
  1477. return 649.13;
  1478. }
  1479. return 918.43;
  1480. case 'C5':
  1481. if (axis === 'x') {
  1482. return 459.21;
  1483. }
  1484. return 649.13;
  1485. case 'C6':
  1486. if (axis === 'x') {
  1487. return 323.15;
  1488. }
  1489. return 459.21;
  1490. case 'C7':
  1491. if (axis === 'x') {
  1492. return 229.61;
  1493. }
  1494. return 323.15;
  1495. case 'C8':
  1496. if (axis === 'x') {
  1497. return 161.57;
  1498. }
  1499. return 229.61;
  1500. case 'C9':
  1501. if (axis === 'x') {
  1502. return 113.39;
  1503. }
  1504. return 161.57;
  1505. case 'C10':
  1506. if (axis === 'x') {
  1507. return 79.37;
  1508. }
  1509. return 113.39;
  1510. case 'RA0':
  1511. if (axis === 'x') {
  1512. return 2437.80;
  1513. }
  1514. return 3458.27;
  1515. case 'RA1':
  1516. if (axis === 'x') {
  1517. return 1729.13;
  1518. }
  1519. return 2437.80;
  1520. case 'RA2':
  1521. if (axis === 'x') {
  1522. return 1218.90;
  1523. }
  1524. return 1729.13;
  1525. case 'RA3':
  1526. if (axis === 'x') {
  1527. return 864.57;
  1528. }
  1529. return 1218.90;
  1530. case 'RA4':
  1531. if (axis === 'x') {
  1532. return 609.45;
  1533. }
  1534. return 864.57;
  1535. case 'SRA0':
  1536. if (axis === 'x') {
  1537. return 2551.18;
  1538. }
  1539. return 3628.35;
  1540. case 'SRA1':
  1541. if (axis === 'x') {
  1542. return 1814.17;
  1543. }
  1544. return 2551.18;
  1545. case 'SRA2':
  1546. if (axis === 'x') {
  1547. return 1275.59;
  1548. }
  1549. return 1814.17;
  1550. case 'SRA3':
  1551. if (axis === 'x') {
  1552. return 907.09;
  1553. }
  1554. return 1275.59;
  1555. case 'SRA4':
  1556. if (axis === 'x') {
  1557. return 637.80;
  1558. }
  1559. return 907.09;
  1560. case 'LETTER':
  1561. if (axis === 'x') {
  1562. return 612.00;
  1563. }
  1564. return 792.00;
  1565. case 'LEGAL':
  1566. if (axis === 'x') {
  1567. return 612.00;
  1568. }
  1569. return 1008.00;
  1570. case 'EXECUTIVE':
  1571. if (axis === 'x') {
  1572. return 521.86;
  1573. }
  1574. return 756.00;
  1575. case 'FOLIO':
  1576. if (axis === 'x') {
  1577. return 612.00;
  1578. }
  1579. return 936.00;
  1580. }
  1581. return 0;
  1582. };
  1583. /**
  1584. * Get checkbox for foreign key checks
  1585. *
  1586. * @return string
  1587. */
  1588. Functions.getForeignKeyCheckboxLoader = function () {
  1589. var html = '';
  1590. html += '<div>';
  1591. html += '<div class="load-default-fk-check-value">';
  1592. html += Functions.getImage('ajax_clock_small');
  1593. html += '</div>';
  1594. html += '</div>';
  1595. return html;
  1596. };
  1597. Functions.loadForeignKeyCheckbox = function () {
  1598. // Load default foreign key check value
  1599. var params = {
  1600. 'ajax_request': true,
  1601. 'server': CommonParams.get('server')
  1602. };
  1603. $.get('index.php?route=/sql/get-default-fk-check-value', params, function (data) {
  1604. var html = '<input type="hidden" name="fk_checks" value="0">' + '<input type="checkbox" name="fk_checks" id="fk_checks"' + (data.default_fk_check_value ? ' checked="checked"' : '') + '>' + '<label for="fk_checks">' + Messages.strForeignKeyCheck + '</label>';
  1605. $('.load-default-fk-check-value').replaceWith(html);
  1606. });
  1607. };
  1608. Functions.getJsConfirmCommonParam = function (elem, parameters) {
  1609. var $elem = $(elem);
  1610. var params = parameters;
  1611. var sep = CommonParams.get('arg_separator');
  1612. if (params) {
  1613. // Strip possible leading ?
  1614. if (params.substring(0, 1) === '?') {
  1615. params = params.substr(1);
  1616. }
  1617. params += sep;
  1618. } else {
  1619. params = '';
  1620. }
  1621. params += 'is_js_confirmed=1' + sep + 'ajax_request=true' + sep + 'fk_checks=' + ($elem.find('#fk_checks').is(':checked') ? 1 : 0);
  1622. return params;
  1623. };
  1624. /**
  1625. * Unbind all event handlers before tearing down a page
  1626. */
  1627. AJAX.registerTeardown('functions.js', function () {
  1628. $(document).off('click', 'a.inline_edit_sql');
  1629. $(document).off('click', 'input#sql_query_edit_save');
  1630. $(document).off('click', 'input#sql_query_edit_discard');
  1631. $('input.sqlbutton').off('click');
  1632. if (codeMirrorEditor) {
  1633. codeMirrorEditor.off('blur');
  1634. } else {
  1635. $(document).off('blur', '#sqlquery');
  1636. }
  1637. $(document).off('change', '#parameterized');
  1638. $(document).off('click', 'input.sqlbutton');
  1639. $('#sqlquery').off('keydown');
  1640. $('#sql_query_edit').off('keydown');
  1641. if (codeMirrorInlineEditor) {
  1642. // Copy the sql query to the text area to preserve it.
  1643. $('#sql_query_edit').text(codeMirrorInlineEditor.getValue());
  1644. $(codeMirrorInlineEditor.getWrapperElement()).off('keydown');
  1645. codeMirrorInlineEditor.toTextArea();
  1646. codeMirrorInlineEditor = false;
  1647. }
  1648. if (codeMirrorEditor) {
  1649. $(codeMirrorEditor.getWrapperElement()).off('keydown');
  1650. }
  1651. });
  1652. /**
  1653. * Jquery Coding for inline editing SQL_QUERY
  1654. */
  1655. AJAX.registerOnload('functions.js', function () {
  1656. // If we are coming back to the page by clicking forward button
  1657. // of the browser, bind the code mirror to inline query editor.
  1658. Functions.bindCodeMirrorToInlineEditor();
  1659. $(document).on('click', 'a.inline_edit_sql', function () {
  1660. if ($('#sql_query_edit').length) {
  1661. // An inline query editor is already open,
  1662. // we don't want another copy of it
  1663. return false;
  1664. }
  1665. var $form = $(this).prev('form');
  1666. var sqlQuery = $form.find('input[name=\'sql_query\']').val().trim();
  1667. var $innerSql = $(this).parent().prev().find('code.sql');
  1668. var newContent = '<textarea name="sql_query_edit" id="sql_query_edit">' + Functions.escapeHtml(sqlQuery) + '</textarea>\n';
  1669. newContent += Functions.getForeignKeyCheckboxLoader();
  1670. newContent += '<input type="submit" id="sql_query_edit_save" class="btn btn-secondary button btnSave" value="' + Messages.strGo + '">\n';
  1671. newContent += '<input type="button" id="sql_query_edit_discard" class="btn btn-secondary button btnDiscard" value="' + Messages.strCancel + '">\n';
  1672. var $editorArea = $('div#inline_editor');
  1673. if ($editorArea.length === 0) {
  1674. $editorArea = $('<div id="inline_editor_outer"></div>');
  1675. $editorArea.insertBefore($innerSql);
  1676. }
  1677. $editorArea.html(newContent);
  1678. Functions.loadForeignKeyCheckbox();
  1679. $innerSql.hide();
  1680. Functions.bindCodeMirrorToInlineEditor();
  1681. return false;
  1682. });
  1683. $(document).on('click', 'input#sql_query_edit_save', function () {
  1684. // hide already existing success message
  1685. var sqlQuery;
  1686. if (codeMirrorInlineEditor) {
  1687. codeMirrorInlineEditor.save();
  1688. sqlQuery = codeMirrorInlineEditor.getValue();
  1689. } else {
  1690. sqlQuery = $(this).parent().find('#sql_query_edit').val();
  1691. }
  1692. var fkCheck = $(this).parent().find('#fk_checks').is(':checked');
  1693. var $form = $('a.inline_edit_sql').prev('form');
  1694. var $fakeForm = $('<form>', {
  1695. action: 'index.php?route=/import',
  1696. method: 'post'
  1697. }).append($form.find('input[name=server], input[name=db], input[name=table], input[name=token]').clone()).append($('<input>', {
  1698. type: 'hidden',
  1699. name: 'show_query',
  1700. value: 1
  1701. })).append($('<input>', {
  1702. type: 'hidden',
  1703. name: 'is_js_confirmed',
  1704. value: 0
  1705. })).append($('<input>', {
  1706. type: 'hidden',
  1707. name: 'sql_query',
  1708. value: sqlQuery
  1709. })).append($('<input>', {
  1710. type: 'hidden',
  1711. name: 'fk_checks',
  1712. value: fkCheck ? 1 : 0
  1713. }));
  1714. if (!Functions.checkSqlQuery($fakeForm[0])) {
  1715. return false;
  1716. }
  1717. $('.alert-success').hide();
  1718. $fakeForm.appendTo($('body')).trigger('submit');
  1719. });
  1720. $(document).on('click', 'input#sql_query_edit_discard', function () {
  1721. var $divEditor = $('div#inline_editor_outer');
  1722. $divEditor.siblings('code.sql').show();
  1723. $divEditor.remove();
  1724. });
  1725. $(document).on('click', 'input.sqlbutton', function (evt) {
  1726. Functions.insertQuery(evt.target.id);
  1727. Functions.handleSimulateQueryButton();
  1728. return false;
  1729. });
  1730. $(document).on('change', '#parameterized', Functions.updateQueryParameters);
  1731. var $inputUsername = $('#input_username');
  1732. if ($inputUsername) {
  1733. if ($inputUsername.val() === '') {
  1734. $inputUsername.trigger('focus');
  1735. } else {
  1736. $('#input_password').trigger('focus');
  1737. }
  1738. }
  1739. });
  1740. /**
  1741. * "inputRead" event handler for CodeMirror SQL query editors for autocompletion
  1742. */
  1743. Functions.codeMirrorAutoCompleteOnInputRead = function (instance) {
  1744. if (!sqlAutoCompleteInProgress && (!instance.options.hintOptions.tables || !sqlAutoComplete)) {
  1745. if (!sqlAutoComplete) {
  1746. // Reset after teardown
  1747. instance.options.hintOptions.tables = false;
  1748. instance.options.hintOptions.defaultTable = '';
  1749. sqlAutoCompleteInProgress = true;
  1750. var params = {
  1751. 'ajax_request': true,
  1752. 'server': CommonParams.get('server'),
  1753. 'db': CommonParams.get('db'),
  1754. 'no_debug': true
  1755. };
  1756. var columnHintRender = function columnHintRender(elem, self, data) {
  1757. $('<div class="autocomplete-column-name">').text(data.columnName).appendTo(elem);
  1758. $('<div class="autocomplete-column-hint">').text(data.columnHint).appendTo(elem);
  1759. };
  1760. $.ajax({
  1761. type: 'POST',
  1762. url: 'index.php?route=/database/sql/autocomplete',
  1763. data: params,
  1764. success: function success(data) {
  1765. if (data.success) {
  1766. var tables = JSON.parse(data.tables);
  1767. sqlAutoCompleteDefaultTable = CommonParams.get('table');
  1768. sqlAutoComplete = [];
  1769. for (var table in tables) {
  1770. if (tables.hasOwnProperty(table)) {
  1771. var columns = tables[table];
  1772. table = {
  1773. text: table,
  1774. columns: []
  1775. };
  1776. for (var column in columns) {
  1777. if (columns.hasOwnProperty(column)) {
  1778. var displayText = columns[column].Type;
  1779. if (columns[column].Key === 'PRI') {
  1780. displayText += ' | Primary';
  1781. } else if (columns[column].Key === 'UNI') {
  1782. displayText += ' | Unique';
  1783. }
  1784. table.columns.push({
  1785. text: column,
  1786. displayText: column + ' | ' + displayText,
  1787. columnName: column,
  1788. columnHint: displayText,
  1789. render: columnHintRender
  1790. });
  1791. }
  1792. }
  1793. }
  1794. sqlAutoComplete.push(table);
  1795. }
  1796. instance.options.hintOptions.tables = sqlAutoComplete;
  1797. instance.options.hintOptions.defaultTable = sqlAutoCompleteDefaultTable;
  1798. }
  1799. },
  1800. complete: function complete() {
  1801. sqlAutoCompleteInProgress = false;
  1802. }
  1803. });
  1804. } else {
  1805. instance.options.hintOptions.tables = sqlAutoComplete;
  1806. instance.options.hintOptions.defaultTable = sqlAutoCompleteDefaultTable;
  1807. }
  1808. }
  1809. if (instance.state.completionActive) {
  1810. return;
  1811. }
  1812. var cur = instance.getCursor();
  1813. var token = instance.getTokenAt(cur);
  1814. var string = '';
  1815. if (token.string.match(/^[.`\w@]\w*$/)) {
  1816. string = token.string;
  1817. }
  1818. if (string.length > 0) {
  1819. CodeMirror.commands.autocomplete(instance);
  1820. }
  1821. };
  1822. /**
  1823. * Remove autocomplete information before tearing down a page
  1824. */
  1825. AJAX.registerTeardown('functions.js', function () {
  1826. sqlAutoComplete = false;
  1827. sqlAutoCompleteDefaultTable = '';
  1828. });
  1829. /**
  1830. * Binds the CodeMirror to the text area used to inline edit a query.
  1831. */
  1832. Functions.bindCodeMirrorToInlineEditor = function () {
  1833. var $inlineEditor = $('#sql_query_edit');
  1834. if ($inlineEditor.length > 0) {
  1835. if (typeof CodeMirror !== 'undefined') {
  1836. var height = $inlineEditor.css('height');
  1837. codeMirrorInlineEditor = Functions.getSqlEditor($inlineEditor);
  1838. codeMirrorInlineEditor.getWrapperElement().style.height = height;
  1839. codeMirrorInlineEditor.refresh();
  1840. codeMirrorInlineEditor.focus();
  1841. $(codeMirrorInlineEditor.getWrapperElement()).on('keydown', Functions.catchKeypressesFromSqlInlineEdit);
  1842. } else {
  1843. $inlineEditor.trigger('focus').on('keydown', Functions.catchKeypressesFromSqlInlineEdit);
  1844. }
  1845. }
  1846. };
  1847. Functions.catchKeypressesFromSqlInlineEdit = function (event) {
  1848. // ctrl-enter is 10 in chrome and ie, but 13 in ff
  1849. if ((event.ctrlKey || event.metaKey) && (event.keyCode === 13 || event.keyCode === 10)) {
  1850. $('#sql_query_edit_save').trigger('click');
  1851. }
  1852. };
  1853. /**
  1854. * Adds doc link to single highlighted SQL element
  1855. */
  1856. Functions.documentationAdd = function ($elm, params) {
  1857. if (typeof mysqlDocTemplate === 'undefined') {
  1858. return;
  1859. }
  1860. var url = Functions.sprintf(decodeURIComponent(mysqlDocTemplate), params[0]);
  1861. if (params.length > 1) {
  1862. url += '#' + params[1];
  1863. }
  1864. var content = $elm.text();
  1865. $elm.text('');
  1866. $elm.append('<a target="mysql_doc" class="cm-sql-doc" href="' + url + '">' + content + '</a>');
  1867. };
  1868. /**
  1869. * Generates doc links for keywords inside highlighted SQL
  1870. */
  1871. Functions.documentationKeyword = function (idx, elm) {
  1872. var $elm = $(elm);
  1873. /* Skip already processed ones */
  1874. if ($elm.find('a').length > 0) {
  1875. return;
  1876. }
  1877. var keyword = $elm.text().toUpperCase();
  1878. var $next = $elm.next('.cm-keyword');
  1879. if ($next) {
  1880. var nextKeyword = $next.text().toUpperCase();
  1881. var full = keyword + ' ' + nextKeyword;
  1882. var $next2 = $next.next('.cm-keyword');
  1883. if ($next2) {
  1884. var next2Keyword = $next2.text().toUpperCase();
  1885. var full2 = full + ' ' + next2Keyword;
  1886. if (full2 in mysqlDocKeyword) {
  1887. Functions.documentationAdd($elm, mysqlDocKeyword[full2]);
  1888. Functions.documentationAdd($next, mysqlDocKeyword[full2]);
  1889. Functions.documentationAdd($next2, mysqlDocKeyword[full2]);
  1890. return;
  1891. }
  1892. }
  1893. if (full in mysqlDocKeyword) {
  1894. Functions.documentationAdd($elm, mysqlDocKeyword[full]);
  1895. Functions.documentationAdd($next, mysqlDocKeyword[full]);
  1896. return;
  1897. }
  1898. }
  1899. if (keyword in mysqlDocKeyword) {
  1900. Functions.documentationAdd($elm, mysqlDocKeyword[keyword]);
  1901. }
  1902. };
  1903. /**
  1904. * Generates doc links for builtins inside highlighted SQL
  1905. */
  1906. Functions.documentationBuiltin = function (idx, elm) {
  1907. var $elm = $(elm);
  1908. var builtin = $elm.text().toUpperCase();
  1909. if (builtin in mysqlDocBuiltin) {
  1910. Functions.documentationAdd($elm, mysqlDocBuiltin[builtin]);
  1911. }
  1912. };
  1913. /**
  1914. * Higlights SQL using CodeMirror.
  1915. */
  1916. Functions.highlightSql = function ($base) {
  1917. var $elm = $base.find('code.sql');
  1918. $elm.each(function () {
  1919. var $sql = $(this);
  1920. var $pre = $sql.find('pre');
  1921. /* We only care about visible elements to avoid double processing */
  1922. if ($pre.is(':visible')) {
  1923. var $highlight = $('<div class="sql-highlight cm-s-default"></div>');
  1924. $sql.append($highlight);
  1925. if (typeof CodeMirror !== 'undefined') {
  1926. CodeMirror.runMode($sql.text(), 'text/x-mysql', $highlight[0]);
  1927. $pre.hide();
  1928. $highlight.find('.cm-keyword').each(Functions.documentationKeyword);
  1929. $highlight.find('.cm-builtin').each(Functions.documentationBuiltin);
  1930. }
  1931. }
  1932. });
  1933. };
  1934. /**
  1935. * Updates an element containing code.
  1936. *
  1937. * @param jQuery Object $base base element which contains the raw and the
  1938. * highlighted code.
  1939. *
  1940. * @param string htmlValue code in HTML format, displayed if code cannot be
  1941. * highlighted
  1942. *
  1943. * @param string rawValue raw code, used as a parameter for highlighter
  1944. *
  1945. * @return bool whether content was updated or not
  1946. */
  1947. Functions.updateCode = function ($base, htmlValue, rawValue) {
  1948. var $code = $base.find('code');
  1949. if ($code.length === 0) {
  1950. return false;
  1951. } // Determines the type of the content and appropriate CodeMirror mode.
  1952. var type = '';
  1953. var mode = '';
  1954. if ($code.hasClass('json')) {
  1955. type = 'json';
  1956. mode = 'application/json';
  1957. } else if ($code.hasClass('sql')) {
  1958. type = 'sql';
  1959. mode = 'text/x-mysql';
  1960. } else if ($code.hasClass('xml')) {
  1961. type = 'xml';
  1962. mode = 'application/xml';
  1963. } else {
  1964. return false;
  1965. } // Element used to display unhighlighted code.
  1966. var $notHighlighted = $('<pre>' + htmlValue + '</pre>'); // Tries to highlight code using CodeMirror.
  1967. if (typeof CodeMirror !== 'undefined') {
  1968. var $highlighted = $('<div class="' + type + '-highlight cm-s-default"></div>');
  1969. CodeMirror.runMode(rawValue, mode, $highlighted[0]);
  1970. $notHighlighted.hide();
  1971. $code.html('').append($notHighlighted, $highlighted[0]);
  1972. } else {
  1973. $code.html('').append($notHighlighted);
  1974. }
  1975. return true;
  1976. };
  1977. /**
  1978. * Show a message on the top of the page for an Ajax request
  1979. *
  1980. * Sample usage:
  1981. *
  1982. * 1) var $msg = Functions.ajaxShowMessage();
  1983. * This will show a message that reads "Loading...". Such a message will not
  1984. * disappear automatically and cannot be dismissed by the user. To remove this
  1985. * message either the Functions.ajaxRemoveMessage($msg) function must be called or
  1986. * another message must be show with Functions.ajaxShowMessage() function.
  1987. *
  1988. * 2) var $msg = Functions.ajaxShowMessage(Messages.strProcessingRequest);
  1989. * This is a special case. The behaviour is same as above,
  1990. * just with a different message
  1991. *
  1992. * 3) var $msg = Functions.ajaxShowMessage('The operation was successful');
  1993. * This will show a message that will disappear automatically and it can also
  1994. * be dismissed by the user.
  1995. *
  1996. * 4) var $msg = Functions.ajaxShowMessage('Some error', false);
  1997. * This will show a message that will not disappear automatically, but it
  1998. * can be dismissed by the user after they have finished reading it.
  1999. *
  2000. * @param string message string containing the message to be shown.
  2001. * optional, defaults to 'Loading...'
  2002. * @param mixed timeout number of milliseconds for the message to be visible
  2003. * optional, defaults to 5000. If set to 'false', the
  2004. * notification will never disappear
  2005. * @param string type string to dictate the type of message shown.
  2006. * optional, defaults to normal notification.
  2007. * If set to 'error', the notification will show message
  2008. * with red background.
  2009. * If set to 'success', the notification will show with
  2010. * a green background.
  2011. * @return jQuery object jQuery Element that holds the message div
  2012. * this object can be passed to Functions.ajaxRemoveMessage()
  2013. * to remove the notification
  2014. */
  2015. Functions.ajaxShowMessage = function (message, timeout, type) {
  2016. var msg = message;
  2017. var newTimeOut = timeout;
  2018. /**
  2019. * @var self_closing Whether the notification will automatically disappear
  2020. */
  2021. var selfClosing = true;
  2022. /**
  2023. * @var dismissable Whether the user will be able to remove
  2024. * the notification by clicking on it
  2025. */
  2026. var dismissable = true; // Handle the case when a empty data.message is passed.
  2027. // We don't want the empty message
  2028. if (msg === '') {
  2029. return true;
  2030. } else if (!msg) {
  2031. // If the message is undefined, show the default
  2032. msg = Messages.strLoading;
  2033. dismissable = false;
  2034. selfClosing = false;
  2035. } else if (msg === Messages.strProcessingRequest) {
  2036. // This is another case where the message should not disappear
  2037. dismissable = false;
  2038. selfClosing = false;
  2039. } // Figure out whether (or after how long) to remove the notification
  2040. if (newTimeOut === undefined) {
  2041. newTimeOut = 5000;
  2042. } else if (newTimeOut === false) {
  2043. selfClosing = false;
  2044. } // Determine type of message, add styling as required
  2045. if (type === 'error') {
  2046. msg = '<div class="alert alert-danger" role="alert">' + msg + '</div>';
  2047. } else if (type === 'success') {
  2048. msg = '<div class="alert alert-success" role="alert">' + msg + '</div>';
  2049. } // Create a parent element for the AJAX messages, if necessary
  2050. if ($('#loading_parent').length === 0) {
  2051. $('<div id="loading_parent"></div>').prependTo('#page_content');
  2052. } // Update message count to create distinct message elements every time
  2053. ajaxMessageCount++; // Remove all old messages, if any
  2054. $('span.ajax_notification[id^=ajax_message_num]').remove();
  2055. /**
  2056. * @var $retval a jQuery object containing the reference
  2057. * to the created AJAX message
  2058. */
  2059. var $retval = $('<span class="ajax_notification" id="ajax_message_num_' + ajaxMessageCount + '"></span>').hide().appendTo('#loading_parent').html(msg).show(); // If the notification is self-closing we should create a callback to remove it
  2060. if (selfClosing) {
  2061. $retval.delay(newTimeOut).fadeOut('medium', function () {
  2062. if ($(this).is(':data(tooltip)')) {
  2063. $(this).tooltip('destroy');
  2064. } // Remove the notification
  2065. $(this).remove();
  2066. });
  2067. } // If the notification is dismissable we need to add the relevant class to it
  2068. // and add a tooltip so that the users know that it can be removed
  2069. if (dismissable) {
  2070. $retval.addClass('dismissable').css('cursor', 'pointer');
  2071. /**
  2072. * Add a tooltip to the notification to let the user know that they
  2073. * can dismiss the ajax notification by clicking on it.
  2074. */
  2075. Functions.tooltip($retval, 'span', Messages.strDismiss);
  2076. } // Hide spinner if this is not a loading message
  2077. if (msg !== Messages.strLoading) {
  2078. $retval.css('background-image', 'none');
  2079. }
  2080. Functions.highlightSql($retval);
  2081. return $retval;
  2082. };
  2083. /**
  2084. * Removes the message shown for an Ajax operation when it's completed
  2085. *
  2086. * @param jQuery object jQuery Element that holds the notification
  2087. *
  2088. * @return nothing
  2089. */
  2090. Functions.ajaxRemoveMessage = function ($thisMessageBox) {
  2091. if ($thisMessageBox !== undefined && $thisMessageBox instanceof jQuery) {
  2092. $thisMessageBox.stop(true, true).fadeOut('medium');
  2093. if ($thisMessageBox.is(':data(tooltip)')) {
  2094. $thisMessageBox.tooltip('destroy');
  2095. } else {
  2096. $thisMessageBox.remove();
  2097. }
  2098. }
  2099. };
  2100. /**
  2101. * Requests SQL for previewing before executing.
  2102. *
  2103. * @param jQuery Object $form Form containing query data
  2104. *
  2105. * @return void
  2106. */
  2107. Functions.previewSql = function ($form) {
  2108. var formUrl = $form.attr('action');
  2109. var sep = CommonParams.get('arg_separator');
  2110. var formData = $form.serialize() + sep + 'do_save_data=1' + sep + 'preview_sql=1' + sep + 'ajax_request=1';
  2111. var $messageBox = Functions.ajaxShowMessage();
  2112. $.ajax({
  2113. type: 'POST',
  2114. url: formUrl,
  2115. data: formData,
  2116. success: function success(response) {
  2117. Functions.ajaxRemoveMessage($messageBox);
  2118. if (response.success) {
  2119. var $dialogContent = $('<div></div>').append(response.sql_data);
  2120. var buttonOptions = {};
  2121. buttonOptions[Messages.strClose] = function () {
  2122. $(this).dialog('close');
  2123. };
  2124. $dialogContent.dialog({
  2125. minWidth: 550,
  2126. maxHeight: 400,
  2127. modal: true,
  2128. buttons: buttonOptions,
  2129. title: Messages.strPreviewSQL,
  2130. close: function close() {
  2131. $(this).remove();
  2132. },
  2133. open: function open() {
  2134. // Pretty SQL printing.
  2135. Functions.highlightSql($(this));
  2136. }
  2137. });
  2138. } else {
  2139. Functions.ajaxShowMessage(response.message);
  2140. }
  2141. },
  2142. error: function error() {
  2143. Functions.ajaxShowMessage(Messages.strErrorProcessingRequest);
  2144. }
  2145. });
  2146. };
  2147. /**
  2148. * Callback called when submit/"OK" is clicked on sql preview/confirm modal
  2149. *
  2150. * @callback onSubmitCallback
  2151. * @param {string} url The url
  2152. */
  2153. /**
  2154. *
  2155. * @param {string} sqlData Sql query to preview
  2156. * @param {string} url Url to be sent to callback
  2157. * @param {onSubmitCallback} callback On submit callback function
  2158. *
  2159. * @return void
  2160. */
  2161. Functions.confirmPreviewSql = function (sqlData, url, callback) {
  2162. var $dialogContent = $('<div class="preview_sql"><code class="sql"><pre>' + sqlData + '</pre></code></div>');
  2163. var buttonOptions = [{
  2164. text: Messages.strOK,
  2165. class: 'submitOK',
  2166. click: function click() {
  2167. callback(url);
  2168. }
  2169. }, {
  2170. text: Messages.strCancel,
  2171. class: 'submitCancel',
  2172. click: function click() {
  2173. $(this).dialog('close');
  2174. }
  2175. }];
  2176. $dialogContent.dialog({
  2177. minWidth: 550,
  2178. maxHeight: 400,
  2179. modal: true,
  2180. buttons: buttonOptions,
  2181. title: Messages.strPreviewSQL,
  2182. close: function close() {
  2183. $(this).remove();
  2184. },
  2185. open: function open() {
  2186. // Pretty SQL printing.
  2187. Functions.highlightSql($(this));
  2188. }
  2189. });
  2190. };
  2191. /**
  2192. * check for reserved keyword column name
  2193. *
  2194. * @param jQuery Object $form Form
  2195. *
  2196. * @returns true|false
  2197. */
  2198. Functions.checkReservedWordColumns = function ($form) {
  2199. var isConfirmed = true;
  2200. $.ajax({
  2201. type: 'POST',
  2202. url: 'index.php?route=/table/structure/reserved-word-check',
  2203. data: $form.serialize(),
  2204. success: function success(data) {
  2205. if (typeof data.success !== 'undefined' && data.success === true) {
  2206. isConfirmed = confirm(data.message);
  2207. }
  2208. },
  2209. async: false
  2210. });
  2211. return isConfirmed;
  2212. }; // This event only need to be fired once after the initial page load
  2213. $(function () {
  2214. /**
  2215. * Allows the user to dismiss a notification
  2216. * created with Functions.ajaxShowMessage()
  2217. */
  2218. var holdStarter = null;
  2219. $(document).on('mousedown', 'span.ajax_notification.dismissable', function () {
  2220. holdStarter = setTimeout(function () {
  2221. holdStarter = null;
  2222. }, 250);
  2223. });
  2224. $(document).on('mouseup', 'span.ajax_notification.dismissable', function (event) {
  2225. if (holdStarter && event.which === 1) {
  2226. clearTimeout(holdStarter);
  2227. Functions.ajaxRemoveMessage($(this));
  2228. }
  2229. });
  2230. /**
  2231. * The below two functions hide the "Dismiss notification" tooltip when a user
  2232. * is hovering a link or button that is inside an ajax message
  2233. */
  2234. $(document).on('mouseover', 'span.ajax_notification a, span.ajax_notification button, span.ajax_notification input', function () {
  2235. if ($(this).parents('span.ajax_notification').is(':data(tooltip)')) {
  2236. $(this).parents('span.ajax_notification').tooltip('disable');
  2237. }
  2238. });
  2239. $(document).on('mouseout', 'span.ajax_notification a, span.ajax_notification button, span.ajax_notification input', function () {
  2240. if ($(this).parents('span.ajax_notification').is(':data(tooltip)')) {
  2241. $(this).parents('span.ajax_notification').tooltip('enable');
  2242. }
  2243. });
  2244. /**
  2245. * Copy text to clipboard
  2246. *
  2247. * @param text to copy to clipboard
  2248. *
  2249. * @returns bool true|false
  2250. */
  2251. function copyToClipboard(text) {
  2252. var $temp = $('<input>');
  2253. $temp.css({
  2254. 'position': 'fixed',
  2255. 'width': '2em',
  2256. 'border': 0,
  2257. 'top': 0,
  2258. 'left': 0,
  2259. 'padding': 0,
  2260. 'background': 'transparent'
  2261. });
  2262. $('body').append($temp);
  2263. $temp.val(text).trigger('select');
  2264. try {
  2265. var res = document.execCommand('copy');
  2266. $temp.remove();
  2267. return res;
  2268. } catch (e) {
  2269. $temp.remove();
  2270. return false;
  2271. }
  2272. }
  2273. $(document).on('click', 'a.copyQueryBtn', function (event) {
  2274. event.preventDefault();
  2275. var res = copyToClipboard($(this).attr('data-text'));
  2276. if (res) {
  2277. $(this).after('<span id=\'copyStatus\'> (' + Messages.strCopyQueryButtonSuccess + ')</span>');
  2278. } else {
  2279. $(this).after('<span id=\'copyStatus\'> (' + Messages.strCopyQueryButtonFailure + ')</span>');
  2280. }
  2281. setTimeout(function () {
  2282. $('#copyStatus').remove();
  2283. }, 2000);
  2284. });
  2285. });
  2286. /**
  2287. * Hides/shows the "Open in ENUM/SET editor" message, depending on the data type of the column currently selected
  2288. */
  2289. Functions.showNoticeForEnum = function (selectElement) {
  2290. var enumNoticeId = selectElement.attr('id').split('_')[1];
  2291. enumNoticeId += '_' + (parseInt(selectElement.attr('id').split('_')[2], 10) + 1);
  2292. var selectedType = selectElement.val();
  2293. if (selectedType === 'ENUM' || selectedType === 'SET') {
  2294. $('p#enum_notice_' + enumNoticeId).show();
  2295. } else {
  2296. $('p#enum_notice_' + enumNoticeId).hide();
  2297. }
  2298. };
  2299. /**
  2300. * Creates a Profiling Chart. Used in sql.js
  2301. * and in server/status/monitor.js
  2302. */
  2303. Functions.createProfilingChart = function (target, data) {
  2304. // create the chart
  2305. var factory = new JQPlotChartFactory();
  2306. var chart = factory.createChart(ChartType.PIE, target); // create the data table and add columns
  2307. var dataTable = new DataTable();
  2308. dataTable.addColumn(ColumnType.STRING, '');
  2309. dataTable.addColumn(ColumnType.NUMBER, '');
  2310. dataTable.setData(data);
  2311. var windowWidth = $(window).width();
  2312. var location = 's';
  2313. if (windowWidth > 768) {
  2314. location = 'se';
  2315. } // draw the chart and return the chart object
  2316. chart.draw(dataTable, {
  2317. seriesDefaults: {
  2318. rendererOptions: {
  2319. showDataLabels: true
  2320. }
  2321. },
  2322. highlighter: {
  2323. tooltipLocation: 'se',
  2324. sizeAdjust: 0,
  2325. tooltipAxes: 'pieref',
  2326. formatString: '%s, %.9Ps'
  2327. },
  2328. legend: {
  2329. show: true,
  2330. location: location,
  2331. rendererOptions: {
  2332. numberColumns: 2
  2333. }
  2334. },
  2335. // from https://web.archive.org/web/20190321233412/http://tango.freedesktop.org/Tango_Icon_Theme_Guidelines
  2336. seriesColors: ['#fce94f', '#fcaf3e', '#e9b96e', '#8ae234', '#729fcf', '#ad7fa8', '#ef2929', '#888a85', '#c4a000', '#ce5c00', '#8f5902', '#4e9a06', '#204a87', '#5c3566', '#a40000', '#babdb6', '#2e3436']
  2337. });
  2338. return chart;
  2339. };
  2340. /**
  2341. * Formats a profiling duration nicely (in us and ms time).
  2342. * Used in server/status/monitor.js
  2343. *
  2344. * @param integer Number to be formatted, should be in the range of microsecond to second
  2345. * @param integer Accuracy, how many numbers right to the comma should be
  2346. * @return string The formatted number
  2347. */
  2348. Functions.prettyProfilingNum = function (number, accuracy) {
  2349. var num = number;
  2350. var acc = accuracy;
  2351. if (!acc) {
  2352. acc = 2;
  2353. }
  2354. acc = Math.pow(10, acc);
  2355. if (num * 1000 < 0.1) {
  2356. num = Math.round(acc * (num * 1000 * 1000)) / acc + 'µ';
  2357. } else if (num < 0.1) {
  2358. num = Math.round(acc * (num * 1000)) / acc + 'm';
  2359. } else {
  2360. num = Math.round(acc * num) / acc;
  2361. }
  2362. return num + 's';
  2363. };
  2364. /**
  2365. * Formats a SQL Query nicely with newlines and indentation. Depends on Codemirror and MySQL Mode!
  2366. *
  2367. * @param string Query to be formatted
  2368. * @return string The formatted query
  2369. */
  2370. Functions.sqlPrettyPrint = function (string) {
  2371. if (typeof CodeMirror === 'undefined') {
  2372. return string;
  2373. }
  2374. var mode = CodeMirror.getMode({}, 'text/x-mysql');
  2375. var stream = new CodeMirror.StringStream(string);
  2376. var state = mode.startState();
  2377. var token;
  2378. var tokens = [];
  2379. var output = '';
  2380. var tabs = function tabs(cnt) {
  2381. var ret = '';
  2382. for (var i = 0; i < 4 * cnt; i++) {
  2383. ret += ' ';
  2384. }
  2385. return ret;
  2386. }; // "root-level" statements
  2387. var statements = {
  2388. 'select': ['select', 'from', 'on', 'where', 'having', 'limit', 'order by', 'group by'],
  2389. 'update': ['update', 'set', 'where'],
  2390. 'insert into': ['insert into', 'values']
  2391. }; // don't put spaces before these tokens
  2392. var spaceExceptionsBefore = {
  2393. ';': true,
  2394. ',': true,
  2395. '.': true,
  2396. '(': true
  2397. }; // don't put spaces after these tokens
  2398. var spaceExceptionsAfter = {
  2399. '.': true
  2400. }; // Populate tokens array
  2401. while (!stream.eol()) {
  2402. stream.start = stream.pos;
  2403. token = mode.token(stream, state);
  2404. if (token !== null) {
  2405. tokens.push([token, stream.current().toLowerCase()]);
  2406. }
  2407. }
  2408. var currentStatement = tokens[0][1];
  2409. if (!statements[currentStatement]) {
  2410. return string;
  2411. } // Holds all currently opened code blocks (statement, function or generic)
  2412. var blockStack = []; // If a new code block is found, newBlock contains its type for one iteration and vice versa for endBlock
  2413. var newBlock;
  2414. var endBlock; // How much to indent in the current line
  2415. var indentLevel = 0; // Holds the "root-level" statements
  2416. var statementPart;
  2417. var lastStatementPart = statements[currentStatement][0];
  2418. blockStack.unshift('statement'); // Iterate through every token and format accordingly
  2419. for (var i = 0; i < tokens.length; i++) {
  2420. // New block => push to stack
  2421. if (tokens[i][1] === '(') {
  2422. if (i < tokens.length - 1 && tokens[i + 1][0] === 'statement-verb') {
  2423. blockStack.unshift(newBlock = 'statement');
  2424. } else if (i > 0 && tokens[i - 1][0] === 'builtin') {
  2425. blockStack.unshift(newBlock = 'function');
  2426. } else {
  2427. blockStack.unshift(newBlock = 'generic');
  2428. }
  2429. } else {
  2430. newBlock = null;
  2431. } // Block end => pop from stack
  2432. if (tokens[i][1] === ')') {
  2433. endBlock = blockStack[0];
  2434. blockStack.shift();
  2435. } else {
  2436. endBlock = null;
  2437. } // A subquery is starting
  2438. if (i > 0 && newBlock === 'statement') {
  2439. indentLevel++;
  2440. output += '\n' + tabs(indentLevel) + tokens[i][1] + ' ' + tokens[i + 1][1].toUpperCase() + '\n' + tabs(indentLevel + 1);
  2441. currentStatement = tokens[i + 1][1];
  2442. i++;
  2443. continue;
  2444. } // A subquery is ending
  2445. if (endBlock === 'statement' && indentLevel > 0) {
  2446. output += '\n' + tabs(indentLevel);
  2447. indentLevel--;
  2448. } // One less indentation for statement parts (from, where, order by, etc.) and a newline
  2449. statementPart = statements[currentStatement].indexOf(tokens[i][1]);
  2450. if (statementPart !== -1) {
  2451. if (i > 0) {
  2452. output += '\n';
  2453. }
  2454. output += tabs(indentLevel) + tokens[i][1].toUpperCase();
  2455. output += '\n' + tabs(indentLevel + 1);
  2456. lastStatementPart = tokens[i][1]; // Normal indentation and spaces for everything else
  2457. } else {
  2458. if (!spaceExceptionsBefore[tokens[i][1]] && !(i > 0 && spaceExceptionsAfter[tokens[i - 1][1]]) && output.charAt(output.length - 1) !== ' ') {
  2459. output += ' ';
  2460. }
  2461. if (tokens[i][0] === 'keyword') {
  2462. output += tokens[i][1].toUpperCase();
  2463. } else {
  2464. output += tokens[i][1];
  2465. }
  2466. } // split columns in select and 'update set' clauses, but only inside statements blocks
  2467. if ((lastStatementPart === 'select' || lastStatementPart === 'where' || lastStatementPart === 'set') && tokens[i][1] === ',' && blockStack[0] === 'statement') {
  2468. output += '\n' + tabs(indentLevel + 1);
  2469. } // split conditions in where clauses, but only inside statements blocks
  2470. if (lastStatementPart === 'where' && (tokens[i][1] === 'and' || tokens[i][1] === 'or' || tokens[i][1] === 'xor')) {
  2471. if (blockStack[0] === 'statement') {
  2472. output += '\n' + tabs(indentLevel + 1);
  2473. } // Todo: Also split and or blocks in newlines & indentation++
  2474. // if (blockStack[0] === 'generic')
  2475. // output += ...
  2476. }
  2477. }
  2478. return output;
  2479. };
  2480. /**
  2481. * jQuery function that uses jQueryUI's dialogs to confirm with user. Does not
  2482. * return a jQuery object yet and hence cannot be chained
  2483. *
  2484. * @param string question
  2485. * @param string url URL to be passed to the callbackFn to make
  2486. * an Ajax call to
  2487. * @param function callbackFn callback to execute after user clicks on OK
  2488. * @param function openCallback optional callback to run when dialog is shown
  2489. */
  2490. Functions.confirm = function (question, url, callbackFn, openCallback) {
  2491. var confirmState = CommonParams.get('confirm');
  2492. if (!confirmState) {
  2493. // user does not want to confirm
  2494. if (typeof callbackFn === 'function') {
  2495. callbackFn.call(this, url);
  2496. return true;
  2497. }
  2498. }
  2499. if (Messages.strDoYouReally === '') {
  2500. return true;
  2501. }
  2502. /**
  2503. * @var button_options Object that stores the options passed to jQueryUI
  2504. * dialog
  2505. */
  2506. var buttonOptions = [{
  2507. text: Messages.strOK,
  2508. 'class': 'submitOK',
  2509. click: function click() {
  2510. $(this).dialog('close');
  2511. if (typeof callbackFn === 'function') {
  2512. callbackFn.call(this, url);
  2513. }
  2514. }
  2515. }, {
  2516. text: Messages.strCancel,
  2517. 'class': 'submitCancel',
  2518. click: function click() {
  2519. $(this).dialog('close');
  2520. }
  2521. }];
  2522. $('<div></div>', {
  2523. 'id': 'confirm_dialog',
  2524. 'title': Messages.strConfirm
  2525. }).prepend(question).dialog({
  2526. buttons: buttonOptions,
  2527. close: function close() {
  2528. $(this).remove();
  2529. },
  2530. open: openCallback,
  2531. modal: true
  2532. });
  2533. };
  2534. jQuery.fn.confirm = Functions.confirm;
  2535. /**
  2536. * jQuery function to sort a table's body after a new row has been appended to it.
  2537. *
  2538. * @param string text_selector string to select the sortKey's text
  2539. *
  2540. * @return jQuery Object for chaining purposes
  2541. */
  2542. Functions.sortTable = function (textSelector) {
  2543. return this.each(function () {
  2544. /**
  2545. * @var table_body Object referring to the table's <tbody> element
  2546. */
  2547. var tableBody = $(this);
  2548. /**
  2549. * @var rows Object referring to the collection of rows in {@link tableBody}
  2550. */
  2551. var rows = $(this).find('tr').get(); // get the text of the field that we will sort by
  2552. $.each(rows, function (index, row) {
  2553. row.sortKey = $(row).find(textSelector).text().toLowerCase().trim();
  2554. }); // get the sorted order
  2555. rows.sort(function (a, b) {
  2556. if (a.sortKey < b.sortKey) {
  2557. return -1;
  2558. }
  2559. if (a.sortKey > b.sortKey) {
  2560. return 1;
  2561. }
  2562. return 0;
  2563. }); // pull out each row from the table and then append it according to it's order
  2564. $.each(rows, function (index, row) {
  2565. $(tableBody).append(row);
  2566. row.sortKey = null;
  2567. });
  2568. });
  2569. };
  2570. jQuery.fn.sortTable = Functions.sortTable;
  2571. /**
  2572. * Unbind all event handlers before tearing down a page
  2573. */
  2574. AJAX.registerTeardown('functions.js', function () {
  2575. $(document).off('submit', '#create_table_form_minimal.ajax');
  2576. $(document).off('submit', 'form.create_table_form.ajax');
  2577. $(document).off('click', 'form.create_table_form.ajax input[name=submit_num_fields]');
  2578. $(document).off('keyup', 'form.create_table_form.ajax input');
  2579. $(document).off('change', 'input[name=partition_count],input[name=subpartition_count],select[name=partition_by]');
  2580. });
  2581. /**
  2582. * jQuery coding for 'Create Table'. Used on /database/operations,
  2583. * /database/structure and /database/tracking (i.e., wherever
  2584. * PhpMyAdmin\Display\CreateTable is used)
  2585. *
  2586. * Attach Ajax Event handlers for Create Table
  2587. */
  2588. AJAX.registerOnload('functions.js', function () {
  2589. /**
  2590. * Attach event handler for submission of create table form (save)
  2591. */
  2592. $(document).on('submit', 'form.create_table_form.ajax', function (event) {
  2593. event.preventDefault();
  2594. /**
  2595. * @var the_form object referring to the create table form
  2596. */
  2597. var $form = $(this);
  2598. /*
  2599. * First validate the form; if there is a problem, avoid submitting it
  2600. *
  2601. * Functions.checkTableEditForm() needs a pure element and not a jQuery object,
  2602. * this is why we pass $form[0] as a parameter (the jQuery object
  2603. * is actually an array of DOM elements)
  2604. */
  2605. if (Functions.checkTableEditForm($form[0], $form.find('input[name=orig_num_fields]').val())) {
  2606. Functions.prepareForAjaxRequest($form);
  2607. if (Functions.checkReservedWordColumns($form)) {
  2608. Functions.ajaxShowMessage(Messages.strProcessingRequest); // User wants to submit the form
  2609. $.post($form.attr('action'), $form.serialize() + CommonParams.get('arg_separator') + 'do_save_data=1', function (data) {
  2610. if (typeof data !== 'undefined' && data.success === true) {
  2611. $('#properties_message').removeClass('alert-danger').html('');
  2612. Functions.ajaxShowMessage(data.message); // Only if the create table dialog (distinct panel) exists
  2613. var $createTableDialog = $('#create_table_dialog');
  2614. if ($createTableDialog.length > 0) {
  2615. $createTableDialog.dialog('close').remove();
  2616. }
  2617. $('#tableslistcontainer').before(data.formatted_sql);
  2618. /**
  2619. * @var tables_table Object referring to the <tbody> element that holds the list of tables
  2620. */
  2621. var tablesTable = $('#tablesForm').find('tbody').not('#tbl_summary_row'); // this is the first table created in this db
  2622. if (tablesTable.length === 0) {
  2623. CommonActions.refreshMain(CommonParams.get('opendb_url'));
  2624. } else {
  2625. /**
  2626. * @var curr_last_row Object referring to the last <tr> element in {@link tablesTable}
  2627. */
  2628. var currLastRow = $(tablesTable).find('tr').last();
  2629. /**
  2630. * @var curr_last_row_index_string String containing the index of {@link currLastRow}
  2631. */
  2632. var currLastRowIndexString = $(currLastRow).find('input:checkbox').attr('id').match(/\d+/)[0];
  2633. /**
  2634. * @var curr_last_row_index Index of {@link currLastRow}
  2635. */
  2636. var currLastRowIndex = parseFloat(currLastRowIndexString);
  2637. /**
  2638. * @var new_last_row_index Index of the new row to be appended to {@link tablesTable}
  2639. */
  2640. var newLastRowIndex = currLastRowIndex + 1;
  2641. /**
  2642. * @var new_last_row_id String containing the id of the row to be appended to {@link tablesTable}
  2643. */
  2644. var newLastRowId = 'checkbox_tbl_' + newLastRowIndex;
  2645. data.newTableString = data.newTableString.replace(/checkbox_tbl_/, newLastRowId); // append to table
  2646. $(data.newTableString).appendTo(tablesTable); // Sort the table
  2647. $(tablesTable).sortTable('th'); // Adjust summary row
  2648. DatabaseStructure.adjustTotals();
  2649. } // Refresh navigation as a new table has been added
  2650. Navigation.reload(); // Redirect to table structure page on creation of new table
  2651. var argsep = CommonParams.get('arg_separator');
  2652. var params12 = 'ajax_request=true' + argsep + 'ajax_page_request=true';
  2653. if (!(history && history.pushState)) {
  2654. params12 += MicroHistory.menus.getRequestParam();
  2655. }
  2656. var tableStructureUrl = 'index.php?route=/table/structure' + argsep + 'server=' + data.params.server + argsep + 'db=' + data.params.db + argsep + 'token=' + data.params.token + argsep + 'goto=' + encodeURIComponent('index.php?route=/database/structure') + argsep + 'table=' + data.params.table + '';
  2657. $.get(tableStructureUrl, params12, AJAX.responseHandler);
  2658. } else {
  2659. Functions.ajaxShowMessage('<div class="alert alert-danger" role="alert">' + data.error + '</div>', false);
  2660. }
  2661. }); // end $.post()
  2662. }
  2663. }
  2664. }); // end create table form (save)
  2665. /**
  2666. * Submits the intermediate changes in the table creation form
  2667. * to refresh the UI accordingly
  2668. */
  2669. function submitChangesInCreateTableForm(actionParam) {
  2670. /**
  2671. * @var the_form object referring to the create table form
  2672. */
  2673. var $form = $('form.create_table_form.ajax');
  2674. var $msgbox = Functions.ajaxShowMessage(Messages.strProcessingRequest);
  2675. Functions.prepareForAjaxRequest($form); // User wants to add more fields to the table
  2676. $.post($form.attr('action'), $form.serialize() + '&' + actionParam, function (data) {
  2677. if (typeof data !== 'undefined' && data.success) {
  2678. var $pageContent = $('#page_content');
  2679. $pageContent.html(data.message);
  2680. Functions.highlightSql($pageContent);
  2681. Functions.verifyColumnsProperties();
  2682. Functions.hideShowConnection($('.create_table_form select[name=tbl_storage_engine]'));
  2683. Functions.ajaxRemoveMessage($msgbox);
  2684. } else {
  2685. Functions.ajaxShowMessage(data.error);
  2686. }
  2687. }); // end $.post()
  2688. }
  2689. /**
  2690. * Attach event handler for create table form (add fields)
  2691. */
  2692. $(document).on('click', 'form.create_table_form.ajax input[name=submit_num_fields]', function (event) {
  2693. event.preventDefault();
  2694. submitChangesInCreateTableForm('submit_num_fields=1');
  2695. }); // end create table form (add fields)
  2696. $(document).on('keydown', 'form.create_table_form.ajax input[name=added_fields]', function (event) {
  2697. if (event.keyCode === 13) {
  2698. event.preventDefault();
  2699. event.stopImmediatePropagation();
  2700. $(this).closest('form').find('input[name=submit_num_fields]').trigger('click');
  2701. }
  2702. });
  2703. /**
  2704. * Attach event handler to manage changes in number of partitions and subpartitions
  2705. */
  2706. $(document).on('change', 'input[name=partition_count],input[name=subpartition_count],select[name=partition_by]', function () {
  2707. var $this = $(this);
  2708. var $form = $this.parents('form');
  2709. if ($form.is('.create_table_form.ajax')) {
  2710. submitChangesInCreateTableForm('submit_partition_change=1');
  2711. } else {
  2712. $form.trigger('submit');
  2713. }
  2714. });
  2715. $(document).on('change', 'input[value=AUTO_INCREMENT]', function () {
  2716. if (this.checked) {
  2717. var col = /\d/.exec($(this).attr('name'));
  2718. col = col[0];
  2719. var $selectFieldKey = $('select[name="field_key[' + col + ']"]');
  2720. if ($selectFieldKey.val() === 'none_' + col) {
  2721. $selectFieldKey.val('primary_' + col).trigger('change', [false]);
  2722. }
  2723. }
  2724. });
  2725. $('body').off('click', 'input.preview_sql').on('click', 'input.preview_sql', function () {
  2726. var $form = $(this).closest('form');
  2727. Functions.previewSql($form);
  2728. });
  2729. });
  2730. /**
  2731. * Validates the password field in a form
  2732. *
  2733. * @see Messages.strPasswordEmpty
  2734. * @see Messages.strPasswordNotSame
  2735. * @param {object} $theForm The form to be validated
  2736. * @return bool
  2737. */
  2738. Functions.checkPassword = function ($theForm) {
  2739. // Did the user select 'no password'?
  2740. if ($theForm.find('#nopass_1').is(':checked')) {
  2741. return true;
  2742. } else {
  2743. var $pred = $theForm.find('#select_pred_password');
  2744. if ($pred.length && ($pred.val() === 'none' || $pred.val() === 'keep')) {
  2745. return true;
  2746. }
  2747. }
  2748. var $password = $theForm.find('input[name=pma_pw]');
  2749. var $passwordRepeat = $theForm.find('input[name=pma_pw2]');
  2750. var alertMessage = false;
  2751. if ($password.val() === '') {
  2752. alertMessage = Messages.strPasswordEmpty;
  2753. } else if ($password.val() !== $passwordRepeat.val()) {
  2754. alertMessage = Messages.strPasswordNotSame;
  2755. }
  2756. if (alertMessage) {
  2757. alert(alertMessage);
  2758. $password.val('');
  2759. $passwordRepeat.val('');
  2760. $password.trigger('focus');
  2761. return false;
  2762. }
  2763. return true;
  2764. };
  2765. /**
  2766. * Attach Ajax event handlers for 'Change Password' on index.php
  2767. */
  2768. AJAX.registerOnload('functions.js', function () {
  2769. /* Handler for hostname type */
  2770. $(document).on('change', '#select_pred_hostname', function () {
  2771. var hostname = $('#pma_hostname');
  2772. if (this.value === 'any') {
  2773. hostname.val('%');
  2774. } else if (this.value === 'localhost') {
  2775. hostname.val('localhost');
  2776. } else if (this.value === 'thishost' && $(this).data('thishost')) {
  2777. hostname.val($(this).data('thishost'));
  2778. } else if (this.value === 'hosttable') {
  2779. hostname.val('').prop('required', false);
  2780. } else if (this.value === 'userdefined') {
  2781. hostname.trigger('focus').select().prop('required', true);
  2782. }
  2783. });
  2784. /* Handler for editing hostname */
  2785. $(document).on('change', '#pma_hostname', function () {
  2786. $('#select_pred_hostname').val('userdefined');
  2787. $('#pma_hostname').prop('required', true);
  2788. });
  2789. /* Handler for username type */
  2790. $(document).on('change', '#select_pred_username', function () {
  2791. if (this.value === 'any') {
  2792. $('#pma_username').val('').prop('required', false);
  2793. $('#user_exists_warning').css('display', 'none');
  2794. } else if (this.value === 'userdefined') {
  2795. $('#pma_username').trigger('focus').trigger('select').prop('required', true);
  2796. }
  2797. });
  2798. /* Handler for editing username */
  2799. $(document).on('change', '#pma_username', function () {
  2800. $('#select_pred_username').val('userdefined');
  2801. $('#pma_username').prop('required', true);
  2802. });
  2803. /* Handler for password type */
  2804. $(document).on('change', '#select_pred_password', function () {
  2805. if (this.value === 'none') {
  2806. $('#text_pma_pw2').prop('required', false).val('');
  2807. $('#text_pma_pw').prop('required', false).val('');
  2808. } else if (this.value === 'userdefined') {
  2809. $('#text_pma_pw2').prop('required', true);
  2810. $('#text_pma_pw').prop('required', true).trigger('focus').trigger('select');
  2811. } else {
  2812. $('#text_pma_pw2').prop('required', false);
  2813. $('#text_pma_pw').prop('required', false);
  2814. }
  2815. });
  2816. /* Handler for editing password */
  2817. $(document).on('change', '#text_pma_pw,#text_pma_pw2', function () {
  2818. $('#select_pred_password').val('userdefined');
  2819. $('#text_pma_pw2').prop('required', true);
  2820. $('#text_pma_pw').prop('required', true);
  2821. });
  2822. /**
  2823. * Unbind all event handlers before tearing down a page
  2824. */
  2825. $(document).off('click', '#change_password_anchor.ajax');
  2826. /**
  2827. * Attach Ajax event handler on the change password anchor
  2828. */
  2829. $(document).on('click', '#change_password_anchor.ajax', function (event) {
  2830. event.preventDefault();
  2831. var $msgbox = Functions.ajaxShowMessage();
  2832. /**
  2833. * @var button_options Object containing options to be passed to jQueryUI's dialog
  2834. */
  2835. var buttonOptions = {};
  2836. buttonOptions[Messages.strGo] = function () {
  2837. event.preventDefault();
  2838. /**
  2839. * @var $the_form Object referring to the change password form
  2840. */
  2841. var $theForm = $('#change_password_form');
  2842. if (!Functions.checkPassword($theForm)) {
  2843. return false;
  2844. }
  2845. /**
  2846. * @var this_value String containing the value of the submit button.
  2847. * Need to append this for the change password form on Server Privileges
  2848. * page to work
  2849. */
  2850. var thisValue = $(this).val();
  2851. var $msgbox = Functions.ajaxShowMessage(Messages.strProcessingRequest);
  2852. $theForm.append('<input type="hidden" name="ajax_request" value="true">');
  2853. $.post($theForm.attr('action'), $theForm.serialize() + CommonParams.get('arg_separator') + 'change_pw=' + thisValue, function (data) {
  2854. if (typeof data === 'undefined' || data.success !== true) {
  2855. Functions.ajaxShowMessage(data.error, false);
  2856. return;
  2857. }
  2858. var $pageContent = $('#page_content');
  2859. $pageContent.prepend(data.message);
  2860. Functions.highlightSql($pageContent);
  2861. $('#change_password_dialog').hide().remove();
  2862. $('#edit_user_dialog').dialog('close').remove();
  2863. Functions.ajaxRemoveMessage($msgbox);
  2864. }); // end $.post()
  2865. };
  2866. buttonOptions[Messages.strCancel] = function () {
  2867. $(this).dialog('close');
  2868. };
  2869. $.get($(this).attr('href'), {
  2870. 'ajax_request': true
  2871. }, function (data) {
  2872. if (typeof data === 'undefined' || !data.success) {
  2873. Functions.ajaxShowMessage(data.error, false);
  2874. return;
  2875. }
  2876. if (data.scripts) {
  2877. AJAX.scriptHandler.load(data.scripts);
  2878. }
  2879. $('<div id="change_password_dialog"></div>').dialog({
  2880. title: Messages.strChangePassword,
  2881. width: 600,
  2882. close: function close() {
  2883. $(this).remove();
  2884. },
  2885. buttons: buttonOptions,
  2886. modal: true
  2887. }).append(data.message); // for this dialog, we remove the fieldset wrapping due to double headings
  2888. $('fieldset#fieldset_change_password').find('legend').remove().end().find('table.noclick').unwrap().addClass('some-margin').find('input#text_pma_pw').trigger('focus');
  2889. $('#fieldset_change_password_footer').hide();
  2890. Functions.ajaxRemoveMessage($msgbox);
  2891. Functions.displayPasswordGenerateButton();
  2892. $('#change_password_form').on('submit', function (e) {
  2893. e.preventDefault();
  2894. $(this).closest('.ui-dialog').find('.ui-dialog-buttonpane .ui-button').first().trigger('click');
  2895. });
  2896. }); // end $.get()
  2897. }); // end handler for change password anchor
  2898. }); // end $() for Change Password
  2899. /**
  2900. * Unbind all event handlers before tearing down a page
  2901. */
  2902. AJAX.registerTeardown('functions.js', function () {
  2903. $(document).off('change', 'select.column_type');
  2904. $(document).off('change', 'select.default_type');
  2905. $(document).off('change', 'select.virtuality');
  2906. $(document).off('change', 'input.allow_null');
  2907. $(document).off('change', '.create_table_form select[name=tbl_storage_engine]');
  2908. });
  2909. /**
  2910. * Toggle the hiding/showing of the "Open in ENUM/SET editor" message when
  2911. * the page loads and when the selected data type changes
  2912. */
  2913. AJAX.registerOnload('functions.js', function () {
  2914. // is called here for normal page loads and also when opening
  2915. // the Create table dialog
  2916. Functions.verifyColumnsProperties(); //
  2917. // needs on() to work also in the Create Table dialog
  2918. $(document).on('change', 'select.column_type', function () {
  2919. Functions.showNoticeForEnum($(this));
  2920. });
  2921. $(document).on('change', 'select.default_type', function () {
  2922. Functions.hideShowDefaultValue($(this));
  2923. });
  2924. $(document).on('change', 'select.virtuality', function () {
  2925. Functions.hideShowExpression($(this));
  2926. });
  2927. $(document).on('change', 'input.allow_null', function () {
  2928. Functions.validateDefaultValue($(this));
  2929. });
  2930. $(document).on('change', '.create_table_form select[name=tbl_storage_engine]', function () {
  2931. Functions.hideShowConnection($(this));
  2932. });
  2933. });
  2934. /**
  2935. * If the chosen storage engine is FEDERATED show connection field. Hide otherwise
  2936. *
  2937. * @param $engineSelector storage engine selector
  2938. */
  2939. Functions.hideShowConnection = function ($engineSelector) {
  2940. var $connection = $('.create_table_form input[name=connection]');
  2941. var $labelTh = $('.create_table_form #storage-engine-connection');
  2942. if ($engineSelector.val() !== 'FEDERATED') {
  2943. $connection.prop('disabled', true).parent('td').hide();
  2944. $labelTh.hide();
  2945. } else {
  2946. $connection.prop('disabled', false).parent('td').show();
  2947. $labelTh.show();
  2948. }
  2949. };
  2950. /**
  2951. * If the column does not allow NULL values, makes sure that default is not NULL
  2952. */
  2953. Functions.validateDefaultValue = function ($nullCheckbox) {
  2954. if (!$nullCheckbox.prop('checked')) {
  2955. var $default = $nullCheckbox.closest('tr').find('.default_type');
  2956. if ($default.val() === 'NULL') {
  2957. $default.val('NONE');
  2958. }
  2959. }
  2960. };
  2961. /**
  2962. * function to populate the input fields on picking a column from central list
  2963. *
  2964. * @param string input_id input id of the name field for the column to be populated
  2965. * @param integer offset of the selected column in central list of columns
  2966. */
  2967. Functions.autoPopulate = function (inputId, offset) {
  2968. var db = CommonParams.get('db');
  2969. var table = CommonParams.get('table');
  2970. var newInputId = inputId.substring(0, inputId.length - 1);
  2971. $('#' + newInputId + '1').val(centralColumnList[db + '_' + table][offset].col_name);
  2972. var colType = centralColumnList[db + '_' + table][offset].col_type.toUpperCase();
  2973. $('#' + newInputId + '2').val(colType);
  2974. var $input3 = $('#' + newInputId + '3');
  2975. $input3.val(centralColumnList[db + '_' + table][offset].col_length);
  2976. if (colType === 'ENUM' || colType === 'SET') {
  2977. $input3.next().show();
  2978. } else {
  2979. $input3.next().hide();
  2980. }
  2981. var colDefault = centralColumnList[db + '_' + table][offset].col_default.toUpperCase();
  2982. var $input4 = $('#' + newInputId + '4');
  2983. if (colDefault !== '' && colDefault !== 'NULL' && colDefault !== 'CURRENT_TIMESTAMP' && colDefault !== 'CURRENT_TIMESTAMP()') {
  2984. $input4.val('USER_DEFINED');
  2985. $input4.next().next().show();
  2986. $input4.next().next().val(centralColumnList[db + '_' + table][offset].col_default);
  2987. } else {
  2988. $input4.val(centralColumnList[db + '_' + table][offset].col_default);
  2989. $input4.next().next().hide();
  2990. }
  2991. $('#' + newInputId + '5').val(centralColumnList[db + '_' + table][offset].col_collation);
  2992. var $input6 = $('#' + newInputId + '6');
  2993. $input6.val(centralColumnList[db + '_' + table][offset].col_attribute);
  2994. if (centralColumnList[db + '_' + table][offset].col_extra === 'on update CURRENT_TIMESTAMP') {
  2995. $input6.val(centralColumnList[db + '_' + table][offset].col_extra);
  2996. }
  2997. if (centralColumnList[db + '_' + table][offset].col_extra.toUpperCase() === 'AUTO_INCREMENT') {
  2998. $('#' + newInputId + '9').prop('checked', true).trigger('change');
  2999. } else {
  3000. $('#' + newInputId + '9').prop('checked', false);
  3001. }
  3002. if (centralColumnList[db + '_' + table][offset].col_isNull !== '0') {
  3003. $('#' + newInputId + '7').prop('checked', true);
  3004. } else {
  3005. $('#' + newInputId + '7').prop('checked', false);
  3006. }
  3007. };
  3008. /**
  3009. * Unbind all event handlers before tearing down a page
  3010. */
  3011. AJAX.registerTeardown('functions.js', function () {
  3012. $(document).off('click', 'a.open_enum_editor');
  3013. $(document).off('click', 'input.add_value');
  3014. $(document).off('click', '#enum_editor td.drop');
  3015. $(document).off('click', 'a.central_columns_dialog');
  3016. });
  3017. /**
  3018. * @var $enumEditorDialog An object that points to the jQuery
  3019. * dialog of the ENUM/SET editor
  3020. */
  3021. var $enumEditorDialog = null;
  3022. /**
  3023. * Opens the ENUM/SET editor and controls its functions
  3024. */
  3025. AJAX.registerOnload('functions.js', function () {
  3026. $(document).on('click', 'a.open_enum_editor', function () {
  3027. // Get the name of the column that is being edited
  3028. var colname = $(this).closest('tr').find('input').first().val();
  3029. var title;
  3030. var i; // And use it to make up a title for the page
  3031. if (colname.length < 1) {
  3032. title = Messages.enum_newColumnVals;
  3033. } else {
  3034. title = Messages.enum_columnVals.replace(/%s/, '"' + Functions.escapeHtml(decodeURIComponent(colname)) + '"');
  3035. } // Get the values as a string
  3036. var inputstring = $(this).closest('td').find('input').val(); // Escape html entities
  3037. inputstring = $('<div></div>').text(inputstring).html(); // Parse the values, escaping quotes and
  3038. // slashes on the fly, into an array
  3039. var values = [];
  3040. var inString = false;
  3041. var curr;
  3042. var next;
  3043. var buffer = '';
  3044. for (i = 0; i < inputstring.length; i++) {
  3045. curr = inputstring.charAt(i);
  3046. next = i === inputstring.length ? '' : inputstring.charAt(i + 1);
  3047. if (!inString && curr === '\'') {
  3048. inString = true;
  3049. } else if (inString && curr === '\\' && next === '\\') {
  3050. buffer += '&#92;';
  3051. i++;
  3052. } else if (inString && next === '\'' && (curr === '\'' || curr === '\\')) {
  3053. buffer += '&#39;';
  3054. i++;
  3055. } else if (inString && curr === '\'') {
  3056. inString = false;
  3057. values.push(buffer);
  3058. buffer = '';
  3059. } else if (inString) {
  3060. buffer += curr;
  3061. }
  3062. }
  3063. if (buffer.length > 0) {
  3064. // The leftovers in the buffer are the last value (if any)
  3065. values.push(buffer);
  3066. }
  3067. var fields = ''; // If there are no values, maybe the user is about to make a
  3068. // new list so we add a few for them to get started with.
  3069. if (values.length === 0) {
  3070. values.push('', '', '', '');
  3071. } // Add the parsed values to the editor
  3072. var dropIcon = Functions.getImage('b_drop');
  3073. for (i = 0; i < values.length; i++) {
  3074. fields += '<tr><td>' + '<input type=\'text\' value=\'' + values[i] + '\'>' + '</td><td class=\'drop\'>' + dropIcon + '</td></tr>';
  3075. }
  3076. /**
  3077. * @var dialog HTML code for the ENUM/SET dialog
  3078. */
  3079. var dialog = '<div id=\'enum_editor\'>' + '<fieldset>' + '<legend>' + title + '</legend>' + '<p>' + Functions.getImage('s_notice') + Messages.enum_hint + '</p>' + '<table class=\'pma-table values\'>' + fields + '</table>' + '</fieldset><fieldset class=\'tblFooters\'>' + '<table class=\'pma-table add\'><tr><td>' + '<div class=\'slider\'></div>' + '</td><td>' + '<form><div><input type=\'submit\' class=\'add_value btn btn-primary\' value=\'' + Functions.sprintf(Messages.enum_addValue, 1) + '\'></div></form>' + '</td></tr></table>' + '<input type=\'hidden\' value=\'' + // So we know which column's data is being edited
  3080. $(this).closest('td').find('input').attr('id') + '\'>' + '</fieldset>' + '</div>';
  3081. /**
  3082. * @var {object} buttonOptions Defines functions to be called when the buttons in
  3083. * the buttonOptions jQuery dialog bar are pressed
  3084. */
  3085. var buttonOptions = {};
  3086. buttonOptions[Messages.strGo] = function () {
  3087. // When the submit button is clicked,
  3088. // put the data back into the original form
  3089. var valueArray = [];
  3090. $(this).find('.values input').each(function (index, elm) {
  3091. var val = elm.value.replace(/\\/g, '\\\\').replace(/'/g, '\'\'');
  3092. valueArray.push('\'' + val + '\'');
  3093. }); // get the Length/Values text field where this value belongs
  3094. var valuesId = $(this).find('input[type=\'hidden\']').val();
  3095. $('input#' + valuesId).val(valueArray.join(','));
  3096. $(this).dialog('close');
  3097. };
  3098. buttonOptions[Messages.strClose] = function () {
  3099. $(this).dialog('close');
  3100. }; // Show the dialog
  3101. var width = parseInt(parseInt($('html').css('font-size'), 10) / 13 * 340, 10);
  3102. if (!width) {
  3103. width = 340;
  3104. }
  3105. $enumEditorDialog = $(dialog).dialog({
  3106. minWidth: width,
  3107. maxHeight: 450,
  3108. modal: true,
  3109. title: Messages.enum_editor,
  3110. buttons: buttonOptions,
  3111. open: function open() {
  3112. // Focus the "Go" button after opening the dialog
  3113. $(this).closest('.ui-dialog').find('.ui-dialog-buttonpane button').first().trigger('focus');
  3114. },
  3115. close: function close() {
  3116. $(this).remove();
  3117. }
  3118. }); // slider for choosing how many fields to add
  3119. $enumEditorDialog.find('.slider').slider({
  3120. animate: true,
  3121. range: 'min',
  3122. value: 1,
  3123. min: 1,
  3124. max: 9,
  3125. slide: function slide(event, ui) {
  3126. $(this).closest('table').find('input[type=submit]').val(Functions.sprintf(Messages.enum_addValue, ui.value));
  3127. }
  3128. }); // Focus the slider, otherwise it looks nearly transparent
  3129. $('a.ui-slider-handle').addClass('ui-state-focus');
  3130. return false;
  3131. });
  3132. $(document).on('click', 'a.central_columns_dialog', function () {
  3133. var href = 'index.php?route=/database/central-columns';
  3134. var db = CommonParams.get('db');
  3135. var table = CommonParams.get('table');
  3136. var maxRows = $(this).data('maxrows');
  3137. var pick = $(this).data('pick');
  3138. if (pick !== false) {
  3139. pick = true;
  3140. }
  3141. var params = {
  3142. 'ajax_request': true,
  3143. 'server': CommonParams.get('server'),
  3144. 'db': CommonParams.get('db'),
  3145. 'cur_table': CommonParams.get('table'),
  3146. 'getColumnList': true
  3147. };
  3148. var colid = $(this).closest('td').find('input').attr('id');
  3149. var fields = '';
  3150. if (!(db + '_' + table in centralColumnList)) {
  3151. centralColumnList.push(db + '_' + table);
  3152. $.ajax({
  3153. type: 'POST',
  3154. url: href,
  3155. data: params,
  3156. success: function success(data) {
  3157. centralColumnList[db + '_' + table] = data.message;
  3158. },
  3159. async: false
  3160. });
  3161. }
  3162. var i = 0;
  3163. var listSize = centralColumnList[db + '_' + table].length;
  3164. var min = listSize <= maxRows ? listSize : maxRows;
  3165. for (i = 0; i < min; i++) {
  3166. fields += '<tr><td><div><span class="font_weight_bold">' + Functions.escapeHtml(centralColumnList[db + '_' + table][i].col_name) + '</span><br><span class="color_gray">' + centralColumnList[db + '_' + table][i].col_type;
  3167. if (centralColumnList[db + '_' + table][i].col_attribute !== '') {
  3168. fields += '(' + Functions.escapeHtml(centralColumnList[db + '_' + table][i].col_attribute) + ') ';
  3169. }
  3170. if (centralColumnList[db + '_' + table][i].col_length !== '') {
  3171. fields += '(' + Functions.escapeHtml(centralColumnList[db + '_' + table][i].col_length) + ') ';
  3172. }
  3173. fields += Functions.escapeHtml(centralColumnList[db + '_' + table][i].col_extra) + '</span>' + '</div></td>';
  3174. if (pick) {
  3175. fields += '<td><input class="btn btn-secondary pick w-100" type="submit" value="' + Messages.pickColumn + '" onclick="Functions.autoPopulate(\'' + colid + '\',' + i + ')"></td>';
  3176. }
  3177. fields += '</tr>';
  3178. }
  3179. var resultPointer = i;
  3180. var searchIn = '<input type="text" class="filter_rows" placeholder="' + Messages.searchList + '">';
  3181. if (fields === '') {
  3182. fields = Functions.sprintf(Messages.strEmptyCentralList, '\'' + Functions.escapeHtml(db) + '\'');
  3183. searchIn = '';
  3184. }
  3185. var seeMore = '';
  3186. if (listSize > maxRows) {
  3187. seeMore = '<fieldset class="tblFooters text-center font_weight_bold">' + '<a href=\'#\' id=\'seeMore\'>' + Messages.seeMore + '</a></fieldset>';
  3188. }
  3189. var centralColumnsDialog = '<div class=\'max_height_400\'>' + '<fieldset>' + searchIn + '<table id=\'col_list\' class=\'pma-table values w-100\'>' + fields + '</table>' + '</fieldset>' + seeMore + '</div>';
  3190. var width = parseInt(parseInt($('html').css('font-size'), 10) / 13 * 500, 10);
  3191. if (!width) {
  3192. width = 500;
  3193. }
  3194. var buttonOptions = {};
  3195. var $centralColumnsDialog = $(centralColumnsDialog).dialog({
  3196. minWidth: width,
  3197. maxHeight: 450,
  3198. modal: true,
  3199. title: Messages.pickColumnTitle,
  3200. buttons: buttonOptions,
  3201. open: function open() {
  3202. $('#col_list').on('click', '.pick', function () {
  3203. $centralColumnsDialog.remove();
  3204. });
  3205. $('.filter_rows').on('keyup', function () {
  3206. $.uiTableFilter($('#col_list'), $(this).val());
  3207. });
  3208. $('#seeMore').on('click', function () {
  3209. fields = '';
  3210. min = listSize <= maxRows + resultPointer ? listSize : maxRows + resultPointer;
  3211. for (i = resultPointer; i < min; i++) {
  3212. fields += '<tr><td><div><span class="font_weight_bold">' + centralColumnList[db + '_' + table][i].col_name + '</span><br><span class="color_gray">' + centralColumnList[db + '_' + table][i].col_type;
  3213. if (centralColumnList[db + '_' + table][i].col_attribute !== '') {
  3214. fields += '(' + centralColumnList[db + '_' + table][i].col_attribute + ') ';
  3215. }
  3216. if (centralColumnList[db + '_' + table][i].col_length !== '') {
  3217. fields += '(' + centralColumnList[db + '_' + table][i].col_length + ') ';
  3218. }
  3219. fields += centralColumnList[db + '_' + table][i].col_extra + '</span>' + '</div></td>';
  3220. if (pick) {
  3221. fields += '<td><input class="btn btn-secondary pick w-100" type="submit" value="' + Messages.pickColumn + '" onclick="Functions.autoPopulate(\'' + colid + '\',' + i + ')"></td>';
  3222. }
  3223. fields += '</tr>';
  3224. }
  3225. $('#col_list').append(fields);
  3226. resultPointer = i;
  3227. if (resultPointer === listSize) {
  3228. $('#seeMore').hide();
  3229. }
  3230. return false;
  3231. });
  3232. $(this).closest('.ui-dialog').find('.ui-dialog-buttonpane button').first().trigger('focus');
  3233. },
  3234. close: function close() {
  3235. $('#col_list').off('click', '.pick');
  3236. $('.filter_rows').off('keyup');
  3237. $(this).remove();
  3238. }
  3239. });
  3240. return false;
  3241. }); // $(document).on('click', 'a.show_central_list',function(e) {
  3242. // });
  3243. // When "add a new value" is clicked, append an empty text field
  3244. $(document).on('click', 'input.add_value', function (e) {
  3245. e.preventDefault();
  3246. var numNewRows = $enumEditorDialog.find('div.slider').slider('value');
  3247. while (numNewRows--) {
  3248. $enumEditorDialog.find('.values').append('<tr class=\'hide\'><td>' + '<input type=\'text\'>' + '</td><td class=\'drop\'>' + Functions.getImage('b_drop') + '</td></tr>').find('tr').last().show('fast');
  3249. }
  3250. }); // Removes the specified row from the enum editor
  3251. $(document).on('click', '#enum_editor td.drop', function () {
  3252. $(this).closest('tr').hide('fast', function () {
  3253. $(this).remove();
  3254. });
  3255. });
  3256. });
  3257. /**
  3258. * Ensures indexes names are valid according to their type and, for a primary
  3259. * key, lock index name to 'PRIMARY'
  3260. * @param string form_id Variable which parses the form name as
  3261. * the input
  3262. * @return boolean false if there is no index form, true else
  3263. */
  3264. Functions.checkIndexName = function (formId) {
  3265. if ($('#' + formId).length === 0) {
  3266. return false;
  3267. } // Gets the elements pointers
  3268. var $theIdxName = $('#input_index_name');
  3269. var $theIdxChoice = $('#select_index_choice'); // Index is a primary key
  3270. if ($theIdxChoice.find('option:selected').val() === 'PRIMARY') {
  3271. $theIdxName.val('PRIMARY');
  3272. $theIdxName.prop('disabled', true);
  3273. } else {
  3274. if ($theIdxName.val() === 'PRIMARY') {
  3275. $theIdxName.val('');
  3276. }
  3277. $theIdxName.prop('disabled', false);
  3278. }
  3279. return true;
  3280. };
  3281. AJAX.registerTeardown('functions.js', function () {
  3282. $(document).off('click', '#index_frm input[type=submit]');
  3283. });
  3284. AJAX.registerOnload('functions.js', function () {
  3285. /**
  3286. * Handler for adding more columns to an index in the editor
  3287. */
  3288. $(document).on('click', '#index_frm input[type=submit]', function (event) {
  3289. event.preventDefault();
  3290. var hadAddButtonHidden = $(this).closest('fieldset').find('.add_fields').hasClass('hide');
  3291. if (hadAddButtonHidden === false) {
  3292. var rowsToAdd = $(this).closest('fieldset').find('.slider').slider('value');
  3293. var tempEmptyVal = function tempEmptyVal() {
  3294. $(this).val('');
  3295. };
  3296. var tempSetFocus = function tempSetFocus() {
  3297. if ($(this).find('option:selected').val() === '') {
  3298. return true;
  3299. }
  3300. $(this).closest('tr').find('input').trigger('focus');
  3301. };
  3302. while (rowsToAdd--) {
  3303. var $indexColumns = $('#index_columns');
  3304. var $newrow = $indexColumns.find('tbody > tr').first().clone().appendTo($indexColumns.find('tbody'));
  3305. $newrow.find(':input').each(tempEmptyVal); // focus index size input on column picked
  3306. $newrow.find('select').on('change', tempSetFocus);
  3307. }
  3308. }
  3309. });
  3310. });
  3311. Functions.indexDialogModal = function (routeUrl, url, title, callbackSuccess, callbackFailure) {
  3312. /* Remove the hidden dialogs if there are*/
  3313. var $editIndexDialog = $('#edit_index_dialog');
  3314. if ($editIndexDialog.length !== 0) {
  3315. $editIndexDialog.remove();
  3316. }
  3317. var $div = $('<div id="edit_index_dialog"></div>');
  3318. /**
  3319. * @var button_options Object that stores the options
  3320. * passed to jQueryUI dialog
  3321. */
  3322. var buttonOptions = {};
  3323. buttonOptions[Messages.strGo] = function () {
  3324. /**
  3325. * @var the_form object referring to the export form
  3326. */
  3327. var $form = $('#index_frm');
  3328. Functions.ajaxShowMessage(Messages.strProcessingRequest);
  3329. Functions.prepareForAjaxRequest($form); // User wants to submit the form
  3330. $.post($form.attr('action'), $form.serialize() + CommonParams.get('arg_separator') + 'do_save_data=1', function (data) {
  3331. var $sqlqueryresults = $('.sqlqueryresults');
  3332. if ($sqlqueryresults.length !== 0) {
  3333. $sqlqueryresults.remove();
  3334. }
  3335. if (typeof data !== 'undefined' && data.success === true) {
  3336. Functions.ajaxShowMessage(data.message);
  3337. Functions.highlightSql($('.result_query'));
  3338. $('.result_query .alert').remove();
  3339. /* Reload the field form*/
  3340. $('#table_index').remove();
  3341. $('<div id=\'temp_div\'><div>').append(data.index_table).find('#table_index').insertAfter('#index_header');
  3342. var $editIndexDialog = $('#edit_index_dialog');
  3343. if ($editIndexDialog.length > 0) {
  3344. $editIndexDialog.dialog('close');
  3345. }
  3346. $('div.no_indexes_defined').hide();
  3347. if (callbackSuccess) {
  3348. callbackSuccess(data);
  3349. }
  3350. Navigation.reload();
  3351. } else {
  3352. var $tempDiv = $('<div id=\'temp_div\'><div>').append(data.error);
  3353. var $error;
  3354. if ($tempDiv.find('.error code').length !== 0) {
  3355. $error = $tempDiv.find('.error code').addClass('error');
  3356. } else {
  3357. $error = $tempDiv;
  3358. }
  3359. if (callbackFailure) {
  3360. callbackFailure();
  3361. }
  3362. Functions.ajaxShowMessage($error, false);
  3363. }
  3364. }); // end $.post()
  3365. };
  3366. buttonOptions[Messages.strPreviewSQL] = function () {
  3367. // Function for Previewing SQL
  3368. var $form = $('#index_frm');
  3369. Functions.previewSql($form);
  3370. };
  3371. buttonOptions[Messages.strCancel] = function () {
  3372. $(this).dialog('close');
  3373. };
  3374. var $msgbox = Functions.ajaxShowMessage();
  3375. $.post(routeUrl, url, function (data) {
  3376. if (typeof data !== 'undefined' && data.success === false) {
  3377. // in the case of an error, show the error message returned.
  3378. Functions.ajaxShowMessage(data.error, false);
  3379. } else {
  3380. Functions.ajaxRemoveMessage($msgbox); // Show dialog if the request was successful
  3381. $div.append(data.message).dialog({
  3382. title: title,
  3383. width: 'auto',
  3384. open: Functions.verifyColumnsProperties,
  3385. modal: true,
  3386. buttons: buttonOptions,
  3387. close: function close() {
  3388. $(this).remove();
  3389. }
  3390. });
  3391. $div.find('.tblFooters').remove();
  3392. Functions.showIndexEditDialog($div);
  3393. }
  3394. }); // end $.get()
  3395. };
  3396. Functions.indexEditorDialog = function (url, title, callbackSuccess, callbackFailure) {
  3397. Functions.indexDialogModal('index.php?route=/table/indexes', url, title, callbackSuccess, callbackFailure);
  3398. };
  3399. Functions.indexRenameDialog = function (url, title, callbackSuccess, callbackFailure) {
  3400. Functions.indexDialogModal('index.php?route=/table/indexes/rename', url, title, callbackSuccess, callbackFailure);
  3401. };
  3402. Functions.showIndexEditDialog = function ($outer) {
  3403. Indexes.checkIndexType();
  3404. Functions.checkIndexName('index_frm');
  3405. var $indexColumns = $('#index_columns');
  3406. $indexColumns.find('td').each(function () {
  3407. $(this).css('width', $(this).width() + 'px');
  3408. });
  3409. $indexColumns.find('tbody').sortable({
  3410. axis: 'y',
  3411. containment: $indexColumns.find('tbody'),
  3412. tolerance: 'pointer'
  3413. });
  3414. Functions.showHints($outer);
  3415. Functions.initSlider(); // Add a slider for selecting how many columns to add to the index
  3416. $outer.find('.slider').slider({
  3417. animate: true,
  3418. value: 1,
  3419. min: 1,
  3420. max: 16,
  3421. slide: function slide(event, ui) {
  3422. $(this).closest('fieldset').find('input[type=submit]').val(Functions.sprintf(Messages.strAddToIndex, ui.value));
  3423. }
  3424. });
  3425. $('div.add_fields').removeClass('hide'); // focus index size input on column picked
  3426. $outer.find('table#index_columns select').on('change', function () {
  3427. if ($(this).find('option:selected').val() === '') {
  3428. return true;
  3429. }
  3430. $(this).closest('tr').find('input').trigger('focus');
  3431. }); // Focus the slider, otherwise it looks nearly transparent
  3432. $('a.ui-slider-handle').addClass('ui-state-focus'); // set focus on index name input, if empty
  3433. var input = $outer.find('input#input_index_name');
  3434. if (!input.val()) {
  3435. input.trigger('focus');
  3436. }
  3437. };
  3438. /**
  3439. * Function to display tooltips that were
  3440. * generated on the PHP side by PhpMyAdmin\Util::showHint()
  3441. *
  3442. * @param object $div a div jquery object which specifies the
  3443. * domain for searching for tooltips. If we
  3444. * omit this parameter the function searches
  3445. * in the whole body
  3446. **/
  3447. Functions.showHints = function ($div) {
  3448. var $newDiv = $div;
  3449. if ($newDiv === undefined || !($newDiv instanceof jQuery) || $newDiv.length === 0) {
  3450. $newDiv = $('body');
  3451. }
  3452. $newDiv.find('.pma_hint').each(function () {
  3453. Functions.tooltip($(this).children('img'), 'img', $(this).children('span').html());
  3454. });
  3455. };
  3456. AJAX.registerOnload('functions.js', function () {
  3457. Functions.showHints();
  3458. });
  3459. Functions.mainMenuResizerCallback = function () {
  3460. // 5 px margin for jumping menu in Chrome
  3461. return $(document.body).width() - 5;
  3462. }; // This must be fired only once after the initial page load
  3463. $(function () {
  3464. // Initialise the menu resize plugin
  3465. $('#topmenu').menuResizer(Functions.mainMenuResizerCallback); // register resize event
  3466. $(window).on('resize', function () {
  3467. $('#topmenu').menuResizer('resize');
  3468. });
  3469. });
  3470. /**
  3471. * Changes status of slider
  3472. */
  3473. Functions.setStatusLabel = function ($element) {
  3474. var text;
  3475. if ($element.css('display') === 'none') {
  3476. text = '+ ';
  3477. } else {
  3478. text = '- ';
  3479. }
  3480. $element.closest('.slide-wrapper').prev().find('span').text(text);
  3481. };
  3482. /**
  3483. * var toggleButton This is a function that creates a toggle
  3484. * sliding button given a jQuery reference
  3485. * to the correct DOM element
  3486. */
  3487. Functions.toggleButton = function ($obj) {
  3488. // In rtl mode the toggle switch is flipped horizontally
  3489. // so we need to take that into account
  3490. var right;
  3491. if ($('span.text_direction', $obj).text() === 'ltr') {
  3492. right = 'right';
  3493. } else {
  3494. right = 'left';
  3495. }
  3496. /**
  3497. * var h Height of the button, used to scale the
  3498. * background image and position the layers
  3499. */
  3500. var h = $obj.height();
  3501. $('img', $obj).height(h);
  3502. $('table', $obj).css('bottom', h - 1);
  3503. /**
  3504. * var on Width of the "ON" part of the toggle switch
  3505. * var off Width of the "OFF" part of the toggle switch
  3506. */
  3507. var on = $('td.toggleOn', $obj).width();
  3508. var off = $('td.toggleOff', $obj).width(); // Make the "ON" and "OFF" parts of the switch the same size
  3509. // + 2 pixels to avoid overflowed
  3510. $('td.toggleOn > div', $obj).width(Math.max(on, off) + 2);
  3511. $('td.toggleOff > div', $obj).width(Math.max(on, off) + 2);
  3512. /**
  3513. * var w Width of the central part of the switch
  3514. */
  3515. var w = parseInt($('img', $obj).height() / 16 * 22, 10); // Resize the central part of the switch on the top
  3516. // layer to match the background
  3517. $($obj).find('table td').eq(1).children('div').width(w);
  3518. /**
  3519. * var imgw Width of the background image
  3520. * var tblw Width of the foreground layer
  3521. * var offset By how many pixels to move the background
  3522. * image, so that it matches the top layer
  3523. */
  3524. var imgw = $('img', $obj).width();
  3525. var tblw = $('table', $obj).width();
  3526. var offset = parseInt((imgw - tblw) / 2, 10); // Move the background to match the layout of the top layer
  3527. $obj.find('img').css(right, offset);
  3528. /**
  3529. * var offw Outer width of the "ON" part of the toggle switch
  3530. * var btnw Outer width of the central part of the switch
  3531. */
  3532. var offw = $('td.toggleOff', $obj).outerWidth();
  3533. var btnw = $($obj).find('table td').eq(1).outerWidth(); // Resize the main div so that exactly one side of
  3534. // the switch plus the central part fit into it.
  3535. $obj.width(offw + btnw + 2);
  3536. /**
  3537. * var move How many pixels to move the
  3538. * switch by when toggling
  3539. */
  3540. var move = $('td.toggleOff', $obj).outerWidth(); // If the switch is initialized to the
  3541. // OFF state we need to move it now.
  3542. if ($('div.toggle-container', $obj).hasClass('off')) {
  3543. if (right === 'right') {
  3544. $('div.toggle-container', $obj).animate({
  3545. 'left': '-=' + move + 'px'
  3546. }, 0);
  3547. } else {
  3548. $('div.toggle-container', $obj).animate({
  3549. 'left': '+=' + move + 'px'
  3550. }, 0);
  3551. }
  3552. } // Attach an 'onclick' event to the switch
  3553. $('div.toggle-container', $obj).on('click', function () {
  3554. if ($(this).hasClass('isActive')) {
  3555. return false;
  3556. } else {
  3557. $(this).addClass('isActive');
  3558. }
  3559. var $msg = Functions.ajaxShowMessage();
  3560. var $container = $(this);
  3561. var callback = $('span.callback', this).text();
  3562. var operator;
  3563. var url;
  3564. var removeClass;
  3565. var addClass; // Perform the actual toggle
  3566. if ($(this).hasClass('on')) {
  3567. if (right === 'right') {
  3568. operator = '-=';
  3569. } else {
  3570. operator = '+=';
  3571. }
  3572. url = $(this).find('td.toggleOff > span').text();
  3573. removeClass = 'on';
  3574. addClass = 'off';
  3575. } else {
  3576. if (right === 'right') {
  3577. operator = '+=';
  3578. } else {
  3579. operator = '-=';
  3580. }
  3581. url = $(this).find('td.toggleOn > span').text();
  3582. removeClass = 'off';
  3583. addClass = 'on';
  3584. }
  3585. var parts = url.split('?');
  3586. $.post(parts[0], parts[1] + '&ajax_request=true', function (data) {
  3587. if (typeof data !== 'undefined' && data.success === true) {
  3588. Functions.ajaxRemoveMessage($msg);
  3589. $container.removeClass(removeClass).addClass(addClass).animate({
  3590. 'left': operator + move + 'px'
  3591. }, function () {
  3592. $container.removeClass('isActive');
  3593. }); // eslint-disable-next-line no-eval
  3594. eval(callback);
  3595. } else {
  3596. Functions.ajaxShowMessage(data.error, false);
  3597. $container.removeClass('isActive');
  3598. }
  3599. });
  3600. });
  3601. };
  3602. /**
  3603. * Unbind all event handlers before tearing down a page
  3604. */
  3605. AJAX.registerTeardown('functions.js', function () {
  3606. $('div.toggle-container').off('click');
  3607. });
  3608. /**
  3609. * Initialise all toggle buttons
  3610. */
  3611. AJAX.registerOnload('functions.js', function () {
  3612. $('div.toggleAjax').each(function () {
  3613. var $button = $(this).show();
  3614. $button.find('img').each(function () {
  3615. if (this.complete) {
  3616. Functions.toggleButton($button);
  3617. } else {
  3618. $(this).on('load', function () {
  3619. Functions.toggleButton($button);
  3620. });
  3621. }
  3622. });
  3623. });
  3624. });
  3625. /**
  3626. * Unbind all event handlers before tearing down a page
  3627. */
  3628. AJAX.registerTeardown('functions.js', function () {
  3629. $(document).off('change', 'select.pageselector');
  3630. $('#update_recent_tables').off('ready');
  3631. $('#sync_favorite_tables').off('ready');
  3632. });
  3633. AJAX.registerOnload('functions.js', function () {
  3634. /**
  3635. * Autosubmit page selector
  3636. */
  3637. $(document).on('change', 'select.pageselector', function (event) {
  3638. event.stopPropagation(); // Check where to load the new content
  3639. if ($(this).closest('#pma_navigation').length === 0) {
  3640. // For the main page we don't need to do anything,
  3641. $(this).closest('form').trigger('submit');
  3642. } else {
  3643. // but for the navigation we need to manually replace the content
  3644. Navigation.treePagination($(this));
  3645. }
  3646. });
  3647. /**
  3648. * Load version information asynchronously.
  3649. */
  3650. if ($('li.jsversioncheck').length > 0) {
  3651. $.ajax({
  3652. dataType: 'json',
  3653. url: 'index.php?route=/version-check',
  3654. method: 'POST',
  3655. data: {
  3656. 'server': CommonParams.get('server')
  3657. },
  3658. success: Functions.currentVersion
  3659. });
  3660. }
  3661. if ($('#is_git_revision').length > 0) {
  3662. setTimeout(Functions.displayGitRevision, 10);
  3663. }
  3664. /**
  3665. * Slider effect.
  3666. */
  3667. Functions.initSlider();
  3668. var $updateRecentTables = $('#update_recent_tables');
  3669. if ($updateRecentTables.length) {
  3670. $.get($updateRecentTables.attr('href'), {
  3671. 'no_debug': true
  3672. }, function (data) {
  3673. if (typeof data !== 'undefined' && data.success === true) {
  3674. $('#pma_recent_list').html(data.list);
  3675. }
  3676. });
  3677. } // Sync favorite tables from localStorage to pmadb.
  3678. if ($('#sync_favorite_tables').length) {
  3679. $.ajax({
  3680. url: $('#sync_favorite_tables').attr('href'),
  3681. cache: false,
  3682. type: 'POST',
  3683. data: {
  3684. 'favoriteTables': isStorageSupported('localStorage') && typeof window.localStorage.favoriteTables !== 'undefined' ? window.localStorage.favoriteTables : '',
  3685. 'server': CommonParams.get('server'),
  3686. 'no_debug': true
  3687. },
  3688. success: function success(data) {
  3689. // Update localStorage.
  3690. if (isStorageSupported('localStorage')) {
  3691. window.localStorage.favoriteTables = data.favoriteTables;
  3692. }
  3693. $('#pma_favorite_list').html(data.list);
  3694. }
  3695. });
  3696. }
  3697. }); // end of $()
  3698. /**
  3699. * Initializes slider effect.
  3700. */
  3701. Functions.initSlider = function () {
  3702. $('div.pma_auto_slider').each(function () {
  3703. var $this = $(this);
  3704. if ($this.data('slider_init_done')) {
  3705. return;
  3706. }
  3707. var $wrapper = $('<div>', {
  3708. 'class': 'slide-wrapper'
  3709. });
  3710. $wrapper.toggle($this.is(':visible'));
  3711. $('<a>', {
  3712. href: '#' + this.id,
  3713. 'class': 'ajax'
  3714. }).text($this.attr('title')).prepend($('<span>')).insertBefore($this).on('click', function () {
  3715. var $wrapper = $this.closest('.slide-wrapper');
  3716. var visible = $this.is(':visible');
  3717. if (!visible) {
  3718. $wrapper.show();
  3719. }
  3720. $this[visible ? 'hide' : 'show']('blind', function () {
  3721. $wrapper.toggle(!visible);
  3722. $wrapper.parent().toggleClass('print_ignore', visible);
  3723. Functions.setStatusLabel($this);
  3724. });
  3725. return false;
  3726. });
  3727. $this.wrap($wrapper);
  3728. $this.removeAttr('title');
  3729. Functions.setStatusLabel($this);
  3730. $this.data('slider_init_done', 1);
  3731. });
  3732. };
  3733. /**
  3734. * Initializes slider effect.
  3735. */
  3736. AJAX.registerOnload('functions.js', function () {
  3737. Functions.initSlider();
  3738. });
  3739. /**
  3740. * Restores sliders to the state they were in before initialisation.
  3741. */
  3742. AJAX.registerTeardown('functions.js', function () {
  3743. $('div.pma_auto_slider').each(function () {
  3744. var $this = $(this);
  3745. $this.removeData();
  3746. $this.parent().replaceWith($this);
  3747. $this.parent().children('a').remove();
  3748. });
  3749. });
  3750. /**
  3751. * Creates a message inside an object with a sliding effect
  3752. *
  3753. * @param msg A string containing the text to display
  3754. * @param $obj a jQuery object containing the reference
  3755. * to the element where to put the message
  3756. * This is optional, if no element is
  3757. * provided, one will be created below the
  3758. * navigation links at the top of the page
  3759. *
  3760. * @return bool True on success, false on failure
  3761. */
  3762. Functions.slidingMessage = function (msg, $object) {
  3763. var $obj = $object;
  3764. if (msg === undefined || msg.length === 0) {
  3765. // Don't show an empty message
  3766. return false;
  3767. }
  3768. if ($obj === undefined || !($obj instanceof jQuery) || $obj.length === 0) {
  3769. // If the second argument was not supplied,
  3770. // we might have to create a new DOM node.
  3771. if ($('#PMA_slidingMessage').length === 0) {
  3772. $('#page_content').prepend('<span id="PMA_slidingMessage" ' + 'class="pma_sliding_message"></span>');
  3773. }
  3774. $obj = $('#PMA_slidingMessage');
  3775. }
  3776. if ($obj.has('div').length > 0) {
  3777. // If there already is a message inside the
  3778. // target object, we must get rid of it
  3779. $obj.find('div').first().fadeOut(function () {
  3780. $obj.children().remove();
  3781. $obj.append('<div>' + msg + '</div>'); // highlight any sql before taking height;
  3782. Functions.highlightSql($obj);
  3783. $obj.find('div').first().hide();
  3784. $obj.animate({
  3785. height: $obj.find('div').first().height()
  3786. }).find('div').first().fadeIn();
  3787. });
  3788. } else {
  3789. // Object does not already have a message
  3790. // inside it, so we simply slide it down
  3791. $obj.width('100%').html('<div>' + msg + '</div>'); // highlight any sql before taking height;
  3792. Functions.highlightSql($obj);
  3793. var h = $obj.find('div').first().hide().height();
  3794. $obj.find('div').first().css('height', 0).show().animate({
  3795. height: h
  3796. }, function () {
  3797. // Set the height of the parent
  3798. // to the height of the child
  3799. $obj.height($obj.find('div').first().height());
  3800. });
  3801. }
  3802. return true;
  3803. };
  3804. /**
  3805. * Attach CodeMirror2 editor to SQL edit area.
  3806. */
  3807. AJAX.registerOnload('functions.js', function () {
  3808. var $elm = $('#sqlquery');
  3809. if ($elm.siblings().filter('.CodeMirror').length > 0) {
  3810. return;
  3811. }
  3812. if ($elm.length > 0) {
  3813. if (typeof CodeMirror !== 'undefined') {
  3814. codeMirrorEditor = Functions.getSqlEditor($elm);
  3815. codeMirrorEditor.focus();
  3816. codeMirrorEditor.on('blur', Functions.updateQueryParameters);
  3817. } else {
  3818. // without codemirror
  3819. $elm.trigger('focus').on('blur', Functions.updateQueryParameters);
  3820. }
  3821. }
  3822. Functions.highlightSql($('body'));
  3823. });
  3824. AJAX.registerTeardown('functions.js', function () {
  3825. if (codeMirrorEditor) {
  3826. $('#sqlquery').text(codeMirrorEditor.getValue());
  3827. codeMirrorEditor.toTextArea();
  3828. codeMirrorEditor = false;
  3829. }
  3830. });
  3831. AJAX.registerOnload('functions.js', function () {
  3832. // initializes all lock-page elements lock-id and
  3833. // val-hash data property
  3834. $('#page_content form.lock-page textarea, ' + '#page_content form.lock-page input[type="text"], ' + '#page_content form.lock-page input[type="number"], ' + '#page_content form.lock-page select').each(function (i) {
  3835. $(this).data('lock-id', i); // val-hash is the hash of default value of the field
  3836. // so that it can be compared with new value hash
  3837. // to check whether field was modified or not.
  3838. $(this).data('val-hash', AJAX.hash($(this).val()));
  3839. }); // initializes lock-page elements (input types checkbox and radio buttons)
  3840. // lock-id and val-hash data property
  3841. $('#page_content form.lock-page input[type="checkbox"], ' + '#page_content form.lock-page input[type="radio"]').each(function (i) {
  3842. $(this).data('lock-id', i);
  3843. $(this).data('val-hash', AJAX.hash($(this).is(':checked')));
  3844. });
  3845. });
  3846. /**
  3847. * jQuery plugin to correctly filter input fields by value, needed
  3848. * because some nasty values may break selector syntax
  3849. */
  3850. (function ($) {
  3851. $.fn.filterByValue = function (value) {
  3852. return this.filter(function () {
  3853. return $(this).val() === value;
  3854. });
  3855. };
  3856. })(jQuery);
  3857. /**
  3858. * Return value of a cell in a table.
  3859. */
  3860. Functions.getCellValue = function (td) {
  3861. var $td = $(td);
  3862. if ($td.is('.null')) {
  3863. return '';
  3864. } else if ((!$td.is('.to_be_saved') || $td.is('.set')) && $td.data('original_data')) {
  3865. return $td.data('original_data');
  3866. } else {
  3867. return $td.text();
  3868. }
  3869. };
  3870. $(window).on('popstate', function () {
  3871. $('#printcss').attr('media', 'print');
  3872. return true;
  3873. });
  3874. /**
  3875. * Unbind all event handlers before tearing down a page
  3876. */
  3877. AJAX.registerTeardown('functions.js', function () {
  3878. $(document).off('click', 'a.themeselect');
  3879. $(document).off('change', '.autosubmit');
  3880. $('a.take_theme').off('click');
  3881. });
  3882. AJAX.registerOnload('functions.js', function () {
  3883. /**
  3884. * Theme selector.
  3885. */
  3886. $(document).on('click', 'a.themeselect', function (e) {
  3887. window.open(e.target, 'themes', 'left=10,top=20,width=510,height=350,scrollbars=yes,status=yes,resizable=yes');
  3888. return false;
  3889. });
  3890. /**
  3891. * Automatic form submission on change.
  3892. */
  3893. $(document).on('change', '.autosubmit', function () {
  3894. $(this).closest('form').trigger('submit');
  3895. });
  3896. /**
  3897. * Theme changer.
  3898. */
  3899. $('a.take_theme').on('click', function () {
  3900. var what = this.name;
  3901. /* eslint-disable compat/compat */
  3902. if (window.opener && window.opener.document.forms.setTheme.elements.set_theme) {
  3903. window.opener.document.forms.setTheme.elements.set_theme.value = what;
  3904. window.opener.document.forms.setTheme.submit();
  3905. window.close();
  3906. return false;
  3907. }
  3908. /* eslint-enable compat/compat */
  3909. return true;
  3910. });
  3911. });
  3912. /**
  3913. * Produce print preview
  3914. */
  3915. Functions.printPreview = function () {
  3916. $('#printcss').attr('media', 'all');
  3917. Functions.createPrintAndBackButtons();
  3918. };
  3919. /**
  3920. * Create print and back buttons in preview page
  3921. */
  3922. Functions.createPrintAndBackButtons = function () {
  3923. var backButton = $('<input>', {
  3924. type: 'button',
  3925. value: Messages.back,
  3926. class: 'btn btn-secondary',
  3927. id: 'back_button_print_view'
  3928. });
  3929. backButton.on('click', Functions.removePrintAndBackButton);
  3930. backButton.appendTo('#page_content');
  3931. var printButton = $('<input>', {
  3932. type: 'button',
  3933. value: Messages.print,
  3934. class: 'btn btn-primary',
  3935. id: 'print_button_print_view'
  3936. });
  3937. printButton.on('click', Functions.printPage);
  3938. printButton.appendTo('#page_content');
  3939. };
  3940. /**
  3941. * Remove print and back buttons and revert to normal view
  3942. */
  3943. Functions.removePrintAndBackButton = function () {
  3944. $('#printcss').attr('media', 'print');
  3945. $('#back_button_print_view').remove();
  3946. $('#print_button_print_view').remove();
  3947. };
  3948. /**
  3949. * Print page
  3950. */
  3951. Functions.printPage = function () {
  3952. if (typeof window.print !== 'undefined') {
  3953. window.print();
  3954. }
  3955. };
  3956. /**
  3957. * Unbind all event handlers before tearing down a page
  3958. */
  3959. AJAX.registerTeardown('functions.js', function () {
  3960. $('input#print').off('click');
  3961. $(document).off('click', 'a.create_view.ajax');
  3962. $(document).off('keydown', '#createViewDialog input, #createViewDialog select');
  3963. $(document).off('change', '#fkc_checkbox');
  3964. });
  3965. AJAX.registerOnload('functions.js', function () {
  3966. $('input#print').on('click', Functions.printPage);
  3967. $('.logout').on('click', function () {
  3968. var form = $('<form method="POST" action="' + $(this).attr('href') + '" class="disableAjax">' + '<input type="hidden" name="token" value="' + Functions.escapeHtml(CommonParams.get('token')) + '">' + '</form>');
  3969. $('body').append(form);
  3970. form.submit();
  3971. sessionStorage.clear();
  3972. return false;
  3973. });
  3974. /**
  3975. * Ajaxification for the "Create View" action
  3976. */
  3977. $(document).on('click', 'a.create_view.ajax', function (e) {
  3978. e.preventDefault();
  3979. Functions.createViewDialog($(this));
  3980. });
  3981. /**
  3982. * Attach Ajax event handlers for input fields in the editor
  3983. * and used to submit the Ajax request when the ENTER key is pressed.
  3984. */
  3985. if ($('#createViewDialog').length !== 0) {
  3986. $(document).on('keydown', '#createViewDialog input, #createViewDialog select', function (e) {
  3987. if (e.which === 13) {
  3988. // 13 is the ENTER key
  3989. e.preventDefault(); // with preventing default, selection by <select> tag
  3990. // was also prevented in IE
  3991. $(this).trigger('blur');
  3992. $(this).closest('.ui-dialog').find('.ui-button').first().trigger('click');
  3993. }
  3994. }); // end $(document).on()
  3995. }
  3996. if ($('textarea[name="view[as]"]').length !== 0) {
  3997. codeMirrorEditor = Functions.getSqlEditor($('textarea[name="view[as]"]'));
  3998. }
  3999. });
  4000. Functions.createViewDialog = function ($this) {
  4001. var $msg = Functions.ajaxShowMessage();
  4002. var sep = CommonParams.get('arg_separator');
  4003. var params = Functions.getJsConfirmCommonParam(this, $this.getPostData());
  4004. params += sep + 'ajax_dialog=1';
  4005. $.post($this.attr('href'), params, function (data) {
  4006. if (typeof data !== 'undefined' && data.success === true) {
  4007. Functions.ajaxRemoveMessage($msg);
  4008. var buttonOptions = {};
  4009. buttonOptions[Messages.strGo] = function () {
  4010. if (typeof CodeMirror !== 'undefined') {
  4011. codeMirrorEditor.save();
  4012. }
  4013. $msg = Functions.ajaxShowMessage();
  4014. $.post('index.php?route=/view/create', $('#createViewDialog').find('form').serialize(), function (data) {
  4015. Functions.ajaxRemoveMessage($msg);
  4016. if (typeof data !== 'undefined' && data.success === true) {
  4017. $('#createViewDialog').dialog('close');
  4018. $('.result_query').html(data.message);
  4019. Navigation.reload();
  4020. } else {
  4021. Functions.ajaxShowMessage(data.error);
  4022. }
  4023. });
  4024. };
  4025. buttonOptions[Messages.strClose] = function () {
  4026. $(this).dialog('close');
  4027. };
  4028. var $dialog = $('<div></div>').attr('id', 'createViewDialog').append(data.message).dialog({
  4029. width: 600,
  4030. minWidth: 400,
  4031. height: $(window).height(),
  4032. modal: true,
  4033. buttons: buttonOptions,
  4034. title: Messages.strCreateView,
  4035. close: function close() {
  4036. $(this).remove();
  4037. }
  4038. }); // Attach syntax highlighted editor
  4039. codeMirrorEditor = Functions.getSqlEditor($dialog.find('textarea'));
  4040. $('input:visible[type=text]', $dialog).first().trigger('focus');
  4041. } else {
  4042. Functions.ajaxShowMessage(data.error);
  4043. }
  4044. });
  4045. };
  4046. /**
  4047. * Makes the breadcrumbs and the menu bar float at the top of the viewport
  4048. */
  4049. $(function () {
  4050. if ($('#floating_menubar').length && $('#PMA_disable_floating_menubar').length === 0) {
  4051. var left = $('html').attr('dir') === 'ltr' ? 'left' : 'right';
  4052. $('#floating_menubar').css('margin-' + left, $('#pma_navigation').width() + $('#pma_navigation_resizer').width()).css(left, 0).css({
  4053. 'position': 'fixed',
  4054. 'top': 0,
  4055. 'width': '100%',
  4056. 'z-index': 99
  4057. }).append($('#server-breadcrumb')).append($('#topmenucontainer')); // Allow the DOM to render, then adjust the padding on the body
  4058. setTimeout(function () {
  4059. $('body').css('padding-top', $('#floating_menubar').outerHeight(true));
  4060. $('#topmenu').menuResizer('resize');
  4061. }, 4);
  4062. }
  4063. });
  4064. /**
  4065. * Scrolls the page to the top if clicking the server-breadcrumb bar
  4066. */
  4067. $(function () {
  4068. $(document).on('click', '#server-breadcrumb, #goto_pagetop', function (event) {
  4069. event.preventDefault();
  4070. $('html, body').animate({
  4071. scrollTop: 0
  4072. }, 'fast');
  4073. });
  4074. });
  4075. var checkboxesSel = 'input.checkall:checkbox:enabled';
  4076. Functions.checkboxesSel = checkboxesSel;
  4077. /**
  4078. * Watches checkboxes in a form to set the checkall box accordingly
  4079. */
  4080. Functions.checkboxesChanged = function () {
  4081. var $form = $(this.form); // total number of checkboxes in current form
  4082. var totalBoxes = $form.find(checkboxesSel).length; // number of checkboxes checked in current form
  4083. var checkedBoxes = $form.find(checkboxesSel + ':checked').length;
  4084. var $checkall = $form.find('input.checkall_box');
  4085. if (totalBoxes === checkedBoxes) {
  4086. $checkall.prop({
  4087. checked: true,
  4088. indeterminate: false
  4089. });
  4090. } else if (checkedBoxes > 0) {
  4091. $checkall.prop({
  4092. checked: true,
  4093. indeterminate: true
  4094. });
  4095. } else {
  4096. $checkall.prop({
  4097. checked: false,
  4098. indeterminate: false
  4099. });
  4100. }
  4101. };
  4102. $(document).on('change', checkboxesSel, Functions.checkboxesChanged);
  4103. $(document).on('change', 'input.checkall_box', function () {
  4104. var isChecked = $(this).is(':checked');
  4105. $(this.form).find(checkboxesSel).not('.row-hidden').prop('checked', isChecked).parents('tr').toggleClass('marked table-active', isChecked);
  4106. });
  4107. $(document).on('click', '.checkall-filter', function () {
  4108. var $this = $(this);
  4109. var selector = $this.data('checkall-selector');
  4110. $('input.checkall_box').prop('checked', false);
  4111. $this.parents('form').find(checkboxesSel).filter(selector).prop('checked', true).trigger('change').parents('tr').toggleClass('marked', true);
  4112. return false;
  4113. });
  4114. /**
  4115. * Watches checkboxes in a sub form to set the sub checkall box accordingly
  4116. */
  4117. Functions.subCheckboxesChanged = function () {
  4118. var $form = $(this).parent().parent(); // total number of checkboxes in current sub form
  4119. var totalBoxes = $form.find(checkboxesSel).length; // number of checkboxes checked in current sub form
  4120. var checkedBoxes = $form.find(checkboxesSel + ':checked').length;
  4121. var $checkall = $form.find('input.sub_checkall_box');
  4122. if (totalBoxes === checkedBoxes) {
  4123. $checkall.prop({
  4124. checked: true,
  4125. indeterminate: false
  4126. });
  4127. } else if (checkedBoxes > 0) {
  4128. $checkall.prop({
  4129. checked: true,
  4130. indeterminate: true
  4131. });
  4132. } else {
  4133. $checkall.prop({
  4134. checked: false,
  4135. indeterminate: false
  4136. });
  4137. }
  4138. };
  4139. $(document).on('change', checkboxesSel + ', input.checkall_box:checkbox:enabled', Functions.subCheckboxesChanged);
  4140. $(document).on('change', 'input.sub_checkall_box', function () {
  4141. var isChecked = $(this).is(':checked');
  4142. var $form = $(this).parent().parent();
  4143. $form.find(checkboxesSel).prop('checked', isChecked).parents('tr').toggleClass('marked', isChecked);
  4144. });
  4145. /**
  4146. * Rows filtering
  4147. *
  4148. * - rows to filter are identified by data-filter-row attribute
  4149. * which contains uppercase string to filter
  4150. * - it is simple substring case insensitive search
  4151. * - optionally number of matching rows is written to element with
  4152. * id filter-rows-count
  4153. */
  4154. $(document).on('keyup', '#filterText', function () {
  4155. var filterInput = $(this).val().toUpperCase().replace(/ /g, '_');
  4156. var count = 0;
  4157. $('[data-filter-row]').each(function () {
  4158. var $row = $(this);
  4159. /* Can not use data() here as it does magic conversion to int for numeric values */
  4160. if ($row.attr('data-filter-row').indexOf(filterInput) > -1) {
  4161. count += 1;
  4162. $row.show();
  4163. $row.find('input.checkall').removeClass('row-hidden');
  4164. } else {
  4165. $row.hide();
  4166. $row.find('input.checkall').addClass('row-hidden').prop('checked', false);
  4167. $row.removeClass('marked');
  4168. }
  4169. });
  4170. setTimeout(function () {
  4171. $(checkboxesSel).trigger('change');
  4172. }, 300);
  4173. $('#filter-rows-count').html(count);
  4174. });
  4175. AJAX.registerOnload('functions.js', function () {
  4176. /* Trigger filtering of the list based on incoming database name */
  4177. var $filter = $('#filterText');
  4178. if ($filter.val()) {
  4179. $filter.trigger('keyup').trigger('select');
  4180. }
  4181. });
  4182. /**
  4183. * Formats a byte number to human-readable form
  4184. *
  4185. * @param bytes the bytes to format
  4186. * @param optional subdecimals the number of digits after the point
  4187. * @param optional pointchar the char to use as decimal point
  4188. */
  4189. Functions.formatBytes = function (bytesToFormat, subDecimals, pointChar) {
  4190. var bytes = bytesToFormat;
  4191. var decimals = subDecimals;
  4192. var point = pointChar;
  4193. if (!decimals) {
  4194. decimals = 0;
  4195. }
  4196. if (!point) {
  4197. point = '.';
  4198. }
  4199. var units = ['B', 'KiB', 'MiB', 'GiB'];
  4200. for (var i = 0; bytes > 1024 && i < units.length; i++) {
  4201. bytes /= 1024;
  4202. }
  4203. var factor = Math.pow(10, decimals);
  4204. bytes = Math.round(bytes * factor) / factor;
  4205. bytes = bytes.toString().split('.').join(point);
  4206. return bytes + ' ' + units[i];
  4207. };
  4208. AJAX.registerOnload('functions.js', function () {
  4209. /**
  4210. * Reveal the login form to users with JS enabled
  4211. * and focus the appropriate input field
  4212. */
  4213. var $loginform = $('#loginform');
  4214. if ($loginform.length) {
  4215. $loginform.find('.js-show').show();
  4216. if ($('#input_username').val()) {
  4217. $('#input_password').trigger('focus');
  4218. } else {
  4219. $('#input_username').trigger('focus');
  4220. }
  4221. }
  4222. var $httpsWarning = $('#js-https-mismatch');
  4223. if ($httpsWarning.length) {
  4224. if (window.location.protocol === 'https:' !== CommonParams.get('is_https')) {
  4225. $httpsWarning.show();
  4226. }
  4227. }
  4228. });
  4229. /**
  4230. * Formats timestamp for display
  4231. */
  4232. Functions.formatDateTime = function (date, seconds) {
  4233. var result = $.datepicker.formatDate('yy-mm-dd', date);
  4234. var timefmt = 'HH:mm';
  4235. if (seconds) {
  4236. timefmt = 'HH:mm:ss';
  4237. }
  4238. return result + ' ' + $.datepicker.formatTime(timefmt, {
  4239. hour: date.getHours(),
  4240. minute: date.getMinutes(),
  4241. second: date.getSeconds()
  4242. });
  4243. };
  4244. /**
  4245. * Check than forms have less fields than max allowed by PHP.
  4246. */
  4247. Functions.checkNumberOfFields = function () {
  4248. if (typeof maxInputVars === 'undefined') {
  4249. return false;
  4250. }
  4251. if (false === maxInputVars) {
  4252. return false;
  4253. }
  4254. $('form').each(function () {
  4255. var nbInputs = $(this).find(':input').length;
  4256. if (nbInputs > maxInputVars) {
  4257. var warning = Functions.sprintf(Messages.strTooManyInputs, maxInputVars);
  4258. Functions.ajaxShowMessage(warning);
  4259. return false;
  4260. }
  4261. return true;
  4262. });
  4263. return true;
  4264. };
  4265. /**
  4266. * Ignore the displayed php errors.
  4267. * Simply removes the displayed errors.
  4268. *
  4269. * @param clearPrevErrors whether to clear errors stored
  4270. * in $_SESSION['prev_errors'] at server
  4271. *
  4272. */
  4273. Functions.ignorePhpErrors = function (clearPrevErrors) {
  4274. var clearPrevious = clearPrevErrors;
  4275. if (typeof clearPrevious === 'undefined' || clearPrevious === null) {
  4276. clearPrevious = false;
  4277. } // send AJAX request to /error-report with send_error_report=0, exception_type=php & token.
  4278. // It clears the prev_errors stored in session.
  4279. if (clearPrevious) {
  4280. var $pmaReportErrorsForm = $('#pma_report_errors_form');
  4281. $pmaReportErrorsForm.find('input[name="send_error_report"]').val(0); // change send_error_report to '0'
  4282. $pmaReportErrorsForm.trigger('submit');
  4283. } // remove displayed errors
  4284. var $pmaErrors = $('#pma_errors');
  4285. $pmaErrors.fadeOut('slow');
  4286. $pmaErrors.remove();
  4287. };
  4288. /**
  4289. * Toggle the Datetimepicker UI if the date value entered
  4290. * by the user in the 'text box' is not going to be accepted
  4291. * by the Datetimepicker plugin (but is accepted by MySQL)
  4292. */
  4293. Functions.toggleDatepickerIfInvalid = function ($td, $inputField) {
  4294. // Regex allowed by the Datetimepicker UI
  4295. var dtexpDate = new RegExp(['^([0-9]{4})', '-(((01|03|05|07|08|10|12)-((0[1-9])|([1-2][0-9])|(3[0-1])))|((02|04|06|09|11)', '-((0[1-9])|([1-2][0-9])|30)))$'].join(''));
  4296. var dtexpTime = new RegExp(['^(([0-1][0-9])|(2[0-3]))', ':((0[0-9])|([1-5][0-9]))', ':((0[0-9])|([1-5][0-9]))(.[0-9]{1,6}){0,1}$'].join('')); // If key-ed in Time or Date values are unsupported by the UI, close it
  4297. if ($td.attr('data-type') === 'date' && !dtexpDate.test($inputField.val())) {
  4298. $inputField.datepicker('hide');
  4299. } else if ($td.attr('data-type') === 'time' && !dtexpTime.test($inputField.val())) {
  4300. $inputField.datepicker('hide');
  4301. } else {
  4302. $inputField.datepicker('show');
  4303. }
  4304. };
  4305. /**
  4306. * Function to submit the login form after validation is done.
  4307. * NOTE: do NOT use a module or it will break the callback, issue #15435
  4308. */
  4309. // eslint-disable-next-line no-unused-vars, camelcase
  4310. var Functions_recaptchaCallback = function Functions_recaptchaCallback() {
  4311. $('#login_form').trigger('submit');
  4312. };
  4313. /**
  4314. * Unbind all event handlers before tearing down a page
  4315. */
  4316. AJAX.registerTeardown('functions.js', function () {
  4317. $(document).off('keydown', 'form input, form textarea, form select');
  4318. });
  4319. AJAX.registerOnload('functions.js', function () {
  4320. /**
  4321. * Handle 'Ctrl/Alt + Enter' form submits
  4322. */
  4323. $('form input, form textarea, form select').on('keydown', function (e) {
  4324. if (e.ctrlKey && e.which === 13 || e.altKey && e.which === 13) {
  4325. var $form = $(this).closest('form'); // There could be multiple submit buttons on the same form,
  4326. // we assume all of them behave identical and just click one.
  4327. if (!$form.find('input[type="submit"]').first() || !$form.find('input[type="submit"]').first().trigger('click')) {
  4328. $form.trigger('submit');
  4329. }
  4330. }
  4331. });
  4332. });
  4333. /**
  4334. * Unbind all event handlers before tearing down a page
  4335. */
  4336. AJAX.registerTeardown('functions.js', function () {
  4337. $(document).off('change', 'input[type=radio][name="pw_hash"]');
  4338. $(document).off('mouseover', '.sortlink');
  4339. $(document).off('mouseout', '.sortlink');
  4340. });
  4341. AJAX.registerOnload('functions.js', function () {
  4342. /*
  4343. * Display warning regarding SSL when sha256_password
  4344. * method is selected
  4345. * Used in /user-password (Change Password link on index.php)
  4346. */
  4347. $(document).on('change', 'select#select_authentication_plugin_cp', function () {
  4348. if (this.value === 'sha256_password') {
  4349. $('#ssl_reqd_warning_cp').show();
  4350. } else {
  4351. $('#ssl_reqd_warning_cp').hide();
  4352. }
  4353. });
  4354. Cookies.defaults.path = CommonParams.get('rootPath'); // Bind event handlers for toggling sort icons
  4355. $(document).on('mouseover', '.sortlink', function () {
  4356. $(this).find('.soimg').toggle();
  4357. });
  4358. $(document).on('mouseout', '.sortlink', function () {
  4359. $(this).find('.soimg').toggle();
  4360. });
  4361. });
  4362. /**
  4363. * Returns an HTML IMG tag for a particular image from a theme,
  4364. * which may be an actual file or an icon from a sprite
  4365. *
  4366. * @param string image The name of the file to get
  4367. * @param string alternate Used to set 'alt' and 'title' attributes of the image
  4368. * @param object attributes An associative array of other attributes
  4369. *
  4370. * @return Object The requested image, this object has two methods:
  4371. * .toString() - Returns the IMG tag for the requested image
  4372. * .attr(name) - Returns a particular attribute of the IMG
  4373. * tag given it's name
  4374. * .attr(name, value) - Sets a particular attribute of the IMG
  4375. * tag to the given value
  4376. */
  4377. Functions.getImage = function (image, alternate, attributes) {
  4378. var alt = alternate;
  4379. var attr = attributes; // custom image object, it will eventually be returned by this functions
  4380. var retval = {
  4381. data: {
  4382. // this is private
  4383. alt: '',
  4384. title: '',
  4385. src: 'themes/dot.gif'
  4386. },
  4387. attr: function attr(name, value) {
  4388. if (value === undefined) {
  4389. if (this.data[name] === undefined) {
  4390. return '';
  4391. } else {
  4392. return this.data[name];
  4393. }
  4394. } else {
  4395. this.data[name] = value;
  4396. }
  4397. },
  4398. toString: function toString() {
  4399. var retval = '<' + 'img';
  4400. for (var i in this.data) {
  4401. retval += ' ' + i + '="' + this.data[i] + '"';
  4402. }
  4403. retval += ' /' + '>';
  4404. return retval;
  4405. }
  4406. }; // initialise missing parameters
  4407. if (attr === undefined) {
  4408. attr = {};
  4409. }
  4410. if (alt === undefined) {
  4411. alt = '';
  4412. } // set alt
  4413. if (attr.alt !== undefined) {
  4414. retval.attr('alt', Functions.escapeHtml(attr.alt));
  4415. } else {
  4416. retval.attr('alt', Functions.escapeHtml(alt));
  4417. } // set title
  4418. if (attr.title !== undefined) {
  4419. retval.attr('title', Functions.escapeHtml(attr.title));
  4420. } else {
  4421. retval.attr('title', Functions.escapeHtml(alt));
  4422. } // set css classes
  4423. retval.attr('class', 'icon ic_' + image); // set all other attributes
  4424. for (var i in attr) {
  4425. if (i === 'src') {
  4426. // do not allow to override the 'src' attribute
  4427. continue;
  4428. }
  4429. retval.attr(i, attr[i]);
  4430. }
  4431. return retval;
  4432. };
  4433. /**
  4434. * Sets a configuration value.
  4435. *
  4436. * A configuration value may be set in both browser's local storage and
  4437. * remotely in server's configuration table.
  4438. *
  4439. * NOTE: Depending on server's configuration, the configuration table may be or
  4440. * not persistent.
  4441. *
  4442. * @param {string} key Configuration key.
  4443. * @param {object} value Configuration value.
  4444. */
  4445. Functions.configSet = function (key, value) {
  4446. var serialized = JSON.stringify(value);
  4447. localStorage.setItem(key, serialized);
  4448. $.ajax({
  4449. url: 'index.php?route=/config/set',
  4450. type: 'POST',
  4451. dataType: 'json',
  4452. data: {
  4453. 'ajax_request': true,
  4454. key: key,
  4455. server: CommonParams.get('server'),
  4456. value: serialized
  4457. },
  4458. success: function success(data) {
  4459. // Updating value in local storage.
  4460. if (!data.success) {
  4461. if (data.error) {
  4462. Functions.ajaxShowMessage(data.error);
  4463. } else {
  4464. Functions.ajaxShowMessage(data.message);
  4465. }
  4466. } // Eventually, call callback.
  4467. }
  4468. });
  4469. };
  4470. /**
  4471. * Gets a configuration value. A configuration value will be searched in
  4472. * browser's local storage first and if not found, a call to the server will be
  4473. * made.
  4474. *
  4475. * If value should not be cached and the up-to-date configuration value from
  4476. * right from the server is required, the third parameter should be `false`.
  4477. *
  4478. * @param {string} key Configuration key.
  4479. * @param {boolean} cached Configuration type.
  4480. * @param {Function} successCallback The callback to call after the value is received
  4481. *
  4482. * @return {void}
  4483. */
  4484. Functions.configGet = function (key, cached, successCallback) {
  4485. var isCached = typeof cached !== 'undefined' ? cached : true;
  4486. var value = localStorage.getItem(key);
  4487. if (isCached && value !== undefined && value !== null) {
  4488. return JSON.parse(value);
  4489. } // Result not found in local storage or ignored.
  4490. // Hitting the server.
  4491. $.ajax({
  4492. url: 'index.php?route=/config/get',
  4493. type: 'POST',
  4494. dataType: 'json',
  4495. data: {
  4496. 'ajax_request': true,
  4497. server: CommonParams.get('server'),
  4498. key: key
  4499. },
  4500. success: function success(data) {
  4501. // Updating value in local storage.
  4502. if (data.success) {
  4503. localStorage.setItem(key, JSON.stringify(data.value));
  4504. } else {
  4505. Functions.ajaxShowMessage(data.message);
  4506. } // Call the callback if it is defined
  4507. if (typeof successCallback === 'function') {
  4508. // Feed it the value previously saved like on async mode
  4509. successCallback(JSON.parse(localStorage.getItem(key)));
  4510. }
  4511. }
  4512. });
  4513. };
  4514. /**
  4515. * Return POST data as stored by Generator::linkOrButton
  4516. */
  4517. Functions.getPostData = function () {
  4518. var dataPost = this.attr('data-post'); // Strip possible leading ?
  4519. if (dataPost !== undefined && dataPost.substring(0, 1) === '?') {
  4520. dataPost = dataPost.substr(1);
  4521. }
  4522. return dataPost;
  4523. };
  4524. jQuery.fn.getPostData = Functions.getPostData;