123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853585458555856585758585859586058615862586358645865586658675868586958705871587258735874587558765877587858795880588158825883588458855886588758885889589058915892589358945895589658975898589959005901590259035904590559065907590859095910591159125913591459155916591759185919592059215922592359245925592659275928592959305931593259335934593559365937593859395940594159425943594459455946594759485949595059515952595359545955595659575958595959605961596259635964596559665967596859695970597159725973597459755976597759785979598059815982598359845985598659875988598959905991599259935994599559965997599859996000600160026003600460056006600760086009601060116012601360146015601660176018601960206021602260236024602560266027602860296030603160326033603460356036603760386039604060416042604360446045604660476048604960506051605260536054605560566057605860596060606160626063606460656066606760686069607060716072607360746075607660776078607960806081608260836084608560866087608860896090609160926093609460956096609760986099610061016102610361046105610661076108610961106111611261136114611561166117611861196120612161226123612461256126612761286129613061316132613361346135613661376138613961406141614261436144614561466147614861496150615161526153615461556156615761586159616061616162616361646165616661676168616961706171617261736174617561766177617861796180618161826183618461856186618761886189619061916192619361946195619661976198619962006201620262036204620562066207620862096210621162126213621462156216621762186219622062216222622362246225622662276228622962306231623262336234623562366237623862396240624162426243624462456246624762486249625062516252625362546255625662576258625962606261626262636264626562666267626862696270627162726273627462756276627762786279628062816282628362846285628662876288628962906291629262936294629562966297629862996300630163026303630463056306630763086309631063116312631363146315631663176318631963206321632263236324632563266327632863296330633163326333633463356336633763386339634063416342634363446345634663476348634963506351635263536354635563566357635863596360636163626363636463656366636763686369637063716372637363746375637663776378637963806381638263836384638563866387638863896390639163926393639463956396639763986399640064016402640364046405640664076408640964106411641264136414641564166417641864196420642164226423642464256426642764286429643064316432643364346435643664376438643964406441644264436444644564466447644864496450645164526453645464556456645764586459646064616462646364646465646664676468646964706471647264736474647564766477647864796480648164826483648464856486648764886489649064916492649364946495649664976498649965006501650265036504650565066507650865096510651165126513651465156516651765186519652065216522652365246525652665276528652965306531653265336534653565366537653865396540654165426543654465456546654765486549655065516552655365546555655665576558655965606561656265636564656565666567656865696570657165726573657465756576657765786579658065816582658365846585658665876588658965906591659265936594659565966597659865996600660166026603660466056606660766086609661066116612661366146615661666176618661966206621662266236624662566266627662866296630663166326633663466356636663766386639664066416642664366446645664666476648664966506651665266536654665566566657665866596660666166626663666466656666666766686669667066716672667366746675667666776678667966806681668266836684668566866687668866896690669166926693669466956696669766986699670067016702670367046705670667076708670967106711671267136714671567166717671867196720672167226723672467256726672767286729673067316732673367346735673667376738673967406741674267436744674567466747674867496750675167526753675467556756675767586759676067616762676367646765676667676768676967706771677267736774677567766777677867796780678167826783678467856786678767886789679067916792679367946795679667976798679968006801680268036804680568066807680868096810681168126813681468156816681768186819682068216822682368246825682668276828682968306831683268336834683568366837683868396840684168426843684468456846684768486849685068516852685368546855685668576858685968606861686268636864686568666867686868696870687168726873687468756876687768786879688068816882688368846885688668876888688968906891689268936894689568966897689868996900690169026903690469056906690769086909691069116912691369146915691669176918691969206921692269236924692569266927692869296930693169326933693469356936693769386939694069416942694369446945694669476948694969506951695269536954695569566957695869596960696169626963696469656966696769686969697069716972697369746975697669776978697969806981698269836984698569866987698869896990699169926993699469956996699769986999700070017002700370047005700670077008700970107011701270137014701570167017701870197020702170227023702470257026702770287029703070317032703370347035703670377038703970407041704270437044704570467047704870497050705170527053705470557056705770587059706070617062706370647065706670677068706970707071707270737074707570767077707870797080708170827083708470857086708770887089709070917092709370947095709670977098709971007101710271037104710571067107710871097110711171127113711471157116711771187119712071217122712371247125712671277128712971307131713271337134713571367137713871397140714171427143714471457146714771487149715071517152715371547155715671577158715971607161716271637164716571667167716871697170717171727173717471757176717771787179718071817182718371847185718671877188718971907191719271937194719571967197719871997200720172027203720472057206720772087209721072117212721372147215721672177218721972207221722272237224722572267227722872297230723172327233723472357236723772387239724072417242724372447245724672477248724972507251725272537254725572567257725872597260726172627263726472657266726772687269727072717272727372747275727672777278727972807281728272837284728572867287728872897290729172927293729472957296729772987299730073017302730373047305730673077308730973107311731273137314731573167317731873197320732173227323732473257326732773287329733073317332733373347335733673377338733973407341734273437344734573467347734873497350735173527353735473557356735773587359736073617362736373647365736673677368736973707371737273737374737573767377737873797380738173827383738473857386738773887389739073917392739373947395739673977398739974007401740274037404740574067407740874097410741174127413741474157416741774187419742074217422742374247425742674277428742974307431743274337434743574367437743874397440744174427443744474457446744774487449745074517452745374547455745674577458745974607461746274637464746574667467746874697470747174727473747474757476747774787479748074817482748374847485748674877488748974907491749274937494749574967497749874997500750175027503750475057506750775087509751075117512751375147515751675177518751975207521752275237524752575267527752875297530753175327533753475357536753775387539754075417542754375447545754675477548754975507551755275537554755575567557755875597560756175627563756475657566756775687569757075717572757375747575757675777578757975807581758275837584758575867587758875897590759175927593759475957596759775987599760076017602760376047605760676077608760976107611761276137614761576167617761876197620762176227623762476257626762776287629763076317632763376347635763676377638763976407641764276437644764576467647764876497650765176527653765476557656765776587659766076617662766376647665766676677668766976707671767276737674767576767677767876797680768176827683768476857686768776887689769076917692769376947695769676977698769977007701770277037704770577067707770877097710771177127713771477157716771777187719772077217722772377247725772677277728772977307731773277337734773577367737773877397740774177427743774477457746774777487749775077517752775377547755775677577758775977607761776277637764776577667767776877697770777177727773777477757776777777787779778077817782778377847785778677877788778977907791779277937794779577967797779877997800780178027803780478057806780778087809781078117812781378147815781678177818781978207821782278237824782578267827782878297830783178327833783478357836783778387839784078417842784378447845784678477848784978507851785278537854785578567857785878597860786178627863786478657866786778687869787078717872787378747875787678777878787978807881788278837884788578867887788878897890789178927893789478957896789778987899790079017902790379047905790679077908790979107911791279137914791579167917791879197920792179227923792479257926792779287929793079317932793379347935793679377938793979407941794279437944794579467947794879497950795179527953795479557956795779587959796079617962796379647965796679677968796979707971797279737974797579767977797879797980798179827983798479857986798779887989799079917992799379947995799679977998799980008001800280038004800580068007800880098010801180128013801480158016801780188019802080218022802380248025802680278028802980308031803280338034803580368037803880398040804180428043804480458046804780488049805080518052805380548055805680578058805980608061806280638064806580668067806880698070807180728073807480758076807780788079808080818082808380848085808680878088808980908091809280938094809580968097809880998100810181028103810481058106810781088109811081118112811381148115811681178118811981208121812281238124812581268127812881298130813181328133813481358136813781388139814081418142814381448145814681478148814981508151815281538154815581568157815881598160816181628163816481658166816781688169817081718172817381748175817681778178817981808181818281838184818581868187818881898190819181928193819481958196819781988199820082018202820382048205820682078208820982108211821282138214821582168217821882198220822182228223822482258226822782288229823082318232823382348235823682378238823982408241824282438244824582468247824882498250825182528253825482558256825782588259826082618262826382648265826682678268826982708271827282738274827582768277827882798280828182828283828482858286828782888289829082918292829382948295829682978298829983008301830283038304830583068307830883098310831183128313831483158316831783188319832083218322832383248325832683278328832983308331833283338334833583368337833883398340834183428343834483458346834783488349835083518352835383548355835683578358835983608361836283638364836583668367836883698370837183728373837483758376837783788379838083818382838383848385838683878388838983908391839283938394839583968397839883998400840184028403840484058406840784088409841084118412841384148415841684178418841984208421842284238424842584268427842884298430843184328433843484358436843784388439844084418442844384448445844684478448844984508451845284538454845584568457845884598460846184628463846484658466846784688469847084718472847384748475847684778478847984808481848284838484848584868487848884898490849184928493849484958496849784988499850085018502850385048505850685078508850985108511851285138514851585168517851885198520852185228523852485258526852785288529853085318532853385348535853685378538853985408541854285438544854585468547854885498550855185528553855485558556855785588559856085618562856385648565856685678568856985708571857285738574857585768577857885798580858185828583858485858586858785888589859085918592859385948595859685978598859986008601860286038604860586068607860886098610861186128613861486158616861786188619862086218622862386248625862686278628862986308631863286338634863586368637863886398640864186428643864486458646864786488649865086518652865386548655865686578658865986608661866286638664866586668667866886698670867186728673867486758676867786788679868086818682868386848685868686878688868986908691869286938694869586968697869886998700870187028703870487058706870787088709871087118712871387148715871687178718871987208721872287238724872587268727872887298730873187328733873487358736873787388739874087418742874387448745874687478748874987508751875287538754875587568757875887598760876187628763876487658766876787688769877087718772877387748775877687778778877987808781878287838784878587868787878887898790879187928793879487958796879787988799880088018802880388048805880688078808880988108811881288138814881588168817881888198820882188228823882488258826882788288829883088318832883388348835883688378838883988408841884288438844884588468847884888498850885188528853885488558856885788588859886088618862886388648865886688678868886988708871887288738874887588768877887888798880888188828883888488858886888788888889889088918892889388948895889688978898889989008901890289038904890589068907890889098910891189128913891489158916891789188919892089218922892389248925892689278928892989308931893289338934893589368937893889398940894189428943894489458946894789488949895089518952895389548955895689578958895989608961896289638964896589668967896889698970897189728973897489758976897789788979898089818982898389848985898689878988898989908991899289938994899589968997899889999000900190029003900490059006900790089009901090119012901390149015901690179018901990209021902290239024902590269027902890299030903190329033903490359036903790389039904090419042904390449045904690479048904990509051905290539054905590569057905890599060906190629063906490659066906790689069907090719072907390749075907690779078907990809081908290839084908590869087908890899090909190929093909490959096909790989099910091019102910391049105910691079108910991109111911291139114911591169117911891199120912191229123912491259126912791289129913091319132913391349135913691379138913991409141914291439144914591469147914891499150915191529153915491559156915791589159916091619162916391649165916691679168916991709171917291739174917591769177917891799180918191829183918491859186918791889189919091919192919391949195919691979198919992009201920292039204920592069207920892099210921192129213921492159216921792189219922092219222922392249225922692279228922992309231923292339234923592369237923892399240924192429243924492459246924792489249925092519252925392549255925692579258925992609261926292639264926592669267926892699270927192729273927492759276927792789279928092819282928392849285928692879288928992909291929292939294929592969297929892999300930193029303930493059306930793089309931093119312931393149315931693179318931993209321932293239324932593269327932893299330933193329333933493359336933793389339934093419342934393449345934693479348934993509351935293539354935593569357935893599360936193629363936493659366936793689369937093719372937393749375937693779378937993809381938293839384938593869387938893899390939193929393939493959396939793989399940094019402940394049405940694079408940994109411941294139414941594169417941894199420942194229423942494259426942794289429943094319432943394349435943694379438943994409441944294439444944594469447944894499450945194529453945494559456945794589459946094619462946394649465946694679468946994709471947294739474947594769477947894799480948194829483948494859486948794889489949094919492949394949495949694979498949995009501950295039504950595069507950895099510951195129513951495159516951795189519952095219522952395249525952695279528952995309531953295339534953595369537953895399540954195429543954495459546954795489549955095519552955395549555955695579558955995609561956295639564956595669567956895699570957195729573957495759576957795789579958095819582958395849585958695879588958995909591959295939594959595969597959895999600960196029603960496059606960796089609961096119612961396149615961696179618961996209621962296239624962596269627962896299630963196329633963496359636963796389639964096419642964396449645964696479648964996509651965296539654965596569657965896599660966196629663966496659666966796689669967096719672967396749675967696779678967996809681968296839684968596869687968896899690969196929693969496959696969796989699970097019702970397049705970697079708970997109711971297139714971597169717971897199720972197229723972497259726972797289729973097319732973397349735973697379738973997409741974297439744974597469747974897499750975197529753975497559756975797589759976097619762976397649765976697679768976997709771977297739774977597769777977897799780978197829783978497859786978797889789979097919792979397949795979697979798979998009801980298039804980598069807980898099810981198129813981498159816981798189819982098219822982398249825982698279828982998309831983298339834983598369837983898399840984198429843984498459846984798489849985098519852985398549855985698579858985998609861986298639864986598669867986898699870987198729873987498759876987798789879988098819882988398849885988698879888988998909891989298939894989598969897989898999900990199029903990499059906990799089909991099119912991399149915991699179918991999209921992299239924992599269927992899299930993199329933993499359936993799389939994099419942994399449945994699479948994999509951995299539954995599569957995899599960996199629963996499659966996799689969997099719972997399749975997699779978997999809981998299839984998599869987998899899990999199929993999499959996999799989999100001000110002100031000410005100061000710008100091001010011100121001310014100151001610017100181001910020100211002210023100241002510026100271002810029100301003110032100331003410035100361003710038100391004010041100421004310044100451004610047100481004910050100511005210053100541005510056100571005810059100601006110062100631006410065100661006710068100691007010071100721007310074100751007610077100781007910080100811008210083100841008510086100871008810089100901009110092100931009410095100961009710098100991010010101101021010310104101051010610107101081010910110101111011210113101141011510116101171011810119101201012110122101231012410125101261012710128101291013010131101321013310134101351013610137101381013910140101411014210143101441014510146101471014810149101501015110152101531015410155101561015710158101591016010161101621016310164101651016610167101681016910170101711017210173101741017510176101771017810179101801018110182101831018410185101861018710188101891019010191101921019310194101951019610197101981019910200102011020210203102041020510206102071020810209102101021110212102131021410215102161021710218102191022010221102221022310224102251022610227102281022910230102311023210233102341023510236102371023810239102401024110242102431024410245102461024710248102491025010251102521025310254102551025610257102581025910260102611026210263102641026510266102671026810269102701027110272102731027410275102761027710278102791028010281102821028310284102851028610287102881028910290102911029210293102941029510296102971029810299103001030110302103031030410305103061030710308103091031010311103121031310314103151031610317103181031910320103211032210323103241032510326103271032810329103301033110332103331033410335103361033710338103391034010341103421034310344103451034610347103481034910350103511035210353103541035510356103571035810359103601036110362103631036410365103661036710368103691037010371103721037310374103751037610377103781037910380103811038210383103841038510386103871038810389103901039110392103931039410395103961039710398103991040010401104021040310404104051040610407104081040910410104111041210413104141041510416104171041810419104201042110422104231042410425104261042710428104291043010431104321043310434104351043610437104381043910440104411044210443104441044510446104471044810449104501045110452104531045410455104561045710458104591046010461104621046310464104651046610467104681046910470104711047210473104741047510476104771047810479104801048110482104831048410485104861048710488104891049010491104921049310494104951049610497104981049910500105011050210503105041050510506105071050810509105101051110512105131051410515105161051710518105191052010521105221052310524105251052610527105281052910530105311053210533105341053510536105371053810539105401054110542105431054410545105461054710548105491055010551105521055310554105551055610557105581055910560105611056210563105641056510566105671056810569105701057110572105731057410575105761057710578105791058010581105821058310584105851058610587105881058910590105911059210593105941059510596105971059810599106001060110602106031060410605106061060710608106091061010611106121061310614106151061610617106181061910620106211062210623106241062510626106271062810629106301063110632106331063410635106361063710638106391064010641106421064310644106451064610647106481064910650106511065210653106541065510656106571065810659106601066110662106631066410665106661066710668106691067010671106721067310674106751067610677106781067910680106811068210683106841068510686106871068810689106901069110692106931069410695106961069710698106991070010701107021070310704107051070610707107081070910710107111071210713107141071510716107171071810719107201072110722107231072410725107261072710728107291073010731107321073310734107351073610737107381073910740107411074210743107441074510746107471074810749107501075110752107531075410755107561075710758107591076010761107621076310764107651076610767107681076910770107711077210773107741077510776107771077810779107801078110782107831078410785107861078710788107891079010791107921079310794107951079610797107981079910800108011080210803108041080510806108071080810809108101081110812108131081410815108161081710818108191082010821108221082310824108251082610827108281082910830108311083210833108341083510836108371083810839108401084110842108431084410845108461084710848108491085010851108521085310854108551085610857108581085910860108611086210863108641086510866108671086810869108701087110872108731087410875108761087710878108791088010881108821088310884108851088610887108881088910890108911089210893108941089510896108971089810899109001090110902109031090410905109061090710908109091091010911109121091310914109151091610917109181091910920109211092210923109241092510926109271092810929109301093110932109331093410935109361093710938109391094010941109421094310944109451094610947109481094910950109511095210953109541095510956 |
- (function($){
- /**
- * The main builder interface class.
- *
- * @since 1.0
- * @class FLBuilder
- */
- FLBuilder = {
- /**
- * An instance of FLBuilderPreview for working
- * with the current live preview.
- *
- * @since 1.3.3
- * @property {FLBuilderPreview} preview
- */
- preview : null,
- /**
- * An instance of FLLightbox for displaying a list
- * of actions a user can take such as publish or cancel.
- *
- * @since 1.0
- * @access private
- * @property {FLLightbox} _actionsLightbox
- */
- _actionsLightbox : null,
- /**
- * An array of AJAX data that needs to be requested
- * after the current request has finished.
- *
- * @since 2.2
- * @property {Array} _ajaxQueue
- */
- _ajaxQueue : [],
- /**
- * A reference to the current AJAX request object.
- *
- * @since 2.2
- * @property {Object} _ajaxRequest
- */
- _ajaxRequest : null,
- /**
- * An object that holds data for column resizing.
- *
- * @since 1.6.4
- * @access private
- * @property {Object} _colResizeData
- */
- _colResizeData : null,
- /**
- * A flag for whether a column is being resized or not.
- *
- * @since 1.6.4
- * @access private
- * @property {Boolean} _colResizing
- */
- _colResizing : false,
- /**
- * The CSS class of the main content wrapper for the
- * current layout that is being worked on.
- *
- * @since 1.0
- * @access private
- * @property {String} _contentClass
- */
- _contentClass : false,
- /**
- * Whether dragging has been enabled or not.
- *
- * @since 1.0
- * @access private
- * @property {Boolean} _dragEnabled
- */
- _dragEnabled : false,
- /**
- * Whether an element is currently being dragged or not.
- *
- * @since 1.0
- * @access private
- * @property {Boolean} _dragging
- */
- _dragging : false,
- /**
- * The initial scroll top of the window when a drag starts.
- * Used to reset the scroll top when a drag is cancelled.
- *
- * @since 1.0
- * @access private
- * @property {Boolean} _dragging
- */
- _dragInitialScrollTop : 0,
- /**
- * The URL to redirect to when a user leaves the builder.
- *
- * @since 1.0
- * @access private
- * @property {String} _exitUrl
- */
- _exitUrl : null,
- /**
- * An instance of FLBuilderAJAXLayout for rendering
- * the layout via AJAX.
- *
- * @since 1.7
- * @property {FLBuilderAJAXLayout} _layout
- */
- _layout : null,
- /**
- * An array of layout data that needs to be rendered
- * after the current rendered is finished.
- *
- * @since 2.2
- * @property {Array} _layoutQueue
- */
- _layoutQueue : [],
- /**
- * A cached copy of custom layout CSS that is used to
- * revert changes if the cancel button is clicked.
- *
- * @since 1.7
- * @property {String} _layoutSettingsCSSCache
- */
- _layoutSettingsCSSCache : null,
- /**
- * A timeout for throttling custom layout CSS changes.
- *
- * @since 1.7
- * @property {Object} _layoutSettingsCSSTimeout
- */
- _layoutSettingsCSSTimeout : null,
- /**
- * An instance of FLLightbox for displaying settings.
- *
- * @since 1.0
- * @access private
- * @property {FLLightbox} _lightbox
- */
- _lightbox : null,
- /**
- * A timeout for refreshing the height of lightbox scrollbars
- * in case the content changes from dynamic settings.
- *
- * @since 1.0
- * @access private
- * @property {Object} _lightboxScrollbarTimeout
- */
- _lightboxScrollbarTimeout : null,
- /**
- * An array that's used to cache which module settings
- * CSS and JS assets have already been loaded so they
- * are only loaded once.
- *
- * @since 1.0
- * @access private
- * @property {Array} _loadedModuleAssets
- */
- _loadedModuleAssets : [],
- /**
- * An object used to store module settings helpers.
- *
- * @since 1.0
- * @access private
- * @property {Object} _moduleHelpers
- */
- _moduleHelpers : {},
- /**
- * An instance of wp.media used to select multiple photos.
- *
- * @since 1.0
- * @access private
- * @property {Object} _multiplePhotoSelector
- */
- _multiplePhotoSelector : null,
- /**
- * A jQuery reference to a group that a new column
- * should be added to once it's finished rendering.
- *
- * @since 2.0
- * @access private
- * @property {Object} _newColParent
- */
- _newColParent : null,
- /**
- * The position a column should be added to within
- * a group once it finishes rendering.
- *
- * @since 2.0
- * @access private
- * @property {Number} _newColPosition
- */
- _newColPosition : 0,
- /**
- * A jQuery reference to a row that a new column group
- * should be added to once it's finished rendering.
- *
- * @since 1.0
- * @access private
- * @property {Object} _newColGroupParent
- */
- _newColGroupParent : null,
- /**
- * The position a column group should be added to within
- * a row once it finishes rendering.
- *
- * @since 1.0
- * @access private
- * @property {Number} _newColGroupPosition
- */
- _newColGroupPosition : 0,
- /**
- * A jQuery reference to a new module's parent.
- *
- * @since 1.7
- * @access private
- * @property {Object} _newModuleParent
- */
- _newModuleParent : null,
- /**
- * The position a new module should be added at once
- * it finishes rendering.
- *
- * @since 1.7
- * @access private
- * @property {Number} _newModulePosition
- */
- _newModulePosition : 0,
- /**
- * The position a row should be added to within
- * the layout once it finishes rendering.
- *
- * @since 1.0
- * @access private
- * @property {Number} _newRowPosition
- */
- _newRowPosition : 0,
- /**
- * The ID of a template that the user has selected.
- *
- * @since 1.0
- * @access private
- * @property {Number} _selectedTemplateId
- */
- _selectedTemplateId : null,
- /**
- * The type of template that the user has selected.
- * Possible values are "core" or "user".
- *
- * @since 1.0
- * @access private
- * @property {String} _selectedTemplateType
- */
- _selectedTemplateType : null,
- /**
- * An instance of wp.media used to select a single photo.
- *
- * @since 1.0
- * @access private
- * @property {Object} _singlePhotoSelector
- */
- _singlePhotoSelector : null,
- /**
- * An instance of wp.media used to select a single video.
- *
- * @since 1.0
- * @access private
- * @property {Object} _singleVideoSelector
- */
- _singleVideoSelector : null,
- /**
- * An instance of wp.media used to select a multiple audio.
- *
- * @since 1.0
- * @access private
- * @property {Object} _multipleAudiosSelector
- */
- _multipleAudiosSelector : null,
- /**
- * @since 2.2.5
- */
- _codeDisabled: false,
- /**
- * Misc data container
- * @since 2.6
- * @access private
- */
- _sandbox: {},
- /**
- * Flag whether to clear preview or not
- * @access private
- */
- _publishAndRemain: false,
- _shapesEdited: false,
- /**
- * Initializes the builder interface.
- *
- * @since 1.0
- * @access private
- * @method _init
- */
- _init: function()
- {
- FLBuilder.UIIFrame.init();
- FLBuilder._initJQueryReadyFix();
- FLBuilder._initGlobalErrorHandling();
- FLBuilder._initPostLock();
- FLBuilder._initClassNames();
- FLBuilder._initMediaUploader();
- FLBuilder._initOverflowFix();
- FLBuilder._initScrollbars();
- FLBuilder._initLightboxes();
- FLBuilder._initDropTargets();
- FLBuilder._initSortables();
- FLBuilder._initStrings();
- FLBuilder._initSanityChecks();
- FLBuilder._initTipTips();
- FLBuilder._initTinyMCE();
- FLBuilder._bindEvents();
- FLBuilder._bindOverlayEvents();
- FLBuilder._setupEmptyLayout();
- FLBuilder._highlightEmptyCols();
- FLBuilder._checkEnv();
- FLBuilder._initColorScheme();
- FLBuilder.addHook('didInitUI', FLBuilder._showTourOrTemplates.bind(FLBuilder) );
- FLBuilder.addHook('endEditingSession', FLBuilder._doStats.bind(this) );
- FLBuilder.triggerHook('init');
- },
- /**
- * Prevent errors thrown in jQuery's ready function
- * from breaking subsequent ready calls.
- *
- * @since 1.4.6
- * @access private
- * @method _initJQueryReadyFix
- */
- _initJQueryReadyFix: function()
- {
- if ( FLBuilderConfig.debug ) {
- return;
- }
- jQuery.fn.oldReady = jQuery.fn.ready;
- jQuery.fn.ready = function( fn ) {
- return jQuery.fn.oldReady( function() {
- try {
- if ( 'function' == typeof fn ) {
- fn( $ );
- }
- }
- catch ( e ){
- FLBuilder.logError( e );
- }
- });
- };
- },
- _initSanityChecks: function() {
- if ( FLBuilderConfig.uploadPath && typeof FLBuilderLayout === 'undefined' ) {
- url = '<a href="' + FLBuilderConfig.uploadUrl + '">wp-admin -> Settings -> Media</a>';
- FLBuilder.alert( '<strong>Critcal Error</strong><p style="font-size:15px;">Please go to ' + url + ' and make sure uploads folder settings is blank</p>');
- $('.fl-builder-alert-close', window.parent.document).hide()
- }
- },
- /**
- * Try to prevent errors from third party plugins
- * from breaking the builder.
- *
- * @since 1.4.6
- * @access private
- * @method _initGlobalErrorHandling
- */
- _initGlobalErrorHandling: function()
- {
- if ( FLBuilderConfig.debug ) {
- return;
- }
- window.onerror = function( message, file, line, col, error ) {
- FLBuilder.logGlobalError( message, file, line, col, error );
- return true;
- };
- },
- /**
- * Send a wp.heartbeat request to lock editing of this
- * post so it can only be edited by the current user.
- *
- * @since 1.0.6
- * @access private
- * @method _initPostLock
- */
- _initPostLock: function()
- {
- if(typeof wp.heartbeat != 'undefined') {
- wp.heartbeat.interval(120);
- wp.heartbeat.enqueue('fl_builder_post_lock', {
- post_id: FLBuilderConfig.postId
- });
- }
- },
- /**
- * Initializes html and body classes as well as the
- * builder content class for this post.
- *
- * @since 1.0
- * @access private
- * @method _initClassNames
- */
- _initClassNames: function()
- {
- var html = $( 'html' ).add( 'html', window.parent.document ),
- body = $( 'body' ).add( 'body', window.parent.document );
- html.addClass( 'fl-builder-edit' );
- body.addClass( 'fl-builder' );
- if ( FLBuilderConfig.simpleUi ) {
- body.addClass( 'fl-builder-simple' );
- }
- FLBuilder._contentClass = '.fl-builder-content-' + FLBuilderConfig.postId;
- $( FLBuilder._contentClass ).addClass( 'fl-builder-content-editing' );
- },
- /**
- * Initializes the WordPress media uploader so any files
- * uploaded will be attached to the current post.
- *
- * @since 1.2.2
- * @access private
- * @method _initMediaUploader
- */
- _initMediaUploader: function()
- {
- wp.media.model.settings.post.id = FLBuilderConfig.postId;
- },
- /**
- * Third party themes that set their content wrappers to
- * overflow:hidden break builder overlays. We set them
- * to overflow:visible while editing.
- *
- * @since 1.0
- * @access private
- * @method _initOverflowFix
- */
- _initOverflowFix: function()
- {
- $(FLBuilder._contentClass).parents().css('overflow', 'visible');
- },
- /**
- * Initializes Nano Scroller scrollbars for the
- * builder interface.
- *
- * @since 1.0
- * @access private
- * @method _initScrollbars
- */
- _initScrollbars: function()
- {
- var scrollers = $('.fl-nanoscroller', window.parent.document).nanoScroller({
- documentContext: window.parent.document,
- alwaysVisible: false,
- preventPageScrolling: true,
- paneClass: 'fl-nanoscroller-pane',
- sliderClass: 'fl-nanoscroller-slider',
- contentClass: 'fl-nanoscroller-content'
- }),
- settingsScroller = scrollers.filter('.fl-builder-settings-fields'),
- pane = settingsScroller.find('.fl-nanoscroller-pane');
- if ( pane.length ) {
- var display = pane.get(0).style.display;
- var content = settingsScroller.find('.fl-nanoscroller-content');
- if ( display === "none" ) {
- content.removeClass('has-scrollbar');
- } else {
- content.addClass('has-scrollbar');
- }
- }
- },
- /**
- * Initializes jQuery sortables for drag and drop.
- *
- * @since 1.0
- * @access private
- * @method _initSortables
- */
- _initSortables: function()
- {
- var defaults = {
- frame: null,
- appendTo: FLBuilder._contentClass,
- scroll: true,
- cursor: 'move',
- cursorAt: {
- left: 85,
- top: 20
- },
- distance: 1,
- helper: FLBuilder._blockDragHelper,
- start : FLBuilder._blockDragStart,
- sort: FLBuilder._blockDragSort,
- change: FLBuilder._blockDragChange,
- stop: FLBuilder._blockDragStop,
- placeholder: 'fl-builder-drop-zone',
- tolerance: 'intersect'
- },
- rowConnections = '',
- columnConnections = '',
- moduleConnections = '';
- // Adjust defaults for the iFrame UI.
- if ( FLBuilder.UIIFrame.isEnabled() ) {
- defaults.frame = $( '#fl-builder-ui-iframe', window.parent.document );
- defaults.appendTo = $( 'body', window.parent.document );
- defaults.scroll = false;
- }
- // Module Connections.
- if ( 'row' == FLBuilderConfig.userTemplateType ) {
- moduleConnections = FLBuilder._contentClass + ' .fl-row:not(.fl-builder-node-loading) .fl-col-group-drop-target, ' +
- FLBuilder._contentClass + ' .fl-row:not(.fl-builder-node-loading) .fl-col-drop-target, ' +
- FLBuilder._contentClass + ' .fl-row:not(.fl-builder-node-loading) .fl-col-content, ' +
- FLBuilder._contentClass + ' .fl-row:not(.fl-builder-node-loading) .fl-module[data-accepts]:not(:has(> .fl-module-content)), ' +
- FLBuilder._contentClass + ' .fl-row:not(.fl-builder-node-loading) .fl-module[data-accepts] > .fl-module-content';
- }
- else if ( 'column' == FLBuilderConfig.userTemplateType ) {
- moduleConnections = FLBuilder._contentClass + ' .fl-col-group-drop-target, ' +
- FLBuilder._contentClass + ' .fl-col-drop-target, ' +
- FLBuilder._contentClass + ' .fl-col-content, ' +
- FLBuilder._contentClass + ' .fl-module[data-accepts]:not(:has(> .fl-module-content)), ' +
- FLBuilder._contentClass + ' .fl-module[data-accepts] > .fl-module-content';
- }
- else if ( 'module' == FLBuilderConfig.userTemplateType ) {
- moduleConnections = FLBuilder._contentClass + ' .fl-module[data-accepts]:not(:has(> .fl-module-content)), ' +
- FLBuilder._contentClass + ' .fl-module[data-accepts] > .fl-module-content';
- }
- else {
- moduleConnections = FLBuilder._contentClass + ' .fl-row-drop-target, ' +
- FLBuilder._contentClass + ' .fl-row:not(.fl-builder-node-loading) .fl-col-group-drop-target, ' +
- FLBuilder._contentClass + ' .fl-row:not(.fl-builder-node-loading) .fl-col-drop-target, ' +
- FLBuilder._contentClass + ' .fl-row:not(.fl-builder-node-loading) .fl-col:not(.fl-builder-node-loading):not(.fl-node-global) .fl-col-content, ' +
- FLBuilder._contentClass + ' .fl-row:not(.fl-builder-node-loading) .fl-module[data-accepts]:not(:has(> .fl-module-content)):not(.fl-node-global), ' +
- FLBuilder._contentClass + ' .fl-row:not(.fl-builder-node-loading) .fl-module[data-accepts]:not(.fl-node-global) > .fl-module-content';
- }
- // Column Connections.
- if ( 'row' == FLBuilderConfig.userTemplateType ) {
- columnConnections = FLBuilder._contentClass + ' .fl-row:not(.fl-builder-node-loading) .fl-col-group-drop-target, ' +
- FLBuilder._contentClass + ' .fl-row:not(.fl-builder-node-loading) .fl-col-drop-target';
- }
- else {
- columnConnections = FLBuilder._contentClass + ' .fl-row-drop-target, ' +
- FLBuilder._contentClass + ' .fl-row:not(.fl-builder-node-loading) .fl-col-group-drop-target, ' +
- FLBuilder._contentClass + ' .fl-row:not(.fl-builder-node-loading) .fl-col-drop-target';
- }
- // Row Connections.
- if ( FLBuilderConfig.nestedColumns ) {
- rowConnections = moduleConnections;
- }
- else if ( 'row' == FLBuilderConfig.userTemplateType ) {
- rowConnections = FLBuilder._contentClass + ' .fl-row:not(.fl-builder-node-loading) .fl-col-group-drop-target, ' +
- FLBuilder._contentClass + ' .fl-row:not(.fl-builder-node-loading) .fl-col-drop-target';
- }
- else {
- rowConnections = FLBuilder._contentClass + ' .fl-row-drop-target, ' +
- FLBuilder._contentClass + ' .fl-row:not(.fl-builder-node-loading) .fl-col-group-drop-target, ' +
- FLBuilder._contentClass + ' .fl-row:not(.fl-builder-node-loading) .fl-col-drop-target';
- }
- // Row layouts from the builder panel.
- $('.fl-builder-rows', window.parent.document).sortable($.extend({}, defaults, {
- connectWith: rowConnections,
- items: '.fl-builder-block-row',
- stop: FLBuilder._rowDragStop
- }));
- // Row templates from the builder panel.
- $('.fl-builder-row-templates', window.parent.document).sortable($.extend({}, defaults, {
- connectWith: FLBuilder._contentClass + ' .fl-row-drop-target',
- items: '.fl-builder-block-row-template:not(.fl-builder-block-disabled)',
- stop: FLBuilder._nodeTemplateDragStop
- }));
- // Saved rows from the builder panel.
- $('.fl-builder-saved-rows', window.parent.document).sortable($.extend({}, defaults, {
- cancel: '.fl-builder-node-template-actions, .fl-builder-node-template-edit, .fl-builder-node-template-delete',
- connectWith: FLBuilder._contentClass + ' .fl-row-drop-target',
- items: '.fl-builder-block-saved-row',
- stop: FLBuilder._nodeTemplateDragStop
- }));
- // Saved columns from the builder panel.
- $('.fl-builder-saved-columns', window.parent.document).sortable($.extend({}, defaults, {
- cancel: '.fl-builder-node-template-actions, .fl-builder-node-template-edit, .fl-builder-node-template-delete',
- connectWith: columnConnections,
- items: '.fl-builder-block-saved-column',
- stop: FLBuilder._nodeTemplateDragStop
- }));
- // Modules from the builder panel.
- $('.fl-builder-modules, .fl-builder-widgets', window.parent.document).sortable($.extend({}, defaults, {
- connectWith: moduleConnections,
- items: '.fl-builder-block-module:not(.fl-builder-block-disabled)',
- stop: FLBuilder._moduleDragStop
- }));
- // Module templates from the builder panel.
- $('.fl-builder-module-templates', window.parent.document).sortable($.extend({}, defaults, {
- connectWith: moduleConnections,
- items: '.fl-builder-block-module-template',
- stop: FLBuilder._nodeTemplateDragStop
- }));
- // Saved modules from the builder panel.
- $('.fl-builder-saved-modules', window.parent.document).sortable($.extend({}, defaults, {
- cancel: '.fl-builder-node-template-actions, .fl-builder-node-template-edit, .fl-builder-node-template-delete',
- connectWith: moduleConnections,
- items: '.fl-builder-block-saved-module',
- stop: FLBuilder._nodeTemplateDragStop
- }));
- // Rows
- $('.fl-row-sortable-proxy', window.parent.document).sortable($.extend({}, defaults, {
- connectWith: FLBuilder._contentClass + ' .fl-row-drop-target',
- helper: FLBuilder._rowDragHelper,
- start: FLBuilder._rowDragStart,
- stop: FLBuilder._rowDragStop
- }));
- // Columns
- $('.fl-col-sortable-proxy', window.parent.document).sortable($.extend({}, defaults, {
- connectWith: moduleConnections,
- helper: FLBuilder._colDragHelper,
- start: FLBuilder._colDragStart,
- stop: FLBuilder._colDragStop
- }));
- // Modules
- $('.fl-module-sortable-proxy', window.parent.document).sortable($.extend({}, defaults, {
- connectWith: moduleConnections,
- helper: FLBuilder._moduleDragHelper,
- start: FLBuilder._moduleDragStart,
- stop: FLBuilder._moduleDragStop
- }));
- // Modules and groups in columns.
- $(FLBuilder._contentClass + ' .fl-col-content').sortable($.extend({}, defaults, {
- cancel: '.fl-module, .fl-col-group',
- handle: '.fl-module-sortable-proxy',
- }));
- // Modules and groups in container modules WITHOUT a wrapper.
- $(FLBuilder._contentClass + ' .fl-module[data-accepts]:not(:has(> .fl-module-content))').sortable($.extend({}, defaults, {
- cancel: '.fl-module, .fl-col-group',
- handle: '.fl-module-sortable-proxy',
- }));
- // Modules and groups in container modules WITH a wrapper.
- $(FLBuilder._contentClass + ' .fl-module[data-accepts] > .fl-module-content').sortable($.extend({}, defaults, {
- cancel: '.fl-module, .fl-col-group',
- handle: '.fl-module-sortable-proxy',
- }));
- // Drop targets
- $(FLBuilder._contentClass + ' .fl-row-drop-target').sortable( defaults );
- $(FLBuilder._contentClass + ' .fl-col-group-drop-target').sortable( defaults );
- $(FLBuilder._contentClass + ' .fl-col-drop-target').sortable( defaults );
- },
- /**
- * Refreshes the items for all jQuery sortables so any
- * new items will be recognized.
- *
- * @since 2.2
- * @access private
- * @method _refreshSortables
- */
- _refreshSortables: function()
- {
- var sortables = $( '.ui-sortable' ).add( '.ui-sortable', window.parent.document );
- sortables.sortable( 'refresh' );
- sortables.sortable( 'refreshPositions' );
- },
- /**
- * Initializes text translation
- *
- * @since 1.0
- * @access private
- * @method _initStrings
- */
- _initStrings: function()
- {
- $.validator.messages.required = FLBuilderStrings.validateRequiredMessage;
- },
- /**
- * Binds most of the events for the builder interface.
- *
- * @since 1.0
- * @access private
- * @method _bindEvents
- */
- _bindEvents: function()
- {
- /* Links */
- $excludedLinks = $('.fl-builder-bar a, .fl-builder--content-library-panel a, .fl-page-nav .nav a'); // links in ui shouldn't be disabled.
- $('a').not($excludedLinks).on('click', FLBuilder._preventDefault);
- $('.fl-page-nav .nav a').on('click', FLBuilder._headerLinkClicked);
- $('body').on( 'click', '.fl-builder-content a', FLBuilder._preventDefault);
- $('body').on( 'mouseup', 'button.fl-builder-button', this._buttonMouseUp.bind(this) );
- /* Heartbeat */
- $(document).on('heartbeat-tick', FLBuilder._initPostLock);
- /* Unload Warning */
- $(window.parent).on('beforeunload', FLBuilder._warnBeforeUnload);
- /* Lite Version */
- $('body', window.parent.document).on( 'click', '.fl-builder-blocks-pro-expand', FLBuilder._toggleProModules);
- $('body', window.parent.document).on( 'click', '.fl-builder-upgrade-button', FLBuilder._upgradeClicked);
- /* Panel */
- $('.fl-builder-panel-actions .fl-builder-panel-close', window.parent.document).on('click', FLBuilder._closePanel);
- $('body', window.parent.document).on( 'mousedown', '.fl-builder-node-template-actions', FLBuilder._stopPropagation);
- $('body', window.parent.document).on( 'mousedown', '.fl-builder-node-template-edit', FLBuilder._stopPropagation);
- $('body', window.parent.document).on( 'mousedown', '.fl-builder-node-template-delete', FLBuilder._stopPropagation);
- $('body', window.parent.document).on( 'click', '.fl-builder-node-template-edit', FLBuilder._editNodeTemplateClicked);
- $('body', window.parent.document).on( 'click', '.fl-builder-node-template-delete', FLBuilder._deleteNodeTemplateClicked);
- $('body', window.parent.document).on( 'mousedown', '.fl-builder-block:not(.fl-builder-block-disabled)', FLBuilder._blockDragInit );
- $('body', window.parent.document).on('mouseup', FLBuilder._blockDragCancel);
- /* Actions Lightbox */
- $('body', window.parent.document).on( 'click', '.fl-builder-actions .fl-builder-cancel-button', FLBuilder._cancelButtonClicked);
- /* Layout and Global Settings */
- $('body', window.parent.document).on( 'click', '.fl-builder-layout-settings .fl-builder-settings-save', FLBuilder._saveLayoutSettingsClicked);
- $('body', window.parent.document).on( 'click', '.fl-builder-layout-settings .fl-builder-settings-cancel', FLBuilder._cancelLayoutSettingsClicked);
- $('body', window.parent.document).on( 'click', '.fl-builder-global-settings .fl-builder-settings-save', FLBuilder._saveGlobalSettingsClicked);
- $('body', window.parent.document).on( 'click', '.fl-builder-global-settings .fl-builder-settings-cancel', FLBuilder._cancelLayoutSettingsClicked);
- /* Template Panel Tab */
- $('body', window.parent.document).on( 'click', '.fl-user-template', FLBuilder._userTemplateClicked);
- $('body', window.parent.document).on( 'click', '.fl-user-template-edit', FLBuilder._editUserTemplateClicked);
- $('body', window.parent.document).on( 'click', '.fl-user-template-delete', FLBuilder._deleteUserTemplateClicked);
- $('body', window.parent.document).on( 'click', '.fl-builder-template-replace-button', FLBuilder._templateReplaceClicked);
- $('body', window.parent.document).on( 'click', '.fl-builder-template-append-button', FLBuilder._templateAppendClicked);
- $('body', window.parent.document).on( 'click', '.fl-builder-template-actions .fl-builder-cancel-button', FLBuilder._templateCancelClicked);
- /* User Template Settings */
- $('body', window.parent.document).on( 'click', '.fl-builder-user-template-settings .fl-builder-settings-save', FLBuilder._saveUserTemplateSettings);
- /* Welcome Actions */
- $('body', window.parent.document).on( 'click', '.fl-builder-no-tour-button', FLBuilder._noTourButtonClicked);
- $('body', window.parent.document).on( 'click', '.fl-builder-yes-tour-button', FLBuilder._yesTourButtonClicked);
- /* Alert Lightbox */
- $('body', window.parent.document).on( 'click', '.fl-builder-alert-close', FLBuilder._alertClose);
- /* General Overlays */
- FLBuilder._bindGeneralOverlayEvents();
- /* Node Templates */
- $('body', window.parent.document).on( 'click', '.fl-builder-settings-save-as', FLBuilder._showNodeTemplateSettings);
- $('body', window.parent.document).on( 'click', '.fl-builder-node-template-settings .fl-builder-settings-save', FLBuilder._saveNodeTemplate);
- /* Settings */
- $('body', window.parent.document).on( 'click', '.fl-builder-settings-tabs a', FLBuilder._settingsTabClicked);
- $('body', window.parent.document).on( 'show', '.fl-builder-settings-tabs a', FLBuilder._calculateSettingsTabsOverflow);
- $('body', window.parent.document).on( 'hide', '.fl-builder-settings-tabs a', FLBuilder._calculateSettingsTabsOverflow);
- $('body', window.parent.document).on( 'click', '.fl-builder-settings-cancel', FLBuilder._settingsCancelClicked);
- /* Settings Tabs Overflow menu */
- $('body', window.parent.document).on( 'click', '.fl-builder-settings-tabs-overflow-menu > a', FLBuilder._settingsTabsToOverflowMenuItemClicked.bind(this));
- $('body', window.parent.document).on( 'click', '.fl-builder-settings-tabs-more', FLBuilder._toggleTabsOverflowMenu.bind(this) );
- $('body', window.parent.document).on( 'click', '.fl-builder-settings-tabs-overflow-click-mask', FLBuilder._hideTabsOverflowMenu.bind(this));
- /* Tooltips */
- $('body', window.parent.document).on( 'mouseover', '.fl-help-tooltip-icon', FLBuilder._showHelpTooltip);
- $('body', window.parent.document).on( 'mouseout', '.fl-help-tooltip-icon', FLBuilder._hideHelpTooltip);
- /* Multiple Fields */
- $('body', window.parent.document).on( 'click', '.fl-builder-field-add', FLBuilder._addFieldClicked);
- $('body', window.parent.document).on( 'click', '.fl-builder-field-copy', FLBuilder._copyFieldClicked);
- $('body', window.parent.document).on( 'click', '.fl-builder-field-delete', FLBuilder._deleteFieldClicked);
- /* Photo Fields */
- $('body', window.parent.document).on( 'click', '.fl-photo-field .fl-photo-select', FLBuilder._selectSinglePhoto);
- $('body', window.parent.document).on( 'click', '.fl-photo-field .fl-photo-edit', FLBuilder._selectSinglePhoto);
- $('body', window.parent.document).on( 'click', '.fl-photo-field .fl-photo-replace', FLBuilder._selectSinglePhoto);
- $('body', window.parent.document).on( 'click', '.fl-photo-field .fl-photo-remove', FLBuilder._singlePhotoRemoved);
- /* Multiple Photo Fields */
- $('body', window.parent.document).on( 'click', '.fl-multiple-photos-field .fl-multiple-photos-select', FLBuilder._selectMultiplePhotos);
- $('body', window.parent.document).on( 'click', '.fl-multiple-photos-field .fl-multiple-photos-edit', FLBuilder._selectMultiplePhotos);
- $('body', window.parent.document).on( 'click', '.fl-multiple-photos-field .fl-multiple-photos-add', FLBuilder._selectMultiplePhotos);
- /* Video Fields */
- $('body', window.parent.document).on( 'click', '.fl-video-field .fl-video-select', FLBuilder._selectSingleVideo);
- $('body', window.parent.document).on( 'click', '.fl-video-field .fl-video-replace', FLBuilder._selectSingleVideo);
- $('body', window.parent.document).on( 'click', '.fl-video-field .fl-video-remove', FLBuilder._singleVideoRemoved);
- /* Multiple Audio Fields */
- $('body', window.parent.document).on( 'click', '.fl-multiple-audios-field .fl-multiple-audios-select', FLBuilder._selectMultipleAudios);
- $('body', window.parent.document).on( 'click', '.fl-multiple-audios-field .fl-multiple-audios-edit', FLBuilder._selectMultipleAudios);
- $('body', window.parent.document).on( 'click', '.fl-multiple-audios-field .fl-multiple-audios-add', FLBuilder._selectMultipleAudios);
- /* Icon Fields */
- $('body', window.parent.document).on( 'click', '.fl-icon-field .fl-icon-select', FLBuilder._selectIcon);
- $('body', window.parent.document).on( 'click', '.fl-icon-field .fl-icon-replace', FLBuilder._selectIcon);
- $('body', window.parent.document).on( 'click', '.fl-icon-field .fl-icon-remove', FLBuilder._removeIcon);
- /* Settings Form Fields */
- $('body', window.parent.document).on( 'click', '.fl-form-field .fl-form-field-edit', FLBuilder._formFieldClicked);
- $('body', window.parent.document).on( 'click', '.fl-form-field-settings .fl-builder-settings-save', FLBuilder._saveFormFieldClicked);
- /* Layout Fields */
- $('body', window.parent.document).on( 'click', '.fl-layout-field-option', FLBuilder._layoutFieldClicked);
- /* Links Fields */
- $('body', window.parent.document).on( 'click', '.fl-link-field-select', FLBuilder._linkFieldSelectClicked);
- $('body', window.parent.document).on( 'click', '.fl-link-field-search-cancel', FLBuilder._linkFieldSelectCancelClicked);
- /* Loop Settings Fields */
- $('body', window.parent.document).on( 'change', '.fl-loop-data-source-select select[name=data_source]', FLBuilder._loopDataSourceChange);
- $('body', window.parent.document).on( 'change', '.fl-custom-query select[name=post_type]', FLBuilder._customQueryPostTypeChange);
- $('body', window.parent.document).on( 'change', '.fl-custom-query select[name="post_type[]"]', FLBuilder._customQueryPostTypesChange);
- /* Text Fields - Add Predefined Value Selector */
- $('body', window.parent.document).on( 'change', '.fl-text-field-add-value', FLBuilder._textFieldAddValueSelectChange);
- /* Number Fields */
- $('body', window.parent.document).on( 'focus', '.fl-field input[type=number]', FLBuilder._onNumberFieldFocus );
- $('body', window.parent.document).on( 'blur', '.fl-field input[type=number]', FLBuilder._onNumberFieldBlur );
- // Live Preview
- FLBuilder.addHook( 'didCompleteAJAX', FLBuilder._refreshSettingsPreviewReference );
- FLBuilder.addHook( 'didRenderLayoutComplete', FLBuilder._refreshSettingsPreviewReference );
- },
- /**
- * Remove events when ending the edit session
- * @since 2.0
- * @access private
- */
- _unbindEvents: function() {
- $('a').off('click', FLBuilder._preventDefault);
- $('.fl-page-nav .nav a').off('click', FLBuilder._headerLinkClicked);
- $('body').undelegate('.fl-builder-content a', 'click', FLBuilder._preventDefault);
- },
- /**
- * Rebind events when restarting the edit session
- * @since 2.1.2.3
- * @access private
- */
- _rebindEvents: function() {
- $('a').on('click', FLBuilder._preventDefault);
- $('.fl-page-nav .nav a').on('click', FLBuilder._headerLinkClicked);
- $('body').on( 'click', '.fl-builder-content a', FLBuilder._preventDefault);
- },
- /**
- * Prevents the default action for an event.
- *
- * @since 1.6.3
- * @access private
- * @method _preventDefault
- * @param {Object} e The event object.
- */
- _preventDefault: function( e )
- {
- e.preventDefault();
- },
- /**
- * Prevents propagation of an event.
- *
- * @since 1.6.3
- * @access private
- * @method _stopPropagation
- * @param {Object} e The event object.
- */
- _stopPropagation: function( e )
- {
- e.stopPropagation();
- },
- /**
- * Launches the builder for another page if a link in the
- * builder theme header is clicked.
- *
- * @since 1.3.9
- * @access private
- * @method _headerLinkClicked
- * @param {Object} e The event object.
- */
- _headerLinkClicked: function(e)
- {
- var link = $(this),
- href = link.attr('href');
- // ignore links with a #hash
- if( this.hash ) {
- return;
- }
- e.preventDefault();
- if ( FLBuilderConfig.isUserTemplate ) {
- return;
- }
- FLBuilder._exitUrl = href.indexOf('?') > -1 ? href : href + '?fl_builder';
- FLBuilder.triggerHook('triggerDone');
- },
- /**
- * Warns the user that their changes won't be saved if
- * they leave the page while editing settings.
- *
- * @since 1.0.6
- * @access private
- * @method _warnBeforeUnload
- * @return {String} The warning message.
- */
- _warnBeforeUnload: function()
- {
- var rowSettings = $('.fl-builder-row-settings', window.parent.document).length > 0,
- colSettings = $('.fl-builder-col-settings', window.parent.document).length > 0,
- moduleSettings = $('.fl-builder-module-settings', window.parent.document).length > 0;
- if(rowSettings || colSettings || moduleSettings) {
- return FLBuilderStrings.unloadWarning;
- }
- },
- /* Lite Version
- ----------------------------------------------------------*/
- /**
- * Opens a new window with the upgrade URL when the
- * upgrade button is clicked.
- *
- * @since 1.0
- * @access private
- * @method _upgradeClicked
- */
- _upgradeClicked: function()
- {
- window.parent.open(FLBuilderConfig.upgradeUrl);
- },
- /**
- * Toggles the pro module section in lite.
- *
- * @since 2.4
- */
- _toggleProModules: function()
- {
- var button = $( '.fl-builder-blocks-pro-expand', window.parent.document ),
- closed = $( '.fl-builder-blocks-pro-closed', window.parent.document ),
- open = $( '.fl-builder-blocks-pro-open', window.parent.document );
- button.toggleClass( 'fl-builder-blocks-pro-expand-rotate' );
- if ( closed.length ) {
- closed.removeClass( 'fl-builder-blocks-pro-closed' );
- closed.addClass( 'fl-builder-blocks-pro-open' );
- } else {
- open.removeClass( 'fl-builder-blocks-pro-open' );
- open.addClass( 'fl-builder-blocks-pro-closed' );
- }
- },
- /**
- * Shows the the pro message lightbox.
- *
- * @since 2.4
- */
- _showProMessage: function( feature )
- {
- if ( ! FLBuilderConfig.lite ) {
- return
- }
- var alert = new FLLightbox({
- className: 'fl-builder-pro-lightbox',
- destroyOnClose: true
- }),
- template = wp.template( 'fl-pro-lightbox' );
- alert.open( template( { feature : feature } ) );
- },
- /* TipTips
- ----------------------------------------------------------*/
- /**
- * Initializes tooltip help messages.
- *
- * @since 1.1.9
- * @access private
- * @method _initTipTips
- */
- _initTipTips: function()
- {
- var classname = '.fl-tip:not(.fl-has-tip)',
- tips = $( classname ).add( classname, window.parent.document );
- tips.each( function(){
- var ele = $( this );
- ele.addClass( 'fl-has-tip' );
- if ( undefined == ele.attr( 'data-title' ) ) {
- ele.attr( 'data-title', ele.attr( 'title' ) );
- }
- } )
- if ( ! FLBuilderLayout._isTouch() ) {
- tips.tipTip( {
- defaultPosition : 'top',
- delay : 300,
- maxWidth : 'auto'
- } );
- }
- },
- /**
- * Removes all tooltip help messages from the screen.
- *
- * @since 1.1.9
- * @access private
- * @method _hideTipTips
- */
- _hideTipTips: function()
- {
- $('#tiptip_holder').stop().hide();
- $('#tiptip_holder', window.parent.document).stop().hide();
- },
- /* Submenus
- ----------------------------------------------------------*/
- /**
- * Callback for when the parent of a submenu is clicked.
- *
- * @since 1.6.4
- * @access private
- * @method _submenuParentClicked
- * @param {Object} e The event object.
- */
- _submenuParentClicked: function( e )
- {
- var body = $( 'body' ),
- parent = $( this ),
- submenu = parent.find( '.fl-builder-submenu' );
- if ( parent.hasClass( 'fl-builder-submenu-open' ) ) {
- body.removeClass( 'fl-builder-submenu-open' );
- parent.removeClass( 'fl-builder-submenu-open' );
- parent.removeClass( 'fl-builder-submenu-right' );
- } else {
- if( parent.offset().left + submenu.width() > $( window ).width() ) {
- parent.addClass( 'fl-builder-submenu-right' );
- }
- body.addClass( 'fl-builder-submenu-open' );
- parent.addClass( 'fl-builder-submenu-open' );
- }
- submenu.closest('.fl-row-overlay').addClass('fl-row-menu-active');
- FLBuilder._hideTipTips();
- e.preventDefault();
- e.stopPropagation();
- },
- /**
- * Callback for when the parent of a submenu is hovered.
- *
- * @since 2.6
- * @access private
- * @method _hoverMenuParentMouseEnter
- * @param {Object} e The event object.
- */
- _hoverMenuParentMouseEnter: function (e) {
- e.stopPropagation();
- var body = $('body'),
- parent = $(this),
- submenu = parent.find('.fl-builder-submenu');
- // remove classes first
- $('.fl-builder-submenu-right').removeClass('fl-builder-submenu-right');
- $('.fl-builder-submenu-open').removeClass('fl-builder-submenu-open');
- $('.fl-row-menu-active').removeClass('fl-row-menu-active');
- // determine align
- if (parent.offset().left + submenu.width() > $(window).width()) {
- parent.addClass('fl-builder-submenu-right');
- }
- // add classes
- parent.closest('.fl-row-overlay').addClass('fl-row-menu-active');
- body.addClass('fl-builder-submenu-open');
- parent.addClass('fl-builder-submenu-open');
- },
- /**
- * Callback for when the parent of a submenu is unhovered.
- *
- * @since 2.6
- * @access private
- * @method _hoverMenuParentMouseLeave
- * @param {Object} e The event object.
- */
- _hoverMenuParentMouseLeave: function (e) {
- // remove classes
- $('.fl-builder-submenu-right').removeClass('fl-builder-submenu-right');
- $('.fl-builder-submenu-open').removeClass('fl-builder-submenu-open');
- $('.fl-row-menu-active').removeClass('fl-row-menu-active');
- },
- /**
- * Callback for when the child of a submenu is clicked.
- *
- * @since 1.6.4
- * @access private
- * @method _submenuChildClicked
- * @param {Object} e The event object.
- */
- _submenuChildClicked: function( e )
- {
- var body = $( 'body' ),
- parent = $( this ).parents( '.fl-builder-has-submenu' );
- if ( ! parent.parents( '.fl-builder-has-submenu' ).length ) {
- body.removeClass( 'fl-builder-submenu-open' );
- parent.removeClass( 'fl-builder-submenu-open' );
- }
- },
- /**
- * Callback for when the mouse enters a submenu.
- *
- * @since 1.6.4
- * @access private
- * @method _submenuMouseenter
- * @param {Object} e The event object.
- */
- _submenuMouseenter: function( e )
- {
- if ($(this).parent().hasClass('fl-builder-submenu-hover')) {
- return;
- }
- var menu = $( this ),
- timeout = menu.data( 'timeout' );
- if ( 'undefined' != typeof timeout ) {
- clearTimeout( timeout );
- }
- },
- /**
- * Callback for when the mouse leaves a submenu.
- *
- * @since 1.6.4
- * @access private
- * @method _submenuMouseleave
- * @param {Object} e The event object.
- */
- _submenuMouseleave: function( e )
- {
- if ($(this).parent().hasClass('fl-builder-submenu-hover')) {
- return;
- }
- var body = $( 'body' ),
- menu = $( this ),
- timeout = setTimeout( function() {
- body.removeClass( 'fl-builder-submenu-open' );
- menu.closest( '.fl-builder-has-submenu' ).removeClass( 'fl-builder-submenu-open' );
- }, 500 );
- menu.closest('.fl-row-overlay').removeClass('fl-row-menu-active');
- menu.data( 'timeout', timeout );
- },
- /**
- * Callback for when the mouse enters the parent
- * of a nested submenu.
- *
- * @since 1.9
- * @access private
- * @method _submenuNestedParentMouseenter
- * @param {Object} e The event object.
- */
- _submenuNestedParentMouseenter: function( e )
- {
- var parent = $( this );
- var submenu = parent.find( '> .fl-builder-submenu' );
- var winWidth = $( window ).width();
- var leftSpace = parent.offset().left;
- var rightSpace = winWidth - ( parent.width() + leftSpace );
- var menusWidth = parent.width() + leftSpace + submenu.width();
- if ( menusWidth > winWidth && submenu.width() < leftSpace ) {
- parent.addClass( 'fl-builder-submenu-right' );
- } else if ( submenu.width() > rightSpace ) {
- var left = parent.width() - ( submenu.width() - rightSpace );
- submenu.css( 'left', left );
- }
- },
- /**
- * Closes all open submenus.
- *
- * @since 1.9
- * @access private
- * @method _closeAllSubmenus
- */
- _closeAllSubmenus: function()
- {
- $( '.fl-builder-submenu-open' ).removeClass( 'fl-builder-submenu-open' );
- },
- /* Bar
- ----------------------------------------------------------*/
- /**
- * Fires blur on mouse up to avoid focus ring when clicked with mouse.
- *
- * @since 2.0
- * @access private
- * @method _buttonMouseUp
- * @param {Event} e
- * @return void
- */
- _buttonMouseUp: function(e) {
- $(e.currentTarget).blur();
- },
- /* Panel
- ----------------------------------------------------------*/
- /**
- * Closes the builder's content panel.
- *
- * @since 1.0
- * @access private
- * @method _closePanel
- */
- _closePanel: function()
- {
- FLBuilder.triggerHook('hideContentPanel');
- },
- /**
- * Opens the builder's content panel.
- *
- * @since 1.0
- * @access private
- * @method _showPanel
- */
- _showPanel: function()
- {
- FLBuilder.triggerHook('showContentPanel');
- },
- /**
- * Toggle the panel open or closed.
- *
- * @since 2.0
- * @access private
- * @method _togglePanel
- */
- _togglePanel: function()
- {
- FLBuilder.triggerHook('toggleContentPanel');
- },
- /* Save Actions
- ----------------------------------------------------------*/
- /**
- * Publish the current layout
- *
- * @since 2.0
- * @access private
- * @method _publishLayout
- * @param {Boolean} shouldExit Whether or not builder should exit after publish
- * @param {Boolean} openLightbox Whether or not to keep the lightboxes open.
- * @return void
- */
- _publishLayout: function( shouldExit, openLightbox ) {
- // Save existing settings first if any exist. Don't proceed if it fails.
- if ( ! FLBuilder._triggerSettingsSave( openLightbox, true ) ) {
- return;
- }
- if ( _.isUndefined( shouldExit ) ) {
- var shouldExit = true;
- }
- const actions = FL.Builder.data.getLayoutActions()
- const callback = FLBuilder._onPublishComplete.bind( FLBuilder, shouldExit )
- actions.saveLayout( true, shouldExit, callback )
- },
- /**
- * Publishes the layout when the publish button is clicked.
- *
- * @since 1.0
- * @access private
- * @param bool whether or not builder should exit after publish
- * @method _publishButtonClicked
- */
- _publishButtonClicked: function( shouldExit )
- {
- FLBuilder._publishLayout( shouldExit );
- },
- /**
- * Fires on successful ajax publish.
- *
- * @since 2.0
- * @access private
- * @param bool whether or not builder should exit after publish
- * @return void
- */
- _onPublishComplete: function( shouldExit ) {
- if ( shouldExit ) {
- if ( FLBuilderConfig.shouldRefreshOnPublish ) {
- FLBuilder._exit();
- } else {
- FLBuilder._exitWithoutRefresh();
- }
- }
- // Change the admin bar status dot to green if it isn't already
- $('#wp-admin-bar-fl-builder-frontend-edit-link .fl-builder-admin-bar-status-dot').css('color', '#6bc373');
- FLBuilder.triggerHook( 'didPublishLayout', {
- shouldExit: shouldExit,
- } );
- },
- /**
- * Exits the builder when the save draft button is clicked.
- *
- * @since 1.0
- * @access private
- * @method _draftButtonClicked
- */
- _draftButtonClicked: function()
- {
- FLBuilder.showAjaxLoader();
- const actions = FL.Builder.data.getLayoutActions()
- actions.saveDraft()
- },
- /**
- * Clears changes to the layout when the discard draft button
- * is clicked.
- *
- * @since 1.0
- * @access private
- * @method _discardButtonClicked
- */
- _discardButtonClicked: function()
- {
- var result = confirm(FLBuilderStrings.discardMessage);
- if(result) {
- FLBuilder.showAjaxLoader();
- const actions = FL.Builder.data.getLayoutActions()
- actions.discardDraft()
- } else {
- FLBuilder.triggerHook('didCancelDiscard');
- }
- },
- /**
- * Closes the actions lightbox when the cancel button is clicked.
- *
- * @since 1.0
- * @access private
- * @method _cancelButtonClicked
- */
- _cancelButtonClicked: function()
- {
- FLBuilder._exitUrl = null;
- FLBuilder._actionsLightbox.close();
- },
- /**
- * Redirects the user to the _exitUrl if defined, otherwise
- * it redirects the user to the current post without the
- * builder active.
- *
- * @since 1.0
- * @since 1.5.7 Closes the window if we're in a child window.
- * @access private
- * @method _exit
- */
- _exit: function()
- {
- var href = window.parent.location.href;
- try {
- var flbuilder = typeof window.parent.opener.FLBuilder != 'undefined'
- }
- catch(err) {
- var flbuilder = false
- }
- if ( FLBuilderConfig.isUserTemplate && typeof window.parent.opener != 'undefined' && window.parent.opener ) {
- if ( flbuilder ) {
- if ( 'undefined' === typeof FLBuilderGlobalNodeId ) {
- window.parent.opener.FLBuilder._updateLayout();
- } else {
- window.parent.opener.FLBuilder._updateNode( FLBuilderGlobalNodeId );
- }
- }
- window.parent.close();
- }
- else {
- if ( FLBuilder._exitUrl ) {
- href = FLBuilder._exitUrl;
- }
- else {
- href = href.replace( '&fl_builder_ui', '' );
- href = href.replace( '?fl_builder&', '?' );
- href = href.replace( '?fl_builder', '' );
- href = href.replace( '&fl_builder', '' );
- }
- if ( window.parent.location.host === 'playground.wordpress.net' ) {
- const meta = '<meta http-equiv="refresh" content="0; URL=\'' + href + '\'" />';
- $( 'head', window.parent.document ).append( meta );
- } else {
- window.parent.location.href = href;
- }
- }
- },
- /**
- * Allow the editing session to end but don't redirect to any url.
- *
- * @since 2.0
- * @return void
- */
- _exitWithoutRefresh: function() {
- var href = window.parent.location.href;
- try {
- var flbuilder = typeof window.parent.opener.FLBuilder != 'undefined'
- }
- catch(err) {
- var flbuilder = false
- }
- if ( FLBuilderConfig.isUserTemplate && flbuilder && window.opener ) {
- if ( flbuilder ) {
- if ( 'undefined' === typeof FLBuilderGlobalNodeId ) {
- window.parent.opener.FLBuilder._updateLayout();
- } else {
- window.parent.opener.FLBuilder._updateNode( FLBuilderGlobalNodeId );
- }
- }
- window.parent.close();
- }
- else {
- FLBuilder.triggerHook('endEditingSession');
- }
- },
- /* Tools Actions
- ----------------------------------------------------------*/
- /**
- * Duplicates the current post and builder layout.
- *
- * @since 1.0
- * @access private
- * @method _duplicateLayoutClicked
- */
- _duplicateLayoutClicked: function()
- {
- FLBuilder.showAjaxLoader();
- FLBuilder.ajax({
- action: 'duplicate_post'
- }, FLBuilder._duplicateLayoutComplete);
- },
- /**
- * Redirects the user to the post edit screen of a
- * duplicated post when duplication is complete.
- *
- * @since 1.0
- * @access private
- * @method _duplicatePageComplete
- * @param {Number} The ID of the duplicated post.
- */
- _duplicateLayoutComplete: function(response)
- {
- var adminUrl = FLBuilderConfig.adminUrl;
- window.parent.location.href = adminUrl + 'post.php?post='+ response +'&action=edit';
- },
- /* Layout Settings
- ----------------------------------------------------------*/
- /**
- * Shows the layout settings lightbox when the layout
- * settings button is clicked.
- *
- * @since 1.7
- * @access private
- * @method _layoutSettingsClicked
- */
- _layoutSettingsClicked: function()
- {
- FLBuilderSettingsForms.render( {
- id : 'layout',
- className : 'fl-builder-layout-settings',
- settings : FLBuilderSettingsConfig.settings.layout
- }, function() {
- FLBuilder._layoutSettingsInitCSS();
- } );
- },
- /**
- * Initializes custom layout CSS for live preview.
- *
- * @since 1.7
- * @access private
- * @method _layoutSettingsInitCSS
- */
- _layoutSettingsInitCSS: function()
- {
- var css = $( '.fl-builder-settings #fl-field-css textarea:not(.ace_text-input)', window.parent.document );
- css.on( 'change', FLBuilder._layoutSettingsCSSChanged );
- FLBuilder._layoutSettingsCSSCache = css.val();
- },
- /**
- * Sets a timeout for throttling custom layout CSS changes.
- *
- * @since 1.7
- * @access private
- * @method _layoutSettingsCSSChanged
- */
- _layoutSettingsCSSChanged: function()
- {
- if ( FLBuilder._layoutSettingsCSSTimeout ) {
- clearTimeout( FLBuilder._layoutSettingsCSSTimeout );
- }
- FLBuilder._layoutSettingsCSSTimeout = setTimeout( $.proxy( FLBuilder._layoutSettingsCSSDoChange, this ), 600 );
- },
- /**
- * Updates the custom layout CSS when changes are made in the editor.
- *
- * @since 1.7
- * @access private
- * @method _layoutSettingsCSSDoChange
- */
- _layoutSettingsCSSDoChange: function()
- {
- var form = $( '.fl-builder-settings', window.parent.document ),
- textarea = $( this ),
- field = textarea.parents( '#fl-field-css' );
- if ( field.find( '.ace_error' ).length > 0 ) {
- return;
- }
- else if ( form.hasClass( 'fl-builder-layout-settings' ) ) {
- $( '#fl-builder-layout-css' ).html( textarea.val() );
- }
- else {
- $( '#fl-builder-global-css' ).html( textarea.val() );
- }
- FLBuilder._layoutSettingsCSSTimeout = null;
- },
- /**
- * Saves the layout settings when the save button is clicked.
- *
- * @since 1.7
- * @access private
- * @method _saveLayoutSettingsClicked
- */
- _saveLayoutSettingsClicked: function()
- {
- var form = $( this ).closest( '.fl-builder-settings' ),
- data = form.serializeArray(),
- settings = {},
- i = 0;
- for( ; i < data.length; i++) {
- settings[ data[ i ].name ] = data[ i ].value;
- }
- FLBuilder.showAjaxLoader();
- FLBuilder._lightbox.close();
- FLBuilder._layoutSettingsCSSCache = null;
- const actions = FL.Builder.data.getLayoutActions()
- actions.saveLayoutSettings( settings )
- },
- /**
- * Reverts changes made when the cancel button for the layout
- * settings has been clicked.
- *
- * @since 1.7
- * @access private
- * @method _cancelLayoutSettingsClicked
- */
- _cancelLayoutSettingsClicked: function()
- {
- var form = $( '.fl-builder-settings', window.parent.document );
- if ( form.hasClass( 'fl-builder-layout-settings' ) ) {
- $( '#fl-builder-layout-css' ).html( FLBuilder._layoutSettingsCSSCache );
- }
- else {
- $( '#fl-builder-global-css' ).html( FLBuilder._layoutSettingsCSSCache );
- }
- FLBuilder._layoutSettingsCSSCache = null;
- },
- /**
- * Completes the ajax call for saving layout settings.
- *
- * @since 2.5
- * @access private
- * @method _saveLayoutSettingsComplete
- * @param {Object} the settings object
- */
- _saveLayoutSettingsComplete: function( settings )
- {
- FLBuilder.triggerHook( 'didSaveLayoutSettingsComplete', settings )
- FLBuilder._updateLayout()
- },
- /* Global Settings
- ----------------------------------------------------------*/
- /**
- * Shows the global builder settings lightbox when the global
- * settings button is clicked.
- *
- * @since 1.0
- * @access private
- * @method _globalSettingsClicked
- */
- _globalSettingsClicked: function()
- {
- const settings = FLBuilderSettingsConfig.settings.global
- settings.color_scheme = FL.Builder.data.getSystemState().colorScheme
- FLBuilderSettingsForms.render( {
- id : 'global',
- className : 'fl-builder-global-settings',
- settings : settings
- }, function() {
- FLBuilder._layoutSettingsInitCSS();
- FLBuilder.original_shapes = FLBuilderSettingsConfig.settings.global.shape_form;
- } );
- },
- /**
- * Saves the global settings when the save button is clicked.
- *
- * @since 1.0
- * @access private
- * @method _saveGlobalSettingsClicked
- */
- _saveGlobalSettingsClicked: function()
- {
- var form = $(this).closest('.fl-builder-settings'),
- valid = form.validate().form(),
- settings = FLBuilder._getSettings( form );
- if(valid) {
- FLBuilder.showAjaxLoader();
- FLBuilder._layoutSettingsCSSCache = null;
- const actions = FL.Builder.data.getLayoutActions()
- actions.saveGlobalSettings( settings )
- FLBuilder._lightbox.close();
- // if number of global shapes has changed then refresh.
- if ( 'undefined' != typeof FLBuilder.original_shapes && FLBuilder.original_shapes.length !== settings.shape_form?.length ) {
- FLBuilder._shapesEdited = true;
- }
- }
- },
- /**
- * Saves the global settings when the save button is clicked.
- *
- * @since 1.0
- * @access private
- * @method _saveGlobalSettingsComplete
- * @param {String} response
- */
- _saveGlobalSettingsComplete: function( response )
- {
- FLBuilder.triggerHook( 'didSaveGlobalSettingsComplete', FLBuilder._jsonParse( response ) );
- FLBuilder._updateLayout();
- if ( FLBuilder._shapesEdited === true ) {
- window.parent.location.reload(true);
- }
- },
- /* Global Styles - See extensions for global styles code.
- ----------------------------------------------------------*/
- /**
- * Shows the global builder styles lightbox when the global
- * styles button is clicked.
- *
- * @since 2.8
- */
- _globalStylesClicked: function()
- {
- if ( FLBuilderConfig.lite ) {
- FLBuilder._showProMessage( 'Global Styles' );
- } else if ( 'undefined' !== typeof FLBuilderGlobalStyles ) {
- FLBuilderGlobalStyles._showPanel();
- }
- },
- /* Template Selector
- ----------------------------------------------------------*/
- /**
- * Shows the template selector when the builder is launched
- * if the current layout is empty.
- *
- * @since 1.0
- * @access private
- * @method _initTemplateSelector
- */
- _initTemplateSelector: function()
- {
- var rows = $(FLBuilder._contentClass).find('.fl-row'),
- layoutHasContent = ( rows.length > 0 );
- if( ! layoutHasContent ) {
- FLBuilder.ContentPanel.show('modules');
- }
- },
- /**
- * Show options for inserting or appending a template when a template is selected.
- * This logic was moved from `_templateClicked` to unbind it from the specific event.
- *
- * @since 2.0
- * @access private
- * @method _requestTemplateInsert
- */
- _requestTemplateInsert: function(index, type) {
- // if there are existing rows in the layout
- if( FLBuilder.layoutHasContent() ) {
- // If the template is blank, no need to ask
- if(index == 0) {
- if( confirm( FLBuilderStrings.changeTemplateMessage ) ) {
- FLBuilder._lightbox._node.hide();
- FLBuilder._applyTemplate(0, false, type);
- }
- }
- // present options Replace or Append
- else {
- FLBuilder._selectedTemplateId = index;
- FLBuilder._selectedTemplateType = type;
- FLBuilder._showTemplateActions();
- FLBuilder._lightbox._node.hide();
- }
- }
- // if there are no rows, just insert the template.
- else {
- FLBuilder._applyTemplate(index, false, type);
- }
- },
- /**
- * Shows the actions lightbox for replacing and appending templates.
- *
- * @since 1.1.9
- * @access private
- * @method _showTemplateActions
- */
- _showTemplateActions: function()
- {
- var buttons = [];
- buttons[ 10 ] = {
- 'key': 'template-replace',
- 'label': FLBuilderStrings.templateReplace
- };
- buttons[ 20 ] = {
- 'key': 'template-append',
- 'label': FLBuilderStrings.templateAppend
- };
- FLBuilder._showActionsLightbox({
- 'className': 'fl-builder-template-actions',
- 'title': FLBuilderStrings.actionsLightboxTitle,
- 'buttons': buttons
- });
- },
- /**
- * Replaces the current layout with a template when the replace
- * button is clicked.
- *
- * @since 1.1.9
- * @access private
- * @method _templateReplaceClicked
- */
- _templateReplaceClicked: function()
- {
- if(confirm(FLBuilderStrings.changeTemplateMessage)) {
- FLBuilder._actionsLightbox.close();
- FLBuilder._applyTemplate(FLBuilder._selectedTemplateId, false, FLBuilder._selectedTemplateType);
- }
- },
- /**
- * Append a template to the current layout when the append
- * button is clicked.
- *
- * @since 1.1.9
- * @access private
- * @method _templateAppendClicked
- */
- _templateAppendClicked: function()
- {
- FLBuilder._actionsLightbox.close();
- FLBuilder._applyTemplate(FLBuilder._selectedTemplateId, true, FLBuilder._selectedTemplateType);
- },
- /**
- * Shows the template selector when the cancel button of
- * the template actions lightbox is clicked.
- *
- * @since 1.1.9
- * @access private
- * @method _templateCancelClicked
- */
- _templateCancelClicked: function()
- {
- FLBuilder.triggerHook( 'showContentPanel' );
- },
- /**
- * Applys a template to the current layout by either appending
- * it or replacing the current layout with it.
- *
- * @since 1.1.9
- * @access private
- * @method _applyTemplate
- * @param {Number} id The template id.
- * @param {Boolean} append Whether the new template should be appended or not.
- * @param {String} type The type of template. Either core or user.
- */
- _applyTemplate: function( id, append, type )
- {
- append = typeof append === 'undefined' || ! append ? '0' : '1'
- type = typeof type === 'undefined' ? 'core' : type
- FLBuilder._lightbox.close();
- FLBuilder.showAjaxLoader();
- const actions = FL.Builder.data.getLayoutActions()
- actions.applyTemplate( id, append, type )
- FLBuilder.triggerHook('didApplyTemplate');
- },
- /**
- * Callback for when applying a template completes.
- * @since 2.0
- * @access private
- * @method _applyTemplateComplete
- * @param {String} response
- */
- _applyTemplateComplete: function( response )
- {
- var data = FLBuilder._jsonParse( response );
- FLBuilder._renderLayout( data.layout );
- FLBuilder.triggerHook( 'didApplyTemplateComplete', data.config );
- },
- /**
- * Callback for when applying a user template completes.
- * @since 1.9.5
- * @access private
- * @method _applyUserTemplateComplete
- * @param {string} response
- */
- _applyUserTemplateComplete: function( response )
- {
- var data = FLBuilder._jsonParse( response );
- if ( null !== data.layout_css ) {
- $( '#fl-builder-layout-css' ).html( data.layout_css );
- }
- FLBuilder._renderLayout( data.layout );
- FLBuilder.triggerHook( 'didApplyTemplateComplete', data.config );
- },
- /* User Template Settings
- ----------------------------------------------------------*/
- /**
- * Shows the settings for saving a user defined template
- * when the save template button is clicked.
- *
- * @since 1.1.3
- * @access private
- * @method _saveUserTemplateClicked
- */
- _saveUserTemplateClicked: function()
- {
- if ( FLBuilderConfig.lite ) {
- FLBuilder._showProMessage( 'Saving Templates' );
- return;
- }
- FLBuilderSettingsForms.render( {
- id : 'user_template',
- className : 'fl-builder-user-template-settings',
- rules : {
- name: {
- required: true
- }
- }
- } );
- },
- /**
- * Saves user template settings when the save button is clicked.
- *
- * @since 1.1.9
- * @access private
- * @method _saveUserTemplateSettings
- */
- _saveUserTemplateSettings: function()
- {
- var form = $(this).closest('.fl-builder-settings'),
- valid = form.validate().form(),
- settings = FLBuilder._getSettings(form);
- if(valid) {
- const actions = FL.Builder.data.getLayoutActions()
- actions.saveUserTemplateSettings( settings )
- FLBuilder._lightbox.close();
- }
- },
- /**
- * Shows a success alert when user template settings have saved.
- *
- * @since 1.1.9
- * @access private
- * @method _saveUserTemplateSettingsComplete
- */
- _saveUserTemplateSettingsComplete: function(data)
- {
- if ( !data ) return;
- var data = FLBuilder._jsonParse(data);
- FLBuilderConfig.contentItems.template.push(data);
- FLBuilder.triggerHook('contentItemsChanged');
- },
- /**
- * Callback for when a user clicks a user defined template in
- * the template selector.
- *
- * @since 1.1.3
- * @access private
- * @method _userTemplateClicked
- */
- _userTemplateClicked: function()
- {
- var id = $(this).attr('data-id');
- if($(FLBuilder._contentClass).children('.fl-row').length > 0) {
- if(id == 'blank') {
- if(confirm(FLBuilderStrings.changeTemplateMessage)) {
- FLBuilder._lightbox._node.hide();
- FLBuilder._applyTemplate('blank', false, 'user');
- }
- }
- else {
- FLBuilder._selectedTemplateId = id;
- FLBuilder._selectedTemplateType = 'user';
- FLBuilder._showTemplateActions();
- FLBuilder._lightbox._node.hide();
- }
- }
- else {
- FLBuilder._applyTemplate(id, false, 'user');
- }
- },
- /**
- * Launches the builder in a new tab to edit a user
- * defined template when the edit link is clicked.
- *
- * @since 1.1.3
- * @access private
- * @method _editUserTemplateClicked
- * @param {Object} e The event object.
- */
- _editUserTemplateClicked: function(e)
- {
- e.preventDefault();
- e.stopPropagation();
- window.parent.open($(this).attr('href'));
- },
- /**
- * Deletes a user defined template when the delete link is clicked.
- *
- * @since 1.1.3
- * @access private
- * @method _deleteUserTemplateClicked
- * @param {Object} e The event object.
- */
- _deleteUserTemplateClicked: function(e)
- {
- var template = $( this ).closest( '.fl-user-template' ),
- id = template.attr( 'data-id' ),
- all = $( '.fl-user-template[data-id=' + id + ']', window.parent.document ),
- parent = null,
- index = null,
- i = null,
- item = null;
- if ( confirm( FLBuilderStrings.deleteTemplate ) ) {
- const actions = FL.Builder.data.getLayoutActions()
- actions.deleteUserTemplate( id )
- // Remove the item from library
- for(i in FLBuilderConfig.contentItems.template) {
- item = FLBuilderConfig.contentItems.template[i];
- if (item.postId == id) {
- index = i;
- }
- }
- if (!_.isNull(index)) {
- FLBuilderConfig.contentItems.template.splice(index, 1);
- FLBuilder.triggerHook('contentItemsChanged');
- }
- }
- e.stopPropagation();
- },
- /* Help Tour
- ----------------------------------------------------------*/
- /**
- * Shows the help tour or template selector when the builder
- * is launched.
- *
- * @since 1.4.9
- * @access private
- * @method _showTourOrTemplates
- */
- _showTourOrTemplates: function()
- {
- if ( ! FLBuilderConfig.simpleUi && ! FLBuilderConfig.isUserTemplate ) {
- if ( FLBuilderConfig.help.tour && FLBuilderConfig.newUser ) {
- FLBuilder._showTourLightbox();
- }
- else {
- FLBuilder._initTemplateSelector();
- }
- }
- },
- /**
- * Save browser stats when builder is loaded.
- * @since 2.1.6
- */
- _doStats: function() {
- if( 1 == FLBuilderConfig.statsEnabled ) {
- args = {
- 'screen-width': screen.width,
- 'screen-height': screen.height,
- 'pixel-ratio': window.devicePixelRatio,
- 'user-agent': window.navigator.userAgent,
- 'isrtl': FLBuilderConfig.isRtl
- }
- FLBuilder.ajax({
- action: 'save_browser_stats',
- browser_data: args
- });
- }
- },
- /**
- * Shows the actions lightbox with a welcome message for new
- * users asking if they would like to take the tour.
- *
- * @since 1.4.9
- * @access private
- * @method _showTourLightbox
- */
- _showTourLightbox: function()
- {
- var template = wp.template( 'fl-tour-lightbox' );
- FLBuilder._actionsLightbox.open( template() );
- },
- /**
- * Closes the actions lightbox and shows the template selector
- * if a new user declines the tour.
- *
- * @since 1.4.9
- * @access private
- * @method _noTourButtonClicked
- */
- _noTourButtonClicked: function()
- {
- FLBuilder._actionsLightbox.close();
- FLBuilder._initTemplateSelector();
- },
- /**
- * Closes the actions lightbox and starts the tour when a new user
- * decides to take the tour.
- *
- * @since 1.4.9
- * @access private
- * @method _yesTourButtonClicked
- */
- _yesTourButtonClicked: function()
- {
- FLBuilder._actionsLightbox.close();
- FLBuilderTour.start();
- },
- /**
- * Starts the help tour.
- *
- * @since 1.4.9
- * @access private
- * @method _startHelpTour
- */
- _startHelpTour: function()
- {
- FLBuilder._actionsLightbox.close();
- FLBuilderTour.start();
- },
- /* Layout
- ----------------------------------------------------------*/
- /**
- * Shows a message to drop a row or module to get started
- * if the layout is empty.
- *
- * @since 1.0
- * @access private
- * @method _setupEmptyLayout
- */
- _setupEmptyLayout: function()
- {
- var content = $(FLBuilder._contentClass);
- if ( FLBuilderConfig.isUserTemplate && 'module' == FLBuilderConfig.userTemplateType ) {
- return;
- }
- else if ( FLBuilderConfig.isUserTemplate && 'column' == FLBuilderConfig.userTemplateType ) {
- return;
- }
- else {
- content.removeClass('fl-builder-empty');
- content.find('.fl-builder-empty-message').remove();
- if ( ! content.find( '.fl-row, .fl-builder-block' ).length ) {
- content.addClass('fl-builder-empty');
- content.append('<span class="fl-builder-empty-message">'+ FLBuilderStrings.emptyMessage +'</span>');
- FLBuilder._initSortables();
- }
- }
- },
- /**
- * Sends an AJAX request to re-render a single node.
- *
- * @since 2.0
- * @access private
- * @method _updateNode
- * @param {String} nodeId
- * @param {Function} callback
- */
- _updateNode: function( nodeId, callback )
- {
- if ( ! $( '.fl-node-' + nodeId ).length ) {
- return;
- }
- FLBuilder._showNodeLoading( nodeId );
- const actions = FL.Builder.data.getLayoutActions()
- actions.renderNode( nodeId, callback )
- },
- /**
- * Sends an AJAX request to render the layout and is typically
- * used as a callback to many of the builder's save operations.
- *
- * @since 1.0
- * @access private
- * @method _updateLayout
- */
- _updateLayout: function()
- {
- FLBuilder.showAjaxLoader();
- const actions = FL.Builder.data.getLayoutActions()
- actions.renderLayout()
- // Refresh Node Data
- actions.fetchLayout()
- },
- /**
- * Removes the current layout and renders a new layout using
- * the provided data. Will render a node instead of the layout
- * if data.partial is true.
- *
- * @since 1.0
- * @access private
- * @method _renderLayout
- * @param {Object} data The layout data. May also be a JSON encoded string.
- * @param {Function} callback A function to call when the layout has finished rendering.
- */
- _renderLayout: function( data, callback )
- {
- if ( FLBuilder._layout ) {
- FLBuilder._layoutQueue.push( {
- data: data,
- callback: callback,
- } );
- } else {
- FLBuilder._layout = new FLBuilderAJAXLayout( data, callback );
- }
- },
- /**
- * Called by the layout's JavaScript file once it's loaded
- * to finish rendering the layout.
- *
- * @since 1.0
- * @access private
- * @method _renderLayoutComplete
- */
- _renderLayoutComplete: function()
- {
- if ( FLBuilder._layout ) {
- FLBuilder._layout._complete();
- FLBuilder._layout = null;
- }
- if ( FLBuilder._layoutQueue.length ) {
- var item = FLBuilder._layoutQueue.shift();
- FLBuilder._layout = new FLBuilderAJAXLayout( item.data, item.callback );
- }
- },
- /**
- * Trigger the resize event on the window so elements
- * in the layout that rely on JavaScript know to resize.
- *
- * @since 1.0
- * @access private
- * @method _resizeLayout
- */
- _resizeLayout: function()
- {
- $(window).trigger('resize');
- if(typeof YUI !== 'undefined') {
- YUI().use('node-event-simulate', function(Y) {
- Y.one(window).simulate("resize");
- });
- }
- },
- /**
- * Checks to see if any rows exist in the layout, or if it is blank.
- *
- * @since 2.0
- * @method layoutHasContent
- * @return {Boolean}
- */
- layoutHasContent: function()
- {
- if( $(FLBuilder._contentClass).children('.fl-row').length > 0) {
- return true;
- } else {
- return false;
- }
- },
- /**
- * Initializes MediaElements.js audio and video players.
- *
- * @since 1.0
- * @access private
- * @method _initMediaElements
- */
- _initMediaElements: function()
- {
- var settings = {};
- if(typeof $.fn.mediaelementplayer != 'undefined') {
- if(typeof _wpmejsSettings !== 'undefined') {
- settings.pluginPath = _wpmejsSettings.pluginPath;
- }
- $('.wp-audio-shortcode, .wp-video-shortcode').not('.mejs-container').mediaelementplayer(settings);
- }
- },
- /* Generic Drag and Drop
- ----------------------------------------------------------*/
- /**
- * Inserts drop targets for nodes such as rows, columns
- * and column groups since making those all sortables
- * makes sorting really jumpy.
- *
- * @since 1.9
- * @access private
- * @method _initDropTargets
- */
- _initDropTargets: function()
- {
- var notGlobal = 'row' == FLBuilderConfig.userTemplateType ? '' : ':not(.fl-node-global)',
- rows = $( FLBuilder._contentClass + ' .fl-row' ),
- row = null,
- groups = $( FLBuilder._contentClass + ' .fl-row' + notGlobal ).find( '.fl-col-group' ),
- group = null,
- cols = null,
- rootCol = 'column' == FLBuilderConfig.userTemplateType ? $( FLBuilder._contentClass + '> .fl-col' ).eq(0) : null,
- modules = $( FLBuilder._contentClass + ' .fl-row' + notGlobal ).find( '.fl-row-content > .fl-module' ),
- i = 0;
- // Remove old drop targets.
- $( '.fl-col-drop-target' ).remove();
- $( '.fl-col-group-drop-target' ).remove();
- $( '.fl-row-drop-target' ).remove();
- // Row drop targets.
- $( FLBuilder._contentClass ).append( '<div class="fl-drop-target fl-row-drop-target"></div>' );
- rows.prepend( '<div class="fl-drop-target fl-row-drop-target"></div>' );
- rows.append( '<div class="fl-drop-target fl-drop-target-last fl-row-drop-target fl-row-drop-target-last"></div>' );
- // Add drop targets to empty rows.
- rows.find( '.fl-row-content' ).prepend( '<div class="fl-drop-target fl-col-group-drop-target fl-col-group-drop-target-empty"></div>' );
- FLBuilder._initEmptyRowDropTargets();
- // Add drop targets to container modules.
- modules.append( '<div class="fl-drop-target fl-col-group-drop-target"></div>' );
- modules.append( '<div class="fl-drop-target fl-drop-target-last fl-col-group-drop-target fl-col-group-drop-target-last"></div>' );
- // Add drop targets to column groups and columns.
- for ( i = 0; i < groups.length; i++ ) {
- group = groups.eq( i );
- cols = group.find( '> .fl-col' );
- // Column group drop targets.
- if ( ! group.hasClass( 'fl-col-group-nested' ) ) {
- group.append( '<div class="fl-drop-target fl-col-group-drop-target"></div>' );
- group.append( '<div class="fl-drop-target fl-drop-target-last fl-col-group-drop-target fl-col-group-drop-target-last"></div>' );
- }
- // Column drop targets.
- cols.append( '<div class="fl-drop-target fl-col-drop-target"></div>' );
- cols.append( '<div class="fl-drop-target fl-drop-target-last fl-col-drop-target fl-col-drop-target-last"></div>' );
- }
- // Add drop targets to root column of a column template.
- if ( rootCol && 0 === groups.length ) {
- groups = rootCol.find( '.fl-col-group' );
- rootCol.append( '<div class="fl-drop-target fl-col-drop-target"></div>' );
- rootCol.append( '<div class="fl-drop-target fl-drop-target-last fl-col-drop-target fl-col-drop-target-last"></div>' );
- }
- },
- /**
- * Shows drop targets in empty rows with no content.
- *
- * @method _initEmptyRowDropTargets
- * @return void
- */
- _initEmptyRowDropTargets: function()
- {
- var emptyRows = $( FLBuilder._contentClass + ' .fl-row-content:not(:has(*:visible))' );
- $( '.fl-row-content-empty' ).removeClass( '.fl-row-content-empty' );
- if ( emptyRows.length ) {
- emptyRows.addClass( 'fl-row-content-empty' );
- }
- },
- /**
- * Returns a helper element for a drag operation.
- *
- * @since 1.0
- * @access private
- * @method _blockDragHelper
- * @param {Object} e The event object.
- * @param {Object} item The item being dragged.
- * @return {Object} The helper element.
- */
- _blockDragHelper: function (e, item)
- {
- var helper = item.clone();
- item.clone().insertAfter(item);
- helper.addClass('fl-builder-block-drag-helper');
- return helper;
- },
- /**
- * Initializes a drag operation.
- *
- * @since 1.0
- * @access private
- * @method _blockDragInit
- * @param {Object} e The event object.
- */
- _blockDragInit: function( e )
- {
- var target = $( e.currentTarget ),
- node = null,
- scrollTop = $( window ).scrollTop(),
- initialPos = 0;
- // Set the _dragEnabled flag.
- FLBuilder._dragEnabled = true;
- // Save the initial scroll position.
- FLBuilder._dragInitialScrollTop = scrollTop;
- // Get the node to scroll to once the node highlights have affected the body height.
- if ( target.closest( '[data-node]' ).length > 0 ) {
- // Set the node to a node instance being dragged.
- node = target.closest( '[data-node]' );
- // Mark this node as initialized for dragging.
- node.addClass( 'fl-node-drag-init' );
- }
- else if ( target.hasClass( 'fl-builder-block' ) ) {
- // Set the node to the first visible row instance.
- $( '.fl-row' ).each( function() {
- if ( node === null && $( this ).offset().top - scrollTop > 0 ) {
- node = $( this );
- }
- } );
- }
- // Get the initial scroll position of the node.
- if ( node !== null ) {
- initialPos = node.offset().top - scrollTop;
- }
- // Setup the UI for dragging.
- FLBuilder._highlightRowsAndColsForDrag( target );
- FLBuilder._adjustColHeightsForDrag();
- FLBuilder._disableGlobalNodes();
- FLBuilder._destroyOverlayEvents();
- FLBuilder._initSortables();
- $( 'body' ).addClass( 'fl-builder-dragging' );
- $( 'body', window.parent.document ).addClass( 'fl-builder-dragging' );
- $( '.fl-builder-empty-message' ).hide();
- $( '.fl-sortable-disabled' ).removeClass( 'fl-sortable-disabled' );
- // Remove all action overlays if this isn't a touch for a proxy item.
- if ( 'touchstart' !== e.type && ! $( e.target ).hasClass( 'fl-sortable-proxy-item ' ) ) {
- FLBuilder._removeAllOverlays();
- }
- // Scroll to the node that is dragging.
- if ( initialPos > 0 ) {
- scrollTo( 0, node.offset().top - initialPos );
- }
- FLBuilder.triggerHook('didInitDrag');
- },
- /**
- * Callback that fires when dragging starts.
- *
- * @since 1.0
- * @access private
- * @method _blockDragStart
- * @param {Object} e The event object.
- * @param {Object} ui An object with additional info for the drag.
- */
- _blockDragStart: function(e, ui)
- {
- // Let the builder know dragging has started.
- FLBuilder._dragging = true;
- // Removed the drag init class as we're done with that.
- $( '.fl-node-drag-init' ).removeClass( 'fl-node-drag-init' );
- FLBuilder.triggerHook('didStartDrag');
- },
- /**
- * Callback that fires when an element that is being
- * dragged is sorted.
- *
- * @since 1.0
- * @access private
- * @method _blockDragSort
- * @param {Object} e The event object.
- * @param {Object} ui An object with additional info for the drag.
- */
- _blockDragSort: function(e, ui)
- {
- var parent = ui.placeholder.parent(),
- title = FLBuilderStrings.insert;
- // Prevent sorting?
- if ( FLBuilder._blockPreventSort( ui.item, parent ) ) {
- return;
- }
- // Reset flex items with a fixed width.
- $( '.fl-sortable-fixed-width' ).css( 'max-width', '' ).removeClass( 'fl-sortable-fixed-width' );
- // Find the placeholder title.
- if(parent.hasClass('fl-col-content') || parent.hasClass('fl-module') || parent.hasClass('fl-module-content')) {
- // Make flex items fixed width while sorting.
- if ((/flex/).test(parent.css('display'))) {
- ui.placeholder.hide();
- parent.css('max-width', '');
- parent.css('max-width', parent.outerWidth());
- parent.addClass( 'fl-sortable-fixed-width' );
- ui.placeholder.show();
- }
- if ((/flex/).test(parent.css('display')) && (/row|row-reverse/).test(parent.css( "flex-direction" ))) {
- title = '';
- }
- else if(ui.item.hasClass('fl-builder-block-row')) {
- title = ui.item.find('.fl-builder-block-title').text();
- }
- else if(ui.item.hasClass('fl-col-sortable-proxy-item')) {
- title = FLBuilderStrings.column;
- }
- else if(ui.item.hasClass('fl-builder-block-module')) {
- title = ui.item.find('.fl-builder-block-title').text();
- }
- else if(ui.item.hasClass('fl-builder-block-saved-module') || ui.item.hasClass('fl-builder-block-module-template')) {
- title = ui.item.find('.fl-builder-block-title').text();
- }
- else {
- title = ui.item.data('name');
- }
- }
- else if(parent.hasClass('fl-col-drop-target')) {
- title = '';
- }
- else if (parent.hasClass('fl-col-group-drop-target')) {
- title = '';
- }
- else if(parent.hasClass('fl-row-drop-target')) {
- if(ui.item.hasClass('fl-builder-block-row')) {
- title = ui.item.find('.fl-builder-block-title').text();
- }
- else if(ui.item.hasClass('fl-builder-block-saved-row')) {
- title = ui.item.find('.fl-builder-block-title').text();
- }
- else if(ui.item.hasClass('fl-builder-block-saved-column')) {
- title = ui.item.find('.fl-builder-block-title').text();
- }
- else if(ui.item.hasClass('fl-row-sortable-proxy-item')) {
- title = FLBuilderStrings.row;
- }
- else {
- title = FLBuilderStrings.newRow;
- }
- }
- // Set the placeholder title.
- ui.placeholder.html(title);
- // Add the global class?
- if ( ui.item.hasClass( 'fl-node-global' ) ||
- ui.item.hasClass( 'fl-builder-block-global' ) ||
- $( '.fl-node-dragging' ).hasClass( 'fl-node-global' )
- ) {
- ui.placeholder.addClass( 'fl-builder-drop-zone-global' );
- }
- else {
- ui.placeholder.removeClass( 'fl-builder-drop-zone-global' );
- }
- },
- /**
- * Callback that fires when an element that is being
- * dragged position changes.
- *
- * What we're doing here keeps it from appearing jumpy when draging
- * between columns. Without this you'd see the placeholder jump into
- * a column position briefly when you didn't intend for it to.
- *
- * @since 1.9
- * @access private
- * @method _blockDragChange
- * @param {Object} e The event object.
- * @param {Object} ui An object with additional info for the drag.
- */
- _blockDragChange: function( e, ui )
- {
- FLBuilder._initEmptyRowDropTargets();
- ui.placeholder.css( 'opacity', '0' );
- ui.placeholder.animate( { 'opacity': '1' }, 100 );
- },
- /**
- * Prevents sorting of items that shouldn't be sorted into
- * specific areas.
- *
- * @since 1.9
- * @access private
- * @method _blockPreventSort
- * @param {Object} item The item being sorted.
- * @param {Object} parent The new parent.
- */
- _blockPreventSort: function( item, parent )
- {
- var prevent = false,
- isRowBlock = item.hasClass( 'fl-builder-block-row' ),
- isCol = item.hasClass( 'fl-col-sortable-proxy-item' ),
- isModule = item.hasClass( 'fl-builder-block-module' ) || item.hasClass( 'fl-module-sortable-proxy-item' ),
- isContainerModule = parent.data( 'accepts' ),
- isParentColGlobal = parent.closest('.fl-col[data-template-url]').hasClass('fl-node-global'),
- isParentCol = parent.hasClass( 'fl-col-content' ),
- isColTarget = parent.hasClass( 'fl-col-drop-target' ),
- group = parent.parents( '.fl-col-group:not(.fl-col-group-nested)' ),
- nestedGroup = parent.parents( '.fl-col-group-nested' ),
- parentType = parent.data( 'type' ),
- itemType = item.data( 'type' );
- // Handle container modules.
- if ( isContainerModule ) {
- const { accepts } = FLBuilderConfig.contentItems.module.filter( config => parentType === config.slug ).pop();
- // Prevent rows and columns in container modules.
- if ( isRowBlock || isCol ) {
- prevent = true;
- }
- // Prevent unaccepted nodes in container modules.
- else if ( 'object' === typeof accepts && accepts.length && ! accepts.includes( itemType ) ) {
- prevent = true;
- }
- }
- // Prevent modules in unaccepted parents.
- // if ( isModule && 'widget' !== itemType ) {
- // const { parents } = FLBuilderConfig.contentItems.module.filter( config => itemType === config.slug ).pop();
- //
- // if ( 'object' === typeof parents && ! parents.includes( parentType ) ) {
- // prevent = true;
- // }
- // }
- // Prevent columns in nested columns.
- if ( ( isRowBlock || isCol ) && isParentCol && nestedGroup.length > 0 ) {
- prevent = true;
- }
- // Prevent 1 column from being nested in an empty column.
- if ( isParentCol && ! parent.find( '.fl-module, .fl-col' ).length ) {
- if ( isRowBlock && '1-col' == item.data( 'cols' ) ) {
- prevent = true;
- }
- else if ( isCol ) {
- prevent = true;
- }
- }
- // Prevent 5 or 6 columns from being nested.
- if ( isRowBlock && isParentCol && $.inArray( item.data( 'cols' ), [ '5-cols', '6-cols' ] ) > -1 ) {
- prevent = true;
- }
- // Prevent columns with nested columns from being dropped in nested columns.
- if ( isCol && $( '.fl-node-dragging' ).find( '.fl-col-group-nested' ).length > 0 ) {
- if ( isParentCol || ( isColTarget && nestedGroup.length > 0 ) ) {
- prevent = true;
- }
- }
- // Prevent more than 12 columns.
- if ( isColTarget && group.length > 0 && 0 === nestedGroup.length && group.find( '> .fl-col:visible' ).length > 11 ) {
- prevent = true;
- }
- // Prevent more than 4 nested columns.
- if ( isColTarget && nestedGroup.length > 0 && nestedGroup.find( '.fl-col:visible' ).length > 3 ) {
- prevent = true;
- }
- // Prevent module from being dropped to a Global Col except when editing a saved column.
- if ( isModule && isParentColGlobal && 'column' !== FLBuilderConfig.userTemplateType ) {
- prevent = true;
- }
- // Prevent dropping into layouts rendered via shortcode.
- if ( parent.closest( '.fl-builder-shortcode-mask-wrap' ).length ) {
- prevent = true;
- }
- // Add the disabled class if we are preventing a sort.
- if ( prevent ) {
- parent.addClass( 'fl-sortable-disabled' );
- }
- return prevent;
- },
- /**
- * Cleans up when a drag operation has stopped.
- *
- * @since 1.0
- * @access private
- * @method _blockDragStop
- * @param {Object} e The event object.
- * @param {Object} ui An object with additional info for the drag.
- */
- _blockDragStop: function( e, ui )
- {
- var scrollTop = $( window ).scrollTop(),
- parent = ui.item.parent(),
- initialPos = null;
- // Get the node to scroll to once removing the node highlights affects the body height.
- if ( parent.hasClass( 'fl-drop-target' ) && parent.closest( '[data-node]' ).length ) {
- parent = parent.closest( '[data-node]' );
- initialPos = parent.offset().top - scrollTop;
- }
- else {
- initialPos = parent.offset().top - scrollTop;
- }
- // Show the panel if a block was dropped back in.
- if ( parent.hasClass( 'fl-builder-blocks-section-content' ) ) {
- FLBuilder._showPanel();
- }
- // Finish dragging.
- FLBuilder._dragEnabled = false;
- FLBuilder._dragging = false;
- FLBuilder._bindOverlayEvents();
- FLBuilder._removeEmptyRowAndColHighlights();
- FLBuilder._highlightEmptyCols();
- FLBuilder._enableGlobalNodes();
- FLBuilder._setupEmptyLayout();
- $( 'body' ).removeClass( 'fl-builder-dragging' );
- $( 'body', window.parent.document ).removeClass( 'fl-builder-dragging' );
- // Scroll the page back to where it was.
- scrollTo( 0, parent.offset().top - initialPos );
- FLBuilder.triggerHook('didStopDrag');
- },
- /**
- * Cleans up when a drag operation has canceled.
- *
- * @since 1.0
- * @access private
- * @method _blockDragCancel
- */
- _blockDragCancel: function()
- {
- if ( FLBuilder._dragEnabled && ! FLBuilder._dragging ) {
- FLBuilder._dragEnabled = false;
- FLBuilder._dragging = false;
- FLBuilder._bindOverlayEvents();
- FLBuilder._removeEmptyRowAndColHighlights();
- FLBuilder._highlightEmptyCols();
- FLBuilder._enableGlobalNodes();
- FLBuilder._setupEmptyLayout();
- $( 'body' ).removeClass( 'fl-builder-dragging' );
- $( 'body', window.parent.document ).removeClass( 'fl-builder-dragging' );
- $( '.fl-node-drag-init' ).removeClass( 'fl-node-drag-init' );
- $( '.fl-node-dragging' ).removeClass( 'fl-node-dragging' );
- scrollTo( 0, FLBuilder._dragInitialScrollTop );
- FLBuilder.triggerHook('didCancelDrag');
- }
- },
- /**
- * Reorders a node within its parent.
- *
- * @since 1.9
- * @access private
- * @method _reorderNode
- * @param {String} nodeId The node ID of the node.
- * @param {Number} position The new position.
- */
- _reorderNode: function( nodeId, position )
- {
- const actions = FL.Builder.getActions()
- actions.moveNode( nodeId, position )
- },
- /**
- * Handle completion after reorder ajax.
- *
- * @since 2.5
- * @access private
- * @method _reorderNodeComplete
- * @param Object ajax response
- */
- _reorderNodeComplete: function( response ) {
- var data = FLBuilder._jsonParse( response );
- var hook = 'didMove' + data.nodeType.charAt(0).toUpperCase() + data.nodeType.slice(1);
- FLBuilder.triggerHook( 'didMoveNode', data );
- FLBuilder.triggerHook( hook, data );
- },
- /**
- * Moves a node up a position within its parent.
- *
- * @since 2.8
- */
- _moveNodeUpClicked: function( e )
- {
- var node = $( e.target ).closest( '[data-node]' );
- var nodeId = node.attr( 'data-node' );
- var data = FL.Builder.data.getNode( nodeId );
- var position = data.position - 1;
- var upBtn = node.find( '.fl-block-move-up' ).first();
- var downBtn = node.find( '.fl-block-move-down' ).first();
- if ( position <= 0 ) {
- upBtn.addClass( 'fl-builder-submenu-disabled' );
- } else {
- upBtn.removeClass( 'fl-builder-submenu-disabled' );
- }
- downBtn.removeClass( 'fl-builder-submenu-disabled' );
- FLBuilder._reorderNode( nodeId, position );
- FLBuilder._selectNodeOverlay( node, false );
- FL.Builder.data.updateNode( nodeId, { ...data, position } );
- },
- /**
- * Moves a node down a position within its parent.
- *
- * @since 2.8
- */
- _moveNodeDownClicked: function( e )
- {
- var node = $( e.target ).closest( '[data-node]' );
- var nodeId = node.attr( 'data-node' );
- var data = FL.Builder.data.getNode( nodeId );
- var position = data.position + 1;
- var upBtn = node.find( '.fl-block-move-up' ).first();
- var downBtn = node.find( '.fl-block-move-down' ).first();
- var parentChildren = node.parent().find( '> [data-node]' );
- if ( position >= parentChildren.length - 1 ) {
- downBtn.addClass( 'fl-builder-submenu-disabled' );
- } else {
- downBtn.removeClass( 'fl-builder-submenu-disabled' );
- }
- upBtn.removeClass( 'fl-builder-submenu-disabled' );
- FLBuilder._reorderNode( nodeId, position );
- FLBuilder._selectNodeOverlay( node, false );
- FL.Builder.data.updateNode( nodeId, { ...data, position } );
- },
- /**
- * Moves a node to a new parent.
- *
- * @since 1.9
- * @access private
- * @method _moveNode
- * @param {String} newParent The node ID of the new parent.
- * @param {String} nodeId The node ID of the node.
- * @param {Number} position The new position.
- */
- _moveNode: function( newParent, nodeId, position )
- {
- const actions = FL.Builder.getActions()
- actions.moveNode( nodeId, position, newParent )
- },
- /**
- * Finishes a move node operation.
- *
- * @since 2.5
- * @access private
- * @method _moveNodeComplete
- * @param Object ajax response
- */
- _moveNodeComplete: function( response )
- {
- const data = FLBuilder._jsonParse( response );
- const hook = 'didMove' + data.nodeType.charAt( 0 ).toUpperCase() + data.nodeType.slice( 1 );
- FLBuilder.triggerHook( 'didMoveNode', data );
- FLBuilder.triggerHook( hook, data );
- },
- /* Generic Node Methods
- ----------------------------------------------------------*/
- /**
- * Returns a JQuery reference to the HTMLElement for particular node.
- *
- * @since 2.5
- * @access private
- * @method _getjQueryElement
- * @return {JQuery} dom reference.
- */
- _getJQueryElement: function( id ) {
- return $( FLBuilder._contentClass ).find( '[data-node="' + id + '"]' )
- },
- /**
- * Returns whether a node lays its children out
- * horizontally, vertically, or layered.
- *
- * @since 2.8
- * @param {Object} node
- */
- _getNodeLayoutDirection: function( node )
- {
- var isFlex = ( /flex/ ).test( node.parent().css( 'display' ) );
- var isFlexRow = ( /row|row-reverse/ ).test( node.parent().css( 'flex-direction' ) );
- var isGrid = ( /grid/ ).test( node.parent().css( 'display' ) );
- var isLayeredGrid = isGrid && '1 / 1 / -1 / -1' === node.css( 'grid-area' );
- var isSingleColumnGrid = 1 === window.getComputedStyle( node.parent()[0] ).getPropertyValue( 'grid-template-columns' ).split( ' ' ).length;
- if ( isFlex && isFlexRow ) {
- return 'horizontal';
- } else if ( isGrid ) {
- if ( isLayeredGrid ) {
- return 'layered';
- } else if ( ! isSingleColumnGrid ) {
- return 'horizontal';
- }
- }
- return 'vertical';
- },
- /**
- * Checks if a node requires a confirmation message before deleting.
- *
- * @since 2.5
- * @access private
- * @method _needsDeleteConfirmation
- * @return bool
- */
- _needsDeleteConfirmation: function( node ) {
- if ( 'module' === node.type ) {
- return true
- }
- // Otherwise check if the container has modules
- const el = FLBuilder._getJQueryElement( node.node )
- return el.find( '.fl-module' ).length > 0
- },
- /**
- * Disables global nodes during drag.
- *
- * @since 2.8
- */
- _disableGlobalNodes: function()
- {
- if ( 'row' !== FLBuilderConfig.userTemplateType ) {
- $( '.fl-row.fl-node-global' ).addClass( 'fl-node-disabled' );
- }
- if ( 'column' !== FLBuilderConfig.userTemplateType ) {
- $( '.fl-row:not(.fl-node-global) .fl-col.fl-node-global' ).addClass( 'fl-node-disabled' );
- }
- if ( 'module' !== FLBuilderConfig.userTemplateType ) {
- $( '.fl-row:not(.fl-node-global) .fl-module.fl-node-global[data-accepts]' ).addClass( 'fl-node-disabled' );
- }
- },
- /**
- * Remove disabled global node class after drag.
- *
- * @since 2.8
- */
- _enableGlobalNodes: function()
- {
- $( '.fl-node-disabled' ).removeClass( 'fl-node-disabled' );
- },
- /**
- * Called when the node settings overlay action is clicked.
- *
- * @since 2.8
- */
- _nodeSettingsClicked: function( e )
- {
- var button = $( this );
- var nodeId = button.attr( 'data-target-node' );
- var actions = FL.Builder.getActions();
- if ( ! nodeId ) {
- nodeId = button.parents( '[data-node]' ).attr( 'data-node' );
- }
- actions.openSettings( nodeId );
- e.stopPropagation();
- },
- /**
- * Called when the node duplicate overlay action is clicked.
- *
- * @since 2.8
- */
- _nodeDuplicateClicked: function( e )
- {
- var button = $( this );
- var nodeId = button.attr( 'data-target-node' );
- var actions = FL.Builder.getActions();
- if ( ! nodeId ) {
- nodeId = button.parents( '[data-node]' ).attr( 'data-node' );
- }
- actions.copyNode( nodeId );
- e.stopPropagation();
- },
- /**
- * Called when the node remove overlay action is clicked.
- *
- * @since 2.8
- */
- _nodeRemoveClicked: function( e )
- {
- var button = $( this );
- var nodeId = button.attr( 'data-target-node' );
- var actions = FL.Builder.getActions();
- if ( ! nodeId ) {
- nodeId = button.parents( '[data-node]' ).attr( 'data-node' );
- }
- actions.deleteNode( nodeId );
- e.stopPropagation();
- },
- /* Rows
- ----------------------------------------------------------*/
- /**
- * Returns a helper element for row drag operations.
- *
- * @since 1.0
- * @access private
- * @method _rowDragHelper
- * @return {Object} The helper element.
- */
- _rowDragHelper: function()
- {
- return $('<div class="fl-builder-block-drag-helper">' + FLBuilderStrings.row + '</div>');
- },
- /**
- * Initializes dragging for row. Rows themselves aren't sortables
- * as nesting that many sortables breaks down quickly and draggable by
- * itself is slow. Instead, we are programmatically triggering the drag
- * of our helper div that isn't a nested sortable but connected to the
- * sortables in the main layout.
- *
- * @since 1.9
- * @access private
- * @method _rowDragInit
- * @param {Object} e The event object.
- */
- _rowDragInit: function( e )
- {
- var handle = $( e.target ),
- helper = $( '.fl-row-sortable-proxy-item', window.parent.document ),
- row = handle.closest( '.fl-row' );
- if ( handle.closest( '.fl-block-move-menu' ).length ) {
- return;
- }
- row.addClass( 'fl-node-dragging' );
- FLBuilder._blockDragInit( e );
- e.target = helper[ 0 ];
- helper.trigger( e );
- },
- /**
- * @method _rowDragInitTouch
- * @param {Object} e The event object.
- */
- _rowDragInitTouch: function( startEvent )
- {
- var handle = $( startEvent.target ),
- helper = $( '.fl-row-sortable-proxy-item', window.parent.document ),
- row = handle.closest( '.fl-row' ),
- moved = false;
- handle.on( 'touchmove', function( moveEvent ) {
- if ( ! moved ) {
- startEvent.currentTarget = row[0];
- FLBuilder._rowDragInit( startEvent );
- moved = true;
- }
- moveEvent.target = helper[0];
- helper.trigger( moveEvent );
- } );
- handle.on( 'touchend', function( endEvent ) {
- endEvent.target = helper[0];
- helper.trigger( endEvent );
- endEvent.stopPropagation();
- } );
- },
- /**
- * Callback that fires when dragging starts for a row.
- *
- * @since 1.9
- * @access private
- * @method _rowDragStart
- * @param {Object} e The event object.
- * @param {Object} ui An object with additional info for the drag.
- */
- _rowDragStart: function( e, ui )
- {
- var rows = $( FLBuilder._contentClass + ' .fl-row' ),
- row = $( '.fl-node-dragging' );
- if ( 1 === rows.length ) {
- $( FLBuilder._contentClass ).addClass( 'fl-builder-empty' );
- }
- row.hide();
- FLBuilder._blockDragStart( e, ui );
- },
- /**
- * Callback for when a row drag operation completes.
- *
- * @since 1.0
- * @access private
- * @method _rowDragStop
- * @param {Object} e The event object.
- * @param {Object} ui An object with additional info for the drag.
- */
- _rowDragStop: function( e, ui )
- {
- var item = ui.item,
- parent = item.parent(),
- row = null,
- group = null,
- position = 0;
- FLBuilder._blockDragStop( e, ui );
- // A row was dropped back into the row list.
- if ( parent.hasClass( 'fl-builder-rows' ) ) {
- item.remove();
- return;
- }
- // A row was dropped back into the sortable proxy.
- else if ( parent.hasClass( 'fl-row-sortable-proxy' ) ) {
- $( '.fl-node-dragging' ).removeClass( 'fl-node-dragging' ).show();
- return;
- }
- // Add a new row.
- else if ( item.hasClass( 'fl-builder-block' ) ) {
- // Cancel the drop if the sortable is disabled?
- if ( parent.hasClass( 'fl-sortable-disabled' ) ) {
- item.remove();
- FLBuilder._showPanel();
- return;
- }
- // A new row was dropped into column.
- else if ( parent.hasClass( 'fl-col-content' ) ) {
- FLBuilder._addColGroup(
- item.closest( '.fl-col' ).attr( 'data-node' ),
- item.attr( 'data-cols' ),
- parent.find( '> .fl-module, .fl-col-group, .fl-builder-block' ).index( item )
- );
- }
- // A new row was dropped next to a column.
- else if ( parent.hasClass( 'fl-col-drop-target' ) ) {
- FLBuilder._addCols(
- parent.closest( '.fl-col' ),
- parent.hasClass( 'fl-col-drop-target-last' ) ? 'after' : 'before',
- item.attr( 'data-cols' ),
- parent.closest( '.fl-col-group-nested' ).length > 0
- );
- }
- // A new row was dropped into a column group position.
- else if ( parent.hasClass( 'fl-col-group-drop-target' ) ) {
- group = item.closest( '.fl-col-group, .fl-module' );
- position = item.closest( '.fl-row' ).find( '.fl-row-content' ).find( '> .fl-col-group, > .fl-module' ).index( group );
- FLBuilder._addColGroup(
- item.closest( '.fl-row' ).attr( 'data-node' ),
- item.attr( 'data-cols' ),
- parent.hasClass( 'fl-drop-target-last' ) ? position + 1 : position
- );
- }
- // A row was dropped into a row position.
- else {
- row = item.closest( '.fl-row' );
- position = ! row.length ? 0 : $( FLBuilder._contentClass + ' > .fl-row' ).index( row );
- FLBuilder._addRow(
- item.attr('data-cols'),
- parent.hasClass( 'fl-drop-target-last' ) ? position + 1 : position
- );
- }
- // Remove the helper.
- item.remove();
- // Show the builder panel.
- FLBuilder._showPanel();
- }
- // Reorder a row.
- else {
- row = $( '.fl-node-dragging' ).removeClass( 'fl-node-dragging' ).show();
- // Make sure a single row wasn't dropped back into the main layout.
- if ( ! parent.parent().hasClass( 'fl-builder-content' ) ) {
- // Move the row in the UI.
- if ( parent.hasClass( 'fl-drop-target-last' ) ) {
- parent.parent().after( row );
- }
- else {
- parent.parent().before( row );
- }
- // Reorder the row.
- FLBuilder._reorderNode(
- row.attr('data-node'),
- row.index()
- );
- }
- // Revert the proxy to its parent.
- $( '.fl-row-sortable-proxy', window.parent.document ).append( ui.item );
- }
- },
- /**
- * Adds a new row to the layout.
- *
- * @since 1.0
- * @access private
- * @method _addRow
- * @param {String} cols The type of column layout to use.
- * @param {Number} position The position of the new row.
- * @param {String} module Optional. The node ID of an existing module to move to this row.
- */
- _addRow: function(cols, position, module)
- {
- FLBuilder._showNodeLoadingPlaceholder( $( FLBuilder._contentClass ), position );
- FLBuilder._newRowPosition = position;
- const actions = FL.Builder.data.getLayoutActions()
- actions.addRow( cols, position, module )
- },
- /**
- * Adds the HTML for a new row to the layout when the AJAX
- * add operation is complete.
- *
- * @since 1.0
- * @access private
- * @method _addRowComplete
- * @param {String} response The JSON response with the HTML for the new row.
- */
- _addRowComplete: function(response)
- {
- var data = 'object' === typeof response ? response : FLBuilder._jsonParse(response),
- content = $(FLBuilder._contentClass),
- rowId = $(data.html).data('node');
- // Add new row info to the data.
- data.nodeParent = content;
- data.nodePosition = FLBuilder._newRowPosition;
- // Render the layout.
- FLBuilder._renderLayout( data, function(){
- FLBuilder._removeNodeLoadingPlaceholder( $( '.fl-node-' + rowId ) );
- FLBuilder.triggerHook( 'didAddRow', rowId );
- } );
- },
- /**
- * Callback for when the delete row button is clicked.
- *
- * @since 1.0
- * @access private
- * @method _deleteRowClicked
- * @param {Object} e The event object.
- */
- _deleteRowClicked: function( e )
- {
- var id = $( e.target ).closest( '.fl-row' ).data( 'node' );
- var actions = FL.Builder.getActions()
- actions.deleteNode( id )
- e.stopPropagation();
- },
- /**
- * Deletes a row.
- *
- * @since 1.0
- * @access private
- * @method _deleteRow
- * @param {Object} row A jQuery reference of the row to delete.
- */
- _deleteRow: function(row)
- {
- var nodeId = row.attr('data-node');
- const actions = FL.Builder.data.getLayoutActions()
- actions.deleteNode( nodeId )
- row.empty();
- row.remove();
- FLBuilder._setupEmptyLayout();
- FLBuilder._removeRowOverlays();
- FLBuilder.triggerHook( 'didDeleteRow', nodeId );
- },
- /**
- * Listen for duplicate click.
- *
- * @since 1.3.8
- * @access private
- * @method _rowCopyClicked
- * @param {Object} e The event object.
- */
- _rowCopyClicked: function(e)
- {
- var id = $( this ).closest( '.fl-row' ).attr( 'data-node' );
- FLBuilder._copyRow( id );
- e.stopPropagation();
- },
- /**
- * Copy settings of a row.
- *
- * @since 2.6
- * @access private
- * @method _rowCopySettingsClicked
- */
- _rowCopySettingsClicked: function () {
- const menuEl = $(this);
- const nodeId = menuEl.closest('.fl-row').data('node');
- // bind copy to the el
- FLBuilderSettingsCopyPaste._bindCopyToElement(menuEl, 'row', nodeId, true);
- },
- /**
- * Paste settings of a row.
- *
- * @since 2.6
- * @access private
- * @method _rowPasteSettingsClicked
- */
- _rowPasteSettingsClicked: function () {
- const menuEl = $(this);
- const menuText = menuEl.text();
- const nodeId = menuEl.closest('.fl-row').data('node');
- const success = FLBuilderSettingsCopyPaste._importFromClipboard('row', nodeId);
- if (!success) {
- // set button text
- menuEl.text(FLBuilderStrings.module_import.error);
- // restore button text
- setTimeout(() => {
- menuEl.text(menuText)
- }, 1000);
- }
- },
- /**
- * Duplicate a row.
- *
- * @since 2.5
- * @access private
- * @method _copyRow
- * @param String id of node
- * @return void
- */
- _copyRow: function( nodeId ) {
- var row = FLBuilder._getJQueryElement( nodeId ),
- clone = row.clone(),
- form = $( '.fl-builder-settings[data-node]', window.parent.document ),
- formNodeId = form.attr( 'data-node' ),
- formNode = ( formNodeId === nodeId ) ? row : row.find( '[data-node="' + formNodeId + '"]' ),
- settings = null;
- if ( form.length && formNode.length ) {
- settings = FLBuilder._getSettings( form );
- FLBuilderSettingsConfig.nodes[ formNodeId ] = settings;
- }
- clone.addClass( 'fl-node-' + nodeId + '-clone fl-builder-node-clone' );
- clone.find( '.fl-block-overlay' ).remove();
- clone.removeAttr( 'data-node' );
- row.after( clone );
- FLBuilder._showNodeLoading( nodeId + '-clone' );
- // Animate scroll to new element
- const el = clone.get(0);
- el.scrollIntoView( {
- behavior: 'smooth',
- block: 'center',
- } );
- const actions = FL.Builder.data.getLayoutActions()
- const callback = function( response ) {
- var data = FLBuilder._jsonParse( response );
- data.nodeParent = $( FLBuilder._contentClass );
- data.nodePosition = $( FLBuilder._contentClass + ' > .fl-row' ).index( clone );
- data.duplicatedRow = nodeId;
- data.onAddNewHTML = function() { clone.remove() };
- FLBuilder._rowCopyComplete( data );
- }
- actions.copyRow( nodeId, settings, formNodeId, callback )
- },
- /**
- * Callback for when a row has been duplicated.
- *
- * @since 1.7
- * @access private
- * @method _rowCopyComplete
- * @param {Object} data
- */
- _rowCopyComplete: function( data )
- {
- FLBuilder._renderLayout( data, function() {
- FLBuilder.triggerHook( 'didDuplicateRow', {
- newNodeId : data.nodeId,
- oldNodeId : data.duplicatedRow
- } );
- } );
- },
- /**
- * Shows the settings lightbox and loads the row settings
- * when the row settings button is clicked.
- *
- * @since 1.0
- * @access private
- * @method _rowSettingsClicked
- */
- _rowSettingsClicked: function( e )
- {
- var button = $( this),
- nodeId = $( this ).closest( '.fl-row' ).attr( 'data-node' ),
- global = button.closest( '.fl-block-overlay-global' ).length > 0;
- const actions = FL.Builder.getActions()
- actions.openSettings( nodeId )
- e.stopPropagation()
- },
- /**
- * Show settings for a row node
- *
- * @since 2.?
- * @access private
- * @method _showRowSettings
- */
- _showRowSettings: function( nodeId, global )
- {
- let win = null;
- // If we're on a global row template page
- if ( global && 'row' != FLBuilderConfig.userTemplateType ) {
- if ( FLBuilderConfig.userCanEditGlobalTemplates ) {
- win = window.parent.open( $( '.fl-row[data-node="' + nodeId + '"]' ).attr( 'data-template-url' ) );
- win.FLBuilderGlobalNodeId = nodeId;
- }
- } else {
- FLBuilderSettingsForms.render( {
- id : 'row',
- nodeId : nodeId,
- className : 'fl-builder-row-settings',
- attrs : 'data-node="' + nodeId + '"',
- buttons : ! global && ! FLBuilderConfig.lite && ! FLBuilderConfig.simpleUi ? ['save-as'] : [],
- badges : global ? [ FLBuilderStrings.global ] : [],
- settings : FLBuilderSettingsConfig.nodes[ nodeId ],
- preview : {
- type: 'row'
- }
- }, function() {
- $( '#fl-field-width select', window.parent.document ).on( 'change', FLBuilder._rowWidthChanged );
- $( '#fl-field-content_width select', window.parent.document ).on( 'change', FLBuilder._rowWidthChanged );
- } );
- }
- },
- /**
- * Shows or hides the row max-width setting when the
- * row or row content width is changed.
- *
- * @since 2.0
- * @access private
- * @method _rowWidthChanged
- */
- _rowWidthChanged: function()
- {
- var rowWidth = $( '#fl-field-width select', window.parent.document ).val(),
- contentWidth = $( '#fl-field-content_width select', window.parent.document ).val(),
- maxWidth = $( '#fl-field-max_content_width', window.parent.document );
- if ( 'fixed' == rowWidth || ( 'full' == rowWidth && 'fixed' == contentWidth ) ) {
- maxWidth.show();
- } else {
- maxWidth.hide();
- }
- },
- /**
- * Resets the max-width of a row.
- *
- * @since 2.0
- * @access private
- * @method _resetRowWidthClicked
- */
- _resetRowWidthClicked: function( e )
- {
- var button = $( this ),
- row = button.closest( '.fl-row' ),
- nodeId = row.attr( 'data-node' ),
- content = row.find( '.fl-row-content' ),
- width = FLBuilderConfig.global.row_width + 'px',
- settings = $( '.fl-builder-row-settings', window.parent.document );
- if ( row.hasClass( 'fl-row-fixed-width' ) ) {
- row.css( 'max-width', width );
- }
- content.css( 'max-width', width );
- if ( settings.length ) {
- settings.find( '[name=max_content_width]' ).val( '' );
- }
- const actions = FL.Builder.data.getLayoutActions()
- actions.resetRowWidth( nodeId )
- FLBuilder._closeAllSubmenus();
- FLBuilder.triggerHook( 'didResetRowWidth', nodeId );
- e.stopPropagation();
- },
- /* Columns
- ----------------------------------------------------------*/
- /**
- * Adds a dashed border to empty columns.
- *
- * @since 1.0
- * @access private
- * @method _highlightEmptyCols
- */
- _highlightEmptyCols: function()
- {
- var notGlobal = FLBuilderConfig.userTemplateType ? '' : ':not(.fl-node-global)',
- cols = $(FLBuilder._contentClass + ' .fl-col' + notGlobal),
- modules = $( FLBuilder._contentClass + ' .fl-module[data-accepts]' + notGlobal );
- cols.removeClass('fl-col-highlight').find('.fl-col-content').css( 'min-height', '' );
- modules.removeClass('fl-module-highlight');
- cols.each(function(){
- var col = $(this);
- if(!col.find('.fl-module, .fl-col').length && !col.closest('.fl-builder-shortcode-mask-wrap').length) {
- col.addClass('fl-col-highlight');
- }
- });
- modules.each(function(){
- var module = $(this);
- if(!module.find('.fl-module').length && !module.closest('.fl-builder-shortcode-mask-wrap').length) {
- module.addClass('fl-module-highlight');
- }
- });
- },
- /**
- * Sets up dashed borders to show where things can
- * be dropped in rows and columns.
- *
- * @since 1.9
- * @access private
- * @method _highlightRowsAndColsForDrag
- * @param {Object} target The event target for the drag.
- */
- _highlightRowsAndColsForDrag: function( target )
- {
- var notGlobal = 'row' == FLBuilderConfig.userTemplateType ? '' : ':not(.fl-node-global)';
- // Do not highlight root parent column.
- if ( 'column' == FLBuilderConfig.userTemplateType ) {
- notGlobal = ':not(:first)';
- }
- // Highlight rows.
- $( FLBuilder._contentClass + ' > .fl-row' ).addClass( 'fl-row-highlight' );
- // Highlight columns.
- if ( ! target || ! target.closest( '.fl-row-overlay' ).length ) {
- $( FLBuilder._contentClass + ' .fl-col' + notGlobal ).each( function() {
- if ( ! $( this ).closest( '.fl-builder-shortcode-mask-wrap' ).length ) {
- $( this ).addClass( 'fl-col-highlight' );
- }
- } );
- }
- // Highlight container modules.
- $( FLBuilder._contentClass + ' .fl-module[data-accepts]' ).each( function() {
- if ( ! $( this ).closest( '.fl-builder-shortcode-mask-wrap' ).length ) {
- $( this ).addClass( 'fl-module-highlight' );
- }
- } );
- },
- /**
- * Remove any column highlights
- *
- * @since 2.0
- * @access private
- * @method _removeEmptyRowAndColHighlights
- */
- _removeEmptyRowAndColHighlights: function() {
- $( '.fl-row-highlight' ).removeClass('fl-row-highlight');
- $( '.fl-col-highlight' ).removeClass('fl-col-highlight');
- $( '.fl-module-highlight' ).removeClass('fl-module-highlight');
- $( '.fl-sortable-fixed-width' ).css( 'max-width', '' ).removeClass( 'fl-sortable-fixed-width' );
- },
- /**
- * Adjust the height of columns with modules in them
- * to account for the drop zone and keep the layout
- * from jumping around.
- *
- * @since 1.9
- * @access private
- * @method _adjustColHeightsForDrag
- */
- _adjustColHeightsForDrag: function()
- {
- var notGlobalRow = 'row' == FLBuilderConfig.userTemplateType ? '' : '.fl-row:not(.fl-node-global) ',
- notGlobalCol = 'column' == FLBuilderConfig.userTemplateType ? '' : '.fl-col:not(.fl-node-global) ',
- content = $( FLBuilder._contentClass ),
- notNested = content.find( notGlobalRow + '.fl-col-group:not(.fl-col-group-nested) > ' + notGlobalCol + '> .fl-col-content' ),
- nested = content.find( notGlobalRow + '.fl-col-group-nested ' + notGlobalCol + '.fl-col-content' ),
- col = null,
- i = 0;
- $( '.fl-node-drag-init' ).hide();
- for ( ; i < nested.length; i++ ) {
- if ( ! nested.eq( i ).closest( '.fl-builder-shortcode-mask-wrap' ).length ) {
- FLBuilder._adjustColHeightForDrag( nested.eq( i ) );
- }
- }
- for ( i = 0; i < notNested.length; i++ ) {
- if ( ! notNested.eq( i ).closest( '.fl-builder-shortcode-mask-wrap' ).length ) {
- FLBuilder._adjustColHeightForDrag( notNested.eq( i ) );
- }
- }
- $( '.fl-node-drag-init' ).show();
- },
- /**
- * Adjust the height of a single column for dragging.
- *
- * @since 1.9
- * @access private
- * @method _adjustColHeightForDrag
- */
- _adjustColHeightForDrag: function( col )
- {
- if ( col.find( '.fl-module:visible, .fl-col:visible' ).length ) {
- col.css( 'min-height', col.height() + 45 );
- }
- },
- /**
- * Returns a helper element for column drag operations.
- *
- * @since 1.9
- * @access private
- * @method _colDragHelper
- * @return {Object} The helper element.
- */
- _colDragHelper: function()
- {
- return $('<div class="fl-builder-block-drag-helper">' + FLBuilderStrings.column + '</div>');
- },
- /**
- * Initializes dragging for columns. Columns themselves aren't sortables
- * as nesting that many sortables breaks down quickly and draggable by
- * itself is slow. Instead, we are programmatically triggering the drag
- * of our helper div that isn't a nested sortable but connected to the
- * sortables in the main layout.
- *
- * @since 1.9
- * @access private
- * @method _colDragInit
- * @param {Object} e The event object.
- */
- _colDragInit: function( e )
- {
- var handle = $( e.target ),
- helper = $( '.fl-col-sortable-proxy-item', window.parent.document ),
- col = handle.closest( '.fl-col' );
- if ( handle.closest( '.fl-block-move-menu' ).length ) {
- return;
- } else if ( handle.hasClass( 'fl-block-col-move-parent' ) ) {
- col = col.parents( '.fl-col' );
- } else if ( handle.data( 'target-node' ) ) {
- col = $( '.fl-col[data-node=' + handle.data( 'target-node' ) + ']' );
- }
- col.addClass( 'fl-node-dragging' );
- FLBuilder._blockDragInit( e );
- e.target = helper[ 0 ];
- helper.trigger( e );
- },
- /**
- * @method _colDragInitTouch
- * @param {Object} e The event object.
- */
- _colDragInitTouch: function( startEvent )
- {
- var handle = $( startEvent.target ),
- helper = $( '.fl-col-sortable-proxy-item', window.parent.document ),
- col = handle.closest( '.fl-col' ),
- moved = false;
- if ( handle.data( 'target-node' ) ) {
- col = $( '.fl-col[data-node=' + handle.data( 'target-node' ) + ']' );
- }
- handle.on( 'touchmove', function( moveEvent ) {
- if ( ! moved ) {
- startEvent.currentTarget = col[0];
- FLBuilder._colDragInit( startEvent );
- moved = true;
- }
- moveEvent.target = helper[0];
- helper.trigger( moveEvent );
- } );
- handle.on( 'touchend', function( endEvent ) {
- endEvent.target = helper[0];
- helper.trigger( endEvent );
- endEvent.stopPropagation();
- } );
- },
- /**
- * Callback that fires when dragging starts for a column.
- *
- * @since 1.9
- * @access private
- * @method _colDragStart
- * @param {Object} e The event object.
- * @param {Object} ui An object with additional info for the drag.
- */
- _colDragStart: function( e, ui )
- {
- var col = $( '.fl-node-dragging' );
- col.hide();
- FLBuilder._resetColumnWidths( col.parent() );
- FLBuilder._blockDragStart( e, ui );
- },
- /**
- * Callback that fires when dragging stops for a column.
- *
- * @since 1.9
- * @access private
- * @method _colDragStop
- * @param {Object} e The event object.
- * @param {Object} ui An object with additional info for the drag.
- */
- _colDragStop: function( e, ui )
- {
- FLBuilder._blockDragStop( e, ui );
- var col = $( '.fl-node-dragging' ).removeClass( 'fl-node-dragging' ).show(),
- colId = col.attr( 'data-node' ),
- newParent = ui.item.parent(),
- oldGroup = col.parent(),
- oldGroupId = oldGroup.attr( 'data-node' )
- newGroup = newParent.closest( '.fl-col-group' ),
- newGroupId = newGroup.attr( 'data-node' ),
- newRow = newParent.closest('.fl-row'),
- position = 0,
- actions = FL.Builder.data.getLayoutActions();
- // Cancel if a column was dropped into itself.
- if ( newParent.closest( '[data-node="' + colId + '"]' ).length ) {
- FLBuilder._resetColumnWidths( oldGroup );
- }
- // Cancel the drop if the sortable is disabled?
- else if ( newParent.hasClass( 'fl-sortable-disabled' ) ) {
- FLBuilder._resetColumnWidths( oldGroup );
- }
- // A column was dropped back into the sortable proxy.
- else if ( newParent.hasClass( 'fl-col-sortable-proxy' ) ) {
- FLBuilder._resetColumnWidths( oldGroup );
- }
- // A column was dropped into a column.
- else if ( newParent.hasClass( 'fl-col-content' ) ) {
- // Remove the column.
- col.remove();
- // Remove empty old groups (needs to be done here for correct position).
- if ( 0 === oldGroup.find( '.fl-col' ).length ) {
- actions.removeNode( oldGroup.attr( 'data-node' ) );
- oldGroup.remove();
- }
- // Find the new group position.
- position = newParent.find( '> .fl-module, .fl-col-group, .fl-col-sortable-proxy-item' ).index( ui.item );
- // Add the new group.
- FLBuilder._addColGroup( newParent.closest( '.fl-col' ).attr('data-node'), colId, position );
- }
- // A column was dropped into a column position.
- else if ( newParent.hasClass( 'fl-col-drop-target' ) ) {
- // Move the column in the UI.
- if ( newParent.hasClass( 'fl-col-drop-target-last' ) ) {
- newParent.parent().after( col );
- }
- else {
- newParent.parent().before( col );
- }
- // Reset the UI column widths.
- FLBuilder._resetColumnWidths( newGroup );
- // Save the column move via AJAX.
- const actions = FL.Builder.data.getLayoutActions()
- if ( oldGroupId == newGroupId ) {
- FL.Builder.getActions().moveNode( colId, col.index() )
- }
- else {
- FL.Builder.getActions().moveNode( colId, col.index(), newGroupId, [ oldGroupId, newGroupId ] )
- }
- // Trigger a layout resize.
- FLBuilder._resizeLayout();
- }
- // A column was dropped into a column group position.
- else if ( newParent.hasClass( 'fl-col-group-drop-target' ) ) {
- // Remove the column.
- col.remove();
- // Remove empty old groups (needs to be done here for correct position).
- if ( 0 === oldGroup.find( '.fl-col' ).length ) {
- actions.removeNode( oldGroup.attr( 'data-node' ) );
- oldGroup.remove();
- }
- // Find the new group position.
- position = newRow.find( '.fl-row-content' ).find( ' > .fl-col-group, > .fl-module' ).index( newGroup );
- position = newParent.hasClass( 'fl-drop-target-last' ) ? position + 1 : position;
- // Add the new group.
- FLBuilder._addColGroup( newRow.attr('data-node'), colId, position );
- }
- // A column was dropped into a row position.
- else if ( newParent.hasClass( 'fl-row-drop-target' ) ) {
- // Remove the column.
- col.remove();
- // Find the new row position.
- position = newParent.closest( '.fl-builder-content' ).find( '.fl-row' ).index( newRow );
- position = newParent.hasClass( 'fl-drop-target-last' ) ? position + 1 : position;
- // Add the new row.
- FLBuilder._addRow( colId, position );
- }
- // Remove empty old groups.
- if ( 0 === oldGroup.find( '.fl-col' ).length ) {
- actions.removeNode( oldGroup.attr( 'data-node' ) );
- oldGroup.remove();
- }
- // Revert the proxy to its parent.
- $( '.fl-col-sortable-proxy', window.parent.document ).append( ui.item );
- // Finish the drag.
- FLBuilder._highlightEmptyCols();
- FLBuilder._initDropTargets();
- FLBuilder._initSortables();
- FLBuilder._closeAllSubmenus();
- },
- /**
- * Shows the settings lightbox and loads the column settings
- * when the column settings button is clicked.
- *
- * @since 1.1.9
- * @access private
- * @method _colSettingsClicked
- * @param {Object} e The event object.
- */
- _colSettingsClicked: function(e)
- {
- var button = $( this ),
- col = button.closest('.fl-col'),
- id = col.attr( 'data-node' ),
- global = button.closest( '.fl-block-overlay-global' ).length > 0;
- if ( FLBuilder._colResizing ) {
- return;
- }
- if ( global && ! FLBuilderConfig.userCanEditGlobalTemplates ) {
- return;
- }
- // If we clicked the edit parent button
- if ( button.hasClass( 'fl-block-col-edit-parent' ) ) {
- id = col.parents( '.fl-col' ).attr( 'data-node' )
- }
- const actions = FL.Builder.data.getLayoutActions()
- actions.displaySettings( id )
- e.stopPropagation();
- },
- /**
- * Copy settings of a column.
- *
- * @since 2.6
- * @access private
- * @method _colCopySettingsClicked
- */
- _colCopySettingsClicked: function () {
- const menuEl = $(this);
- const nodeId = menuEl.closest('.fl-col').data('node');
- // bind copy to the el
- FLBuilderSettingsCopyPaste._bindCopyToElement(menuEl, 'column', nodeId);
- },
- /**
- * Paste settings of a column.
- *
- * @since 2.6
- * @access private
- * @method _colPasteSettingsClicked
- */
- _colPasteSettingsClicked: function () {
- const menuEl = $(this);
- const menuText = menuEl.text();
- const nodeId = menuEl.closest('.fl-col').data('node');
- const success = FLBuilderSettingsCopyPaste._importFromClipboard('column', nodeId);
- if (!success) {
- // set button text
- menuEl.text(FLBuilderStrings.module_import.error);
- // restore button text
- setTimeout(() => {
- menuEl.text(menuText)
- }, 1000);
- }
- },
- /**
- * Show Column Settings Form
- *
- * @since 2.?
- * @access private
- * @method _showColSettings
- */
- _showColSettings: function( nodeId, global, isNodeTemplate ) {
- if ( global && isNodeTemplate && 'row' !== FLBuilderConfig.userTemplateType ) {
- if ( FLBuilderConfig.userCanEditGlobalTemplates ) {
- let win = window.parent.open( $( '.fl-col[data-node="' + nodeId + '"]' ).attr( 'data-template-url' ) );
- win.FLBuilderGlobalNodeId = nodeId;
- }
- }
- else {
- FLBuilderSettingsForms.render( {
- id : 'col',
- nodeId : nodeId,
- className : 'fl-builder-col-settings',
- attrs : 'data-node="' + nodeId + '"',
- buttons : ! global && ! FLBuilderConfig.lite && ! FLBuilderConfig.simpleUi ? ['save-as'] : [],
- badges : global ? [ FLBuilderStrings.global ] : [],
- settings : FLBuilderSettingsConfig.nodes[ nodeId ],
- preview : {
- type: 'col'
- }
- }, function() {
- var col = $('.fl-col.fl-node-' + nodeId )
- if ( col.siblings( '.fl-col' ).length === 0 ) {
- $( '#fl-field-equal_height, #fl-field-content_alignment', window.parent.document ).hide();
- }
- } );
- }
- },
- /**
- * Callback for when the copy column button is clicked.
- *
- * @since 2.0
- * @access private
- * @method _copyColClicked
- * @param {Object} e The event object.
- */
- _copyColClicked: function( e )
- {
- var id = $( this ).closest( '.fl-col' ).attr( 'data-node' );
- FLBuilder._copyColumn( id );
- e.stopPropagation();
- },
- /**
- * Handle Column Duplication
- *
- * @since 2.5
- * @access private
- * @method _copyColumn
- * @param String id of node
- * @return void
- */
- _copyColumn: function( nodeId ) {
- var col = FLBuilder._getJQueryElement( nodeId ),
- clone = col.clone(),
- group = col.parent(),
- form = $( '.fl-builder-settings[data-node]', window.parent.document ),
- formNodeId = form.attr( 'data-node' ),
- formNode = ( formNodeId === nodeId ) ? col : col.find( '[data-node="' + formNodeId + '"]' ),
- settings = null;
- if ( form.length && formNode.length ) {
- settings = FLBuilder._getSettings( form );
- FLBuilderSettingsConfig.nodes[ formNodeId ] = settings;
- }
- clone.addClass( 'fl-node-' + nodeId + '-clone fl-builder-node-clone' );
- clone.find( '.fl-block-overlay' ).remove();
- clone.removeAttr( 'data-node' );
- col.after( clone );
- FLBuilder._showNodeLoading( nodeId + '-clone' );
- FLBuilder._resetColumnWidths( group );
- const actions = FL.Builder.data.getLayoutActions()
- const callback = function( response ) {
- var data = FLBuilder._jsonParse( response );
- data.nodeParent = group;
- data.nodePosition = clone.index();
- data.duplicatedColumn = nodeId;
- data.onAddNewHTML = function() { clone.remove() };
- FLBuilder._copyColComplete( data );
- }
- actions.copyColumn( nodeId, settings, formNodeId, callback )
- },
- /**
- * Callback for when a column has been duplicated.
- *
- * @since 2.0
- * @access private
- * @method _copyColComplete
- * @param {Object} data
- */
- _copyColComplete: function( data )
- {
- FLBuilder._renderLayout( data, function(){
- FLBuilder._resetColumnWidths( data.nodeParent );
- FLBuilder.triggerHook( 'didDuplicateColumn', {
- newNodeId : data.nodeId,
- oldNodeId : data.duplicatedColumn
- } );
- } );
- },
- /**
- * Callback for when the delete column button is clicked.
- *
- * @since 1.0
- * @access private
- * @method _deleteColClicked
- * @param {Object} e The event object.
- */
- _deleteColClicked: function( e )
- {
- var id = $( e.target ).closest( '.fl-col' ).data( 'node' );
- var actions = FL.Builder.getActions();
- actions.deleteNode( id );
- e.stopPropagation();
- },
- /**
- * Handle selecting the proper dom node to delete in nested column situations.
- */
- _getColToDelete: function( initialCol ) {
- var col = initialCol,
- parentGroup = col.closest( '.fl-col-group' ),
- parentCol = col.parents( '.fl-col' ),
- hasParentCol = parentCol.length > 0,
- parentChildren = parentCol.find( '> .fl-col-content > .fl-module, > .fl-col-content > .fl-col-group' ),
- siblingCols = col.siblings( '.fl-col' );
- // Handle deleting of nested columns.
- if ( hasParentCol && 1 === parentChildren.length ) {
- if ( 0 === siblingCols.length ) {
- col = parentCol;
- }
- else if ( 1 === siblingCols.length && ! siblingCols.find( '.fl-module' ).length ) {
- col = parentGroup;
- }
- }
- return col
- },
- /**
- * Deletes a column.
- *
- * @since 1.0
- * @access private
- * @method _deleteCol
- * @param {Object} col A jQuery reference of the column to delete (can also be a group).
- */
- _deleteCol: function(col)
- {
- var nodeId = col.attr('data-node'),
- row = col.closest('.fl-row'),
- group = col.closest('.fl-col-group'),
- cols = null,
- width = 0;
- col.remove();
- rowCols = row.find('.fl-row-content > .fl-col-group > .fl-col, .fl-row-content > .fl-module');
- groupCols = group.find(' > .fl-col');
- if(0 === rowCols.length && 'row' != FLBuilderConfig.userTemplateType && 'column' != FLBuilderConfig.userTemplateType) {
- FLBuilder._deleteRow(row);
- }
- else {
- if(0 === groupCols.length) {
- group.remove();
- }
- else {
- if ( 6 === groupCols.length ) {
- width = 16.65;
- }
- else if ( 7 === groupCols.length ) {
- width = 14.28;
- }
- else {
- width = Math.round( 100 / groupCols.length * 100 ) / 100;
- }
- groupCols.css('width', width + '%');
- FLBuilder.triggerHook( 'didResetColumnWidths', {
- cols : groupCols
- } );
- }
- const actions = FL.Builder.data.getLayoutActions()
- actions.deleteColumn( nodeId, width )
- FLBuilder._initDropTargets();
- FLBuilder._initSortables();
- FLBuilder.triggerHook( 'col-deleted' );
- FLBuilder.triggerHook( 'didDeleteColumn', nodeId );
- }
- },
- /**
- * Deletes a column group.
- *
- * @since 2.8
- * @param {Object} group A jQuery reference of the group to delete.
- */
- _deleteColGroup: function( group )
- {
- var nodeId = group.attr( 'data-node' );
- const actions = FL.Builder.data.getLayoutActions()
- actions.deleteNode( nodeId )
- group.empty();
- group.remove();
- FLBuilder.triggerHook( 'didDeleteColumnGroup', nodeId );
- },
- /**
- * Inserts a column (or columns) before or after another column.
- *
- * @since 1.6.4
- * @access private
- * @method _addCols
- * @param {Object|String} col A jQuery reference of the column to insert before or after.
- * @param {String} insert Either before or after.
- * @param {String} type The type of column(s) to insert.
- * @param {Boolean} nested Whether these columns are nested or not.
- * @param {String} module Optional. The node ID of an existing module to move to this group.
- */
- _addCols: function( col, insert, type, nested, module )
- {
- var col = 'string' === typeof col ? $( '.fl-node-' + col ) : col,
- parent = col.closest( '.fl-col-group' ),
- position = parent.find( '.fl-col' ).index( col ),
- id = col.attr('data-node');
- type = typeof type == 'undefined' ? '1-col' : type;
- nested = typeof nested == 'undefined' ? false : nested;
- nested = nested ? 1 : 0
- if ( 'after' == insert ) {
- position++;
- }
- FLBuilder._showNodeLoadingPlaceholder( parent, position );
- FLBuilder._removeAllOverlays();
- const actions = FL.Builder.data.getLayoutActions()
- actions.addColumns( id, insert, type, nested, module )
- },
- /**
- * Adds the HTML for columns to the layout when the AJAX add
- * operation is complete. Adds a module if one is queued to
- * go in a new column.
- *
- * @since 1.9
- * @access private
- * @method _addColsComplete
- * @param {Object|String} response The JSON response with the HTML for the new column(s).
- */
- _addColsComplete: function( response )
- {
- var data = 'object' === typeof response ? response : FLBuilder._jsonParse( response ),
- col = null;
- data.nodeParent = FLBuilder._newColParent;
- data.nodePosition = FLBuilder._newColPosition;
- // Render the layout.
- FLBuilder._renderLayout( data, function() {
- FLBuilder._removeNodeLoadingPlaceholder( $( '.fl-node-' + data.nodeId ) );
- FLBuilder.triggerHook( 'didAddColumn', data.nodeId );
- FLBuilder.triggerHook( 'didResetColumnWidths', {
- cols : $( '.fl-node-' + data.nodeId ).find( '> .fl-col' )
- } );
- } );
- },
- /**
- * Adds a new column group to the layout.
- *
- * @since 1.0
- * @access private
- * @method _addColGroup
- * @param {String} nodeId The node ID of the parent row.
- * @param {String} cols The type of column layout to use.
- * @param {Number} position The position of the new column group.
- * @param {String} module Optional. The node ID of an existing module to move to this group.
- */
- _addColGroup: function( nodeId, cols, position, module )
- {
- var parent = $( '.fl-node-' + nodeId );
- // Save the new column group info.
- FLBuilder._newColGroupPosition = position;
- if ( parent.hasClass( 'fl-col' ) ) {
- FLBuilder._newColGroupParent = parent.find( ' > .fl-col-content' );
- }
- else {
- FLBuilder._newColGroupParent = parent.find( '.fl-row-content' );
- }
- // Show the loader.
- FLBuilder._showNodeLoadingPlaceholder( FLBuilder._newColGroupParent, position );
- const actions = FL.Builder.data.getLayoutActions()
- actions.addColumnGroup( nodeId, cols, position, module )
- },
- /**
- * Adds the HTML for a new column group to the layout when
- * the AJAX add operation is complete. Adds a module if one
- * is queued to go in the new column group.
- *
- * @since 1.0
- * @access private
- * @method _addColGroupComplete
- * @param {String} response The JSON response with the HTML for the new column group.
- */
- _addColGroupComplete: function(response)
- {
- var data = FLBuilder._jsonParse(response),
- html = $(data.html),
- groupId = html.data('node'),
- colId = html.find('.fl-col').data('node');
- // Add new column group info to the data.
- data.nodeParent = FLBuilder._newColGroupParent;
- data.nodePosition = FLBuilder._newColGroupPosition;
- // Render the layout.
- FLBuilder._renderLayout( data, function(){
- // Added the nested columns class if needed.
- if ( data.nodeParent.hasClass( 'fl-col-content' ) ) {
- data.nodeParent.parents( '.fl-col' ).addClass( 'fl-col-has-cols' );
- }
- // Remove the loading placeholder.
- FLBuilder._removeNodeLoadingPlaceholder( $( '.fl-node-' + groupId ) );
- FLBuilder.triggerHook( 'didAddColumnGroup', groupId );
- } );
- },
- /**
- * Initializes draggables for column resizing.
- *
- * @since 1.6.4
- * @access private
- * @method _initColDragResizing
- */
- _initColDragResizing: function()
- {
- $( '.fl-block-col-resize' ).not( '.fl-block-row-resize' ).draggable( {
- axis : 'x',
- start : FLBuilder._colDragResizeStart,
- drag : FLBuilder._colDragResize,
- stop : FLBuilder._colDragResizeStop
- } );
- },
- /**
- * Fires when dragging for a column resize starts.
- *
- * @since 1.6.4
- * @access private
- * @method _colDragResizeStart
- * @param {Object} e The event object.
- * @param {Object} ui An object with additional info for the drag.
- */
- _colDragResizeStart: function( e, ui )
- {
- // Setup resize vars.
- var handle = $( ui.helper ),
- direction = '',
- resizeParent = handle.hasClass( 'fl-block-col-resize-parent' ),
- parentCol = resizeParent ? handle.closest( '.fl-col' ).parents( '.fl-col' ) : null,
- group = resizeParent ? parentCol.parents( '.fl-col-group' ) : handle.closest( '.fl-col-group' ),
- cols = group.find( '> .fl-col' ),
- col = resizeParent ? parentCol : handle.closest( '.fl-col' ),
- colId = col.attr( 'data-node' ),
- colSetting = $( '[data-node=' + colId + '] #fl-field-size input', window.parent.document ),
- sibling = null,
- siblingId = null,
- siblingSetting = null,
- availWidth = 100,
- i = 0,
- setting = null,
- settingType = null;
- // Find the direction and sibling.
- if ( handle.hasClass( 'fl-block-col-resize-e' ) ) {
- direction = 'e';
- sibling = col.nextAll( '.fl-col' ).first();
- }
- else {
- direction = 'w';
- sibling = col.prevAll( '.fl-col' ).first();
- }
- siblingId = sibling.attr( 'data-node' );
- siblingSetting = $( '[data-node=' + siblingId + '] #fl-field-size input', window.parent.document );
- // Find the available width.
- for ( ; i < cols.length; i++ ) {
- if ( cols.eq( i ).data( 'node' ) == col.data( 'node' ) ) {
- continue;
- }
- if ( cols.eq( i ).data( 'node' ) == sibling.data( 'node' ) ) {
- continue;
- }
- availWidth -= parseFloat( cols.eq( i )[ 0 ].style.width );
- }
- // Find the setting if a column form is open.
- if ( colSetting.length ) {
- setting = colSetting;
- settingType = 'col';
- } else if ( siblingSetting.length ) {
- setting = siblingSetting;
- settingType = 'sibling';
- }
- // Build the resize data object.
- FLBuilder._colResizeData = {
- handle : handle,
- feedbackLeft : handle.find( '.fl-block-col-resize-feedback-left' ),
- feedbackRight : handle.find( '.fl-block-col-resize-feedback-right' ),
- direction : direction,
- groupWidth : group.outerWidth(),
- col : col,
- id : col.attr( 'data-node' ),
- colWidth : parseFloat( col[ 0 ].style.width ) / 100,
- sibling : sibling,
- siblingId : sibling.attr( 'data-node' ),
- offset : ui.position.left,
- availWidth : availWidth,
- setting : setting,
- settingType : settingType,
- layoutActions : FL.Builder.data.getLayoutActions()
- };
- // Set the resizing flag.
- FLBuilder._colResizing = true;
- // Add the body col resize class.
- $( 'body' ).addClass( 'fl-builder-col-resizing' );
- // Close the builder panel and destroy overlay events.
- FLBuilder._closePanel();
- FLBuilder._destroyOverlayEvents();
- // Trigger the col-resize-start hook.
- FLBuilder.triggerHook( 'col-resize-start' );
- },
- /**
- * Fires when dragging for a column resize is in progress.
- *
- * @since 1.6.4
- * @access private
- * @method _colDragResize
- * @param {Object} e The event object.
- * @param {Object} ui An object with additional info for the drag.
- */
- _colDragResize: function( e, ui )
- {
- // Setup resize vars.
- var data = FLBuilder._colResizeData,
- directionRef = FLBuilderConfig.isRtl ? 'w' : 'e',
- overlay = data.handle.closest( '.fl-block-overlay' ),
- change = ( data.offset - ui.position.left ) / data.groupWidth,
- colWidth = directionRef == data.direction ? ( data.colWidth - change ) * 100 : ( data.colWidth + change ) * 100,
- colRound = Math.round( colWidth * 100 ) / 100,
- siblingWidth = data.availWidth - colWidth,
- siblingRound = Math.round( siblingWidth * 100 ) / 100,
- minRound = 8,
- maxRound = Math.round( ( data.availWidth - minRound ) * 100 ) / 100;
- // Set the min/max width if needed.
- if ( colRound < minRound ) {
- colRound = minRound;
- siblingRound = maxRound;
- }
- else if ( siblingRound < minRound ) {
- colRound = maxRound;
- siblingRound = minRound;
- }
- // Show the feedback divs. Race condition with requestAnimationFrame
- // and drag stop requires it to be done here.
- data.feedbackLeft.show();
- data.feedbackRight.show();
- requestAnimationFrame( () => {
- // rapid DOM manipulations should generally happen inside a requestAnimationFrame
- // Set the feedback values.
- if ( directionRef == data.direction ) {
- data.feedbackLeft.html( colRound.toFixed( 1 ) + '%' );
- data.feedbackRight.html( siblingRound.toFixed( 1 ) + '%' );
- }
- else {
- data.feedbackLeft.html( siblingRound.toFixed( 1 ) + '%' );
- data.feedbackRight.html( colRound.toFixed( 1 ) + '%' );
- }
- // Set the width attributes.
- data.col.css( 'width', colRound + '%' );
- data.sibling.css( 'width', siblingRound + '%' );
- // Update the setting if the col or sibling's settings are open.
- if ( data.setting ) {
- if ( 'col' === data.settingType ) {
- data.setting.val( parseFloat( data.col[ 0 ].style.width ) );
- } else if ( 'sibling' === data.settingType ) {
- data.setting.val( parseFloat( data.sibling[ 0 ].style.width ) );
- }
- }
- // Dispatch to store
- data.layoutActions.resizeColumn( data.id, colRound, data.siblingId, siblingRound, false );
- // Build the overlay overflow menu if needed.
- FLBuilder._buildOverlayOverflowMenu( overlay );
- } )
- // Trigger the col-resize-drag hook.
- FLBuilder.triggerHook( 'col-resize-drag' );
- },
- /**
- * Fires when dragging for a column resize stops.
- *
- * @since 1.6.4
- * @access private
- * @method _colDragResizeStop
- * @param {Object} e The event object.
- * @param {Object} ui An object with additional info for the drag.
- */
- _colDragResizeStop: function( e, ui )
- {
- var data = FLBuilder._colResizeData,
- overlay = FLBuilder._colResizeData.handle.closest( '.fl-block-overlay' ),
- colId = data.id,
- colWidth = parseFloat( data.col[ 0 ].style.width ),
- siblingId = data.sibling.data( 'node' ),
- siblingWidth = parseFloat( data.sibling[ 0 ].style.width );
- // Hide the feedback divs.
- FLBuilder._colResizeData.feedbackLeft.hide();
- FLBuilder._colResizeData.feedbackRight.hide();
- // Update layout store
- const actions = FL.Builder.data.getLayoutActions()
- actions.resizeColumn( colId, colWidth, siblingId, siblingWidth )
- // Build the overlay overflow menu if needed.
- FLBuilder._buildOverlayOverflowMenu( overlay );
- // Reset the resize data.
- FLBuilder._colResizeData = null;
- // Remove the body col resize class.
- $( 'body' ).removeClass( 'fl-builder-col-resizing' );
- // Rebind overlay events.
- FLBuilder._bindOverlayEvents();
- // Set the resizing flag to false with a timeout so other events get the right value.
- setTimeout( function() { FLBuilder._colResizing = false; }, 50 );
- // Trigger the col-resize-stop hook.
- FLBuilder.triggerHook( 'col-resize-stop' );
- FLBuilder.triggerHook( 'didResizeColumn', {
- colId : colId,
- colWidth : colWidth,
- siblingId : siblingId,
- siblingWidth : siblingWidth
- } );
- },
- /**
- * Resets the widths of all columns in a row when the
- * Reset Column Widths button is clicked.
- *
- * @since 1.6.4
- * @access private
- * @method _resetColumnWidthsClicked
- * @param {Object} e The event object.
- */
- _resetColumnWidthsClicked: function( e )
- {
- var button = $( this ),
- isRow = !! button.closest( '.fl-row-overlay' ).length,
- group = null,
- groups = null,
- groupIds = [],
- children = null,
- i = 0,
- settings = $( '.fl-builder-col-settings', window.parent.document ),
- col = null;
- if ( isRow ) {
- groups = button.closest( '.fl-row' ).find( '.fl-row-content > .fl-col-group' );
- } else {
- groups = button.parents( '.fl-col-group' ).last();
- }
- groups.each( function() {
- group = $( this );
- children = group.find( '.fl-col-group' );
- groupIds.push( group.data( 'node' ) );
- FLBuilder._resetColumnWidths( group );
- for ( i = 0; i < children.length; i++ ) {
- FLBuilder._resetColumnWidths( children.eq( i ) );
- groupIds.push( children.eq( i ).data( 'node' ) );
- }
- } );
- if ( settings.length ) {
- col = $( '.fl-node-' + settings.attr( 'data-node' ) );
- settings.find( '#fl-field-size input' ).val( parseFloat( col[ 0 ].style.width ) );
- }
- const actions = FL.Builder.data.getLayoutActions()
- actions.resetColWidths( groupIds )
- FLBuilder.triggerHook( 'col-reset-widths' );
- FLBuilder._closeAllSubmenus();
- e.stopPropagation();
- },
- /**
- * Resets the widths of all columns in a group.
- *
- * @since 1.6.4
- * @access private
- * @method _resetColumnWidths
- * @param {Object} jQuery|HTMLElement - the column group node.
- */
- _resetColumnWidths: function( group )
- {
- // Check jQuery object first. This allows passing a plain HTMLElement in.
- var isJQueryObject = group instanceof jQuery
- var colGroup = group
- if ( ! isJQueryObject ){
- colGroup = $( group );
- }
- var cols = colGroup.find( ' > .fl-col:visible' ),
- width = 0;
- if ( 6 === cols.length ) {
- width = 16.65;
- }
- else if ( 7 === cols.length ) {
- width = 14.28;
- }
- else {
- width = Math.round( 100 / cols.length * 100 ) / 100;
- }
- cols.css( 'width', width + '%' );
- FLBuilder.triggerHook( 'didResetColumnWidths', {
- cols : cols
- } );
- },
- /* Modules
- ----------------------------------------------------------*/
- /**
- * Returns a helper element for module drag operations.
- *
- * @since 1.0
- * @access private
- * @method _moduleDragHelper
- * @param {Object} e The event object.
- * @param {Object} item The element being dragged.
- * @return {Object} The helper element.
- */
- _moduleDragHelper: function(e, item)
- {
- var module = $( '.fl-node-' + item.data( 'node' ) )
- return $('<div class="fl-builder-block-drag-helper">' + module.attr('data-name') + '</div>');
- },
- /**
- * @method _moduleDragInit
- * @param {Object} e The event object.
- */
- _moduleDragInit: function( e )
- {
- var handle = $( e.target ),
- helper = $( '.fl-module-sortable-proxy-item', window.parent.document ),
- module = handle.closest( '.fl-module' );
- if ( handle.closest( '.fl-block-move-menu' ).length ) {
- return;
- } else if ( handle.data( 'target-node' ) ) {
- module = $( '.fl-module[data-node=' + handle.data( 'target-node' ) + ']' );
- }
- helper.data( 'type', module.data('type' ) );
- module.addClass( 'fl-node-dragging' );
- FLBuilder._blockDragInit( e );
- e.target = helper[ 0 ];
- helper.data( 'node', module.data( 'node' ) )
- helper.data( 'name', module.data( 'name' ) )
- helper.data( 'type', module.data('type' ) )
- helper.trigger( e );
- },
- /**
- * @method _moduleDragInitTouch
- * @param {Object} e The event object.
- */
- _moduleDragInitTouch: function( startEvent )
- {
- var handle = $( startEvent.target ),
- helper = $( '.fl-module-sortable-proxy-item', window.parent.document ),
- module = handle.closest( '.fl-module' ),
- moved = false;
- if ( handle.data( 'target-node' ) ) {
- module = $( '.fl-module[data-node=' + handle.data( 'target-node' ) + ']' );
- }
- handle.on( 'touchmove', function( moveEvent ) {
- if ( ! moved ) {
- startEvent.currentTarget = module[0];
- FLBuilder._moduleDragInit( startEvent );
- moved = true;
- }
- moveEvent.target = helper[0];
- helper.trigger( moveEvent );
- } );
- handle.on( 'touchend', function( endEvent ) {
- endEvent.target = helper[0];
- helper.trigger( endEvent );
- endEvent.stopPropagation();
- } );
- },
- /**
- * Callback that fires when dragging starts for a module.
- *
- * @since 1.9
- * @access private
- * @method _moduleDragStart
- * @param {Object} e The event object.
- * @param {Object} ui An object with additional info for the drag.
- */
- _moduleDragStart: function( e, ui )
- {
- var module = $( '.fl-node-dragging' );
- module.hide();
- FLBuilder._removeRowOverlays();
- FLBuilder._blockDragStart( e, ui );
- },
- /**
- * Callback for when a module drag operation completes.
- *
- * @since 1.0
- * @access private
- * @method _moduleDragStop
- * @param {Object} e The event object.
- * @param {Object} ui An object with additional info for the drag.
- */
- _moduleDragStop: function(e, ui)
- {
- FLBuilder._blockDragStop( e, ui );
- var module = $( '.fl-node-dragging' ).removeClass( 'fl-node-dragging' ),
- item = ui.item,
- parent = ui.item.parent(),
- node = null,
- position = 0,
- parentId = 0,
- cols = null;
- // A module was dropped back into the module list.
- if ( parent.hasClass( 'fl-builder-modules' ) || parent.hasClass( 'fl-builder-widgets' ) ) {
- item.remove();
- return;
- }
- // A new module was dropped.
- else if ( item.hasClass( 'fl-builder-block' ) ) {
- // Cancel the drop if the sortable is disabled?
- if ( parent.hasClass( 'fl-sortable-disabled' ) ) {
- item.remove();
- FLBuilder._showPanel();
- return;
- }
- // A new module was dropped into a row position.
- else if ( parent.hasClass( 'fl-row-drop-target' ) ) {
- parent = item.closest('.fl-builder-content');
- parentId = 0;
- node = item.closest('.fl-row');
- position = parent.find( '.fl-row' ).index( node );
- }
- // A new module was dropped into a column group position.
- else if ( parent.hasClass( 'fl-col-group-drop-target' ) ) {
- parent = item.closest( '.fl-row-content' );
- parentId = parent.closest( '.fl-row' ).attr( 'data-node' );
- node = item.closest( '.fl-col-group, .fl-module' );
- position = parent.find( '> .fl-col-group, > .fl-module' ).index( node );
- }
- // A new module was dropped into a column position.
- else if ( parent.hasClass( 'fl-col-drop-target' ) ) {
- parent = item.closest( '.fl-col-group' );
- parentId = parent.attr( 'data-node' );
- node = item.closest( '.fl-col' );
- position = parent.find( ' > .fl-col' ).index( node );
- }
- // A new module was dropped into a container module WITHOUT a wrapper.
- else if ( parent.hasClass( 'fl-module' ) ) {
- var layoutDirection = FLBuilder._getNodeLayoutDirection( item );
- if ( 'layered' === layoutDirection ) {
- // Drop as top most item in layers.
- position = parent.find( '> .fl-module' ).length;
- } else {
- position = parent.find( '> .fl-module, .fl-builder-block' ).index( item );
- }
- parentId = parent.attr( 'data-node' );
- }
- // A new module was dropped into a container module WITH a wrapper.
- else if ( parent.hasClass( 'fl-module-content' ) ) {
- position = parent.find( '> .fl-module, .fl-builder-block' ).index( item );
- parentId = item.closest( '.fl-module' ).attr( 'data-node' );
- }
- // A new module was dropped into a column.
- else {
- position = parent.find( '> .fl-module, .fl-col-group, .fl-builder-block' ).index( item );
- parentId = item.closest( '.fl-col' ).attr( 'data-node' );
- }
- // Increment the position?
- if ( item.closest( '.fl-drop-target-last' ).length ) {
- position += 1;
- }
- // Add the new module.
- FLBuilder._addModule( parent, parentId, item.attr( 'data-type' ), position, item.attr( 'data-widget' ), item.attr( 'data-alias' ) );
- // Remove the drag helper.
- item.remove();
- }
- // An existing module was dropped.
- else {
- // Cancel the drop if the sortable is disabled?
- if ( parent.hasClass( 'fl-sortable-disabled' ) ) {
- FLBuilder._highlightEmptyCols();
- module.show();
- }
- // A module was dropped into a row position.
- else if ( parent.hasClass( 'fl-row-drop-target' ) ) {
- node = item.closest( '.fl-row' );
- position = item.closest( '.fl-builder-content' ).children( '.fl-row' ).index( node );
- position = item.closest( '.fl-drop-target-last' ).length ? position + 1 : position;
- cols = module.attr( 'data-accepts' ) ? null : '1-col';
- FLBuilder._addRow( cols, position, module.attr( 'data-node' ) );
- module.remove();
- }
- // A module was dropped into a column group position.
- else if ( parent.hasClass( 'fl-col-group-drop-target' ) ) {
- node = item.closest( '.fl-col-group' );
- position = item.closest( '.fl-row-content ').find( ' > .fl-col-group' ).index( node );
- position = item.closest( '.fl-drop-target-last' ).length ? position + 1 : position;
- parentId = item.closest( '.fl-row' ).attr( 'data-node' );
- if ( module.attr( 'data-accepts' ) ) {
- FLBuilder._moveNode( parentId, module.attr( 'data-node' ), position );
- module.show();
- } else {
- FLBuilder._addColGroup( parentId, '1-col', position, module.attr( 'data-node' ) );
- module.remove();
- }
- }
- // A module was dropped into a column position.
- else if ( parent.hasClass( 'fl-col-drop-target' ) ) {
- node = item.closest( '.fl-col' );
- position = item.closest( '.fl-col-drop-target-last' ).length ? 'after' : 'before';
- FLBuilder._addCols( node, position, '1-col', item.closest( '.fl-col-group-nested' ).length > 0, module.attr( 'data-node' ) );
- module.remove();
- }
- // A module was dropped into another column or container module.
- else {
- var isContainer = item.closest( '[data-accepts]' ).length;
- var layoutDirection = FLBuilder._getNodeLayoutDirection( item );
- if ( isContainer && 'layered' === layoutDirection ) {
- // Drop as top most item in layers.
- item.parent().append( module );
- } else {
- item.after( module );
- }
- item.remove();
- module.show();
- FLBuilder._reorderModule( module );
- }
- // Revert the proxy to its parent.
- $( '.fl-module-sortable-proxy', window.parent.document ).append( item );
- }
- FLBuilder._resizeLayout();
- FLBuilder._initDropTargets();
- },
- /**
- * Reorders a module within a column.
- *
- * @since 1.0
- * @access private
- * @method _reorderModule
- * @param {Object} module The module element being dragged.
- */
- _reorderModule: function(module)
- {
- var newParent = module.parents('.fl-row, .fl-col, .fl-module').eq(0).attr('data-node'),
- oldParent = module.attr('data-parent'),
- nodeId = module.attr('data-node'),
- position = module.index();
- if(newParent == oldParent) {
- FLBuilder._reorderNode( nodeId, position );
- }
- else {
- module.attr('data-parent', newParent);
- FLBuilder._moveNode( newParent, nodeId, position );
- }
- },
- /**
- * Callback for when the delete module button is clicked.
- *
- * @since 1.0
- * @access private
- * @method _deleteModuleClicked
- * @param {Object} e The event object.
- */
- _deleteModuleClicked: function(e)
- {
- var id = $( e.target ).closest('.fl-module').data( 'node' );
- var actions = FL.Builder.getActions()
- actions.deleteNode( id )
- e.stopPropagation();
- },
- /**
- * Deletes a module.
- *
- * @since 1.0
- * @access private
- * @method _deleteModule
- * @param {Object} module A jQuery reference of the module to delete.
- */
- _deleteModule: function(module)
- {
- var row = module.closest('.fl-row'),
- rowNodes = row.find( '[data-node]' ),
- nodeId = module.attr('data-node'),
- acceptsChildren = module.attr('data-accepts');
- const actions = FL.Builder.data.getLayoutActions()
- actions.deleteNode( nodeId )
- // Delete the row if this is the last node in it.
- if ( acceptsChildren && 1 === rowNodes.length ) {
- FLBuilder._deleteRow( row );
- }
- module.empty();
- module.remove();
- row.removeClass('fl-block-overlay-muted');
- FLBuilder._highlightEmptyCols();
- FLBuilder._removeAllOverlays();
- FLBuilder.triggerHook( 'didDeleteModule', {
- nodeId: nodeId,
- moduleType: module.attr( 'data-type' ),
- } );
- },
- /**
- * Duplicates a module.
- *
- * @since 1.0
- * @access private
- * @method _moduleCopyClicked
- * @param {Object} e The event object.
- */
- _moduleCopyClicked: function(e)
- {
- const id = $( this ).closest( '.fl-module' ).data( 'node' );
- const actions = FL.Builder.getActions()
- actions.copyNode( id );
- e.stopPropagation();
- },
- /**
- * Duplicate a module for a given id.
- */
- _copyModule: function( id )
- {
- var module = FLBuilder._getJQueryElement( id ),
- clone = module.clone(),
- parent = module.parent(),
- form = $( '.fl-builder-module-settings[data-node=' + id + ']', window.parent.document ),
- settings = {};
- if ( form.length ) {
- settings = FLBuilder._getSettings( form );
- FLBuilderSettingsConfig.nodes[ id ] = settings;
- }
- // Setup clone
- clone.addClass( 'fl-node-' + id + '-clone fl-builder-node-clone' );
- clone.find( '.fl-block-overlay' ).remove();
- clone.removeAttr( 'data-node' );
- module.after( clone );
- // Show Loader
- FLBuilder._showNodeLoading( id + '-clone' );
- // Animate scroll to new element
- const el = clone.get(0);
- el.scrollIntoView( {
- behavior: 'smooth',
- block: 'center',
- } );
- const actions = FL.Builder.data.getLayoutActions()
- const callback = function( response ) {
- var data = FLBuilder._jsonParse( response );
- data.nodeParent = parent;
- data.nodePosition = parent.find( ' > .fl-col-group, > .fl-module' ).index( clone );
- data.duplicatedModule = id;
- data.onAddNewHTML = function() { clone.remove() };
- FLBuilder._moduleCopyComplete( data );
- }
- actions.copyModule( id, settings, callback )
- },
- /**
- * Callback for when a module has been duplicated.
- *
- * @since 1.7
- * @access private
- * @method _moduleCopyComplete
- * @param {Object}
- */
- _moduleCopyComplete: function( data )
- {
- FLBuilder._renderLayout( data, function(){
- FLBuilder.triggerHook( 'didDuplicateModule', {
- newNodeId : data.nodeId,
- oldNodeId : data.duplicatedModule,
- moduleType : data.moduleType,
- } );
- } );
- },
- /**
- * Shows the settings lightbox and loads the module settings
- * when the module settings button is clicked.
- *
- * @since 1.0
- * @access private
- * @method _moduleSettingsClicked
- * @param {Object} e The event object.
- */
- _moduleSettingsClicked: function(e)
- {
- var button = $( this ),
- overlay = button.closest( '.fl-block-overlay' ),
- type = button.closest( '.fl-module' ).attr( 'data-type' ),
- nodeId = button.closest( '.fl-module' ).attr( 'data-node' ),
- parentId = button.closest( '.fl-col' ).attr( 'data-node' ),
- global = button.closest( '.fl-block-overlay-global' ).length > 0;
- e.stopPropagation();
- if ( FLBuilder._colResizing ) {
- return;
- }
- if ( global && ! FLBuilderConfig.userCanEditGlobalTemplates ) {
- return;
- }
- // Show module settings
- const actions = FL.Builder.getActions();
- actions.openSettings( nodeId );
- },
- /**
- * Copy settings of a module.
- *
- * @since 2.6
- * @access private
- * @method _moduleCopySettingsClicked
- */
- _moduleCopySettingsClicked: function () {
- const menuEl = $(this);
- const nodeId = menuEl.closest('.fl-module').data('node');
- const type = menuEl.closest('.fl-module').data('type');
- // bind copy to the el
- FLBuilderSettingsCopyPaste._bindCopyToElement(menuEl, type, nodeId);
- },
- /**
- * Copy settings of a module.
- *
- * @since 2.6
- * @access private
- * @method _modulePasteSettingsClicked
- */
- _modulePasteSettingsClicked: function () {
- const menuEl = $(this);
- const menuText = menuEl.text();
- const nodeId = menuEl.closest('.fl-module').data('node');
- const type = menuEl.closest('.fl-module').data('type');
- const success = FLBuilderSettingsCopyPaste._importFromClipboard(type, nodeId);
- if (!success) {
- // set button text
- menuEl.text(FLBuilderStrings.module_import.error);
- // restore button text
- setTimeout(() => {
- menuEl.text(menuText)
- }, 1000);
- }
- },
- /**
- * Shows the lightbox and loads the settings for a module.
- *
- * @since 1.0
- * @access private
- * @method _showModuleSettings
- * @param {Object} data
- * @param {Function} callback
- */
- _showModuleSettings: function( data, callback )
- {
- if ( ! FLBuilderSettingsConfig.modules ) {
- return;
- }
- var config = FLBuilderSettingsConfig.modules[ data.type ],
- settings = data.settings ? data.settings : FLBuilderSettingsConfig.nodes[ data.nodeId ],
- head = $( 'head', window.parent.document ),
- module = $( '.fl-module[data-node="' + data.nodeId + '"]' ),
- layout = null;
- if ( data.global && ! FLBuilderConfig.userTemplateType && module.attr( 'data-accepts' ) ) {
- if ( FLBuilderConfig.userCanEditGlobalTemplates ) {
- win = window.parent.open( module.attr( 'data-template-url' ) );
- win.FLBuilderGlobalNodeId = data.nodeId;
- }
- } else {
- // Add settings CSS and JS.
- if ( -1 === $.inArray( data.type, FLBuilder._loadedModuleAssets ) ) {
- if ( '' !== config.assets.css ) {
- head.append( config.assets.css );
- }
- if ( '' !== config.assets.js ) {
- head.append( config.assets.js );
- }
- FLBuilder._loadedModuleAssets.push( data.type );
- }
- // Render the form.
- FLBuilderSettingsForms.render( {
- type : 'module',
- id : data.type,
- nodeId : data.nodeId,
- className : 'fl-builder-module-settings fl-builder-' + data.type + '-settings',
- attrs : 'data-node="' + data.nodeId + '" data-parent="' + data.parentId + '" data-type="' + data.type + '"',
- buttons : ! data.global && ! FLBuilderConfig.lite && ! FLBuilderConfig.simpleUi ? ['save-as'] : [],
- badges : data.global ? [ FLBuilderStrings.global ] : [],
- settings : settings,
- legacy : data.legacy,
- helper : FLBuilder._moduleHelpers[ data.type ],
- rules : FLBuilder._moduleHelpers[ data.type ] ? FLBuilder._moduleHelpers[ data.type ].rules : null,
- messages : FLBuilder._moduleHelpers[ data.type ] ? FLBuilder._moduleHelpers[ data.type ].messages : null,
- hide : ( ! FLBuilderConfig.userCanEditGlobalTemplates && data.global ) ? true : false,
- preview : {
- type : 'module',
- layout : data.layout,
- callback : function() {
- FLBuilder.triggerHook( 'didAddModule', {
- nodeId: data.nodeId,
- moduleType: settings.type,
- settings: settings
- } );
- }
- }
- }, callback );
- }
- },
- /**
- * Validates the module settings and saves them if
- * the form is valid.
- *
- * @since 1.0
- * @access private
- * @method _saveModuleClicked
- */
- _saveModuleClicked: function()
- {
- var form = $(this).closest('.fl-builder-settings'),
- type = form.attr('data-type'),
- id = form.attr('data-node'),
- helper = FLBuilder._moduleHelpers[type],
- valid = true;
- if(typeof helper !== 'undefined') {
- form.find('label.error').remove();
- form.validate().hideErrors();
- valid = form.validate().form();
- if(valid) {
- valid = helper.submit();
- }
- }
- if(valid) {
- FLBuilder._saveSettings();
- }
- else {
- FLBuilder._toggleSettingsTabErrors();
- }
- },
- /**
- * Adds a new module to a column and loads the settings.
- *
- * @since 1.0
- * @access private
- * @method _addModule
- * @param {Object} parent A jQuery reference to the new module's parent.
- * @param {String} parentId The node id of the new module's parent.
- * @param {String} type The type of module to add.
- * @param {Number} position The position of the new module within its parent.
- * @param {String} widget The type of widget if this module is a widget.
- * @param {String} alias A module alias key if this module is an alias to another module.
- */
- _addModule: function( parent, parentId, type, position, widget, alias )
- {
- // Show the loader.
- FLBuilder._showNodeLoadingPlaceholder( parent, position );
- // Save the new module data.
- if ( parent.hasClass( 'fl-col-group' ) ) {
- FLBuilder._newModuleParent = null;
- FLBuilder._newModulePosition = 0;
- }
- else {
- FLBuilder._newModuleParent = parent;
- FLBuilder._newModulePosition = position;
- }
- // Dispatch to layout store
- const actions = FL.Builder.data.getLayoutActions()
- actions.addModule( type, parentId, position, {
- widget: typeof widget === 'undefined' ? '' : widget,
- alias: typeof alias === 'undefined' ? '' : alias,
- nodePreview: 1
- } )
- },
- /**
- * Shows the settings lightbox and sets the content when
- * the module settings have finished loading.
- *
- * @since 1.0
- * @access private
- * @method _addModuleComplete
- * @param {String} response The JSON encoded response.
- */
- _addModuleComplete: function( response )
- {
- var data = FLBuilder._jsonParse( response ),
- showSettingsForm = false;
- // Setup a preview layout if we have one.
- if ( data.layout ) {
- if ( FLBuilder._newModuleParent ) {
- FLBuilder._newModuleParent.find( '.fl-builder-node-loading-placeholder' ).hide();
- }
- data.layout.nodeParent = FLBuilder._newModuleParent;
- data.layout.nodePosition = FLBuilder._newModulePosition;
- }
- // Make sure we have settings before rendering the form.
- if ( ! data.settings ) {
- data.settings = FLBuilderSettingsConfig.defaults.modules[ data.type ];
- }
- // Render the module if a settings form is already open.
- if ( $( 'form.fl-builder-settings', window.parent.document ).length ) {
- if ( data.layout ) {
- FLBuilder._renderLayout( data.layout );
- showSettingsForm = true;
- }
- } else {
- showSettingsForm = true;
- }
- if ( showSettingsForm ) {
- FLBuilder._showModuleSettings( data, function() {
- $( '.fl-builder-module-settings', window.parent.document ).data( 'new-module', '1' );
- } );
- }
- },
- /**
- * Registers a helper class for a module's settings.
- *
- * @since 1.0
- * @method registerModuleHelper
- * @param {String} type The type of module.
- * @param {Object} obj The module helper.
- */
- registerModuleHelper: function(type, obj)
- {
- var defaults = {
- node: null,
- form: null,
- rules: {},
- init: function(){},
- submit: function(){ return true; },
- preview: function(){},
- getForm: function() {
- if ( ! this.form ) {
- this.form = $( 'form.fl-builder-settings:visible', window.parent.document ).get(0)
- }
- return this.form
- },
- getSettings: function() { return FLBuilder._getSettings( $( this.form ) ) },
- getNodeID: function() { return this.getForm().dataset.node },
- getNode: function() {
- if ( ! this.node ) {
- this.node = document.querySelector( `${FLBuilder._contentClass} .fl-module[data-node="${this.getNodeID()}"]` )
- }
- return this.node
- },
- };
- FLBuilder._moduleHelpers[type] = $.extend({}, defaults, obj);
- },
- /**
- * Deprecated. Use the public method registerModuleHelper instead.
- *
- * @since 1.0
- * @access private
- * @method _registerModuleHelper
- * @param {String} type The type of module.
- * @param {Object} obj The module helper.
- */
- _registerModuleHelper: function(type, obj)
- {
- FLBuilder.registerModuleHelper(type, obj);
- },
- /**
- * Update the margin placeholders for modules inside containers
- * such as the box module that set different default margins.
- *
- * @since 2.8
- * @return void
- */
- _initModuleMarginPlaceholders: function()
- {
- var form = $( '.fl-builder-module-settings:visible', window.parent.document );
- var nodeId = form.data( 'node' );
- var node = $( '.fl-node-' + nodeId );
- var content = node.find( '.fl-node-content' );
- var sides = [ 'top', 'right', 'bottom', 'left' ];
- if ( ! form.length ) {
- return;
- } else if ( ! node.closest( '.fl-module[data-accepts]' ).length ) {
- return;
- } else if ( ! content.length ) {
- content = node;
- }
- for ( var key in sides ) {
- var input = form.find( 'input[name="margin_' + sides[ key ] + '"]' );
- var value = input.val();
- node.removeClass( 'fl-node-' + nodeId );
- input.attr( 'placeholder', parseInt( content.css( 'margin-' + sides[ key ] ) ) );
- node.addClass( 'fl-node-' + nodeId );
- }
- },
- /* Node Templates
- ----------------------------------------------------------*/
- /**
- * Saves a node's settings and shows the node template settings
- * when the Save As button is clicked.
- *
- * @since 1.6.3
- * @access private
- * @method _showNodeTemplateSettings
- * @param {Object} e An event object.
- */
- _showNodeTemplateSettings: function( e )
- {
- var form = $( '.fl-builder-settings-lightbox .fl-builder-settings', window.parent.document ),
- nodeId = form.attr( 'data-node' ),
- title = FLBuilderStrings.saveModule;
- if ( form.hasClass( 'fl-builder-row-settings' ) ) {
- title = FLBuilderStrings.saveRow;
- }
- else if ( form.hasClass( 'fl-builder-col-settings' ) ) {
- title = FLBuilderStrings.saveColumn;
- }
- if ( ! FLBuilder._triggerSettingsSave( false, false, false ) ) {
- return false;
- }
- FLBuilderSettingsForms.render( {
- id : 'node_template',
- nodeId : nodeId,
- title : title,
- attrs : 'data-node="' + nodeId + '"',
- className : 'fl-builder-node-template-settings',
- rules : {
- name: {
- required: true
- }
- }
- }, function() {
- var form = $( '.fl-builder-settings:visible' );
- var cats = FLBuilderConfig.nodeCategoies;
- select = form.find('#fl-field-categories').find('select');
- desc = select.parent().find( '.fl-field-description' ).hide();
- $.each(cats, function (i, item) {
- select.append($('<option>', {
- value: item.id,
- text : item.name
- }
- ));
- });
- select.on('change', function(){
- if ( 'add_new' === $(this).val() ) {
- $( '<input type="text" name="categories" value="" class="text text-full">' ).insertBefore(select);
- select.remove();
- desc.show();
- }
- });
- if ( ! FLBuilderConfig.userCanEditGlobalTemplates ) {
- $( '#fl-field-global', window.parent.document ).hide();
- }
- } );
- },
- /**
- * Saves a node as a template when the save button is clicked.
- *
- * @since 1.6.3
- * @access private
- * @method _saveNodeTemplate
- */
- _saveNodeTemplate: function()
- {
- var form = $( '.fl-builder-node-template-settings', window.parent.document ),
- nodeId = form.attr( 'data-node' ),
- valid = form.validate().form();
- if ( valid ) {
- FLBuilder._showNodeLoading( nodeId );
- const actions = FL.Builder.data.getLayoutActions()
- actions.saveNodeTemplate( nodeId, FLBuilder._getSettings( form ) )
- FLBuilder._lightbox.close();
- }
- },
- /**
- * Callback for when a node template has been saved.
- *
- * @since 1.6.3
- * @access private
- * @method _saveNodeTemplateComplete
- */
- _saveNodeTemplateComplete: function( response )
- {
- var data = FLBuilder._jsonParse( response ),
- panel = $( '.fl-builder-saved-' + data.type + 's', window.parent.document ),
- blocks = panel.find( '.fl-builder-block' ),
- block = null,
- text = '',
- name = data.name.toLowerCase(),
- i = 0,
- template = wp.template( 'fl-node-template-block' ),
- newLibraryItem = {
- name: data.name,
- isGlobal: data.global,
- content: data.type,
- id: data.id,
- postID: data.postID,
- kind: "template",
- type: "user",
- link: data.link,
- category: {
- uncategorized: FLBuilderStrings.uncategorized
- }
- };
- FLBuilderConfig.contentItems.template.push(newLibraryItem);
- FLBuilder.triggerHook('contentItemsChanged');
- // Update the layout for global templates.
- if ( data.layout ) {
- FLBuilder._renderLayout( data.layout );
- FLBuilder.triggerHook( 'didSaveGlobalNodeTemplate', data.config );
- }
- // Add the new template to the builder panel.
- if ( 0 === blocks.length ) {
- panel.append( template( data ) );
- }
- else {
- for ( ; i < blocks.length; i++ ) {
- block = blocks.eq( i );
- text = block.text().toLowerCase().trim();
- if ( 0 === i && name < text ) {
- panel.prepend( template( data ) );
- break;
- }
- else if ( name < text ) {
- block.before( template( data ) );
- break;
- }
- else if ( blocks.length - 1 === i ) {
- panel.append( template( data ) );
- break;
- }
- }
- }
- // Remove the no templates placeholder.
- panel.find( '.fl-builder-block-no-node-templates' ).remove();
- },
- /**
- * Callback for when a node template drag from the
- * builder panel has stopped.
- *
- * @since 1.6.3
- * @access private
- * @method _nodeTemplateDragStop
- * @param {Object} e The event object.
- * @param {Object} ui An object with additional info for the drag.
- */
- _nodeTemplateDragStop: function( e, ui )
- {
- FLBuilder._blockDragStop( e, ui );
- var item = ui.item,
- parent = item.parent(),
- parentId = null,
- position = 0,
- node = null,
- action = '',
- callback = null;
- // A node template was dropped back into the templates list.
- if ( parent.hasClass( 'fl-builder-blocks-section-content' ) ) {
- item.remove();
- return;
- }
- // A saved row was dropped.
- else if ( item.hasClass( 'fl-builder-block-saved-row' ) || item.hasClass( 'fl-builder-block-row-template' ) ) {
- node = item.closest( '.fl-row' );
- position = ! node.length ? 0 : $( FLBuilder._contentClass + ' .fl-row' ).index( node );
- position = parent.hasClass( 'fl-drop-target-last' ) ? position + 1 : position;
- parentId = null;
- action = 'render_new_row_template';
- callback = FLBuilder._addRowComplete;
- FLBuilder._newRowPosition = position;
- FLBuilder._showNodeLoadingPlaceholder( $( FLBuilder._contentClass ), position );
- }
- // A saved column was dropped.
- else if ( item.hasClass( 'fl-builder-block-saved-column' ) ) {
- node = item.closest( '.fl-col' ),
- colGroup = parent.closest( '.fl-col-group' ),
- colGroupId = colGroup.attr( 'data-node' );
- action = 'render_new_col_template';
- callback = FLBuilder._addColsComplete;
- // Cancel the drop if the sortable is disabled?
- if ( parent.hasClass( 'fl-sortable-disabled' ) ) {
- item.remove();
- FLBuilder._showPanel();
- return;
- }
- // A column was dropped into a row position.
- else if ( parent.hasClass( 'fl-row-drop-target' ) ) {
- node = item.closest( '.fl-row' ),
- parentId = 0;
- parent = $( FLBuilder._contentClass );
- position = ! node.length ? 0 : parent.find( '.fl-row' ).index( node );
- }
- // A column was dropped into a column group position.
- else if ( parent.hasClass( 'fl-col-group-drop-target' ) ) {
- parent = item.closest( '.fl-row-content' );
- parentId = item.closest( '.fl-row' ).attr( 'data-node' );
- position = item.closest( '.fl-row' ).find( '.fl-row-content' ).find( ' > .fl-col-group, > .fl-module' ).index( item.closest( '.fl-col-group, .fl-module' ) );
- }
- // A column was dropped into a column position.
- else if ( parent.hasClass( 'fl-col-drop-target' ) ) {
- parent = item.closest('.fl-col-group');
- position = parent.children('.fl-col').index( item.closest('.fl-col') );
- parentId = parent.attr('data-node');
- }
- // Increment the position?
- if ( item.closest( '.fl-drop-target-last' ).length ) {
- position += 1;
- }
- if ( parent.hasClass( 'fl-col-group' ) ) {
- FLBuilder._newColParent = null;
- }
- else {
- FLBuilder._newColParent = parent;
- }
- FLBuilder._newColPosition = position;
- // Show the loader.
- FLBuilder._showNodeLoadingPlaceholder( parent, position );
- }
- // A saved module was dropped.
- else if ( item.hasClass( 'fl-builder-block-saved-module' ) || item.hasClass( 'fl-builder-block-module-template' ) ) {
- action = 'render_new_module';
- callback = FLBuilder._addModuleComplete;
- // Cancel the drop if the sortable is disabled?
- if ( parent.hasClass( 'fl-sortable-disabled' ) ) {
- item.remove();
- FLBuilder._showPanel();
- return;
- }
- // Dropped into a row position.
- else if ( parent.hasClass( 'fl-row-drop-target' ) ) {
- parent = item.closest('.fl-builder-content');
- parentId = 0;
- position = parent.find( '.fl-row' ).index( item.closest('.fl-row') );
- }
- // Dropped into a column group position.
- else if ( parent.hasClass( 'fl-col-group-drop-target' ) ) {
- parent = item.closest( '.fl-row-content' );
- parentId = parent.closest( '.fl-row' ).attr( 'data-node' );
- position = parent.find( ' > .fl-col-group, > .fl-module' ).index( item.closest( '.fl-col-group, .fl-module' ) );
- }
- // Dropped into a column position.
- else if ( parent.hasClass( 'fl-col-drop-target' ) ) {
- parent = item.closest('.fl-col-group');
- position = parent.children('.fl-col').index( item.closest('.fl-col') );
- parentId = parent.attr('data-node');
- }
- // Dropped into a container module WITHOUT a wrapper.
- else if ( parent.hasClass( 'fl-module' ) ) {
- var layoutDirection = FLBuilder._getNodeLayoutDirection( item );
- if ( 'layered' === layoutDirection ) {
- // Drop as top most item in layers.
- position = parent.find( '> .fl-module' ).length;
- } else {
- position = parent.find( '> .fl-module, .fl-builder-block' ).index( item );
- }
- parentId = parent.attr( 'data-node' );
- }
- // Dropped into a container module WITH a wrapper.
- else if ( parent.hasClass( 'fl-module-content' ) ) {
- position = parent.find( '> .fl-module, .fl-builder-block' ).index( item );
- parentId = item.closest( '.fl-module' ).attr( 'data-node' );
- }
- // Dropped into a column.
- else {
- position = parent.children( '.fl-module, .fl-builder-block' ).index( item );
- parentId = item.closest( '.fl-col' ).attr( 'data-node' );
- }
- // Increment the position?
- if ( item.closest( '.fl-drop-target-last' ).length ) {
- position += 1;
- }
- // Save the new module data.
- if ( parent.hasClass( 'fl-col-group' ) ) {
- FLBuilder._newModuleParent = null;
- FLBuilder._newModulePosition = 0;
- }
- else {
- FLBuilder._newModuleParent = parent;
- FLBuilder._newModulePosition = position;
- }
- // Show the loader.
- FLBuilder._showNodeLoadingPlaceholder( parent, position );
- }
- const templateId = item.attr( 'data-id' )
- const templateType = item.attr( 'data-type' )
- const ajaxCallback = function( response ) {
- if ( action.indexOf( 'row' ) > -1 ) {
- var data = FLBuilder._jsonParse( response );
- FLBuilder.triggerHook( 'didApplyRowTemplateComplete', data.config );
- callback( data.layout );
- } else if ( action.indexOf( 'col' ) > -1 ) {
- var data = FLBuilder._jsonParse( response );
- FLBuilder.triggerHook( 'didApplyColTemplateComplete', data.config );
- callback( data.layout );
- } else {
- callback( response );
- }
- }
- let type = 'module'
- if ( 'render_new_col_template' === action ) {
- type = 'column'
- }
- if ( 'render_new_row_template' === action ) {
- type = 'row'
- }
- // Dispatch to layout store
- const actions = FL.Builder.data.getLayoutActions()
- actions.addNodeTemplate( type, templateId, templateType, parentId, position, ajaxCallback )
- // Remove the helper.
- item.remove();
- },
- /**
- * Launches the builder in a new tab to edit a user
- * defined node template when the edit link is clicked.
- *
- * @since 1.6.3
- * @access private
- * @method _editUserTemplateClicked
- * @param {Object} e The event object.
- */
- _editNodeTemplateClicked: function( e )
- {
- e.preventDefault();
- e.stopPropagation();
- window.parent.open( $( this ).attr( 'href' ) );
- },
- /**
- * Fires when the delete node template icon is clicked in the builder's panel.
- *
- * @since 1.6.3
- * @access private
- * @method _deleteNodeTemplateClicked
- * @param {Object} e The event object.
- */
- _deleteNodeTemplateClicked: function( e )
- {
- var button = $( e.target ),
- section = button.closest( '.fl-builder-blocks-section' ),
- panel = section.find( '.fl-builder-blocks-section-content' ),
- blocks = panel.find( '.fl-builder-block' ),
- block = button.closest( '.fl-builder-block' ),
- global = block.hasClass( 'fl-builder-block-global' ),
- message = global ? FLBuilderStrings.deleteGlobalTemplate : FLBuilderStrings.deleteTemplate,
- index = null,
- id = block.attr( 'data-id' );
- if ( confirm( message ) ) {
- // Delete the UI block.
- block.remove();
- // Add the no templates placeholder?
- if ( 1 === blocks.length ) {
- if ( block.hasClass( 'fl-builder-block-saved-row' ) ) {
- panel.append( '<span class="fl-builder-block-no-node-templates">' + FLBuilderStrings.noSavedRows + '</span>' );
- }
- else {
- panel.append( '<span class="fl-builder-block-no-node-templates">' + FLBuilderStrings.noSavedModules + '</span>' );
- }
- }
- // Show the loader?
- if ( block.hasClass( 'fl-builder-block-global' ) ) {
- FLBuilder.showAjaxLoader();
- }
- // Delete the template.
- const actions = FL.Builder.data.getLayoutActions()
- actions.deleteNodeTemplate( id, global )
- // Remove the item from library
- index = _.findIndex(FLBuilderConfig.contentItems.template, {
- id: block.attr('data-id'),
- type: 'user'
- });
- FLBuilderConfig.contentItems.template.splice(index, 1);
- FLBuilder.triggerHook('contentItemsChanged');
- }
- },
- /* Settings
- ----------------------------------------------------------*/
- /**
- * Initializes logic for settings forms.
- *
- * @since 1.0
- * @access private
- * @method _initSettingsForms
- */
- _initSettingsForms: function()
- {
- FLBuilder._initSettingsSections();
- FLBuilder._initButtonGroupFields();
- FLBuilder._initCompoundFields();
- FLBuilder._CodeFieldSSLCheck();
- FLBuilder._initCodeFields();
- FLBuilder._initColorPickers();
- FLBuilder._initGradientPickers();
- FLBuilder._initIconFields();
- FLBuilder._initPhotoFields();
- FLBuilder._initSelectFields();
- FLBuilder._initEditorFields();
- FLBuilder._initMultipleFields();
- FLBuilder._initAutoSuggestFields();
- FLBuilder._initLinkFields();
- FLBuilder._initFontFields();
- FLBuilder._initPostTypeFields();
- FLBuilder._initOrderingFields();
- FLBuilder._initTimezoneFields();
- FLBuilder._initDimensionFields();
- FLBuilder._initFieldPopupSliders();
- FLBuilder._initPresetFields();
- FLBuilder._initModuleMarginPlaceholders();
- FLBuilder._focusFirstSettingsControl();
- FLBuilder._calculateSettingsTabsOverflow();
- FLBuilder._lightbox._resizeEditors();
- $( '.fl-builder-settings-fields', window.parent.document ).css( 'visibility', 'visible' );
- $( '.fl-builder-settings button', window.parent.document ).on( 'click', function( e ) { e.preventDefault() } )
- /**
- * Hook for settings form init.
- */
- FLBuilder.triggerHook('settings-form-init');
- },
- /**
- * Destroys all active settings forms.
- *
- * @since 2.0
- * @access private
- * @method _destroySettingsForms
- */
- _destroySettingsForms: function()
- {
- FLBuilder._destroyEditorFields();
- },
- /**
- * Inserts settings forms rendered with PHP. This method is only around for
- * backwards compatibility with third party settings forms that are
- * still being rendered via AJAX. Going forward, all settings forms
- * should be rendered on the frontend using FLBuilderSettingsForms.render.
- *
- * @since 1.0
- * @access private
- * @method _setSettingsFormContent
- * @param {String} html
- */
- _setSettingsFormContent: function( html )
- {
- $( '.fl-legacy-settings', window.parent.document ).remove();
- $( 'body', window.parent.document ).append( html );
- },
- /**
- * Shows the content for a settings form tab when it is clicked.
- *
- * @since 1.0
- * @access private
- * @method _settingsTabClicked
- * @param {Object} e The event object.
- */
- _settingsTabClicked: function(e)
- {
- var tab = $( this ),
- form = tab.closest( '.fl-builder-settings' ),
- id = tab.attr( 'href' ).split( '#' ).pop();
- FLBuilder._resetSettingsTabsState();
- form.find( '.fl-builder-settings-tab' ).removeClass( 'fl-active' );
- form.find( '#' + id ).addClass( 'fl-active' );
- form.find( '.fl-builder-settings-tabs .fl-active' ).removeClass( 'fl-active' );
- form.find( 'a[href*=' + id + ']' ).addClass( 'fl-active' );
- if ( FLBuilderConfig.rememberTab ) {
- localStorage.setItem( 'fl-builder-settings-tab', id );
- } else {
- localStorage.setItem( 'fl-builder-settings-tab', '' );
- }
- FLBuilder._focusFirstSettingsControl();
- e.preventDefault();
- },
- _resetSettingsTabsState: function() {
- var $lightbox = $('.fl-lightbox:visible', window.parent.document);
- FLBuilder._hideTabsOverflowMenu();
- $lightbox.find('.fl-builder-settings-tabs .fl-active').removeClass('fl-active');
- $lightbox.find('.fl-builder-settings-tabs-overflow-menu .fl-active').removeClass('fl-active');
- $lightbox.find('.fl-contains-active').removeClass('fl-contains-active');
- },
- /**
- * Measures tabs and adds extra items to overflow menu.
- *
- * @since 2.0
- * @access private
- * @return void
- * @method _settingsTabsToOverflowMenu
- */
- _calculateSettingsTabsOverflow: function() {
- var $lightbox = $('.fl-lightbox:visible', window.parent.document),
- lightboxWidth = $lightbox.outerWidth(),
- isSlim = $lightbox.hasClass('fl-lightbox-width-slim'),
- $tabWrap = $lightbox.find('.fl-builder-settings-tabs'),
- $overflowMenu = $lightbox.find('.fl-builder-settings-tabs-overflow-menu'),
- $overflowMenuBtn = $lightbox.find('.fl-builder-settings-tabs-more'),
- $tabs = $tabWrap.find('a'),
- shouldEjectRemainingTabs = false,
- tabsAreaWidth = lightboxWidth - 44, /* 60 is size of "more" btn */
- tabsWidthTotal = 0,
- tabPadding = isSlim ? ( 5 * 2 ) : ( 10 * 2 );
- // Reset the menu
- $overflowMenu.html('');
- FLBuilder._hideTabsOverflowMenu();
- $tabs.removeClass('fl-overflowed');
- // Measure each tab
- $tabs.each(function() {
- if ( !$(this).is(":visible") ) {
- return true;
- }
- // Calculate size until too wide for tab area.
- if ( !shouldEjectRemainingTabs ) {
- // Width of text + padding + bumper space
- var currentTabWidth = $(this).textWidth() + tabPadding + 12;
- tabsWidthTotal += currentTabWidth;
- if ( tabsWidthTotal >= tabsAreaWidth ) {
- shouldEjectRemainingTabs = true;
- } else {
- }
- }
- if ( shouldEjectRemainingTabs ) {
- var label = $(this).html(),
- handle = $(this).attr('href'),
- classAttr = "";
- if ( $(this).hasClass('fl-active') ) {
- classAttr = 'fl-active';
- }
- if ( $(this).hasClass('error') ) {
- classAttr += ' error';
- }
- if ( classAttr !== '' ) {
- classAttr = 'class="' + classAttr + '"';
- }
- var $item = $('<a href="' + handle + '" ' + classAttr + '>' + label + '</a>');
- $overflowMenu.append( $item );
- $(this).addClass('fl-overflowed');
- } else {
- $(this).removeClass('fl-overflowed');
- }
- });
- if ( shouldEjectRemainingTabs ) {
- $lightbox.addClass('fl-lightbox-has-tab-overflow');
- } else {
- $lightbox.removeClass('fl-lightbox-has-tab-overflow');
- }
- if ( $overflowMenu.find('.fl-active').length > 0 ) {
- $overflowMenuBtn.addClass('fl-contains-active');
- } else {
- $overflowMenuBtn.removeClass('fl-contains-active');
- }
- if ( $overflowMenu.find('.error').length > 0 ) {
- $overflowMenuBtn.addClass('fl-contains-errors');
- } else {
- $overflowMenuBtn.removeClass('fl-contains-errors');
- }
- },
- /**
- * Trigger the orignal tab when a menu item is clicked.
- *
- * @since 2.0
- * @var {Event} e
- * @return void
- */
- _settingsTabsToOverflowMenuItemClicked: function(e) {
- var $item = $(e.currentTarget, window.parent.document),
- handle = $item.attr('href'),
- $tabsWrap = $item.closest('.fl-lightbox-header-wrap').find('.fl-builder-settings-tabs'),
- $tab = $tabsWrap.find('a[href="' + handle + '"]'),
- $moreBtn = $tabsWrap.find('.fl-builder-settings-tabs-more');
- FLBuilder._resetSettingsTabsState();
- $tab.trigger('click');
- $item.addClass('fl-active');
- $moreBtn.addClass('fl-contains-active');
- FLBuilder._hideTabsOverflowMenu();
- e.preventDefault();
- },
- /**
- * Check if overflow menu contains any tabs
- *
- * @since 2.0
- * @return bool
- */
- _hasOverflowTabs: function() {
- var $lightbox = $('.fl-lightbox:visible', window.parent.document),
- $tabs = $lightbox.find('.fl-builder-settings-tabs-overflow-menu a');
- if ( $tabs.length > 0 ) {
- return true;
- } else {
- return false;
- }
- },
- /**
- * Show the overflow menu
- *
- */
- _showTabsOverflowMenu: function() {
- if ( ! FLBuilder._hasOverflowTabs() ) return;
- var $lightbox = $('.fl-lightbox:visible', window.parent.document);
- $lightbox.find('.fl-builder-settings-tabs-overflow-menu').css('display', 'flex');
- $lightbox.find('.fl-builder-settings-tabs-overflow-click-mask').show();
- this.isShowingSettingsTabsOverflowMenu = true;
- },
- /**
- * Hide the overflow menu
- */
- _hideTabsOverflowMenu: function() {
- var $lightbox = $('.fl-lightbox:visible', window.parent.document);
- $lightbox.find('.fl-builder-settings-tabs-overflow-menu').css('display', 'none');
- $lightbox.find('.fl-builder-settings-tabs-overflow-click-mask').hide();
- this.isShowingSettingsTabsOverflowMenu = false;
- },
- /**
- * Toggle the overflow menu
- */
- _toggleTabsOverflowMenu: function( e ) {
- if ( FLBuilder.isShowingSettingsTabsOverflowMenu ) {
- FLBuilder._hideTabsOverflowMenu();
- } else {
- FLBuilder._showTabsOverflowMenu();
- }
- e.stopPropagation();
- },
- /**
- * Setup section toggling for all sections
- *
- * @since 2.2
- * @access private
- * @method _initSettingsSections
- * @return void
- */
- _initSettingsSections: function() {
- $( '.fl-builder-settings:visible', window.parent.document ).find( '.fl-builder-settings-section' ).each( FLBuilder._initSection );
- },
- /**
- * Reverts an active preview and hides the lightbox when
- * the cancel button of a settings lightbox is clicked.
- *
- * @since 1.0
- * @access private
- * @method _settingsCancelClicked
- * @param {Object} e The event object.
- */
- _settingsCancelClicked: function(e)
- {
- var nestedLightbox = $( '.fl-builder-lightbox[data-parent]', window.parent.document ),
- moduleSettings = $( '.fl-builder-module-settings', window.parent.document ),
- existingNodes = null,
- previewModule = null,
- previewContainer = null,
- previewCol = null,
- existingCol = null,
- isRootCol = 'column' == FLBuilderConfig.userTemplateType;
- // Close a nested settings lightbox.
- if ( nestedLightbox.length > 0 ) {
- FLBuilder._closeNestedSettings();
- return;
- }
- // Delete a new module preview?
- else if(moduleSettings.length > 0 && typeof moduleSettings.data('new-module') != 'undefined') {
- existingNodes = $(FLBuilder.preview.state.html);
- previewModule = $('.fl-node-' + moduleSettings.data('node'));
- previewContainer = previewModule.parents('.fl-module[data-accepts]');
- previewCol = previewModule.closest('.fl-col');
- existingCol = existingNodes.find('.fl-node-' + previewCol.data('node'));
- if(previewContainer.length > 0 || existingCol.length > 0 || isRootCol) {
- FLBuilder._deleteModule(previewModule);
- }
- else {
- FLBuilder._deleteCol(previewCol);
- }
- }
- // Do a standard preview revert.
- else if( FLBuilder.preview ) {
- FLBuilder.preview.revert();
- }
- const actions = FL.Builder.data.getLayoutActions()
- actions.cancelDisplaySettings()
- FLBuilder.preview = null;
- FLLightbox.closeParent(this);
- FLBuilder.triggerHook( 'didCancelNodeSettings' );
- },
- /**
- * Focus the first visible control in a settings panel
- *
- * @since 2.0
- */
- _focusFirstSettingsControl: function() {
- var form = $( '.fl-builder-settings:visible', window.parent.document ),
- tab = form.find( '.fl-builder-settings-tab.fl-active' ),
- nodeId = form.data( 'node' ),
- field = tab.find('.fl-field').first(),
- input = field.find( 'input:not([type="hidden"]), textarea, select, button, a, .fl-editor-field' ).first();
- // Don't focus fields that have an inline editor.
- if ( nodeId && $( '.fl-node-' + nodeId + ' .fl-inline-editor' ).length ) {
- return;
- }
- if ( 'undefined' !== typeof window.parent.tinyMCE && input.hasClass('fl-editor-field') ) {
- // TinyMCE fields
- var id = input.find('textarea.wp-editor-area').attr('id');
- window.parent.tinyMCE.get( id ).focus();
- } else {
- // Everybody else
- setTimeout(function() {
- input.focus().css('animation-name', 'fl-grab-attention');
- }, 300 );
- }
- // Grab attention
- field.css('animation-name', 'fl-grab-attention');
- field.on('animationend', function() {
- field.css('animation-name', '');
- });
- },
- /**
- * Initializes validation logic for a settings form.
- *
- * @since 1.0
- * @access private
- * @method _initSettingsValidation
- * @param {Object} rules The validation rules object.
- * @param {Object} messages Custom messages to show for invalid fields.
- */
- _initSettingsValidation: function(rules, messages)
- {
- var form = $('.fl-builder-settings', window.parent.document).last();
- if ( ! messages ) {
- messages = {}
- }
- form.validate({
- ignore: '.fl-ignore-validation',
- rules: rules,
- messages: messages,
- errorPlacement: FLBuilder._settingsErrorPlacement
- });
- },
- /**
- * Places a validation error after the invalid field.
- *
- * @since 1.0
- * @access private
- * @method _settingsErrorPlacement
- * @param {Object} error The error element.
- * @param {Object} element The invalid field.
- */
- _settingsErrorPlacement: function(error, element)
- {
- error.appendTo(element.parent());
- },
- /**
- * Resets all tab error icons and then shows any for tabs
- * that have fields with errors.
- *
- * @since 1.0
- * @access private
- * @method _toggleSettingsTabErrors
- */
- _toggleSettingsTabErrors: function()
- {
- var form = $('.fl-builder-settings:visible', window.parent.document),
- tabs = form.find('.fl-builder-settings-tab'),
- tab = null,
- tabErrors = null,
- i = 0;
- for( ; i < tabs.length; i++) {
- tab = tabs.eq(i);
- tabErrors = tab.find('label.error');
- tabLink = form.find('.fl-builder-settings-tabs a[href*='+ tab.attr('id') +']');
- tabLink.find('.fl-error-icon').remove();
- tabLink.removeClass('error');
- if(tabErrors.length > 0) {
- tabLink.append('<span class="fl-error-icon"></span>');
- tabLink.addClass('error');
- }
- }
- FLBuilder._calculateSettingsTabsOverflow();
- },
- /**
- * Returns an object with key/value pairs for all fields
- * within a settings form.
- *
- * @since 1.0
- * @access private
- * @method _getSettings
- * @param {Object} form The settings form element.
- * @return {Object} The settings object.
- */
- _getSettings: function( form )
- {
- FLBuilder._updateEditorFields();
- var data = form.serializeArray(),
- i = 0,
- k = 0,
- value = '',
- key = '',
- keys = [],
- setting = null,
- settings = {};
- // Loop through the form data.
- for ( i = 0; i < data.length; i++ ) {
- value = data[ i ].value.replace( /\r/gm, '' ).replace( /'/g, "'" );
- // Don't save fields without a name.
- if ( 'undefined' === data[ i ].name ) {
- continue;
- }
- // Don't save text editor textareas.
- if ( data[ i ].name.indexOf( 'flrich' ) > -1 ) {
- continue;
- }
- // Support foo[]... setting keys.
- if ( data[ i ].name.indexOf( '[' ) > -1 ) {
- // Get the top level settings key and subkeys.
- key = data[ i ].name.replace( /\[(.*)\]/, '' );
- keys = data[ i ].name.replace( key, '' ).replace( '[', '' ).replaceAll( ']', '' ).split( '[' );
- // Set the top level settings key.
- if ( 'undefined' === typeof settings[ key ] ) {
- if ( 1 === keys.length && '' === keys[ 0 ] ) {
- settings[ key ] = [];
- } else {
- settings[ key ] = {};
- }
- }
- // Reference to use in the keys loop below.
- setting = settings[ key ];
- // Set the subkeys and value.
- for ( k = 0; k < keys.length; k++ ) {
- // Handle foo[] arrays otherwise ignore empty keys.
- if ( '' === keys[ k ] ) {
- if ( 1 === keys.length ) {
- setting.push( value );
- break;
- } else {
- continue;
- }
- }
- // Handle foo[][bar][] or foo[][bar][][baz][] style arrays.
- if ( keys.length - 2 === k && '' === keys[ keys.length - 1 ] ) {
- if ( $.inArray( typeof setting[ keys[ k ] ], [ 'undefined', 'string' ] ) > -1 ) {
- setting[ keys[ k ] ] = [];
- }
- setting[ keys[ k ] ].push( value );
- break;
- }
- // Handle everything else as objects like foo[bar], foo[][bar] or foo[bar][baz].
- if ( keys.length - 1 === k ) {
- setting[ keys[ k ] ] = value;
- } else {
- if ( $.inArray( typeof setting[ keys[ k ] ], [ 'undefined', 'string' ] ) > -1 ) {
- setting[ keys[ k ] ] = {};
- }
- setting = setting[ keys[ k ] ];
- }
- }
- }
- // Standard name/value pair.
- else {
- settings[ data[ i ].name ] = value;
- }
- }
- // Update auto suggest values.
- for ( key in settings ) {
- if ( 'undefined' !== typeof settings[ 'as_values_' + key ] ) {
- settings[ key ] = $.grep(
- settings[ 'as_values_' + key ].split( ',' ),
- function( n ) {
- return n !== '';
- }
- ).join( ',' );
- try {
- delete settings[ 'as_values_' + key ];
- }
- catch( e ) {}
- }
- }
- // In the case of multi-select or checkboxes we need to put the blank setting back in.
- $.each( form.find( '[name]' ), function( key, input ) {
- var name = $( input ).attr( 'name' ).replace( /\[(.*)\]/, '' );
- if ( ! ( name in settings ) && 'undefined' !== name ) {
- settings[ name ] = '';
- }
- });
- // Merge in the original settings in case legacy fields haven't rendered yet.
- settings = $.extend( {}, FLBuilder._getOriginalSettings( form ), settings );
- // Return the settings.
- return settings;
- },
- /**
- * Returns JSON encoded settings to be used in HTML form elements.
- *
- * @since 2.0
- * @access private
- * @method _getSettingsJSONForHTML
- * @param {Object} settings The settings object.
- * @return {String} The settings JSON.
- */
- _getSettingsJSONForHTML: function( settings )
- {
- return JSON.stringify( settings ).replace( /\'/g, ''' ).replace( '<wbr \/>', '<wbr>' );
- },
- /**
- * Returns the original settings for a settings form. This is only
- * used to work with legacy PHP settings fields.
- *
- * @since 2.0
- * @access private
- * @method _getOriginalSettings
- * @param {Object} form The settings form element.
- * @param {Boolean} all Whether to include all of the settings or just those with fields.
- * @return {Object} The settings object.
- */
- _getOriginalSettings: function( form, all )
- {
- var formJSON = form.find( '.fl-builder-settings-json' ),
- nodeId = form.data( 'node' ),
- config = FLBuilderSettingsConfig.nodes,
- original = null,
- settings = {};
- if ( nodeId && config[ nodeId ] ) {
- original = config[ nodeId ];
- } else if ( formJSON.length ) {
- original = FLBuilder._jsonParse( formJSON.val().replace( /'/g, "'" ) );
- }
- if ( original ) {
- for ( key in original ) {
- if ( key.match( /[a-z0-9-_]+$/ ) && $( '#fl-field-' + key ).length || all ) {
- settings[ key ] = original[ key ];
- }
- }
- }
- return settings;
- },
- /**
- * Gets the settings that are saved to see if settings
- * have changed when saving or canceling.
- *
- * @since 2.1
- * @method getSettingsForChangedCheck
- * @param {Object} form
- * @return {Object}
- */
- _getSettingsForChangedCheck: function( nodeId, form ) {
- var settings = FLBuilder._getSettings( form );
- // Make sure we're getting the original setting if even it
- // was changed by inline editing before the form loaded.
- if ( nodeId ) {
- var node = $( '.fl-node-' + nodeId );
- if ( node.hasClass( 'fl-module' ) ) {
- var type = node.data( 'type' );
- var config = FLBuilderSettingsConfig.editables[ type ];
- if ( config && FLBuilderSettingsConfig.nodes[ nodeId ] ) {
- for ( var key in config ) {
- settings[ key ] = FLBuilderSettingsConfig.nodes[ nodeId ][ key ]
- }
- }
- }
- }
- return settings;
- },
- /**
- * Saves the settings for the current settings form, shows
- * the loader and hides the lightbox.
- *
- * @since 1.0
- * @access private
- * @method _saveSettings
- * @param {Boolean} render Whether the layout should render after saving.
- */
- _saveSettings: function( render )
- {
- var form = $( '.fl-builder-settings-lightbox .fl-builder-settings', window.parent.document ),
- newModule = form.data( 'new-module' ),
- nodeId = form.attr( 'data-node' ),
- settings = FLBuilder._getSettings( form ),
- preview = FLBuilder.preview;
- // Default to true for render.
- if ( FLBuilder.isUndefined( render ) || ! FLBuilder.isBoolean( render ) ) {
- render = true;
- }
- // Only proceed if the settings have changed.
- if ( preview && ! preview._settingsHaveChanged() && FLBuilder.isUndefined( newModule ) ) {
- preview.clear();
- FLBuilder._lightbox.close();
- return;
- }
- function finishSavingSettings() {
- // Show the loader.
- FLBuilder._showNodeLoading( nodeId );
- // Update the settings config object.
- FLBuilderSettingsConfig.nodes[ nodeId ] = settings;
- // Dispatch to store
- const actions = FL.Builder.data.getLayoutActions()
- const callback = FLBuilder._saveSettingsComplete.bind( this, render )
- actions.updateNodeSettings( nodeId, settings, callback )
- // Trigger the hook.
- FLBuilder.triggerHook( 'didSaveNodeSettings', {
- nodeId : nodeId,
- settings : settings
- } );
- // Close the lightbox.
- FLBuilder._lightbox.close();
- }
- if ( FLBuilderConfig.userCaps.unfiltered_html ) {
- finishSavingSettings()
- } else {
- FLBuilderSettingsForms.showLightboxLoader()
- FLBuilder.ajax( {
- action : 'verify_settings',
- settings : settings,
- }, function( response ) {
- if ( 'true' === response ) {
- finishSavingSettings()
- } else {
- msg = '<p style="font-weight:bold;text-align:center;">' + FLBuilderStrings.noScriptWarn.heading + '</p>';
- if ( FLBuilderConfig.userCaps.global_unfiltered_html ) {
- msg += '<p>' + FLBuilderStrings.noScriptWarn.global + '</p>';
- } else {
- msg += '<p>' + FLBuilderStrings.noScriptWarn.message + '</p>';
- }
- msg += '<p><div class="fl-diff"></div></p>';
- msg += '<p>' + FLBuilderStrings.noScriptWarn.footer + '</p>';
- FLBuilderSettingsForms.hideLightboxLoader()
- FLBuilder.alert( msg );
- data = $.parseJSON(response);
- if ( '' !== data.diff ) {
- $('.fl-diff', window.parent.document).html( data.diff );
- $('.fl-diff', window.parent.document).prepend( '<p>' + FLBuilderStrings.codeErrorDetected + '</p>');
- $('.fl-diff .diff-deletedline', window.parent.document).each(function(){
- if ( $(this).find('del').length < 1 ) {
- $(this).css('background-color', 'rgb(255, 192, 203, 0.7)').css('padding', '10px').css('border', '1px solid pink');
- } else {
- $(this).find('del').css('background-color', 'rgb(255, 192, 203, 0.7)').css('border', '1px solid pink');
- }
- });
- console.log( '============' );
- console.log( 'key: ' + data.key );
- console.log( 'value: ' + data.value );
- console.log( 'parsed: ' + data.parsed );
- console.log( '============' );
- }
- }
- } );
- }
- },
- /**
- * Renders a new layout when the settings for the current
- * form have finished saving.
- *
- * @since 1.0
- * @access private
- * @method _saveSettingsComplete
- * @param {Boolean} render Whether the layout should render after saving.
- * @param {String} response The layout data from the server.
- */
- _saveSettingsComplete: function( render, response )
- {
- var data = FLBuilder._jsonParse( response ),
- type = data.layout.nodeType,
- moduleType = data.layout.moduleType,
- hook = 'didSave' + type.charAt(0).toUpperCase() + type.slice(1) + 'SettingsComplete',
- preview = FLBuilder.preview,
- callback = function() {
- var clearPreview = preview && data.layout.partial && data.layout.nodeId === preview.nodeId && !FLBuilder._publishAndRemain;
- if ( clearPreview ) {
- preview.clear();
- FLBuilder.preview = null;
- }
- FLBuilder._publishAndRemain = false;
- };
- if ( true === render ) {
- FLBuilder._renderLayout( data.layout, callback );
- } else {
- callback();
- }
- FLBuilder.triggerHook( 'didSaveNodeSettingsComplete', {
- nodeId : data.node_id,
- nodeType : type,
- moduleType : moduleType,
- settings : data.settings
- } );
- FLBuilder.triggerHook( hook, {
- nodeId : data.node_id,
- nodeType : type,
- moduleType : moduleType,
- settings : data.settings
- } );
- },
- /**
- * Triggers a click on the settings save button so all save
- * logic runs for any form that is currently in the lightbox.
- *
- * @since 2.0
- * @access private
- * @method _triggerSettingsSave
- * @param {Boolean} disableClose
- * @param {Boolean} showAlert
- * @param {Boolean} destroy
- * @return {Boolean}
- */
- _triggerSettingsSave: function( disableClose, showAlert, destroy )
- {
- var form = FLBuilder._lightbox._node.find( 'form.fl-builder-settings' ),
- lightboxId = FLBuilder._lightbox._node.data( 'instance-id' ),
- lightbox = FLLightbox._instances[ lightboxId ],
- nested = $( '.fl-lightbox-wrap[data-parent]:visible', window.parent.document ),
- changed = false,
- valid = true;
- disableClose = _.isUndefined( disableClose ) ? false : disableClose;
- showAlert = _.isUndefined( showAlert ) ? false : showAlert;
- destroy = _.isUndefined( destroy ) ? ! disableClose : destroy;
- // prevent clearing preview.
- if (!destroy) {
- FLBuilder._publishAndRemain = true;
- }
- if ( form.length ) {
- // Save any nested settings forms.
- if ( nested.length ) {
- // Save the form.
- nested.find( '.fl-builder-settings-save' ).trigger( 'click' );
- // Don't proceed if not saved.
- if ( nested.find( 'label.error' ).length || $( '.fl-builder-alert-lightbox:visible', window.parent.document ).length ) {
- valid = false;
- }
- }
- // Do a validation check of the main form to see if we should save.
- if ( valid && ! form.validate({ignore: '.fl-ignore-validation'}).form() ) {
- valid = false;
- }
- // Check to see if the main settings have changed.
- changed = FLBuilderSettingsForms.settingsHaveChanged();
- // Save the main settings form if it has changes.
- if ( valid && changed ) {
- // Disable lightbox close?
- if ( disableClose ) {
- lightbox.disableClose();
- }
- // Save the form.
- form.find( '.fl-builder-settings-save' ).trigger( 'click' );
- // Enable lightbox close if it was disabled.
- if ( disableClose ) {
- lightbox.enableClose();
- }
- // Don't proceed if not saved.
- if ( form.find( 'label.error' ).length || $( '.fl-builder-alert-lightbox:visible', window.parent.document ).length ) {
- valid = false;
- }
- }
- // Destroy the settings form?
- if ( destroy ) {
- FLBuilder._destroySettingsForms();
- // Destroy the preview if settings don't have changes.
- if ( ! changed && FLBuilder.preview ) {
- FLBuilder.preview.clear();
- FLBuilder.preview = null;
- }
- } else {
- // cache current settings
- FLBuilderSettingsForms.cacheCurrentSettings();
- }
- // Close the main lightbox if it doesn't have changes and closing isn't disabled.
- if ( ! changed && ! disableClose ) {
- lightbox.close();
- }
- }
- if ( ! valid ) {
- FLBuilder._publishAndRemain = false; // Reset preview clearing
- FLBuilder.triggerHook( 'didFailSettingsSave' );
- FLBuilder._toggleSettingsTabErrors();
- if ( showAlert && ! $( '.fl-builder-alert-lightbox:visible', window.parent.document ).length ) {
- FLBuilder.alert( FLBuilderStrings.settingsHaveErrors );
- }
- } else {
- FLBuilder.triggerHook( 'didTriggerSettingsSave' );
- }
- return valid;
- },
- /**
- * Refreshes preview references for a node's settings panel
- * in case they have been broken by work in the layout.
- *
- * @since 2.0
- * @access private
- * @method _refreshSettingsPreviewReference
- */
- _refreshSettingsPreviewReference: function()
- {
- if ( FLBuilder.preview ) {
- FLBuilder.preview._initElementsAndClasses();
- }
- },
- /* Nested Settings Forms
- ----------------------------------------------------------*/
- /**
- * Opens a nested settings lightbox.
- *
- * @since 1.10
- * @access private
- * @method _openNestedSettings
- * @return object The settings lightbox object.
- */
- _openNestedSettings: function( settings )
- {
- if ( settings.className && -1 === settings.className.indexOf( 'fl-builder-settings-lightbox' ) ) {
- settings.className += ' fl-builder-settings-lightbox';
- }
- settings = $.extend( {
- className: 'fl-builder-lightbox fl-builder-settings-lightbox',
- destroyOnClose: true,
- resizable: true
- }, settings );
- var parentBoxWrap = $( '.fl-lightbox-wrap:visible', window.parent.document ),
- parentBox = parentBoxWrap.find( '.fl-lightbox' ),
- nestedBoxObj = new FLLightbox( settings ),
- nestedBoxWrap = nestedBoxObj._node,
- nestedBox = nestedBoxWrap.find( '.fl-lightbox' );
- parentBoxWrap.hide();
- nestedBoxWrap.attr( 'data-parent', parentBoxWrap.attr( 'data-instance-id' ) );
- nestedBox.attr( 'style', parentBox.attr( 'style' ) );
- nestedBoxObj.on( 'resized', FLBuilder._calculateSettingsTabsOverflow );
- nestedBoxObj.open( '<div class="fl-builder-lightbox-loading"></div>' );
- return nestedBoxObj;
- },
- /**
- * Opens the active nested settings lightbox.
- *
- * @since 1.10
- * @access private
- * @method _closeNestedSettings
- */
- _closeNestedSettings: function()
- {
- var nestedBoxWrap = $( '.fl-builder-lightbox[data-parent]:visible', window.parent.document ),
- nestedBox = nestedBoxWrap.find( '.fl-lightbox' ),
- nestedBoxId = nestedBoxWrap.attr( 'data-instance-id' ),
- nestedBoxObj = FLLightbox._instances[ nestedBoxId ],
- parentBoxId = nestedBoxWrap.attr( 'data-parent' ),
- parentBoxWrap = $( '[data-instance-id="' + parentBoxId + '"]', window.parent.document ),
- parentBox = parentBoxWrap.find( '.fl-lightbox' ),
- parentBoxForm = parentBoxWrap.find('form'),
- parentBoxObj = FLLightbox._instances[ parentBoxId ];
- if ( ! nestedBoxObj ) {
- return
- }
- nestedBoxObj.on( 'close', function() {
- parentBox.attr( 'style', nestedBox.attr( 'style' ) );
- parentBoxWrap.show();
- parentBoxObj._resize();
- parentBoxWrap.find( 'label.error' ).remove();
- parentBoxForm.validate().hideErrors();
- FLBuilder._toggleSettingsTabErrors();
- FLBuilder._initMultipleFields();
- } );
- nestedBoxObj.close();
- },
- /* Tooltips
- ----------------------------------------------------------*/
- /**
- * Shows a help tooltip in the settings lightbox.
- *
- * @since 1.0
- * @access private
- * @method _showHelpTooltip
- */
- _showHelpTooltip: function()
- {
- $(this).siblings('.fl-help-tooltip-text').fadeIn();
- },
- /**
- * Hides a help tooltip in the settings lightbox.
- *
- * @since 1.0
- * @access private
- * @method _hideHelpTooltip
- */
- _hideHelpTooltip: function()
- {
- $(this).siblings('.fl-help-tooltip-text').fadeOut();
- },
- /**
- * Setup section toggling
- *
- * @since 2.2
- * @access private
- * @method _initSection
- * @return void
- */
- _initSection: function() {
- var wrap = $(this),
- button = wrap.find('.fl-builder-settings-section-header');
- button.on('click', function() {
- wrap.toggleClass('fl-builder-settings-section-collapsed')
- });
- },
- /* Align Fields
- ----------------------------------------------------------*/
- /**
- * Initializes all button group fields within a settings form.
- *
- * @since 2.2
- * @access private
- * @method _initButtonGroupFields
- */
- _initButtonGroupFields: function()
- {
- $( '.fl-builder-settings:visible', window.parent.document )
- .find( '.fl-button-group-field' ).each( FLBuilder._initButtonGroupField );
- },
- /**
- * Initializes a button group field within a settings form.
- *
- * @since 2.2
- * @access private
- * @method _initButtonGroupField
- */
- _initButtonGroupField: function()
- {
- var wrap = $( this ),
- options = wrap.find( '.fl-button-group-field-option' ),
- multiple = wrap.data( 'multiple' ),
- min = wrap.data( 'min' ),
- max = wrap.data( 'max' ),
- input = wrap.find( 'input:not(.fl-preview-ignore)' ),
- allowEmpty = !! wrap.data( 'allowEmpty' ),
- value = function( format ) {
- var val = [];
- options.each(function(i, option) {
- if ($(option).attr('data-selected') === '1') {
- val.push($(option).attr('data-value'));
- }
- })
- if ( 'array' == format ) {
- return val;
- }
- return val.join(',');
- };
- options.on( 'click', function() {
- var option = $( this ),
- length = value( 'array' ).length,
- isSelected = '1' === option.attr( 'data-selected' );
- if ( ! allowEmpty && isSelected ) {
- return;
- }
- if ( isSelected ) {
- if ( false == min ) {
- option.attr( 'data-selected', '0' );
- } else {
- if ( length - 1 >= min ) {
- option.attr( 'data-selected', '0' );
- }
- }
- } else {
- // Unset other options
- if ( multiple !== true ) {
- options.attr( 'data-selected', '0' );
- }
- if ( false == max ) {
- option.attr( 'data-selected', '1' );
- } else {
- if ( length + 1 <= max ) {
- option.attr( 'data-selected', '1' );
- }
- }
- }
- input.val( value( '' ) ).trigger( 'change' );
- } );
- // Handle value being changed externally
- input.on( 'change', function( e ) {
- var value = input.val().split(',');
- // Unset other options
- if (multiple !== true) {
- options.attr('data-selected', '0' );
- }
- $.each(value, function(i, val) {
- var option = options.filter( '[data-value="' + val + '"]' );
- // Set the matching one.
- option.attr( 'data-selected', '1' );
- });
- });
- },
- /* Compound Fields
- ----------------------------------------------------------*/
- /**
- * Initializes all compound fields within a settings form.
- *
- * @since 2.2
- * @access private
- * @method _initCompoundFields
- */
- _initCompoundFields: function()
- {
- $( '.fl-builder-settings:visible', window.parent.document )
- .find( '.fl-compound-field' ).each( FLBuilder._initCompoundField );
- },
- /**
- * Initializes a compound field within a settings form.
- *
- * @since 2.2
- * @access private
- * @method _initCompoundField
- */
- _initCompoundField: function()
- {
- var wrap = $( this ),
- sections = wrap.find( '.fl-compound-field-section' ),
- toggles = wrap.find( '.fl-compound-field-section-toggle' ),
- dimensions = wrap.find( '.fl-compound-field-setting' ).has( '.fl-dimension-field-units' );
- sections.each( function() {
- var section = $( this );
- if ( ! section.find( '.fl-compound-field-section-toggle' ).length ) {
- section.addClass( 'fl-compound-field-section-visible' );
- }
- } );
- toggles.on( 'click', function() {
- var toggle = $( this ),
- field = toggle.closest( '.fl-field' ),
- section = toggle.closest( '.fl-compound-field-section' ),
- className = '.' + section.attr( 'class' ).split( ' ' ).join( '.' );
- field.find( className ).toggleClass( 'fl-compound-field-section-visible' );
- } );
- // Init linking for compound dimension fields.
- dimensions.each( function() {
- var field = $( this ),
- label = field.find( '.fl-compound-field-label' ),
- icon = '<i class="fl-dimension-field-link fl-tip dashicons dashicons-admin-links" title="Link Values"></i>';
- if ( ! label.length || field.find( '.fl-shadow-field' ).length ) {
- return;
- }
- label.append( icon );
- } );
- },
- /* Auto Suggest Fields
- ----------------------------------------------------------*/
- /**
- * Initializes all auto suggest fields within a settings form.
- *
- * @since 1.2.3
- * @access private
- * @method _initAutoSuggestFields
- */
- _initAutoSuggestFields: function()
- {
- var fields = $('.fl-builder-settings:visible .fl-suggest-field', window.parent.document),
- field = null,
- values = null,
- name = null,
- data = [];
- fields.each( function() {
- field = $( this );
- if ( '' !== field.attr( 'data-value' ) ) {
- FLBuilderSettingsForms.showFieldLoader( field );
- data.push( {
- name : field.attr( 'name' ),
- value : field.attr( 'data-value' ),
- action : field.attr( 'data-action' ),
- data : field.attr( 'data-action-data' ),
- } );
- }
- } );
- if ( data.length ) {
- FLBuilder.ajax( {
- action: 'get_autosuggest_values',
- fields: data
- }, function( response ) {
- values = FLBuilder._jsonParse( response );
- for ( name in values ) {
- $( '.fl-suggest-field[name="' + name + '"]', window.parent.document )
- .attr( 'data-value', values[ name ] );
- }
- fields.each( FLBuilder._initAutoSuggestField );
- } );
- } else {
- fields.each( FLBuilder._initAutoSuggestField );
- }
- },
- /**
- * Initializes a single auto suggest field.
- *
- * @since 1.2.3
- * @access private
- * @method _initAutoSuggestField
- */
- _initAutoSuggestField: function()
- {
- var field = $(this);
- field.autoSuggest(FLBuilder._ajaxUrl({
- 'fl_action' : 'fl_builder_autosuggest',
- 'fl_as_action' : field.data('action'),
- 'fl_as_action_data' : field.data('action-data'),
- '_wpnonce' : FLBuilderConfig.ajaxNonce
- }), $.extend({}, {
- asHtmlID : field.attr('name'),
- selectedItemProp : 'name',
- searchObjProps : 'name',
- minChars : 2,
- keyDelay : 1000,
- fadeOut : false,
- usePlaceholder : true,
- emptyText : FLBuilderStrings.noResultsFound,
- showResultListWhenNoMatch : true,
- preFill : field.data('value'),
- queryParam : 'fl_as_query',
- afterSelectionAdd : FLBuilder._updateAutoSuggestField,
- afterSelectionRemove : FLBuilder._updateAutoSuggestField,
- selectionLimit : field.data('limit'),
- canGenerateNewSelections : false
- }, field.data( 'args' )));
- FLBuilderSettingsForms.hideFieldLoader( field );
- },
- /**
- * Updates the value of an auto suggest field.
- *
- * @since 1.2.3
- * @access private
- * @method _initAutoSuggestField
- * @param {Object} element The auto suggest field.
- * @param {Object} item The current selection.
- * @param {Array} selections An array of selected values.
- */
- _updateAutoSuggestField: function(element, item, selections)
- {
- var that = this;
- $(this).siblings('.as-values').val(selections.join(',')).trigger('change');
- // sortable stuff.
- $(this).parents( '.as-selections').sortable({
- items : ':not(.as-original)',
- 'update': function( event, ui ) {
- var selected = [];
- set = that.parents( '.as-selections').find('li.as-selection-item');
- $.each(set, function(i,n) {
- selected.push($(n).attr('data-value'));
- });
- $(that).siblings('.as-values').val(selected.join(',')).trigger('change');
- }
- })
- },
- /* Code Fields
- ----------------------------------------------------------*/
- /**
- * SiteGround ForceSSL fix
- */
- _CodeFieldSSLCheck: function() {
- $('body').append('<div class="sg-test" style="display:none"><svg xmlns="http://www.w3.org/2000/svg"></svg></div>');
- if ( 'https://www.w3.org/2000/svg' === $('.sg-test').find('svg').attr('xmlns') ) {
- FLBuilder._codeDisabled = true;
- }
- $('.sg-test').remove()
- },
- /**
- * Initializes all code fields in a settings form.
- *
- * @since 2.0
- * @access private
- * @method _initCodeFields
- */
- _initCodeFields: function()
- {
- if ( ! FLBuilder._codeDisabled ) {
- $( '.fl-builder-settings:visible', window.parent.document )
- .find( '.fl-code-field' ).each( FLBuilder._initCodeField );
- }
- },
- /**
- * Initializes a single code field in a settings form.
- *
- * @since 2.0
- * @access private
- * @method _initCodeField
- */
- _initCodeField: function()
- {
- var field = $( this ),
- settings = field.closest( '.fl-builder-settings' ),
- textarea = field.find( 'textarea' ),
- editorId = textarea.attr( 'id' ),
- mode = textarea.data( 'editor' ),
- wrap = textarea.data( 'wrap' ),
- editDiv = $( '<div>', {
- position: 'absolute',
- height: parseInt( textarea.attr( 'rows' ), 10 ) * 20
- } ),
- editor = null,
- global_layout = ( settings.hasClass('fl-builder-global-settings') || settings.hasClass('fl-builder-layout-settings' ) ) ? true : false;
- editDiv.insertBefore( textarea );
- editDiv.attr('contentEditable', true );
- editDiv.addClass('fl-ignore-validation');
- textarea.css( 'display', 'none' );
- ace.require( 'ace/ext/language_tools' );
- editor = ace.edit( editDiv[0] );
- editor.$blockScrolling = Infinity;
- editor.getSession().setValue( textarea.val() );
- editor.getSession().setMode( 'ace/mode/' + mode );
- if ( wrap ) {
- editor.getSession().setUseWrapMode( true );
- }
- editor.setOptions( FLBuilderConfig.AceEditorSettings );
- editor.getSession().on( 'change', function( e ) {
- textarea.val( editor.getSession().getValue() ).trigger( 'change' );
- } );
- /**
- * Watch the editor for annotation changes and let the
- * user know if there are any errors.
- */
- editor.getSession().on( 'changeAnnotation', function() {
- var annot = editor.getSession().getAnnotations();
- var saveBtn = settings.find( '.fl-builder-settings-save' );
- var errorBtn = settings.find( '.fl-builder-settings-error' );
- var hasError = false;
- for ( var i = 0; i < annot.length; i++ ) {
- if ( annot[ i ].text.indexOf( 'DOCTYPE' ) > -1 ) {
- continue;
- }
- if ( annot[ i ].text.indexOf( 'Named entity expected' ) > -1 ) {
- continue;
- }
- if ( annot[ i ].text.indexOf( '@supports' ) > -1 ) {
- continue;
- }
- if ( 'error' === annot[ i ].type ) {
- hasError = true;
- break;
- }
- }
- val = editor.getSession().getValue();
- if( global_layout && hasError && null !== val.match( /<\/iframe>|<\/script>|<meta/gm ) ) {
- saveBtn.addClass( 'fl-builder-settings-error' );
- saveBtn.on( 'click', FLBuilder._showCodeFieldCriticalError );
- }
- if ( hasError && settings.find( '#fl-builder-settings-section-bb_js_code' ).length > 0 && 'fl-field-bb_js_code' === field.closest( '.fl-field' ).attr('ID') ) {
- saveBtn.addClass( 'fl-builder-settings-error' );
- saveBtn.on( 'click', FLBuilder._showCodeFieldCriticalError );
- }
- if ( hasError && ! saveBtn.hasClass( 'fl-builder-settings-error' ) && errorBtn.length && FLBuilderConfig.CheckCodeErrors ) {
- saveBtn.addClass( 'fl-builder-settings-error' );
- saveBtn.on( 'click', FLBuilder._showCodeFieldError );
- }
- if ( ! hasError ) {
- errorBtn.removeClass( 'fl-builder-settings-error' );
- errorBtn.off( 'click', FLBuilder._showCodeFieldError );
- errorBtn.off( 'click', FLBuilder._showCodeFieldCriticalError );
- }
- });
- textarea.closest( '.fl-field' ).data( 'editor', editor );
- },
- /**
- * Shows the code error alert when a code field
- * has an error.
- *
- * @since 2.1
- * @access private
- * @method _showCodeFieldError
- */
- _showCodeFieldError: function( e ) {
- e.stopImmediatePropagation();
- FLBuilder.confirm( {
- message: FLBuilderStrings.codeError,
- cancel: function(){
- var saveBtn = $( '.fl-builder-settings:visible .fl-builder-settings-save', window.parent.document );
- saveBtn.removeClass( 'fl-builder-settings-error' );
- saveBtn.off( 'click', FLBuilder._showCodeFieldError );
- saveBtn.trigger( 'click' );
- },
- strings: {
- ok: FLBuilderStrings.codeErrorFix,
- cancel: FLBuilderStrings.codeErrorIgnore
- }
- } );
- },
- _showCodeFieldCriticalError: function( e ) {
- e.stopImmediatePropagation();
- FLBuilder.alert( FLBuilderStrings.codeerrorhtml );
- },
- /* Multiple Fields
- ----------------------------------------------------------*/
- /**
- * Initializes all multiple fields in a settings form.
- *
- * @since 1.0
- * @access private
- * @method _initMultipleFields
- */
- _initMultipleFields: function()
- {
- $('.fl-builder-settings:visible .fl-builder-field-multiples', window.parent.document).each(function(){
- var multiples = $(this),
- multiple = null,
- fields = null,
- i = 0,
- cursorAt = FLBuilderConfig.isRtl ? { left: 10 } : { right: 10 },
- limit = multiples.attr( 'data-limit' ) || 0,
- count = multiples.find('tr').length || 0
- if( parseInt(limit) > 0 && count -1 >= parseInt( limit ) ) {
- multiples.find('.fl-builder-field-copy').hide()
- multiples.find('.fl-builder-field-add').fadeOut()
- } else {
- multiples.find('.fl-builder-field-copy, .fl-builder-field-add').show()
- }
- for( ; i < multiples.length; i++) {
- multiple = multiples.eq(i);
- fields = multiple.find('.fl-builder-field-multiple');
- if(fields.length === 1) {
- fields.eq(0).find('.fl-builder-field-actions').addClass('fl-builder-field-actions-single');
- }
- else {
- fields.find('.fl-builder-field-actions').removeClass('fl-builder-field-actions-single');
- }
- }
- $('.fl-builder-field-multiples', window.parent.document).sortable({
- items: '.fl-builder-field-multiple',
- cursor: 'move',
- cursorAt: cursorAt,
- distance: 5,
- opacity: 0.5,
- placeholder: 'fl-builder-field-dd-zone',
- stop: FLBuilder._fieldDragStop,
- tolerance: 'pointer',
- axis: "y"
- });
- }); // end loop
- },
- /**
- * Adds a new multiple field to the list when the add
- * button is clicked.
- *
- * @since 1.0
- * @access private
- * @method _addFieldClicked
- */
- _addFieldClicked: function()
- {
- var button = $(this),
- fieldName = button.attr('data-field'),
- fieldRow = button.closest('tr').siblings('tr[data-field='+ fieldName +']').last(),
- clone = fieldRow.clone(),
- form = clone.find( '.fl-form-field' ),
- formType = null,
- defaultVal = null,
- index = parseInt(fieldRow.find('label span.fl-builder-field-index').html(), 10) + 1;
- clone.find('th label span.fl-builder-field-index').html(index);
- clone.find('.fl-form-field-preview-text').html('');
- clone.find('.fl-form-field-before').remove();
- clone.find('.fl-form-field-after').remove();
- clone.find('input, textarea, select').val('');
- // clear color
- clone.find('.fl-color-picker-color').css('background-color', 'transparent');
- clone.find('.fl-color-picker-color').addClass('fl-color-picker-empty');
- fieldRow.after(clone);
- FLBuilder._initMultipleFields();
- if ( form.length ) {
- formType = form.find( '.fl-form-field-edit' ).data( 'type' );
- form.find( 'input' ).val( JSON.stringify( FLBuilderSettingsConfig.defaults.forms[ formType ] ) );
- }
- else {
- form = button.closest('form.fl-builder-settings');
- formType = form.data( 'type' );
- if ( formType && form.hasClass( 'fl-builder-module-settings' ) ) {
- defaultVal = FLBuilderSettingsConfig.defaults.modules[ formType ][ fieldName ][0];
- clone.find('input, textarea, select').val( defaultVal );
- }
- FLBuilder._renumberFields( clone.closest( '.fl-field' ) );
- }
- },
- /**
- * Copies a multiple field and adds it to the list when
- * the copy button is clicked.
- *
- * @since 1.0
- * @access private
- * @method _copyFieldClicked
- */
- _copyFieldClicked: function()
- {
- var button = $(this),
- row = button.closest('tr'),
- clone = row.clone(),
- index = parseInt(row.find('label span.fl-builder-field-index').html(), 10) + 1;
- clone.find('th label span.fl-builder-field-index').html(index);
- row.after(clone);
- FLBuilder._renumberFields(row.parent());
- FLBuilder._initMultipleFields();
- if ( FLBuilder.preview ) {
- FLBuilder.preview.delayPreview();
- }
- },
- /**
- * Deletes a multiple field from the list when the
- * delete button is clicked.
- *
- * @since 1.0
- * @access private
- * @method _deleteFieldClicked
- */
- _deleteFieldClicked: function()
- {
- var row = $(this).closest('tr'),
- parent = row.parent(),
- global = parent.closest('.fl-builder-settings').hasClass('fl-builder-global-styles'),
- result = confirm(global ? FLBuilderStrings.deleteGlobalColorsWarning : FLBuilderStrings.deleteFieldMessage);
- if(result) {
- row.remove();
- FLBuilder._renumberFields(parent);
- FLBuilder._initMultipleFields();
- if ( FLBuilder.preview ) {
- FLBuilder.preview.delayPreview();
- }
- }
- },
- /**
- * Renumbers the labels for a list of multiple fields.
- *
- * @since 1.0
- * @access private
- * @method _renumberFields
- * @param {Object} table A table element with multiple fields.
- */
- _renumberFields: function( table )
- {
- var rows = table.find( '.fl-builder-field-multiple' );
- rows.each( function( i, row ) {
- $( row ).find( 'th label span.fl-builder-field-index' ).html( i + 1 );
- FLBuilder._renumberFieldAttr( row, 'name', i );
- FLBuilder._renumberFieldAttr( row, 'id', i );
- FLBuilder._renumberFieldAttr( row, 'for', i );
- } );
- },
- /**
- * @since 2.5
- * @access private
- * @method _renumberFieldAttr
- * @param {String} value
- */
- _renumberFieldAttr: function( row, key, i )
- {
- $( row ).find( '[' + key + ']' ).each( function( k, ele ) {
- var value = $( ele ).attr( key );
- value = value.replace( /\[(\d+)\]/, '[' + ( i ) + ']' );
- $( ele ).attr( key, value );
- } );
- },
- /**
- * Returns an element for multiple field drag operations.
- *
- * @since 1.0
- * @access private
- * @method _fieldDragHelper
- * @return {Object} The helper element.
- */
- _fieldDragHelper: function()
- {
- return $('<div class="fl-builder-field-dd-helper"></div>');
- },
- /**
- * Renumbers and triggers a preview when a multiple field
- * has finished dragging.
- *
- * @since 1.0
- * @access private
- * @method _fieldDragStop
- * @param {Object} e The event object.
- * @param {Object} ui An object with additional info for the drag.
- */
- _fieldDragStop: function(e, ui)
- {
- FLBuilder._renumberFields(ui.item.parent());
- if ( FLBuilder.preview ) {
- FLBuilder.preview.delayPreview();
- }
- },
- /* Select Fields
- ----------------------------------------------------------*/
- /**
- * Initializes select fields for a settings form.
- *
- * @since 1.0
- * @access private
- * @method _initSelectFields
- */
- _initSelectFields: function()
- {
- var selects = $( '.fl-builder-settings:visible', window.parent.document )
- .find( 'select:not(.fl-preview-ignore)' );
- selects.on( 'change', FLBuilder._settingsSelectChanged );
- selects.trigger( 'change' );
- selects.on( 'change', FLBuilder._calculateSettingsTabsOverflow );
- // Button groups use the same options and toggling behavior as selects.
- var buttonGroups = $( '.fl-builder-settings:visible', window.parent.document )
- .find( '.fl-button-group-field input[type=hidden]:not(.fl-preview-ignore)' );
- buttonGroups.on( 'change', FLBuilder._settingsSelectChanged );
- buttonGroups.trigger( 'change' );
- buttonGroups.on( 'change', FLBuilder._calculateSettingsTabsOverflow );
- // Remove hook first, just in case.
- FLBuilder.removeHook('settings-form-init', FLBuilder._initSelectFieldSetFields );
- FLBuilder.addHook('settings-form-init', FLBuilder._initSelectFieldSetFields );
- },
- /**
- * Callback for when a settings form select has been changed.
- * If toggle data is present, other fields will be toggled
- * when this select changes.
- *
- * @since 1.0
- * @access private
- * @method _settingsSelectChanged
- */
- _settingsSelectChanged: function( e )
- {
- var select = $(this),
- name = select.attr('data-root-name'),
- toggle = select.attr('data-toggle'),
- hide = select.attr('data-hide'),
- trigger = select.attr('data-trigger'),
- set = select.attr('data-set'),
- val = select.val(),
- rawVal = val,
- i = 0,
- mode = FLBuilderResponsiveEditing._mode,
- responsive = select.closest( '.fl-field-responsive-setting' ),
- modeClass = 'fl-field-responsive-setting-' + mode,
- allowToggle = false;
- if ( '' === val && name ) {
- const field = FLBuilderSettingsForms.getField( name )
- const inheritedVal = field?.getInheritedValue( mode )
- val = inheritedVal ? inheritedVal : val
- }
- if ( responsive.length ) {
- if(
- ! select.parent().hasClass( modeClass ) &&
- ! select.parent().parent().hasClass( modeClass )
- ) {
- return;
- }
- }
- // TOGGLE sections, fields or tabs.
- if( typeof toggle !== 'undefined' ) {
- toggle = FLBuilder._jsonParse(toggle);
- allowToggle = true;
- for(i in toggle) {
- if ( allowToggle ){
- FLBuilder._settingsSelectToggle(toggle[i].fields, 'hide', '#fl-field-');
- FLBuilder._settingsSelectToggle(toggle[i].sections, 'hide', '#fl-builder-settings-section-');
- FLBuilder._settingsSelectToggle(toggle[i].tabs, 'hide', 'a[href*=fl-builder-settings-tab-', ']');
- }
- }
- if(typeof toggle[val] !== 'undefined') {
- if ( allowToggle ){
- FLBuilder._settingsSelectToggle(toggle[val].fields, 'show', '#fl-field-');
- FLBuilder._settingsSelectToggle(toggle[val].sections, 'show', '#fl-builder-settings-section-');
- FLBuilder._settingsSelectToggle(toggle[val].tabs, 'show', 'a[href*=fl-builder-settings-tab-', ']');
- }
- }
- }
- // HIDE sections, fields or tabs.
- if( typeof hide !== 'undefined' ) {
- hide = FLBuilder._jsonParse(hide);
- for(i in hide) {
- FLBuilder._settingsSelectToggle(hide[i].fields, 'show', '#fl-field-');
- FLBuilder._settingsSelectToggle(hide[i].sections, 'show', '#fl-builder-settings-section-');
- FLBuilder._settingsSelectToggle(hide[i].tabs, 'show', 'a[href*=fl-builder-settings-tab-', ']');
- }
- if(typeof hide[val] !== 'undefined') {
- FLBuilder._settingsSelectToggle(hide[val].fields, 'hide', '#fl-field-');
- FLBuilder._settingsSelectToggle(hide[val].sections, 'hide', '#fl-builder-settings-section-');
- FLBuilder._settingsSelectToggle(hide[val].tabs, 'hide', 'a[href*=fl-builder-settings-tab-', ']');
- }
- }
- // TRIGGER select inputs.
- if( typeof trigger !== 'undefined' ) {
- trigger = FLBuilder._jsonParse(trigger);
- if(typeof trigger[val] !== 'undefined') {
- if(typeof trigger[val].fields !== 'undefined') {
- for(i = 0; i < trigger[val].fields.length; i++) {
- $('#fl-field-' + trigger[val].fields[i]).find('select').trigger('change');
- }
- }
- }
- }
- },
- /**
- * @since 1.0
- * @access private
- * @method _settingsSelectToggle
- * @param {Array} inputArray
- * @param {Function} func
- * @param {String} prefix
- * @param {String} suffix
- */
- _settingsSelectToggle: function(inputArray, func, prefix, suffix)
- {
- var i = 0;
- suffix = 'undefined' == typeof suffix ? '' : suffix;
- if(typeof inputArray !== 'undefined') {
- for( ; i < inputArray.length; i++) {
- $('.fl-builder-settings:visible', window.parent.document).find(prefix + inputArray[i] + suffix)[func]();
- // Resize code editor fields.
- $( prefix + inputArray[i] + suffix ).parent().find( '.fl-field[data-type="code"]' ).each( function() {
- if ( ! FLBuilder._codeDisabled && $( this ).data( 'editor' ) ) {
- $( this ).data( 'editor' ).resize();
- }
- } );
- }
- }
- },
- /**
- * Setup field 'set' configuration. Sets other fields when the value of a selector button group changes.
- * This is set up after the form is done rendering to avoid initial change event.
- *
- * @since 2.8
- * @access private
- * @method _initSelectFieldSetFields
- * @return void
- */
- _initSelectFieldSetFields: function()
- {
- var selects = $( '.fl-builder-settings:visible', window.parent.document )
- .find( 'select:not(.fl-preview-ignore)' );
- var buttonGroups = $( '.fl-builder-settings:visible', window.parent.document )
- .find( '.fl-button-group-field input[type=hidden]:not(.fl-preview-ignore)' );
- selects.on( 'change', FLBuilder._settingsSelectSetFields );
- buttonGroups.on( 'change', FLBuilder._settingsSelectSetFields );
- },
- _settingsSelectSetFields: function()
- {
- var select = $(this),
- name = select.attr('data-root-name'),
- set = select.attr('data-set'),
- val = select.val(),
- mode = FLBuilderResponsiveEditing._mode;
- // SET other fields based on value
- if ( undefined !== set ) {
- set = FLBuilder._jsonParse(set)
- if ( typeof set[val] !== 'undefined' ) {
- // Handle setting fields
- for( let name in set[val] ) {
- const field = FLBuilderSettingsForms.getField( name )
- const value = set[val][name]
- if ( 'object' === typeof value ) {
- for( let key in value ) {
- field.setSubValue( key, value[key], mode )
- }
- } else {
- field.setValue( value, mode )
- }
- }
- }
- }
- },
- _toggleForm: function() {
- const form = $( '.fl-builder-settings:visible', window.parent.document )
- const fields = form.find('select:visible, .fl-button-group-field:visible input[type=hidden]:not(.fl-preview-ignore)')
- fields.each( function() {
- if ( this.hasAttribute('data-toggle') ) {
- const toggle = FLBuilder._jsonParse( this.getAttribute('data-toggle') )
- const hide = FLBuilder._jsonParse( this.getAttribute('data-hide') )
- const name = this.getAttribute('data-root-name')
- let val = this.value
- if ( '' === val && name ) {
- const field = FLBuilderSettingsForms.getField( name )
- const inheritedVal = field?.getInheritedValue( FLBuilderResponsiveEditing._mode )
- val = inheritedVal ? inheritedVal : val
- }
- // Hide everything
- if ( toggle ) {
- for( let i in toggle ) {
- FLBuilder._settingsSelectToggle(toggle[i].fields, 'hide', '#fl-field-');
- FLBuilder._settingsSelectToggle(toggle[i].sections, 'hide', '#fl-builder-settings-section-');
- FLBuilder._settingsSelectToggle(toggle[i].tabs, 'hide', 'a[href*=fl-builder-settings-tab-', ']');
- }
- // Toggle things on
- if( typeof toggle[val] !== 'undefined' ) {
- FLBuilder._settingsSelectToggle(toggle[val].fields, 'show', '#fl-field-');
- FLBuilder._settingsSelectToggle(toggle[val].sections, 'show', '#fl-builder-settings-section-');
- FLBuilder._settingsSelectToggle(toggle[val].tabs, 'show', 'a[href*=fl-builder-settings-tab-', ']');
- }
- }
- if ( hide ) {
- for( let i in hide ) {
- FLBuilder._settingsSelectToggle(hide[i].fields, 'show', '#fl-field-');
- FLBuilder._settingsSelectToggle(hide[i].sections, 'show', '#fl-builder-settings-section-');
- FLBuilder._settingsSelectToggle(hide[i].tabs, 'show', 'a[href*=fl-builder-settings-tab-', ']');
- }
- if( typeof hide[val] !== 'undefined' ) {
- FLBuilder._settingsSelectToggle(hide[val].fields, 'hide', '#fl-field-');
- FLBuilder._settingsSelectToggle(hide[val].sections, 'hide', '#fl-builder-settings-section-');
- FLBuilder._settingsSelectToggle(hide[val].tabs, 'hide', 'a[href*=fl-builder-settings-tab-', ']');
- }
- }
- }
- } )
- },
- /* Color Pickers
- ----------------------------------------------------------*/
- /**
- * Initializes color picker fields for a settings form.
- *
- * @since 1.0
- * @access private
- * @method _initColorPickers
- */
- _initColorPickers: function()
- {
- if ( FLBuilder.colorPicker ) {
- FLBuilder.colorPicker._init();
- return;
- }
- FLBuilder.colorPicker = new FLBuilderColorPicker({
- mode: 'hsv',
- elements: '.fl-color-picker .fl-color-picker-value',
- presets: FLBuilderConfig.colorPresets ? FLBuilderConfig.colorPresets : [],
- globals: function () {
- var styles = FLBuilderConfig.styles;
- var themeJSON = FLBuilderConfig.themeJSON;
- return {
- bb: styles ? styles.colors : [],
- wp: themeJSON && themeJSON.color.palette.default ? themeJSON.color.palette.default : [],
- theme: themeJSON && themeJSON.color.palette.theme ? themeJSON.color.palette.theme : []
- }
- },
- labels: {
- colorPresets : FLBuilderStrings.colorPresets,
- colorPicker : FLBuilderStrings.colorPicker,
- placeholder : FLBuilderStrings.placeholder,
- removePresetConfirm : FLBuilderStrings.removePresetConfirm,
- noneColorSelected : FLBuilderStrings.noneColorSelected,
- alreadySaved : FLBuilderStrings.alreadySaved,
- noPresets : FLBuilderStrings.noPresets,
- presetAdded : FLBuilderStrings.presetAdded,
- }
- });
- $( FLBuilder.colorPicker ).on( 'presetRemoved presetAdded presetSorted', function( event, data ) {
- FLBuilder.ajax({
- action: 'save_color_presets',
- presets: data.presets
- });
- });
- },
- /* Color Pickers
- ----------------------------------------------------------*/
- /**
- * Initializes gradient picker fields for a settings form.
- *
- * @since 2.2
- * @access private
- * @method _initGradientPickers
- */
- _initGradientPickers: function()
- {
- $( '.fl-builder-settings:visible .fl-gradient-picker', window.parent.document )
- .each( FLBuilder._initGradientPicker );
- },
- /**
- * Initializes a single gradient picker field.
- *
- * @since 2.2
- * @access private
- * @method _initGradientPicker
- */
- _initGradientPicker: function()
- {
- var picker = $( this ),
- type = picker.find( '.fl-gradient-picker-type-select' ),
- angle = picker.find( '.fl-gradient-picker-angle-wrap' ),
- position = picker.find( '.fl-gradient-picker-position' );
- type.on( 'change', function() {
- if ( 'linear' === $( this ).val() ) {
- angle.show();
- position.hide();
- } else {
- angle.hide();
- position.show();
- }
- } );
- },
- /* Single Photo Fields
- ----------------------------------------------------------*/
- /**
- * Initializes photo fields for a settings form.
- *
- * @since 2.2
- * @access private
- * @method _initPhotoFields
- */
- _initPhotoFields: function()
- {
- var selects = $( '.fl-builder-settings:visible', window.parent.document )
- .find( '.fl-photo-field select' );
- selects.on( 'change', FLBuilder._toggleSettingsOnIconChange );
- selects.trigger( 'change' );
- },
- /**
- * Initializes the single photo selector.
- *
- * @since 1.8.6
- * @access private
- * @method _initSinglePhotoSelector
- */
- _initSinglePhotoSelector: function()
- {
- if(FLBuilder._singlePhotoSelector === null) {
- FLBuilder._singlePhotoSelector = wp.media({
- title: FLBuilderStrings.selectPhoto,
- button: { text: FLBuilderStrings.selectPhoto },
- library : { type : FLBuilderConfig.uploadTypes.image },
- multiple: false
- });
- FLBuilder._singlePhotoSelector.on( 'open', FLBuilder._wpmedia_reset_errors );
- window.parent._wpPluploadSettings['defaults']['multipart_params']['fl_upload_type']= 'photo';
- }
- },
- /**
- * Shows the single photo selector.
- *
- * @since 1.0
- * @access private
- * @method _selectSinglePhoto
- */
- _selectSinglePhoto: function()
- {
- FLBuilder._initSinglePhotoSelector();
- FLBuilder._singlePhotoSelector.once('open', $.proxy(FLBuilder._singlePhotoOpened, this));
- FLBuilder._singlePhotoSelector.once('select', $.proxy(FLBuilder._singlePhotoSelected, this));
- FLBuilder._singlePhotoSelector.open();
- },
- /**
- * Callback for when the single photo selector is shown.
- *
- * @since 1.0
- * @access private
- * @method _singlePhotoOpened
- */
- _singlePhotoOpened: function()
- {
- var selection = FLBuilder._singlePhotoSelector.state().get('selection'),
- wrap = $(this).closest('.fl-photo-field'),
- photoField = wrap.find('input[type=hidden]'),
- photo = photoField.val(),
- attachment = null;
- if($(this).hasClass('fl-photo-replace')) {
- selection.reset();
- wrap.addClass('fl-photo-empty');
- photoField.val('');
- }
- else if(photo !== '') {
- attachment = wp.media.attachment(photo);
- attachment.fetch();
- selection.add(attachment ? [attachment] : []);
- }
- else {
- selection.reset();
- }
- },
- /**
- * Callback for when a single photo is selected.
- *
- * @since 1.0
- * @access private
- * @method _singlePhotoSelected
- */
- _singlePhotoSelected: function()
- {
- var photo = FLBuilder._singlePhotoSelector.state().get('selection').first().toJSON(),
- wrap = $(this).closest('.fl-photo-field'),
- photoField = wrap.find('input[type=hidden]'),
- preview = wrap.find('.fl-photo-preview img'),
- srcSelect = wrap.find('select');
- if ( photo.url && photo.url.endsWith( '.svg' ) ) {
- photo.sizes = {
- full: {
- url: photo.url,
- filename: photo.url.split( '/' ).pop(),
- height: '',
- width: ''
- }
- }
- }
- photoField.val(photo.id);
- preview.attr('src', FLBuilder._getPhotoSrc(photo));
- wrap.removeClass('fl-photo-empty').removeClass('fl-photo-no-attachment');
- wrap.find('label.error').remove();
- srcSelect.show();
- srcSelect.html(FLBuilder._getPhotoSizeOptions(photo,srcSelect.val()));
- srcSelect.trigger('change');
- FLBuilderSettingsConfig.attachments[ photo.id ] = photo;
- },
- /**
- * Clears a photo that has been selected in a single photo field.
- *
- * @since 1.6.4.3
- * @access private
- * @method _singlePhotoRemoved
- */
- _singlePhotoRemoved: function()
- {
- FLBuilder._initSinglePhotoSelector();
- var state = FLBuilder._singlePhotoSelector.state(),
- selection = 'undefined' != typeof state ? state.get('selection') : null,
- wrap = $(this).closest('.fl-photo-field'),
- photoField = wrap.find('input[type=hidden]'),
- srcSelect = wrap.find('select');
- if ( selection ) {
- selection.reset();
- }
- wrap.addClass('fl-photo-empty');
- photoField.val('');
- srcSelect.html('<option value="" selected></option>');
- srcSelect.trigger('change');
- },
- /**
- * Returns the src URL for a photo.
- *
- * @since 1.0
- * @access private
- * @method _getPhotoSrc
- * @param {Object} photo A photo data object.
- * @return {String} The src URL for a photo.
- */
- _getPhotoSrc: function(photo)
- {
- if(typeof photo.sizes === 'undefined') {
- return photo.url;
- }
- else if(typeof photo.sizes.thumbnail !== 'undefined') {
- return photo.sizes.thumbnail.url;
- }
- else {
- return photo.sizes.full.url;
- }
- },
- /**
- * Builds the options for a photo size select.
- *
- * @since 1.0
- * @access private
- * @method _getPhotoSizeOptions
- * @param {Object} photo A photo data object.
- * @param {String} selectedSize The selected photo size if one is set.
- * @return {String} The HTML for the photo size options.
- */
- _getPhotoSizeOptions: function( photo, selectedSize )
- {
- var html = '',
- size = null,
- selected = null,
- check = null,
- doneselected = false,
- title = '',
- titleSize = '',
- titles = {
- full : FLBuilderStrings.fullSize,
- large : FLBuilderStrings.large,
- medium : FLBuilderStrings.medium,
- thumbnail : FLBuilderStrings.thumbnail
- };
- if(typeof photo.sizes === 'undefined' || 0 === photo.sizes.length) {
- html += '<option value="' + photo.url + '">' + FLBuilderStrings.fullSize + '</option>';
- }
- else {
- // Check the selected value without the protocol so we get a match if
- // a site has switched to HTTPS since selecting this photo (#641).
- if ( selectedSize ) {
- selectedSize = selectedSize.split(/[\\/]/).pop();
- }
- /**
- * We need to make a check here to be sure that the selectedSize
- * is actually the image we selected, or the previous image
- * otherwise it will default to the 1st in the select (thumbnail)
- */
- selectedverified = false;
- for(sizecheck in photo.sizes) {
- if ( photo.sizes[ sizecheck ].url.split(/[\\/]/).pop() === selectedSize ) {
- selectedverified = true;
- break;
- }
- }
- if ( ! selectedverified ) {
- selectedSize = false;
- }
- for(size in photo.sizes) {
- selected = '';
- if ( 'undefined' != typeof titles[ size ] ) {
- title = titles[ size ];
- }
- else if ( 'undefined' != typeof FLBuilderConfig.customImageSizeTitles[ size ] ) {
- title = FLBuilderConfig.customImageSizeTitles[ size ];
- }
- else {
- title = '';
- }
- if ( ! selectedSize ) {
- if ( typeof FLBuilderConfig.photomodulesize !== 'undefined' && size === FLBuilderConfig.photomodulesize && ! doneselected ) {
- selected = ' selected="selected"';
- doneselected = true;
- } else if( size == FLBuilderConfig.defaultImageSize && ! doneselected ){
- selected = ' selected="selected"';
- doneselected = true;
- }
- } else if( selectedSize === photo.sizes[ size ].url.split(/[\\/]/).pop() && ! doneselected ) {
- selected = ' selected="selected"';
- doneselected = true;
- }
- if ( photo.sizes[size].width && photo.sizes[size].height ) {
- title = title ? title + ' - ' : title
- titleSize = photo.sizes[size].width + ' x ' + photo.sizes[size].height
- }
- html += '<option data-size="' + size + '" value="' + photo.sizes[size].url + '"' + selected + '>' + title + titleSize + '</option>';
- }
- }
- return html;
- },
- /* Multiple Photo Fields
- ----------------------------------------------------------*/
- /**
- * Shows the multiple photo selector.
- *
- * @since 1.0
- * @access private
- * @method _selectMultiplePhotos
- */
- _selectMultiplePhotos: function()
- {
- var wrap = $(this).closest('.fl-multiple-photos-field'),
- photosField = wrap.find('input[type=hidden]'),
- photosFieldVal = photosField.val(),
- parsedVal = photosFieldVal === '' ? '' : FLBuilder._jsonParse(photosFieldVal),
- defaultPostId = wp.media.gallery.defaults.id,
- content = '[gallery ids="-1"]',
- shortcode = null,
- attachments = null,
- selection = null,
- i = null,
- ids = [];
- // Builder the gallery shortcode.
- if ( 'object' == typeof parsedVal ) {
- for ( i in parsedVal ) {
- ids.push( parsedVal[ i ] );
- }
- content = '[gallery ids="'+ ids.join() +'"]';
- }
- shortcode = wp.shortcode.next('gallery', content).shortcode;
- if(_.isUndefined(shortcode.get('id')) && !_.isUndefined(defaultPostId)) {
- shortcode.set('id', defaultPostId);
- }
- // Get the selection object.
- attachments = wp.media.gallery.attachments(shortcode);
- selection = new wp.media.model.Selection(attachments.models, {
- props: attachments.props.toJSON(),
- multiple: true
- });
- selection.gallery = attachments.gallery;
- // Fetch the query's attachments, and then break ties from the
- // query to allow for sorting.
- selection.more().done(function() {
- if ( ! selection.length ) {
- FLBuilder._multiplePhotoSelector.setState( 'gallery-library' );
- }
- // Break ties with the query.
- selection.props.set({ query: false });
- selection.unmirror();
- selection.props.unset('orderby');
- });
- // Destroy the previous gallery frame.
- if(FLBuilder._multiplePhotoSelector) {
- FLBuilder._multiplePhotoSelector.dispose();
- }
- window.parent._wpPluploadSettings['defaults']['multipart_params']['fl_upload_type']= 'photo';
- // Store the current gallery frame.
- FLBuilder._multiplePhotoSelector = wp.media({
- frame: 'post',
- state: $(this).hasClass('fl-multiple-photos-edit') ? 'gallery-edit' : 'gallery-library',
- title: wp.media.view.l10n.editGalleryTitle,
- editing: true,
- multiple: true,
- selection: selection
- }).open();
- $(FLBuilder._multiplePhotoSelector.views.view.el).addClass('fl-multiple-photos-lightbox');
- FLBuilder._multiplePhotoSelector.once('update', $.proxy(FLBuilder._multiplePhotosSelected, this));
- },
- /**
- * Callback for when multiple photos have been selected.
- *
- * @since 1.0
- * @access private
- * @method _multiplePhotosSelected
- * @param {Object} data The photo data object.
- */
- _multiplePhotosSelected: function(data)
- {
- var wrap = $(this).closest('.fl-multiple-photos-field'),
- photosField = wrap.find('input[type=hidden]'),
- count = wrap.find('.fl-multiple-photos-count'),
- photos = [],
- i = 0;
- for( ; i < data.models.length; i++) {
- photos.push(data.models[i].id);
- }
- if(photos.length == 1) {
- count.html('1 ' + FLBuilderStrings.photoSelected);
- }
- else {
- count.html(photos.length + ' ' + FLBuilderStrings.photosSelected);
- }
- wrap.removeClass('fl-multiple-photos-empty');
- wrap.find('label.error').remove();
- photosField.val(JSON.stringify(photos)).trigger('change');
- },
- /* Single Video Fields
- ----------------------------------------------------------*/
- /**
- * Initializes the single video selector.
- *
- * @since 1.10.8
- * @access private
- * @method _initSingleVideoSelector
- */
- _initSingleVideoSelector: function()
- {
- if(FLBuilder._singleVideoSelector === null) {
- var defaultFileExtensions = window.parent._wpPluploadSettings.defaults.filters.mime_types[0].extensions;
- window.parent._wpPluploadSettings['defaults']['multipart_params']['fl_upload_type'] = 'video';
- window.parent._wpPluploadSettings.defaults.filters.mime_types[0].extensions = FLBuilderConfig.uploadTypes.videoTypes;
- FLBuilder._singleVideoSelector = wp.media({
- title: FLBuilderStrings.selectVideo,
- button: { text: FLBuilderStrings.selectVideo },
- library: { type: [ 'video/mp4', 'video/webm' ] },
- multiple: false
- });
- FLBuilder._singleVideoSelector.on( 'open', FLBuilder._wpmedia_reset_errors );
- FLBuilder._singleVideoSelector.on( 'close', function () {
- window.parent._wpPluploadSettings.defaults.filters.mime_types[0].extensions = defaultFileExtensions;
- });
- }
- },
- /**
- * Shows the single video selector.
- *
- * @since 1.0
- * @access private
- * @method _selectSingleVideo
- */
- _selectSingleVideo: function()
- {
- FLBuilder._initSingleVideoSelector();
- FLBuilder._singleVideoSelector.once('select', $.proxy(FLBuilder._singleVideoSelected, this));
- FLBuilder._singleVideoSelector.open();
- },
- /**
- * Callback for when a single video is selected.
- *
- * @since 1.0
- * @access private
- * @method _singleVideoSelected
- */
- _singleVideoSelected: function()
- {
- var video = FLBuilder._singleVideoSelector.state().get('selection').first().toJSON(),
- wrap = $(this).closest('.fl-video-field'),
- image = wrap.find('.fl-video-preview-img'),
- filename = wrap.find('.fl-video-preview-filename'),
- videoField = wrap.find('input[type=hidden]');
- image.html('<span class="dashicons dashicons-media-video"></span>');
- filename.html(video.filename);
- wrap.removeClass('fl-video-empty');
- wrap.find('label.error').remove();
- videoField.val(video.id).trigger('change');
- FLBuilderSettingsConfig.attachments[ video.id ] = video;
- },
- /**
- * Clears a video that has been selected in a single video field.
- *
- * @since 2.1
- * @access private
- * @method _singleVideoRemoved
- */
- _singleVideoRemoved: function()
- {
- FLBuilder._initSingleVideoSelector();
- var state = FLBuilder._singleVideoSelector.state(),
- selection = 'undefined' != typeof state ? state.get('selection') : null,
- wrap = $(this).closest('.fl-video-field'),
- image = wrap.find('.fl-video-preview-img img'),
- filename = wrap.find('.fl-video-preview-filename'),
- videoField = wrap.find('input[type=hidden]');
- if ( selection ) {
- selection.reset();
- }
- image.attr('src', '');
- filename.html('');
- wrap.addClass('fl-video-empty');
- videoField.val('').trigger('change');
- },
- /* Multiple Audios Field
- ----------------------------------------------------------*/
- /**
- * Shows the multiple audio selector.
- *
- * @since 1.0
- * @access private
- * @method _selectMultipleAudios
- */
- _selectMultipleAudios: function()
- {
- var wrap = $(this).closest('.fl-multiple-audios-field'),
- audiosField = wrap.find('input[type=hidden]'),
- audiosFieldVal = audiosField.val(),
- content = audiosFieldVal == '' ? '[playlist ids="-1"]' : '[playlist ids="'+ FLBuilder._jsonParse(audiosFieldVal).join() +'"]',
- shortcode = wp.shortcode.next('playlist', content).shortcode,
- defaultPostId = wp.media.playlist.defaults.id,
- attachments = null,
- selection = null;
- if(_.isUndefined(shortcode.get('id')) && !_.isUndefined(defaultPostId)) {
- shortcode.set('id', defaultPostId);
- }
- attachments = wp.media.playlist.attachments(shortcode);
- selection = new wp.media.model.Selection(attachments.models, {
- props: attachments.props.toJSON(),
- multiple: true
- });
- selection.playlist = attachments.playlist;
- // Fetch the query's attachments, and then break ties from the
- // query to allow for sorting.
- selection.more().done(function() {
- // Break ties with the query.
- selection.props.set({ query: false });
- selection.unmirror();
- selection.props.unset('orderby');
- });
- // Destroy the previous frame.
- if(FLBuilder._multipleAudiosSelector) {
- FLBuilder._multipleAudiosSelector.dispose();
- }
- // Store the current frame.
- FLBuilder._multipleAudiosSelector = wp.media({
- frame: 'post',
- state: $(this).hasClass('fl-multiple-audios-edit') ? 'playlist-edit' : 'playlist-library',
- title: wp.media.view.l10n.editPlaylistTitle,
- editing: true,
- multiple: true,
- selection: selection
- }).open();
- // Hide the default playlist settings since we have them added in the audio settings
- FLBuilder._multipleAudiosSelector.content.get('view').sidebar.unset('playlist');
- FLBuilder._multipleAudiosSelector.on( 'content:render:browse', function( browser ) {
- if ( !browser ) return;
- // Hide Playlist Settings in sidebar
- browser.sidebar.on('ready', function(){
- browser.sidebar.unset('playlist');
- });
- });
- FLBuilder._multipleAudiosSelector.once('update', $.proxy(FLBuilder._multipleAudiosSelected, this));
- },
- /**
- * Callback for when a single/multiple audo is selected.
- *
- * @since 1.0
- * @access private
- * @method _multipleAudiosSelected
- */
- _multipleAudiosSelected: function(data)
- {
- var wrap = $(this).closest('.fl-multiple-audios-field'),
- count = wrap.find('.fl-multiple-audios-count'),
- audioField = wrap.find('input[type=hidden]'),
- audios = [],
- i = 0;
- for( ; i < data.models.length; i++) {
- audios.push(data.models[i].id);
- }
- if(audios.length == 1) {
- count.html('1 ' + FLBuilderStrings.audioSelected);
- }
- else {
- count.html(audios.length + ' ' + FLBuilderStrings.audiosSelected);
- }
- audioField.val(JSON.stringify(audios)).trigger('change');
- wrap.removeClass('fl-multiple-audios-empty');
- wrap.find('label.error').remove();
- },
- /* Icon Fields
- ----------------------------------------------------------*/
- /**
- * Initializes icon fields for a settings form.
- *
- * @since 2.2
- * @access private
- * @method _initIconFields
- */
- _initIconFields: function()
- {
- var inputs = $( '.fl-builder-settings:visible', window.parent.document )
- .find( '.fl-icon-field input' );
- inputs.on( 'change', FLBuilder._toggleSettingsOnIconChange );
- inputs.trigger( 'change' );
- },
- /**
- * Callback for when an icon field changes. If the field
- * isn't empty the specified elements (if any) will be shown.
- *
- * @since 2.2
- * @access private
- * @method _toggleSettingsOnIconChange
- */
- _toggleSettingsOnIconChange: function()
- {
- var input = $( this ),
- val = input.val(),
- show = input.attr( 'data-show' ),
- i = 0;
- if ( typeof show === 'undefined' ) {
- return;
- }
- show = FLBuilder._jsonParse( show );
- FLBuilder._settingsSelectToggle( show.fields, 'hide', '#fl-field-' );
- FLBuilder._settingsSelectToggle( show.sections, 'hide', '#fl-builder-settings-section-' );
- FLBuilder._settingsSelectToggle( show.tabs, 'hide', 'a[href*=fl-builder-settings-tab-', ']' );
- if ( val ) {
- FLBuilder._settingsSelectToggle( show.fields, 'show', '#fl-field-' );
- FLBuilder._settingsSelectToggle( show.sections, 'show', '#fl-builder-settings-section-' );
- FLBuilder._settingsSelectToggle( show.tabs, 'show', 'a[href*=fl-builder-settings-tab-', ']' );
- FLBuilder._calculateSettingsTabsOverflow();
- }
- },
- /**
- * Shows the icon selector.
- *
- * @since 1.0
- * @access private
- * @method _selectIcon
- */
- _selectIcon: function()
- {
- var self = this;
- FLIconSelector.open(function(icon){
- FLBuilder._iconSelected.apply(self, [icon]);
- });
- },
- /**
- * Callback for when an icon is selected.
- *
- * @since 1.0
- * @access private
- * @method _iconSelected
- * @param {String} icon The selected icon's CSS classname.
- */
- _iconSelected: function(icon)
- {
- var wrap = $(this).closest('.fl-icon-field'),
- iconField = wrap.find('input[type=hidden]'),
- iconTag = wrap.find('i'),
- oldIcon = iconTag.attr('data-icon');
- iconField.val(icon).trigger('change');
- iconTag.removeClass(oldIcon);
- iconTag.addClass(icon);
- iconTag.attr('data-icon', icon);
- wrap.removeClass('fl-icon-empty');
- wrap.find('label.error').remove();
- },
- /**
- * Callback for when a selected icon is removed.
- *
- * @since 1.0
- * @access private
- * @method _removeIcon
- */
- _removeIcon: function()
- {
- var wrap = $(this).closest('.fl-icon-field'),
- iconField = wrap.find('input[type=hidden]'),
- iconTag = wrap.find('i');
- iconField.val('').trigger('change');
- iconTag.removeClass();
- iconTag.attr('data-icon', '');
- wrap.addClass('fl-icon-empty');
- },
- /* Settings Form Fields
- ----------------------------------------------------------*/
- /**
- * Shows the settings for a nested form field when the
- * edit link is clicked.
- *
- * @since 1.0
- * @access private
- * @method _formFieldClicked
- */
- _formFieldClicked: function()
- {
- var link = $( this ),
- form = link.closest( '.fl-builder-settings' ),
- type = link.attr( 'data-type' ),
- settings = link.siblings( 'input' ).val(),
- helper = FLBuilder._moduleHelpers[ type ],
- config = FLBuilderSettingsConfig.forms[ type ],
- lightbox = FLBuilder._openNestedSettings( { className: 'fl-builder-lightbox fl-form-field-settings' } );
- if ( '' === settings ) {
- settings = JSON.stringify( FLBuilderSettingsConfig.forms[ type ] );
- }
- // Trigger refresh preview
- FLBuilder.preview?.preview()
- FLBuilderSettingsForms.render( {
- id : type,
- nodeId : form.attr( 'data-node' ),
- nodeSettings : FLBuilder._getSettings( form ),
- settings : FLBuilder._jsonParse( settings.replace( /'/g, "'" ) ),
- lightbox : lightbox,
- rules : helper ? helper.rules : null,
- helper : {
- init: function () {
- if ( helper ) {
- helper.init();
- }
- FLBuilder._initFormFieldSettingsPreview( lightbox );
- }
- },
- }, function() {
- link.attr( 'id', 'fl-' + lightbox._node.attr( 'data-instance-id' ) );
- lightbox._node.find( 'form.fl-builder-settings' ).attr( 'data-type', type );
- FLBuilderResponsiveEditing._switchAllSettingsToCurrentMode();
- } );
- },
- /**
- * Saves the settings for a nested form field when the
- * save button is clicked.
- *
- * @since 1.0
- * @access private
- * @method _saveFormFieldClicked
- * @return {Boolean} Whether the save was successful or not.
- */
- _saveFormFieldClicked: function()
- {
- var form = $( this ).closest( '.fl-builder-settings' );
- var saved = FLBuilder._saveFormFieldSettings( form );
- if ( saved ) {
- FLBuilder._closeNestedSettings();
- } else {
- FLBuilder._toggleSettingsTabErrors();
- }
- },
- /**
- * Initialize settings preview for a form field.
- *
- * @since 2.5
- * @access private
- * @method _initFormFieldSettingsPreview
- * @param {Object} lightbox
- */
- _initFormFieldSettingsPreview: function( lightbox )
- {
- var fields = lightbox._node.find( '.fl-field' );
- var editors = lightbox._node.find( 'textarea.wp-editor-area' );
- fields.find( 'input:not([type=hidden]), textarea' ).on( 'input', FLBuilder._previewFormFieldSettings );
- fields.find( 'input[type=hidden], select' ).on( 'change', FLBuilder._previewFormFieldSettings );
- shapename = fields.find('input[name=shape_name]');
- shapeorig = fields.find('input[name=shape_original]');
- if ( shapename.length > 0 ) {
- shapeorig.hide();
- shapename.on( 'keyup', function() {
- FLBuilder._shapesEdited = true;
- });
- if ( shapename.val().length > 0 && shapeorig.val().length < 1 ) {
- shapeorig.val( shapename.val() );
- }
- }
- if ( 'undefined' !== typeof window.parent.tinyMCE ) {
- editors.each( function ( i, editor ) {
- editor = window.parent.tinyMCE.get( $( editor ).attr( 'id' ) );
- editor.on( 'change', FLBuilder._previewFormFieldSettings );
- editor.on( 'keyup', FLBuilder._previewFormFieldSettings );
- } );
- }
- },
- /**
- * Previews the settings for a nested form field when
- * a setting is changed.
- *
- * @since 2.5
- * @access private
- * @method _previewFormFieldSettings
- */
- _previewFormFieldSettings: function()
- {
- var ele = this.formElement ? this.formElement : this;
- var form = $( ele ).closest( '.fl-builder-settings' );
- var timeout = form.data( 'timeout' );
- if ( timeout ) {
- clearTimeout( timeout );
- }
- timeout = setTimeout( function () {
- FLBuilder._saveFormFieldSettings( form );
- }, 1000 );
- form.data( 'timeout', timeout );
- },
- /**
- * Saves the settings for a nested form field.
- *
- * @since 2.5
- * @access private
- * @method _saveFormFieldSettings
- * @param {Object} form
- * @return {Boolean} Whether the save was successful or not.
- */
- _saveFormFieldSettings: function( form )
- {
- var lightboxId = form.closest( '.fl-lightbox-wrap' ).attr( 'data-instance-id' ),
- type = form.attr( 'data-type' ),
- settings = FLBuilder._getSettings( form ),
- oldSettings = {},
- helper = FLBuilder._moduleHelpers[ type ],
- link = $( '.fl-builder-settings #fl-' + lightboxId, window.parent.document ),
- preview = link.parent().attr( 'data-preview-text' ),
- previewOptions = [],
- activePreview = '',
- previewField = null,
- previewText = '',
- selectPreview = null,
- tmp = document.createElement( 'div' ),
- valid = true;
- if ( preview ) {
- previewOptions = preview.split(',');
- }
- // Handle 'preview_text' fields
- for ( var i = 0; i < previewOptions.length; i++ ) {
- if ( settings[ previewOptions[i] ] ) {
- activePreview = previewOptions[i];
- previewText = settings[ activePreview ];
- previewField = form.find( '#fl-field-' + activePreview );
- selectPreview = $( 'select[name="' + activePreview + '"]', window.parent.document )
- break;
- }
- }
- if ( selectPreview && selectPreview.length > 0 ) {
- previewText = selectPreview.find( 'option[value="' + previewText + '"]' ).text();
- }
- if ( typeof helper !== 'undefined' ) {
- form.find('label.error').remove();
- form.validate().hideErrors();
- valid = form.validate().form();
- if ( valid ) {
- valid = helper.submit();
- }
- }
- if ( valid ) {
- if ( typeof preview !== 'undefined' && typeof previewText !== 'undefined' ) {
- if ( 'icon' === previewField?.data( 'type' ) ) {
- previewText = '<i class="' + previewText + '"></i>';
- }
- else if ( previewText.length > 35 ) {
- tmp.innerHTML = previewText;
- previewText = ( tmp.textContent || tmp.innerText || '' ).replace( /^(.{35}[^\s]*).*/, "$1" ) + '...';
- }
- if( 'filter_meta_label' == preview && ! previewText ) {
- previewText = settings[ 'filter_meta_key' ];
- }
- link.siblings( '.fl-form-field-preview-text' ).html( previewText );
- }
- if ( link.length > 0 ) {
- oldSettings = link.siblings( 'input' ).val().replace( /'/g, "'" );
- if ( '' != oldSettings ) {
- settings = $.extend( FLBuilder._jsonParse( oldSettings ), settings );
- }
- link.siblings( 'input' ).val( JSON.stringify( settings ) ).trigger( 'change' );
- }
- return true;
- }
- return false;
- },
- /* Layout Fields
- ----------------------------------------------------------*/
- /**
- * Callback for when the item of a layout field is clicked.
- *
- * @since 1.0
- * @access private
- * @method _layoutFieldClicked
- */
- _layoutFieldClicked: function()
- {
- var option = $(this);
- option.siblings().removeClass('fl-layout-field-option-selected');
- option.addClass('fl-layout-field-option-selected');
- option.siblings('input').val(option.attr('data-value'));
- },
- /* Link Fields
- ----------------------------------------------------------*/
- /**
- * Initializes all link fields in a settings form.
- *
- * @since 1.3.9
- * @access private
- * @method _initLinkFields
- */
- _initLinkFields: function()
- {
- $('.fl-builder-settings:visible .fl-link-field', window.parent.document)
- .each(FLBuilder._initLinkField);
- },
- /**
- * Initializes a single link field in a settings form.
- *
- * @since 1.3.9
- * @access private
- * @method _initLinkFields
- */
- _initLinkField: function()
- {
- var wrap = $(this),
- searchInput = wrap.find('.fl-link-field-search-input'),
- checkboxes = wrap.find( '.fl-link-field-options-wrap input[type=checkbox]' );
- searchInput.autoSuggest(FLBuilder._ajaxUrl({
- 'fl_action' : 'fl_builder_autosuggest',
- 'fl_as_action' : 'fl_as_links',
- '_wpnonce' : FLBuilderConfig.ajaxNonce
- }), {
- asHtmlID : searchInput.attr('name'),
- selectedItemProp : 'name',
- searchObjProps : 'name',
- minChars : 3,
- keyDelay : 1000,
- fadeOut : false,
- usePlaceholder : true,
- emptyText : FLBuilderStrings.noResultsFound,
- showResultListWhenNoMatch : true,
- queryParam : 'fl_as_query',
- selectionLimit : 1,
- afterSelectionAdd : FLBuilder._updateLinkField,
- formatList: function(data, elem){
- var new_elem = elem.html(data.name + '<span class="type">[' + data.type + ']</span>');
- return new_elem;
- }
- });
- checkboxes.on( 'click', FLBuilder._linkFieldCheckboxClicked );
- },
- /**
- * Updates the value of a link field when a link has been
- * selected from the auto suggest menu.
- *
- * @since 1.3.9
- * @access private
- * @method _updateLinkField
- * @param {Object} element The auto suggest field.
- * @param {Object} item The current selection.
- * @param {Array} selections An array of selected values.
- */
- _updateLinkField: function(element, item, selections)
- {
- var wrap = element.closest('.fl-link-field'),
- search = wrap.find('.fl-link-field-search'),
- searchInput = wrap.find('.fl-link-field-search-input'),
- field = wrap.find('.fl-link-field-input');
- field.val(item.value).trigger('keyup');
- searchInput.autoSuggest('remove', item.value);
- search.hide();
- },
- /**
- * Shows the auto suggest input for a link field.
- *
- * @since 1.3.9
- * @access private
- * @method _linkFieldSelectClicked
- */
- _linkFieldSelectClicked: function()
- {
- var $el = $(this).closest('.fl-link-field').find('.fl-link-field-search');
- $el.show();
- $el.find('input').focus();
- },
- /**
- * Hides the auto suggest input for a link field.
- *
- * @since 1.3.9
- * @access private
- * @method _linkFieldSelectCancelClicked
- */
- _linkFieldSelectCancelClicked: function()
- {
- var $button = $(this);
- $button.parent().hide();
- $button.closest('.fl-link-field').find('input.fl-link-field-input').focus();
- },
- /**
- * Handles when a link field checkbox option is clicked.
- *
- * @since 2.2
- * @access private
- * @method _linkFieldCheckboxClicked
- */
- _linkFieldCheckboxClicked: function()
- {
- var checkbox = $( this ),
- checked = checkbox.is( ':checked' ),
- input = checkbox.siblings( 'input[type=hidden]' ),
- value = '';
- if ( checkbox.hasClass( 'fl-link-field-target-cb' ) ) {
- value = checked ? '_blank' : '_self';
- } else {
- value = checked ? 'yes' : 'no';
- }
- input.val( value );
- },
- /* Font Fields
- ----------------------------------------------------------*/
- /**
- * Initializes all font fields in a settings form.
- *
- * @since 1.6.3
- * @access private
- * @method _initFontFields
- */
- _initFontFields: function(){
- $('.fl-builder-settings:visible .fl-font-field', window.parent.document)
- .each( FLBuilder._initFontField );
- },
- /**
- * Initializes a single font field in a settings form.
- *
- * @since 1.6.3
- * @access private
- * @method _initFontFields
- */
- _initFontField: function(){
- var wrap = $( this ),
- value = wrap.attr( 'data-value' ),
- font = wrap.find( '.fl-font-field-font' ),
- weight = wrap.find( '.fl-font-field-weight' );
- if ( FLBuilderConfig.select2Enabled ) {
- font.select2({width:'100%'})
- .on('select2:open', function(e){
- $('.select2-search__field').attr('placeholder', FLBuilderStrings.placeholderSelect2);
- })
- }
- $('body', window.parent.document).on({
- mouseenter: function () {
- var highlighted_item = jQuery(this).text(),
- group = jQuery(this).parent().parent().attr("aria-label"),
- head = jQuery("head", window.parent.document),
- url = 'https://fonts.googleapis.com/css?family=' + highlighted_item,
- link_id = highlighted_item.toLowerCase().replaceAll( ' ', '-' ),
- parent_id = $(this).parent().parent().parent().attr('id');
- if ( ! parent_id ) {
- return false;
- }
- if( parent_id.indexOf('typographyfont_family') < 0 ) {
- return false;
- }
- if ( 'Google' === group ) {
- linkElement = "<link id='" + link_id + "' rel='stylesheet' href='" + url + "' type='text/css' media='screen'>";
- jQuery(this).css('font-family', '"' + highlighted_item + '"' );
- if ( jQuery( '#' + link_id ).length > 0 ) {
- return false;
- }
- head.append(linkElement);
- }
- }
- }, '.select2-results__option.select2-results__option--highlighted');
- font.on( 'change', function(){
- FLBuilder._getFontWeights( font );
- } );
- if ( value.indexOf( 'family' ) > -1 ) {
- var value = FLBuilder._jsonParse( value );
- var valid = false;
- fonts = FLBuilderFontFamilies;
- Object.keys(fonts.system).forEach(function(name){
- if ( name === value.family ) {
- valid = true
- }
- });
- Object.keys(fonts.google).forEach(function(name){
- if ( name === value.family ) {
- valid = true
- }
- });
- if ( ! valid && 'Default' !== value.family ) {
- value = {
- 'family' : 'Default',
- 'weight' : '400'
- };
- }
- font.val( value.family );
- font.trigger( 'change' );
- if ( weight.find( 'option[value=' + value.weight + ']' ).length ) {
- weight.val( value.weight );
- }
- }
- },
- /**
- * Renders the correct weights list for a respective font.
- *
- * @since 1.6.3
- * @acces private
- * @method _getFontWeights
- * @param {Object} currentFont The font field element.
- */
- _getFontWeights: function( currentFont ){
- var selectWeight = currentFont.closest( '.fl-font-field' ).find( '.fl-font-field-weight' ),
- font = currentFont.val(),
- weight = selectWeight.val(),
- weightMap = FLBuilderConfig.FontWeights,
- weights = {},
- recentList = currentFont.closest( '.fl-font-field' ).find( '.recent-fonts option' )
- selectWeight.html( '' );
- if( recentList.length > 0 ) {
- var exists = $(recentList)
- .filter(function (i, o) { return o.value === font; })
- .length > 0;
- if ( false === exists && 'Default' !== font ) {
- currentFont.closest( '.fl-font-field' ).find( '.recent-fonts' ).append( $('<option>', {
- value: font,
- text: font
- }));
- }
- }
- if ( 'undefined' != typeof FLBuilderFontFamilies.system[ font ] ) {
- weights = FLBuilderFontFamilies.system[ font ].weights;
- } else if ( 'undefined' != typeof FLBuilderFontFamilies.google[ font ] ) {
- weights = FLBuilderFontFamilies.google[ font ];
- } else {
- weights = FLBuilderFontFamilies.default[ font ];
- }
- $.each( weights, function( key, value ){
- var selected = weight === value ? ' selected' : '';
- selectWeight.append( '<option value="' + value + '"' + selected + '>' + weightMap[ value ] + '</option>' );
- } );
- },
- /* Editor Fields
- ----------------------------------------------------------*/
- /**
- * InitializeS TinyMCE when the builder is first loaded.
- *
- * @since 2.0
- * @access private
- * @method _initEditorFields
- */
- _initTinyMCE: function()
- {
- if ( typeof tinymce === 'object' && typeof tinymce.ui.FloatPanel !== 'undefined' ) {
- tinymce.ui.FloatPanel.zIndex = 100100; // Fix zIndex issue in wp 4.8.1
- }
- $( '.fl-builder-hidden-editor', window.parent.document ).each( function() {
- FLBuilder._initEditorField.call( this, window.parent );
- } );
- if ( FLBuilder.UIIFrame.isEnabled() ) {
- $( '.fl-builder-hidden-editor' ).each( function() {
- FLBuilder._initEditorField.call( this, window );
- } );
- }
- if ( 'undefined' !== typeof window.parent.acf ) {
- window.parent.acf.add_filter( 'wysiwyg_tinymce_settings', function( mceInit, id ) {
- return $.extend( {}, tinyMCEPreInit.mceInit.flbuildereditor, mceInit );
- } )
- }
- },
- /**
- * Initialize all TinyMCE editor fields.
- *
- * @since 1.10
- * @access private
- * @method _initEditorFields
- */
- _initEditorFields: function()
- {
- $( '.fl-builder-settings:visible .fl-editor-field', window.parent.document )
- .each( function() {
- FLBuilder._initEditorField.call( this, window.parent );
- } );
- },
- /**
- * Initialize a single TinyMCE editor field.
- *
- * @since 2.0
- * @method _initEditorField
- * @param {Object} The window object to init on.
- */
- _initEditorField: function( win )
- {
- var field = $( this ),
- textarea = field.find( 'textarea' ),
- name = field.attr( 'data-name' ),
- editorId = 'flrich' + new Date().getTime() + '_' + name,
- html = FLBuilderConfig.wp_editor,
- config = win.tinyMCEPreInit,
- buttons = Number( field.attr( 'data-buttons' ) ),
- rows = field.attr( 'data-rows' ),
- init = null,
- wrap = null;
- html = html.replace( /flbuildereditor/g , editorId );
- config = FLBuilder._jsonParse( JSON.stringify( config ).replace( /flbuildereditor/g , editorId ) );
- config = JSONfn.parse( JSONfn.stringify( config ).replace( /flbuildereditor/g , editorId ) );
- textarea.after( html ).remove();
- $( 'textarea#' + editorId, win.document ).val( textarea.val() )
- if ( undefined !== typeof win.tinymce && undefined !== config.mceInit[ editorId ] ) {
- init = config.mceInit[ editorId ];
- init.setup = function (editor) {
- editor.on('SaveContent', function (e) {
- e.content = e.content.replace(/<a href="(\.\.\/){1,2}/g, '<a href="' + FLBuilderConfig.homeUrl + '/' );
- e.content = e.content.replace(/src="(\.\.\/){1,2}/g, 'src="' + FLBuilderConfig.homeUrl + '/' );
- });
- }
- wrap = $( '#wp-' + editorId + '-wrap', win.document );
- wrap.find( 'textarea' ).attr( 'rows', rows );
- wrap.find( 'textarea' ).attr( 'contentEditable', true );
- if ( ! buttons ) {
- wrap.find( '.wp-media-buttons' ).remove();
- }
- if ( ( wrap.hasClass( 'tmce-active' ) || ! config.qtInit.hasOwnProperty( editorId ) ) && ! init.wp_skip_init ) {
- win.tinymce.init( init );
- }
- }
- if ( undefined !== typeof win.quicktags ) {
- win.quicktags( config.qtInit[ editorId ] );
- }
- win.wpActiveEditor = editorId;
- },
- /**
- * Reinitialize all TinyMCE editor fields.
- *
- * @since 2.0
- * @access private
- * @method _reinitEditorFields
- */
- _reinitEditorFields: function()
- {
- if ( ! $( '.fl-lightbox-resizable:visible', window.parent.document ).length ) {
- return;
- }
- // Do this on a timeout so TinyMCE doesn't hold up other operations.
- setTimeout( function() {
- var i, id;
- if ( 'undefined' === typeof window.parent.tinymce ) {
- return;
- }
- for ( i = window.parent.tinymce.editors.length - 1; i > -1 ; i-- ) {
- if ( ! window.parent.tinymce.editors[ i ].inline ) {
- id = window.parent.tinymce.editors[ i ].id;
- window.parent.tinyMCE.execCommand( 'mceRemoveEditor', true, id );
- window.parent.tinyMCE.execCommand( 'mceAddEditor', true, id );
- }
- }
- if ( FLBuilder.preview ) {
- var fields = $( '.fl-field[data-type="editor"]', window.parent.document );
- FLBuilder.preview._initDefaultFieldPreviews( fields );
- }
- }, 1 );
- },
- /**
- * Destroy all TinyMCE editors.
- *
- * @since 1.10.8
- * @method _destroyEditorFields
- */
- _destroyEditorFields: function()
- {
- var i, id;
- if ( 'undefined' === typeof window.parent.tinymce ) {
- return;
- }
- for ( i = window.parent.tinymce.editors.length - 1; i > -1 ; i-- ) {
- if ( ! window.parent.tinymce.editors[ i ].inline ) {
- window.parent.tinyMCE.execCommand( 'mceRemoveEditor', true, window.parent.tinymce.editors[ i ].id );
- }
- }
- $( '.wplink-autocomplete', window.parent.document ).remove();
- $( '.ui-helper-hidden-accessible', window.parent.document ).remove();
- },
- /**
- * Updates all editor fields within a settings form.
- *
- * @since 1.0
- * @access private
- * @method _updateEditorFields
- */
- _updateEditorFields: function()
- {
- var wpEditors = $('.fl-builder-settings:visible textarea.wp-editor-area', window.parent.document);
- wpEditors.each(FLBuilder._updateEditorField);
- },
- /**
- * Updates a single editor field within a settings form.
- * Creates a hidden textarea with the editor content so
- * this field can be saved.
- *
- * @since 1.0
- * @access private
- * @method _updateEditorField
- */
- _updateEditorField: function()
- {
- var textarea = $( this ),
- field = textarea.closest( '.fl-editor-field' ),
- form = textarea.closest( '.fl-builder-settings' ),
- wrap = textarea.closest( '.wp-editor-wrap' ),
- id = textarea.attr( 'id' ),
- setting = field.attr( 'data-name' ),
- editor = typeof window.parent.tinymce == 'undefined' ? false : window.parent.tinymce.get( id ),
- hidden = textarea.siblings( 'textarea[name="' + setting + '"]' ),
- wpautop = field.data( 'wpautop' );
- // Add a hidden textarea if we don't have one.
- if ( 0 === hidden.length ) {
- hidden = $( '<textarea name="' + setting + '"></textarea>', window.parent.document ).hide();
- textarea.after( hidden );
- }
- // Save editor content.
- if ( wpautop ) {
- if ( editor && wrap.hasClass( 'tmce-active' ) ) {
- hidden.val( editor.getContent() );
- }
- else if ( 'undefined' != typeof window.parent.switchEditors ) {
- hidden.val( window.parent.switchEditors.wpautop( textarea.val() ) );
- }
- else {
- hidden.val( textarea.val() );
- }
- }
- else {
- if ( editor && wrap.hasClass( 'tmce-active' ) ) {
- editor.save();
- }
- hidden.val( textarea.val() );
- }
- },
- /* Loop Settings Fields
- ----------------------------------------------------------*/
- /**
- * Initializes all post type fields in a settings form.
- *
- * @since 2.6
- * @access private
- * @method _initPostTypeFields
- */
- _initPostTypeFields: function(){
- $('.fl-builder-settings:visible #fl-field-post_type').each( FLBuilder._initPostTypeField );
- },
- /**
- * @since 2.6
- */
- _initPostTypeField: function() {
- var wrap = $( this ),
- postType = wrap.find('select');
- if ( FLBuilderConfig.select2Enabled ) {
- postType.select2({width:'50%'});
- }
- },
- /**
- * Callback for the data source of loop settings changes.
- *
- * @since 1.10
- * @access private
- * @method _loopDataSourceChange
- */
- _loopDataSourceChange: function()
- {
- var val = $( this ).val();
- $('.fl-loop-data-source', window.parent.document).hide();
- $('.fl-loop-data-source[data-source="' + val + '"]', window.parent.document).show();
- },
- /**
- * Callback for when the post types of a custom query changes.
- *
- * @since 2.6.2
- * @access private
- * @method _customQueryPostTypesChange
- */
- _customQueryPostTypesChange: function()
- {
- var postTypes = $(this).val();
- $('.fl-custom-query-filter').hide();
- for ( val of postTypes ) {
- $('.fl-custom-query-' + val + '-filter').show();
- }
- },
- /**
- * Callback for when the post type of a custom query changes.
- *
- * @since 1.2.3
- * @access private
- * @method _customQueryPostTypeChange
- */
- _customQueryPostTypeChange: function()
- {
- var val = $(this).val();
- $('.fl-custom-query-filter', window.parent.document).hide();
- $('.fl-custom-query-' + val + '-filter', window.parent.document).show();
- },
- /* Ordering Fields
- ----------------------------------------------------------*/
- /**
- * Initializes all ordering fields in a settings form.
- *
- * @since 1.10
- * @access private
- * @method _initOrderingFields
- */
- _initOrderingFields: function()
- {
- $( '.fl-builder-settings:visible .fl-ordering-field-options', window.parent.document )
- .each( FLBuilder._initOrderingField );
- },
- /**
- * Initializes a single ordering field in a settings form.
- *
- * @since 1.10
- * @access private
- * @method _initOrderingField
- */
- _initOrderingField: function()
- {
- $( this ).sortable( {
- items: '.fl-ordering-field-option',
- containment: 'parent',
- tolerance: 'pointer',
- stop: FLBuilder._updateOrderingField
- } );
- },
- /**
- * Updates an ordering field when dragging stops.
- *
- * @since 1.10
- * @access private
- * @method _updateOrderingField
- * @param {Object} e The event object.
- */
- _updateOrderingField: function( e )
- {
- var options = $( e.target ),
- input = options.siblings( 'input[type=hidden]' ),
- value = [];
- options.find( '.fl-ordering-field-option' ).each( function() {
- value.push( $( this ).attr( 'data-key' ) );
- } );
- input.val( JSON.stringify( value ) ).trigger( 'change' );
- },
- /* Text Fields - Add Predefined Value Selector
- ----------------------------------------------------------*/
- /**
- * Callback for when "add value" selectors for text fields changes.
- *
- * @since 1.6.5
- * @access private
- * @method _textFieldAddValueSelectChange
- */
- _textFieldAddValueSelectChange: function()
- {
- var dropdown = $( this ),
- textField = $( 'input[name="' + dropdown.data( 'target' ) + '"]', window.parent.document ),
- currentValue = textField.val(),
- addingValue = dropdown.val(),
- newValue = '';
- // Adding selected value to target text field only once
- if ( -1 == currentValue.indexOf( addingValue ) ) {
- newValue = ( currentValue.trim() + ' ' + addingValue.trim() ).trim();
- textField
- .val( newValue )
- .trigger( 'change' )
- .trigger( 'keyup' );
- }
- // Resetting the selector
- dropdown.val( '' );
- },
- /* Number Fields
- ----------------------------------------------------------*/
- /**
- * @since 2.0
- * @access private
- * @method _onNumberFieldFocus
- */
- _onNumberFieldFocus: function(e) {
- var $input = $(e.currentTarget);
- $input.addClass('mousetrap');
- Mousetrap.bind('up', function() {
- $input.attr('step', 1);
- });
- Mousetrap.bind('down', function() {
- $input.attr('step', 1);
- });
- Mousetrap.bind('shift+up', function() {
- $input.attr('step', 10);
- });
- Mousetrap.bind('shift+down', function() {
- $input.attr('step', 10);
- });
- },
- /**
- * @since 2.0
- * @access private
- * @method _onNumberFieldBlur
- */
- _onNumberFieldBlur: function(e) {
- var $input = $(e.currentTarget);
- $input.attr('step', 'any').removeClass('mousetrap');
- },
- /* Timezone Fields
- ----------------------------------------------------------*/
- /**
- * @since 2.0
- * @access private
- * @method _initTimezoneFields
- */
- _initTimezoneFields: function() {
- $( '.fl-builder-settings:visible .fl-field[data-type=timezone]', window.parent.document )
- .each( FLBuilder._initTimezoneField );
- },
- /**
- * @since 2.0
- * @access private
- * @method _initTimezoneField
- */
- _initTimezoneField: function() {
- var select = $( this ).find( 'select' ),
- value = select.attr( 'data-value' );
- select.find( 'option[value="' + value + '"]' ).prop('selected', true);
- },
- /* Dimension Fields
- ----------------------------------------------------------*/
- /**
- * Initializes all dimension fields in a form.
- *
- * @since 2.2
- * @access private
- * @method _initDimensionFields
- */
- _initDimensionFields: function() {
- var form = $( '.fl-builder-settings:visible', window.parent.document );
- form.find( '.fl-field[data-type=dimension]' ).each( FLBuilder._initDimensionField );
- form.find( '.fl-dimension-field-link' ).on( 'click', FLBuilder._dimensionFieldLinkClicked );
- FLBuilder.addHook( 'responsive-editing-switched', this._initResponsiveDimensionFieldLinking );
- form.find( '.fl-compound-field-setting' ).has( '.fl-dimension-field-link' ).each( FLBuilder._initDimensionFieldLinking );
- },
- /**
- * Initializes a single dimension field.
- *
- * @since 2.2
- * @access private
- * @method _initDimensionField
- */
- _initDimensionField: function() {
- var field = $( this ),
- label = field.find( '.fl-field-label label' ),
- wrap = field.find( '.fl-field-control-wrapper' ),
- icon = '<i class="fl-dimension-field-link fl-tip dashicons dashicons-admin-links" title="Link Values"></i>';
- label.append( icon );
- wrap.prepend( icon );
- FLBuilder._initTipTips();
- FLBuilder._initDimensionFieldLinking.apply( this );
- },
- /**
- * Initializes input linking for a dimension field by
- * linking inputs if they all have the same value.
- *
- * @since 2.2
- * @access private
- * @method _initDimensionFieldLinking
- */
- _initDimensionFieldLinking: function() {
- var field = $( this ),
- icon = field.find( '.fl-dimension-field-link' ),
- inputs = FLBuilder._getDimensionFieldLinkingInputs( field ),
- equal = FLBuilder._dimensionFieldInputsAreEqual( inputs );
- if ( equal ) {
- icon.removeClass( 'dashicons-admin-links' );
- icon.addClass( 'dashicons-editor-unlink' );
- inputs.off( 'input', FLBuilder._dimensionFieldLinkedValueChange );
- inputs.on( 'input', FLBuilder._dimensionFieldLinkedValueChange );
- } else {
- icon.addClass( 'dashicons-admin-links' );
- icon.removeClass( 'dashicons-editor-unlink' );
- }
- },
- /**
- * Initializes input linking for responsive dimension fields
- * when the responsive mode is switched.
- *
- * @since 2.2
- * @access private
- * @method _initDimensionFieldLinking
- */
- _initResponsiveDimensionFieldLinking: function() {
- var form = $( '.fl-builder-settings:visible', window.parent.document );
- form.find( '.fl-field[data-type=dimension]' ).each( FLBuilder._initDimensionFieldLinking );
- },
- /**
- * Handles logic for when dimension fields are linked
- * or unlinked from each other.
- *
- * @since 2.2
- * @access private
- * @method _dimensionFieldLinkClicked
- */
- _dimensionFieldLinkClicked: function() {
- var target = $( this ),
- compound = target.closest( '.fl-compound-field-setting' ),
- field = compound.length ? compound : target.closest( '.fl-field' ),
- icon = field.find( '.fl-dimension-field-link' ),
- linked = icon.hasClass( 'dashicons-editor-unlink' ),
- inputs = FLBuilder._getDimensionFieldLinkingInputs( field );
- icon.toggleClass( 'dashicons-admin-links' );
- icon.toggleClass( 'dashicons-editor-unlink' );
- if ( linked ) {
- inputs.off( 'input', FLBuilder._dimensionFieldLinkedValueChange );
- } else {
- inputs.val( inputs.eq( 0 ).val() ).trigger( 'input' );
- inputs.on( 'input', FLBuilder._dimensionFieldLinkedValueChange );
- }
- },
- /**
- * Updates dimension inputs when a linked input changes.
- *
- * @since 2.2
- * @access private
- * @method _dimensionFieldLinkedValueChange
- */
- _dimensionFieldLinkedValueChange: function() {
- var input = $( this ),
- name = input.attr( 'name' ),
- wrap = input.closest( '.fl-dimension-field-units' ),
- inputs = wrap.find( 'input:not([name="' + name + '"])' );
- inputs.off( 'input', FLBuilder._dimensionFieldLinkedValueChange );
- inputs.val( input.val() ).trigger( 'input' );
- inputs.on( 'input', FLBuilder._dimensionFieldLinkedValueChange );
- },
- /**
- * Returns the inputs for dimension field linking. If this field
- * is responsive, then only returns inputs for the current mode.
- *
- * @since 2.2
- * @access private
- * @method _getDimensionFieldLinkingInputs
- * @param {Object} field
- * @return {Object}
- */
- _getDimensionFieldLinkingInputs: function( field ) {
- var responsive = field.find( '.fl-field-responsive-setting' ).length ? true : false,
- mode = FLBuilderResponsiveEditing._mode,
- inputs = null;
- if ( responsive ) {
- inputs = field.find( '.fl-field-responsive-setting-' + mode + ' input' );
- } else {
- inputs = field.find( '.fl-dimension-field-unit input' );
- }
- return inputs;
- },
- /**
- * Checks to see if all inputs for a dimension field have
- * the same value or not.
- *
- * @since 2.2
- * @access private
- * @method _dimensionFieldInputsAreEqual
- * @param {Object} inputs
- * @return {Boolean}
- */
- _dimensionFieldInputsAreEqual: function( inputs ) {
- var first = inputs.eq( 0 ).val();
- if ( '' === first ) {
- return false;
- }
- for ( var i = 1; i < 4; i++ ) {
- if ( inputs.eq( i ).val() !== first ) {
- return false;
- }
- }
- return true;
- },
- /* Field Popup Sliders
- ----------------------------------------------------------*/
- /**
- * Initializes unit and dimension field popup slider controls.
- *
- * @since 2.2
- * @access private
- * @method _initFieldPopupSliders
- */
- _initFieldPopupSliders: function() {
- var form = $( '.fl-builder-settings:visible', window.parent.document ),
- sliders = form.find( '.fl-field-popup-slider' );
- sliders.each( FLBuilder._initFieldPopupSlider );
- },
- /**
- * Initializes a single popup slider control.
- *
- * @since 2.2
- * @access private
- * @method _initFieldPopupSlider
- */
- _initFieldPopupSlider: function() {
- var body = $( 'body', window.parent.document ),
- wrapper = $( this ),
- slider = wrapper.find( '.fl-field-popup-slider-input' ),
- arrow = wrapper.find( '.fl-field-popup-slider-arrow' ),
- name = wrapper.data( 'input' ),
- input = $( 'input[name="' + name + '"]', window.parent.document );
- input.on( 'click', function() {
- if ( ! slider.hasClass( 'fl-field-popup-slider-init' ) ) {
- slider.slider( {
- value: input.val(),
- slide: function( e, ui ) {
- input.val( ui.value ).trigger( 'input' );
- },
- } );
- input.on( 'input', function() {
- slider.slider( 'value', $( this ).val() );
- } );
- slider.addClass( 'fl-field-popup-slider-init' );
- slider.find( '.ui-slider-handle' ).removeAttr( 'tabindex' );
- }
- FLBuilder._setFieldPopupSliderMinMax( slider );
- FLBuilder._hideFieldPopupSliders();
- body.on( 'mousedown', FLBuilder._hideFieldPopupSliders );
- input.addClass( 'fl-field-popup-slider-focus' );
- wrapper.show();
- var tab = $( '.fl-builder-settings:visible .fl-builder-settings-tab.fl-active', window.parent.document ),
- tabOffset = tab.offset(),
- inputOffset = input.offset(),
- inputWidth = input.width(),
- wrapperOffset = wrapper.offset();
- if ( wrapperOffset.top + wrapper.outerHeight() > tabOffset.top + tab.outerHeight() ) {
- wrapper.addClass( 'fl-field-popup-slider-top' );
- }
- arrow.css( 'left', ( 2 + inputOffset.left - wrapperOffset.left + inputWidth / 2 ) + 'px' );
- } );
- input.on( 'focus', function() {
- FLBuilder._hideFieldPopupSliders();
- } );
- },
- /**
- * Hides all single slider controls.
- *
- * @since 2.2
- * @access private
- * @param {Object} e
- * @method _hideFieldPopupSliders
- */
- _hideFieldPopupSliders: function( e ) {
- var target = e ? $( e.target ) : null,
- body = $( 'body', window.parent.document ),
- sliders = $( '.fl-field-popup-slider:visible', window.parent.document ),
- inputs = $( '.fl-field-popup-slider-focus', window.parent.document );
- if ( target ) {
- if ( target.closest( '.fl-field-popup-slider' ).length ) {
- return;
- } else if ( target.closest( '.fl-field-popup-slider-focus' ).length ) {
- return;
- }
- }
- body.off( 'mousedown', FLBuilder._hideFieldPopupSliders );
- inputs.removeClass( 'fl-field-popup-slider-focus' );
- sliders.hide();
- },
- /**
- * Sets the min/max/step config for a popup slider.
- *
- * @since 2.2
- * @access private
- * @method _setFieldPopupSliderMinMax
- * @param {Object} slider
- */
- _setFieldPopupSliderMinMax: function( slider ) {
- var wrapper = slider.parent(),
- parent = wrapper.parent().parent(),
- select = parent.find( 'select.fl-field-unit-select' ),
- unit = select.val(),
- data = wrapper.data( 'slider' ),
- min = 0,
- max = 100,
- step = 1;
- if ( '' === unit || 'em' === unit || 'rem' === unit ) {
- max = 10;
- step = .1;
- }
- if ( 'object' === typeof data ) {
- min = data.min ? parseFloat( data.min ) : min;
- max = data.max ? parseFloat( data.max ) : max;
- step = data.step ? parseFloat( data.step ) : step;
- if ( select.length && data[ unit ] ) {
- min = data[ unit ].min ? parseFloat( data[ unit ].min ) : min;
- max = data[ unit ].max ? parseFloat( data[ unit ].max ) : max;
- step = data[ unit ].step ? parseFloat( data[ unit ].step ) : step;
- }
- }
- slider.slider( {
- min: min,
- max: max,
- step: step,
- } );
- },
- /* Preset Fields
- ---------------------------------------------------- */
- _initPresetFields: function() {
- var form = $( '.fl-builder-settings:visible', window.parent.document ),
- fields = form.find( '.fl-preset-select-controls' );
- fields.each( FLBuilder._initPresetField );
- },
- _initPresetField: function() {
- var field = $( this ),
- select = field.find('select'),
- presetType = field.data('presets'),
- prefix = field.data('prefix');
- select.on( 'change', FLBuilder._setFormPreset.bind( this, presetType, prefix ) );
- },
- _setFormPreset: function( type, prefix, e ) {
- var value = $( e.currentTarget ).val();
- presetLists = FLBuilderConfig.presets,
- presets = presetLists[type],
- form = $( '.fl-builder-settings:visible', window.parent.document );
- if ( 'undefined' !== presets && 'undefined' !== presets[value] ) {
- var settings = presets[value].settings;
- for( var name in settings ) {
- var value = settings[name],
- input;
- if ( 'undefined' !== typeof prefix && '' !== prefix ) {
- // Prefix setting name
- input = form.find('[name="' + prefix + name + '"]');
- } else {
- input = form.find('[name="' + name + '"]');
- }
- input.val(value).trigger('change').trigger('input');
- }
- }
- },
- /* AJAX
- ----------------------------------------------------------*/
- /**
- * Frontend AJAX for the builder interface.
- *
- * @since 1.0
- * @method ajax
- * @param {Object} data The data for the AJAX request.
- * @param {Function} callback A function to call when the request completes.
- */
- ajax: function(data, callback)
- {
- var prop;
- // Queue this request if one is already in progress.
- if ( FLBuilder._ajaxRequest ) {
- FLBuilder._ajaxQueue.push( {
- data: data,
- callback: callback,
- } );
- return;
- }
- FLBuilder.triggerHook('didBeginAJAX', data );
- // Undefined props don't get sent to the server, so make them null.
- for ( prop in data ) {
- if ( 'undefined' == typeof data[ prop ] ) {
- data[ prop ] = null;
- }
- }
- // Add the ajax nonce to the data.
- data._wpnonce = FLBuilderConfig.ajaxNonce;
- // Send the post id to the server.
- data.post_id = FLBuilderConfig.postId;
- // Tell the server that the builder is active.
- data.fl_builder = 1;
- data.safemode = FLBuilderConfig.safemode
- // Append the builder namespace to the action.
- data.fl_action = data.action;
- // Prevent ModSecurity false positives if our fix is enabled.
- if ( 'undefined' != typeof data.settings ) {
- data.settings = FLBuilder._ajaxModSecFix( $.extend( true, {}, data.settings ) );
- data.settings = FLBuilder._inputVarsCheck( data.settings );
- }
- if ( 'undefined' != typeof data.node_settings ) {
- data.node_settings = FLBuilder._ajaxModSecFix( $.extend( true, {}, data.node_settings ) );
- data.node_settings = FLBuilder._inputVarsCheck( data.node_settings );
- }
- if ( 'undefined' != typeof data.node_preview ) {
- data.node_preview = FLBuilder._ajaxModSecFix( $.extend( true, {}, data.node_preview ) );
- data.node_preview = FLBuilder._inputVarsCheck( data.node_preview );
- }
- if ( 'error' === data.settings || 'error' === data.node_settings || 'error' === data.node_preview ) {
- return 0;
- }
- // Store the data in a single variable to avoid conflicts.
- data = { fl_builder_data: data };
- // Do the ajax call.
- FLBuilder._ajaxRequest = $.post(FLBuilder._ajaxUrl(), data, function(response) {
- if(typeof callback !== 'undefined') {
- callback.call(this, response);
- }
- FLBuilder.triggerHook('didCompleteAJAX', data );
- })
- .always( FLBuilder._ajaxComplete )
- .fail( function( xhr, status, error ){
- msg = false;
- switch(xhr.status) {
- case 403:
- case 409:
- case 418: // Dreamhost trying to be funny
- msg = 'Something you entered has triggered a ' + xhr.status + ' error.<br /><br />This is nearly always due to mod_security settings from your hosting provider.'
- if ( ! window.crash_vars.white_label ) {
- msg += '<br /><br />See this <a target="_blank" style="color: #428bca;font-size:inherit" href="https://docs.wpbeaverbuilder.com/beaver-builder/troubleshooting/common-issues/403-forbidden-or-blocked-error">Knowledge Base</a> article for more info.</br />'
- }
- break;
- }
- if ( msg ) {
- console.log(xhr)
- console.log(error)
- FLBuilder.alert(msg)
- }
- })
- return FLBuilder._ajaxRequest;
- },
- _inputVarsCheck: function( o ) {
- let maxInput = FLBuilderConfig.MaxInputVars || 0;
- let debug = FLBuilderConfig.debug || false;
- let data = JSON.stringify(o).match(/[^\\]":/g) || {};
- let count = data.length;
- if ( 'undefined' != typeof count ) {
- if ( debug ) {
- console.log('Debug: Input Vars ' + count + '/' + maxInput );
- }
- if ( count > maxInput ) {
- FLBuilder.alert( '<h1 style="font-size:2em;text-align:center">Critical Issue</h1><br />The number of settings being saved (' + count + ') exceeds the PHP Max Input Vars setting (' + maxInput + ').<br />Please contact your host to have this value increased, the default is 1000.' );
- console.log( 'Vars Count: ' + count );
- console.log( 'Max Input: ' + maxInput );
- return 'error';
- }
- }
- return o;
- },
- /**
- * Callback for when an AJAX request is complete.
- *
- * @since 1.0
- * @access private
- * @method _ajaxComplete
- */
- _ajaxComplete: function()
- {
- FLBuilder._ajaxRequest = null;
- FLBuilder.hideAjaxLoader();
- if ( FLBuilder._ajaxQueue.length ) {
- var item = FLBuilder._ajaxQueue.shift();
- FLBuilder.ajax( item.data, item.callback );
- }
- },
- /**
- * Returns a URL for an AJAX request.
- *
- * @since 1.0
- * @access private
- * @method _ajaxUrl
- * @param {Object} params An object with key/value pairs for the AJAX query string.
- * @return {String} The AJAX URL.
- */
- _ajaxUrl: function(params)
- {
- var config = FLBuilderConfig,
- url = config.shortlink,
- param = null;
- if(typeof params !== 'undefined') {
- for(param in params) {
- url += url.indexOf('?') > -1 ? '&' : '?';
- url += param + '=' + params[param];
- }
- }
- return url;
- },
- /**
- * Shows the AJAX loading overlay.
- *
- * @since 1.0
- * @method showAjaxLoader
- */
- showAjaxLoader: function()
- {
- if( 0 === $( '.fl-builder-lightbox-loading', window.parent.document ).length ) {
- $( '.fl-builder-loading', window.parent.document ).show();
- }
- },
- /**
- * Hides the AJAX loading overlay.
- *
- * @since 1.0
- * @method hideAjaxLoader
- */
- hideAjaxLoader: function()
- {
- $( '.fl-builder-loading', window.parent.document ).hide();
- },
- /**
- * Fades a node when it is being loaded.
- *
- * @since 1.10
- * @access private
- * @param {String} nodeId
- * @method _showNodeLoading
- */
- _showNodeLoading: function( nodeId )
- {
- var node = $( '.fl-node-' + nodeId );
- node.addClass( 'fl-builder-node-loading' );
- FLBuilder._removeAllOverlays();
- FLBuilder.triggerHook( 'didStartNodeLoading', node );
- },
- /**
- * Brings a node back to 100% opacity when it's done loading.
- *
- * @since 2.0
- * @access private
- * @param {String} nodeId
- * @method _hideNodeLoading
- */
- _hideNodeLoading: function( nodeId )
- {
- var node = $( '.fl-node-' + nodeId );
- node.removeClass( 'fl-builder-node-loading' );
- },
- /**
- * Inserts a placeholder in place of where a node will be
- * that is currently loading.
- *
- * @since 1.10
- * @access private
- * @param {Object} parent
- * @param {Number} position
- * @method _showNodeLoadingPlaceholder
- */
- _showNodeLoadingPlaceholder: function( parent, position )
- {
- var placeholder = $( '<div class="fl-builder-node-loading-placeholder"></div>' );
- // Make sure we only have one placeholder at a time.
- $( '.fl-builder-node-loading-placeholder' ).remove();
- // Get sibling rows.
- if ( parent.hasClass( 'fl-builder-content' ) ) {
- siblings = parent.find( ' > .fl-row' );
- }
- // Get sibling column groups.
- else if ( parent.hasClass( 'fl-row-content' ) ) {
- siblings = parent.find( ' > .fl-col-group, > .fl-module' );
- }
- // Get sibling columns.
- else if ( parent.hasClass( 'fl-col-group' ) ) {
- parent.addClass( 'fl-col-group-has-child-loading' );
- siblings = parent.find( ' > .fl-col' );
- }
- // Get sibling modules.
- else {
- siblings = parent.find( ' > .fl-col-group, > .fl-module' );
- }
- // Add the placeholder.
- if ( 0 === siblings.length || siblings.length == position) {
- parent.append( placeholder );
- }
- else {
- siblings.eq( position ).before( placeholder );
- }
- },
- /**
- * Removes the node loading placeholder for a node.
- *
- * @since 1.10
- * @access private
- * @param {Object} node
- * @method _removeNodeLoadingPlaceholder
- */
- _removeNodeLoadingPlaceholder: function( node )
- {
- var prev = node.prev( '.fl-builder-node-loading-placeholder' ),
- next = node.next( '.fl-builder-node-loading-placeholder' );
- if ( prev.length ) {
- prev.remove();
- } else {
- next.remove();
- }
- },
- /**
- * Base64 encode settings to prevent ModSecurity false
- * positives if our fix is enabled.
- *
- * @since 1.8.4
- * @access private
- * @method _ajaxModSecFix
- */
- _ajaxModSecFix: function( settings )
- {
- var prop;
- if ( FLBuilderConfig.modSecFix && 'undefined' != typeof btoa ) {
- if ( 'string' == typeof settings ) {
- settings = FLBuilder._btoa( settings );
- }
- else {
- for ( prop in settings ) {
- type = typeof settings[ prop ]
- if ( 'string' == type || 'number' == type ) {
- settings[ prop ] = FLBuilder._btoa( settings[ prop ] );
- }
- else if( 'object' == type ) {
- settings[ prop ] = FLBuilder._ajaxModSecFix( settings[ prop ] );
- }
- }
- }
- }
- return settings;
- },
- /**
- * Helper function for _ajaxModSecFix
- * btoa() does not handle utf8/16 characters
- * See: https://stackoverflow.com/questions/30106476/using-javascripts-atob-to-decode-base64-doesnt-properly-decode-utf-8-strings
- *
- * @since 1.10.7
- * @access private
- * @method _btoa
- */
- _btoa: function(str) {
- return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
- return String.fromCharCode('0x' + p1);
- }));
- },
- /**
- * @since 1.10.8
- * @access private
- * @method _wpmedia_reset_errors
- */
- _wpmedia_reset_errors: function() {
- $( '.upload-error', window.parent.document ).remove()
- $( '.media-uploader-status', window.parent.document ).removeClass( 'errors' ).hide()
- },
- /* Lightboxes
- ----------------------------------------------------------*/
- /**
- * Initializes the lightboxes for the builder interface.
- *
- * @since 1.0
- * @access private
- * @method _initLightboxes
- */
- _initLightboxes: function()
- {
- /* Main builder lightbox */
- FLBuilder._lightbox = new FLLightbox({
- className: 'fl-builder-lightbox fl-builder-settings-lightbox',
- resizable: true
- });
- FLBuilder._lightbox.on('resized', FLBuilder._calculateSettingsTabsOverflow);
- FLBuilder._lightbox.on('close', FLBuilder._lightboxClosed);
- FLBuilder._lightbox.on('beforeCloseLightbox', FLBuilder._destroyEditorFields);
- /* Actions lightbox */
- FLBuilder._actionsLightbox = new FLLightbox({
- className: 'fl-builder-actions-lightbox'
- });
- },
- /**
- * Shows the settings lightbox.
- *
- * @since 1.0
- * @access private
- * @method _showLightbox
- */
- _showLightbox: function( content )
- {
- if ( ! content ) {
- content = '<div class="fl-builder-lightbox-loading"></div>';
- }
- FLBuilder._lightbox.open( content );
- FLBuilder._initLightboxScrollbars();
- },
- /**
- * Set the content for the settings lightbox.
- *
- * @since 1.0
- * @access private
- * @method _setLightboxContent
- * @param {String} content The HTML content for the lightbox.
- */
- _setLightboxContent: function(content)
- {
- FLBuilder._lightbox.setContent(content);
- },
- /**
- * Initializes the scrollbars for the settings lightbox.
- *
- * @since 1.0
- * @access private
- * @method _initLightboxScrollbars
- */
- _initLightboxScrollbars: function()
- {
- FLBuilder._initScrollbars();
- clearTimeout( FLBuilder._lightboxScrollbarTimeout );
- FLBuilder._lightboxScrollbarTimeout = setTimeout(FLBuilder._initLightboxScrollbars, 500);
- },
- /**
- * Callback to clean things up when the settings lightbox
- * is closed.
- *
- * @since 1.0
- * @access private
- * @method _lightboxClosed
- */
- _lightboxClosed: function()
- {
- FL.Builder.data.getOutlinePanelActions().setActiveNode( false );
- FLBuilder.triggerHook( 'settings-lightbox-closed' );
- FLBuilder._lightbox.empty();
- clearTimeout( FLBuilder._lightboxScrollbarTimeout );
- FLBuilder._lightboxScrollbarTimeout = null;
- },
- /**
- * Shows the actions lightbox.
- *
- * @since 1.0
- * @access private
- * @method _showActionsLightbox
- * @param {Object} settings An object with settings for the lightbox buttons.
- */
- _showActionsLightbox: function(settings)
- {
- var template = wp.template( 'fl-actions-lightbox' );
- // Allow extensions to modify the settings object.
- FLBuilder.triggerHook( 'actions-lightbox-settings', settings );
- // Open the lightbox.
- FLBuilder._actionsLightbox.open( template( settings ) );
- },
- /* Alert Lightboxes
- ----------------------------------------------------------*/
- _checkEnv: function() {
- if ( 'svg' === FLBuilderConfig.fontAwesome ) {
- FLBuilder.alert( FLBuilderStrings.fontAwesome)
- }
- },
- /**
- * Shows the alert lightbox with a message.
- *
- * @since 1.0
- * @method alert
- * @param {String} message The message to show.
- */
- alert: function(message)
- {
- var alert = new FLLightbox({
- className: 'fl-builder-alert-lightbox',
- destroyOnClose: true
- }),
- template = wp.template( 'fl-alert-lightbox' );
- alert.open( template( { message : message } ) );
- },
- crashMessage: function(debug)
- {
- FLLightbox.closeAll();
- var alert = new FLLightbox({
- className: 'fl-builder-alert-lightbox fl-builder-crash-lightbox',
- destroyOnClose: true
- }),
- template = wp.template( 'fl-crash-lightbox' ),
- product = window.crash_vars.product,
- labeled = window.crash_vars.white_label,
- label_txt = window.crash_vars.labeled_txt,
- info = '';
- message = product + ' ' + window.crash_vars.strings.intro;
- info += "<h3 style='font-size:26px;line-height:26px;'>" + window.crash_vars.strings.try + "</h3>";
- info += "<p>" + window.crash_vars.strings.troubleshoot + "</p>";
- info += "<h3 style='font-size:22px;line-height:22px;padding-top:20px;border-top:1px solid black;'>" + window.crash_vars.strings.hand + "</h3>";
- info += "<h3 style='font-size:18px;line-height:18px;'>" + window.crash_vars.strings.step_one + "</h3>";
- info += "<p>" + window.crash_vars.strings.if_contact + "</p>";
- info +="<div><div style='width:49%;float:left;'>"
- info +="<p>MacOS Users:<br />Chrome: View > Developer > JavaScript Console<br />Firefox: Tools > Web Developer > Browser Console<br />Safari: Develop > Show JavaScript console</p>"
- info +="</div>"
- info +="<div style='width:49%;float:right;'>"
- info +="<p>Windows Users:<br />Chrome: Settings > More Tools > Developer > Console<br />Firefox: Menu/Settings > Web Developer > Web Console<br />Edge: Settings and More > More Tools > Console</p>"
- info +="</div></div>"
- info += "<h3 style='font-size:18px;line-height:18px;display:inline-block'>" + window.crash_vars.strings.step_two + "</h3>";
- info +="<p style='display:inline-block;'>" + window.crash_vars.strings.contact + "</p>"
- if ( FLBuilderConfig.MaxInputVars <= 3000 ) {
- info += '<br /><br />The PHP config value max_input_vars is only set to ' + FLBuilderConfig.MaxInputVars + '. If you are using 3rd party addons this could very likely be the cause of this error. [<a class="link" href="https://docs.wpbeaverbuilder.com/beaver-builder/troubleshooting/common-issues/exceeds-php-max-input-vars">doc link</a>].'
- }
- debug = false
- if ( labeled ) {
- info = label_txt
- }
- alert.open( template( { message : message, info: info, debug: debug } ) );
- },
- /**
- * Closes the alert lightbox when a child element is clicked.
- *
- * @since 1.0
- * @access private
- * @method _alertClose
- */
- _alertClose: function()
- {
- FLLightbox.closeParent(this);
- },
- /**
- * Shows the confirm lightbox with a message.
- *
- * @since 1.10
- * @method confirm
- * @param {Object} o The config object that overrides the defaults.
- */
- confirm: function( o )
- {
- var defaults = {
- cssClass: '',
- message : '',
- ok : function(){},
- cancel : function(){},
- strings : {
- 'ok' : FLBuilderStrings.ok,
- 'cancel' : FLBuilderStrings.cancel
- }
- },
- config = $.extend( {}, defaults, ( 'undefined' == typeof o ? {} : o ) )
- lightbox = new FLLightbox({
- className: 'fl-builder-confirm-lightbox fl-builder-alert-lightbox' + config.cssClass,
- destroyOnClose: true
- }),
- template = wp.template( 'fl-confirm-lightbox' );
- lightbox.open( template( config ) );
- lightbox._node.find( '.fl-builder-confirm-ok' ).on( 'click', config.ok );
- lightbox._node.find( '.fl-builder-confirm-cancel' ).on( 'click', config.cancel );
- },
- /* Simple JS hooks similar to WordPress PHP hooks.
- ----------------------------------------------------------*/
- /**
- * Trigger a hook.
- *
- * @since 1.8
- * @method triggerHook
- * @param {String} hook The hook to trigger.
- * @param {Array} args An array of args to pass to the hook.
- */
- triggerHook: function( hook, args )
- {
- $( 'body' ).trigger( 'fl-builder.' + hook, args );
- },
- /**
- * Add a hook.
- *
- * @since 1.8
- * @method addHook
- * @param {String} hook The hook to add.
- * @param {Function} callback A function to call when the hook is triggered.
- */
- addHook: function( hook, callback )
- {
- $( 'body' ).on( 'fl-builder.' + hook, callback );
- },
- /**
- * Remove a hook.
- *
- * @since 1.8
- * @method removeHook
- * @param {String} hook The hook to remove.
- * @param {Function} callback The callback function to remove.
- */
- removeHook: function( hook, callback )
- {
- $( 'body' ).off( 'fl-builder.' + hook, callback );
- },
- /* Console Logging
- ----------------------------------------------------------*/
- /**
- * Logs a message in the console if the console is available.
- *
- * @since 1.4.6
- * @method log
- * @param {String} message The message to log.
- */
- log: function( message )
- {
- if ( 'undefined' == typeof window.console || 'undefined' == typeof window.console.log ) {
- return;
- }
- console.log( message );
- },
- /**
- * Logs an error in the console if the console is available.
- *
- * @since 1.4.6
- * @method logError
- * @param {String} error The error to log.
- */
- logError: function( error, data )
- {
- var message = null;
- if ( 'undefined' == typeof error ) {
- return;
- }
- else if ( 'undefined' != typeof error.stack ) {
- message = error.stack;
- }
- else if ( 'undefined' != typeof error.message ) {
- message = error.message;
- }
- if ( message ) {
- FLBuilder.log( '************************************************************************' );
- FLBuilder.log( FLBuilderStrings.errorMessage );
- FLBuilder.log( message );
- if ( 'undefined' != typeof data && data ) {
- FLBuilder.log( "Debug Info" );
- console.log( data );
- }
- // Show debug data in console.
- $.each( window.crash_vars.vars, function(i,t) {
- console.log(i + ': ' + t)
- })
- FLBuilder.log( '************************************************************************' );
- if ( 'undefined' != typeof data && data ) {
- message = data + "\n" + message
- }
- FLBuilder.crashMessage(message)
- }
- },
- /**
- * Logs a global error in the console if the console is available.
- *
- * @since 1.4.6
- * @method logGlobalError
- * @param {String} message
- * @param {String} file
- * @param {String} line
- * @param {String} col
- * @param {String} error
- */
- logGlobalError: function( message, file, line, col, error )
- {
- FLBuilder.log( '************************************************************************' );
- FLBuilder.log( FLBuilderStrings.errorMessage );
- FLBuilder.log( FLBuilderStrings.globalErrorMessage.replace( '{message}', message ).replace( '{line}', line ).replace( '{file}', file ) );
- if ( 'undefined' != typeof error && 'undefined' != typeof error.stack ) {
- FLBuilder.log( error.stack );
- }
- FLBuilder.log( '************************************************************************' );
- },
- /**
- * Parse JSON with try/catch and print useful debug info on error.
- * @since 2.2.2
- * @param {string} data JSON data
- */
- _jsonParse: function( data ) {
- try {
- data = JSON.parse( data );
- } catch (e) {
- FLBuilder.logError( e, FLBuilder._parseError( data ) );
- }
- return data;
- },
- /**
- * Parse data for php error on 1st line.
- * @since 2.2.2
- * @param {string} data the JSON containing error(s)
- */
- _parseError: function( data ) {
- if( data.indexOf('</head>') ) {
- return 'AJAX returned HTML page instead of data. (Possible 404 or max_input_vars)';
- }
- php = data.match(/^<.*/gm) || false;
- if ( php && php.length > 0 ) {
- var txt = '';
- $.each( php, function(i,t) {
- txt += t
- })
- return $(txt).text();
- }
- return false;
- },
- /**
- * Helper taken from lodash
- * @since 2.2.2
- */
- isUndefined: function(obj) {
- return obj === void 0;
- },
- /**
- * Helper taken from lodash
- * @since 2.2.2
- */
- isBoolean: function(value) {
- return value === true || value === false
- },
- /**
- * Check if the operating system is set to dark mode.
- *
- * @since 2.6
- * @return bool
- */
- _isSystemColorSchemeDark: function() {
- return window.matchMedia && window.matchMedia( '( prefers-color-scheme: dark )' ).matches
- },
- /**
- * Get the static color scheme value even if matching the operating system.
- *
- * @since 2.6
- * @return string (light|dark)
- */
- _getComputedColorScheme: function() {
- const colorScheme = FL.Builder.data.getSystemState().colorScheme
- if ( 'auto' === colorScheme ) {
- return FLBuilder._isSystemColorSchemeDark() ? 'dark' : 'light'
- }
- return colorScheme
- },
- /**
- * Setup color scheme handling
- *
- * @since 2.6
- * @param {string} key
- * @param {any} data
- * @return void
- */
- _initColorScheme: function() {
- FLBuilder._setColorSchemeBodyClasses( FLBuilder._getComputedColorScheme() )
- // Listen for system color scheme changes
- window.matchMedia( '(prefers-color-scheme: dark)' )
- .addEventListener( 'change', FLBuilder._systemColorSchemeChanged )
- },
- /**
- * Listen for changes to the operating system color scheme value.
- *
- * @since 2.6
- * @param MediaQueryListEvent e
- * @param {any} data
- * @return void
- */
- _systemColorSchemeChanged: function( e ) {
- const colorScheme = FL.Builder.data.getSystemState().colorScheme
- if ( 'auto' !== colorScheme ) {
- return
- }
- if ( e.matches ) {
- FLBuilder._setColorSchemeBodyClasses( 'dark' )
- } else {
- FLBuilder._setColorSchemeBodyClasses( 'light' )
- }
- },
- /**
- * Add/Remove appropriate color scheme body classes.
- *
- * @since 2.6
- * @param String name (light|dark|auto)
- * @return void
- */
- _setColorSchemeBodyClasses: function( name ) {
- const parentClasses = window.parent.document.body.classList
- const childClasses = document.body.classList
- let add = name
- // Handle 'auto' value
- if ( 'auto' === name ) {
- add = FLBuilder._getComputedColorScheme()
- }
- const remove = 'dark' === add ? 'light' : 'dark'
- parentClasses.remove( `fl-builder-ui-skin--${remove}`, `fluid-color-scheme-${remove}` );
- childClasses.remove( `fl-builder-ui-skin--${remove}`, `fluid-color-scheme-${remove}` );
- parentClasses.add( `fl-builder-ui-skin--${add}`, `fluid-color-scheme-${add}` );
- childClasses.add( `fl-builder-ui-skin--${add}`, `fluid-color-scheme-${add}` );
- },
- /**
- * Get sandbox data.
- * @since 2.6
- * @param {string} data the JSON containing error(s)
- */
- getSandbox: function (key) {
- if (key in this._sandbox) {
- return this._sandbox[key];
- }
- return false;
- },
- /**
- * Set sandbox data.
- * @since 2.6
- * @param {string} key
- * @param {any} data
- * @return {void}
- */
- setSandbox: function (key, data) {
- this._sandbox[key] = data;
- },
- /**
- * Delete sandbox data.
- * @since 2.6
- * @param {string} key
- * @param {any} data
- * @return {void}
- */
- deleteSandbox: function (key) {
- delete this._sandbox[key];
- },
- };
- /* Start the party!!! */
- $(function(){
- FLBuilder._init();
- });
- })(jQuery);
|